##// END OF EJS Templates
Merge pull request 382 from SciQLop-fork develop...
trabillard -
r1108:0b3fbaa8bda5 merge
parent child
Show More
@@ -0,0 +1,27
1 #ifndef SCIQLOP_ACTIONSGUICONTROLLER_H
2 #define SCIQLOP_ACTIONSGUICONTROLLER_H
3
4 #include <Actions/SelectionZoneAction.h>
5 #include <Common/spimpl.h>
6
7 #include <memory>
8
9 class ActionsGuiController {
10 public:
11 ActionsGuiController();
12
13 std::shared_ptr<SelectionZoneAction>
14 addSectionZoneAction(const QString &name, SelectionZoneAction::ExecuteFunction function);
15
16 std::shared_ptr<SelectionZoneAction>
17 addSectionZoneAction(const QStringList &subMenuList, const QString &name,
18 SelectionZoneAction::ExecuteFunction function);
19
20 QVector<std::shared_ptr<SelectionZoneAction> > selectionZoneActions() const;
21
22 private:
23 class ActionsGuiControllerPrivate;
24 spimpl::unique_impl_ptr<ActionsGuiControllerPrivate> impl;
25 };
26
27 #endif // SCIQLOP_ACTIONSGUICONTROLLER_H
@@ -0,0 +1,75
1 #ifndef SCIQLOP_SELECTIONZONEACTION_H
2 #define SCIQLOP_SELECTIONZONEACTION_H
3
4 #include <Common/spimpl.h>
5
6 #include <QLoggingCategory>
7 #include <QObject>
8
9 #include <functional>
10
11 class VisualizationSelectionZoneItem;
12
13 Q_DECLARE_LOGGING_CATEGORY(LOG_SelectionZoneAction)
14
15 /**
16 * @brief The SelectionZoneAction class represents an action on a selection zone in the
17 * visualization.
18 *
19 * The action is a function that will be executed when the slot execute() is called.
20 */
21 class SelectionZoneAction : public QObject {
22
23 Q_OBJECT
24
25 public:
26 /// Signature of the function associated to the action
27 using ExecuteFunction
28 = std::function<void(const QVector<VisualizationSelectionZoneItem *> &item)>;
29
30 using EnableFunction
31 = std::function<bool(const QVector<VisualizationSelectionZoneItem *> &item)>;
32
33 /**
34 * @param name the name of the action, displayed to the user
35 * @param fun the function that will be called when the action is executed
36 * @sa execute()
37 */
38 explicit SelectionZoneAction(const QString &name, ExecuteFunction fun);
39
40 /**
41 * @param name the name of the action, displayed to the user
42 * @param subMenusList the list of sub menus where the action should be inserted
43 * @param fun the function that will be called when the action is executed
44 * @sa execute()
45 */
46 explicit SelectionZoneAction(const QStringList &subMenuList, const QString &name,
47 ExecuteFunction fun);
48
49 /// Sets the function which determine if the action should be enabled or disabled
50 void setEnableFunction(EnableFunction fun);
51
52 /// Sets the shortcut displayed by the action.
53 /// Note: The shortcut is only displayed and not active because it is not permanently stored
54 void setDisplayedShortcut(const QKeySequence &shortcut);
55 QKeySequence displayedShortcut() const;
56
57 /// The name of the action
58 QString name() const noexcept;
59
60 /// The path in the sub menus, if any
61 QStringList subMenuList() const noexcept;
62
63 public slots:
64 /// Executes the action
65 void execute(const QVector<VisualizationSelectionZoneItem *> &item);
66
67 /// Returns true if the action is enabled
68 bool isEnabled(const QVector<VisualizationSelectionZoneItem *> &item);
69
70 private:
71 class SelectionZoneActionPrivate;
72 spimpl::unique_impl_ptr<SelectionZoneActionPrivate> impl;
73 };
74
75 #endif // SCIQLOP_SELECTIONZONEACTION_H
@@ -0,0 +1,12
1 #ifndef SCIQLOP_VISUALIZATIONACTIONMANAGER_H
2 #define SCIQLOP_VISUALIZATIONACTIONMANAGER_H
3
4
5 class VisualizationActionManager {
6 public:
7 VisualizationActionManager();
8
9 void installSelectionZoneActions();
10 };
11
12 #endif // SCIQLOP_VISUALIZATIONACTIONMANAGER_H
@@ -0,0 +1,30
1 #ifndef SCIQLOP_VISUALIZATIONMULTIZONESELECTIONDIALOG_H
2 #define SCIQLOP_VISUALIZATIONMULTIZONESELECTIONDIALOG_H
3
4 #include <Common/spimpl.h>
5 #include <QDialog>
6
7 namespace Ui {
8 class VisualizationMultiZoneSelectionDialog;
9 }
10
11 class VisualizationSelectionZoneItem;
12
13 class VisualizationMultiZoneSelectionDialog : public QDialog {
14 Q_OBJECT
15
16 public:
17 explicit VisualizationMultiZoneSelectionDialog(QWidget *parent = 0);
18 ~VisualizationMultiZoneSelectionDialog();
19
20 void setZones(const QVector<VisualizationSelectionZoneItem *> &zones);
21 QMap<VisualizationSelectionZoneItem *, bool> selectedZones() const;
22
23 private:
24 Ui::VisualizationMultiZoneSelectionDialog *ui;
25
26 class VisualizationMultiZoneSelectionDialogPrivate;
27 spimpl::unique_impl_ptr<VisualizationMultiZoneSelectionDialogPrivate> impl;
28 };
29
30 #endif // SCIQLOP_VISUALIZATIONMULTIZONESELECTIONDIALOG_H
@@ -0,0 +1,36
1 #include "Actions/ActionsGuiController.h"
2
3 struct ActionsGuiController::ActionsGuiControllerPrivate {
4
5 QVector<std::shared_ptr<SelectionZoneAction> > m_SelectionZoneActions;
6 };
7
8 ActionsGuiController::ActionsGuiController()
9 : impl{spimpl::make_unique_impl<ActionsGuiControllerPrivate>()}
10 {
11 }
12
13 std::shared_ptr<SelectionZoneAction>
14 ActionsGuiController::addSectionZoneAction(const QString &name,
15 SelectionZoneAction::ExecuteFunction function)
16 {
17 auto action = std::make_shared<SelectionZoneAction>(name, function);
18 impl->m_SelectionZoneActions.push_back(action);
19
20 return action;
21 }
22
23 std::shared_ptr<SelectionZoneAction>
24 ActionsGuiController::addSectionZoneAction(const QStringList &subMenuList, const QString &name,
25 SelectionZoneAction::ExecuteFunction function)
26 {
27 auto action = std::make_shared<SelectionZoneAction>(subMenuList, name, function);
28 impl->m_SelectionZoneActions.push_back(action);
29
30 return action;
31 }
32
33 QVector<std::shared_ptr<SelectionZoneAction> > ActionsGuiController::selectionZoneActions() const
34 {
35 return impl->m_SelectionZoneActions;
36 }
@@ -0,0 +1,66
1 #include <Actions/SelectionZoneAction.h>
2 #include <Visualization/VisualizationSelectionZoneItem.h>
3
4 Q_LOGGING_CATEGORY(LOG_SelectionZoneAction, "SelectionZoneAction")
5
6 struct SelectionZoneAction::SelectionZoneActionPrivate {
7 explicit SelectionZoneActionPrivate(const QString &name, const QStringList &subMenuList,
8 SelectionZoneAction::ExecuteFunction fun)
9 : m_Name{name}, m_SubMenuList{subMenuList}, m_Fun{std::move(fun)}
10 {
11 }
12
13 QString m_Name;
14 QStringList m_SubMenuList;
15 QKeySequence m_DisplayedShortcut;
16 SelectionZoneAction::ExecuteFunction m_Fun;
17 SelectionZoneAction::EnableFunction m_EnableFun = [](auto zones) { return true; };
18 };
19
20 SelectionZoneAction::SelectionZoneAction(const QString &name, ExecuteFunction fun)
21 : impl{spimpl::make_unique_impl<SelectionZoneActionPrivate>(name, QStringList{},
22 std::move(fun))}
23 {
24 }
25
26 SelectionZoneAction::SelectionZoneAction(const QStringList &subMenuList, const QString &name,
27 SelectionZoneAction::ExecuteFunction fun)
28 : impl{spimpl::make_unique_impl<SelectionZoneActionPrivate>(name, subMenuList,
29 std::move(fun))}
30 {
31 }
32
33 void SelectionZoneAction::setEnableFunction(EnableFunction fun)
34 {
35 impl->m_EnableFun = std::move(fun);
36 }
37
38 void SelectionZoneAction::setDisplayedShortcut(const QKeySequence &shortcut)
39 {
40 impl->m_DisplayedShortcut = shortcut;
41 }
42
43 QKeySequence SelectionZoneAction::displayedShortcut() const
44 {
45 return impl->m_DisplayedShortcut;
46 }
47
48 QString SelectionZoneAction::name() const noexcept
49 {
50 return impl->m_Name;
51 }
52
53 QStringList SelectionZoneAction::subMenuList() const noexcept
54 {
55 return impl->m_SubMenuList;
56 }
57
58 void SelectionZoneAction::execute(const QVector<VisualizationSelectionZoneItem *> &item)
59 {
60 impl->m_Fun(item);
61 }
62
63 bool SelectionZoneAction::isEnabled(const QVector<VisualizationSelectionZoneItem *> &item)
64 {
65 return impl->m_EnableFun(item);
66 }
@@ -0,0 +1,109
1 #include "Visualization/VisualizationActionManager.h"
2 #include "Visualization/VisualizationGraphWidget.h"
3 #include "Visualization/VisualizationSelectionZoneItem.h"
4
5 #include <Actions/ActionsGuiController.h>
6 #include <SqpApplication.h>
7
8 VisualizationActionManager::VisualizationActionManager() {}
9
10 void VisualizationActionManager::installSelectionZoneActions()
11 {
12 auto &actionController = sqpApp->actionsGuiController();
13
14 auto removeZonesAction
15 = actionController.addSectionZoneAction("Remove Selected Zone(s)", [](auto zones) {
16 for (auto selectionZone : zones) {
17 if (auto graph = selectionZone->parentGraphWidget()) {
18 graph->removeSelectionZone(selectionZone);
19 }
20 }
21 });
22 removeZonesAction->setDisplayedShortcut(QKeySequence::Delete);
23
24 auto alignEnableFuntion = [](auto items) { return items.count() > 1; };
25
26 // Vertical alignment actions
27 auto alignLeftAction = actionController.addSectionZoneAction(
28 QStringList{"Align Vertically"}, "Left", [](auto zones) {
29 Q_ASSERT(zones.count() > 1);
30 auto ref = zones.takeFirst();
31 ref->alignZonesVerticallyOnLeft(zones, false);
32 });
33 alignLeftAction->setEnableFunction(alignEnableFuntion);
34
35 auto alignLeftBorderAction = actionController.addSectionZoneAction(
36 QStringList{"Align Vertically"}, "Left Borders", [](auto zones) {
37 Q_ASSERT(zones.count() > 1);
38 auto ref = zones.takeFirst();
39 ref->alignZonesVerticallyOnLeft(zones, true);
40 });
41 alignLeftBorderAction->setEnableFunction(alignEnableFuntion);
42
43 auto alignRightAction = actionController.addSectionZoneAction(
44 QStringList{"Align Vertically"}, "Right", [](auto zones) {
45 Q_ASSERT(zones.count() > 1);
46 auto ref = zones.takeFirst();
47 ref->alignZonesVerticallyOnRight(zones, false);
48 });
49 alignRightAction->setEnableFunction(alignEnableFuntion);
50
51 auto alignRightBorderAction = actionController.addSectionZoneAction(
52 QStringList{"Align Vertically"}, "Right Borders", [](auto zones) {
53 Q_ASSERT(zones.count() > 1);
54 auto ref = zones.takeFirst();
55 ref->alignZonesVerticallyOnRight(zones, true);
56 });
57 alignRightBorderAction->setEnableFunction(alignEnableFuntion);
58
59 auto alignLeftAndRightAction = actionController.addSectionZoneAction(
60 QStringList{"Align Vertically"}, "Left and Right", [](auto zones) {
61 Q_ASSERT(zones.count() > 1);
62 auto ref = zones.takeFirst();
63 ref->alignZonesVerticallyOnLeft(zones, false);
64 ref->alignZonesVerticallyOnRight(zones, true);
65 });
66 alignLeftAndRightAction->setEnableFunction(alignEnableFuntion);
67
68 // Temporal alignment actions
69 auto alignLeftTemporallyAction = actionController.addSectionZoneAction(
70 QStringList{"Align Temporally"}, "Left", [](auto zones) {
71 Q_ASSERT(zones.count() > 1);
72 auto ref = zones.takeFirst();
73 ref->alignZonesTemporallyOnLeft(zones, false);
74 });
75 alignLeftTemporallyAction->setEnableFunction(alignEnableFuntion);
76
77 auto alignLeftBorderTemporallyAction = actionController.addSectionZoneAction(
78 QStringList{"Align Temporally"}, "Left Borders", [](auto zones) {
79 Q_ASSERT(zones.count() > 1);
80 auto ref = zones.takeFirst();
81 ref->alignZonesTemporallyOnLeft(zones, true);
82 });
83 alignLeftBorderTemporallyAction->setEnableFunction(alignEnableFuntion);
84
85 auto alignRightTemporallyAction = actionController.addSectionZoneAction(
86 QStringList{"Align Temporally"}, "Right", [](auto zones) {
87 Q_ASSERT(zones.count() > 1);
88 auto ref = zones.takeFirst();
89 ref->alignZonesTemporallyOnRight(zones, false);
90 });
91 alignRightTemporallyAction->setEnableFunction(alignEnableFuntion);
92
93 auto alignRightBorderTemporallyAction = actionController.addSectionZoneAction(
94 QStringList{"Align Temporally"}, "Right Borders", [](auto zones) {
95 Q_ASSERT(zones.count() > 1);
96 auto ref = zones.takeFirst();
97 ref->alignZonesTemporallyOnRight(zones, true);
98 });
99 alignRightBorderTemporallyAction->setEnableFunction(alignEnableFuntion);
100
101 auto alignLeftAndRightTemporallyAction = actionController.addSectionZoneAction(
102 QStringList{"Align Temporally"}, "Left and Right", [](auto zones) {
103 Q_ASSERT(zones.count() > 1);
104 auto ref = zones.takeFirst();
105 ref->alignZonesTemporallyOnLeft(zones, false);
106 ref->alignZonesTemporallyOnRight(zones, true);
107 });
108 alignLeftAndRightTemporallyAction->setEnableFunction(alignEnableFuntion);
109 }
@@ -0,0 +1,69
1 #include "Visualization/VisualizationMultiZoneSelectionDialog.h"
2 #include "ui_VisualizationMultiZoneSelectionDialog.h"
3
4 #include "Common/DateUtils.h"
5 #include "Visualization/VisualizationSelectionZoneItem.h"
6
7 const auto DATETIME_FORMAT = QStringLiteral("yyyy/MM/dd hh:mm:ss");
8
9 struct VisualizationMultiZoneSelectionDialog::VisualizationMultiZoneSelectionDialogPrivate {
10 QVector<VisualizationSelectionZoneItem *> m_Zones;
11 };
12
13 VisualizationMultiZoneSelectionDialog::VisualizationMultiZoneSelectionDialog(QWidget *parent)
14 : QDialog(parent, Qt::Tool),
15 ui(new Ui::VisualizationMultiZoneSelectionDialog),
16 impl{spimpl::make_unique_impl<VisualizationMultiZoneSelectionDialogPrivate>()}
17 {
18 ui->setupUi(this);
19
20 connect(ui->buttonBox, &QDialogButtonBox::accepted, this,
21 &VisualizationMultiZoneSelectionDialog::accept);
22 connect(ui->buttonBox, &QDialogButtonBox::rejected, this,
23 &VisualizationMultiZoneSelectionDialog::reject);
24 }
25
26 VisualizationMultiZoneSelectionDialog::~VisualizationMultiZoneSelectionDialog()
27 {
28 delete ui;
29 }
30
31 void VisualizationMultiZoneSelectionDialog::setZones(
32 const QVector<VisualizationSelectionZoneItem *> &zones)
33 {
34 impl->m_Zones = zones;
35
36 // Sorts the zones to display them in temporal order
37 std::sort(impl->m_Zones.begin(), impl->m_Zones.end(), [](auto zone1, auto zone2) {
38 return zone1->range().m_TStart < zone2->range().m_TStart;
39 });
40
41 // Adds the zones in the listwidget
42 for (auto zone : impl->m_Zones) {
43 auto name = zone->name();
44 if (!name.isEmpty()) {
45 name += tr(": ");
46 }
47
48 auto range = zone->range();
49 name += DateUtils::dateTime(range.m_TStart).toString(DATETIME_FORMAT);
50 name += " - ";
51 name += DateUtils::dateTime(range.m_TEnd).toString(DATETIME_FORMAT);
52
53 auto item = new QListWidgetItem(name, ui->listWidget);
54 item->setSelected(zone->selected());
55 }
56 }
57
58 QMap<VisualizationSelectionZoneItem *, bool>
59 VisualizationMultiZoneSelectionDialog::selectedZones() const
60 {
61 QMap<VisualizationSelectionZoneItem *, bool> selectedZones;
62
63 for (auto i = 0; i < ui->listWidget->count(); ++i) {
64 auto item = ui->listWidget->item(i);
65 selectedZones[impl->m_Zones[i]] = item->isSelected();
66 }
67
68 return selectedZones;
69 }
@@ -0,0 +1,50
1 <?xml version="1.0" encoding="UTF-8"?>
2 <ui version="4.0">
3 <class>VisualizationMultiZoneSelectionDialog</class>
4 <widget class="QDialog" name="VisualizationMultiZoneSelectionDialog">
5 <property name="geometry">
6 <rect>
7 <x>0</x>
8 <y>0</y>
9 <width>303</width>
10 <height>139</height>
11 </rect>
12 </property>
13 <property name="windowTitle">
14 <string>Select...</string>
15 </property>
16 <layout class="QVBoxLayout" name="verticalLayout">
17 <property name="spacing">
18 <number>6</number>
19 </property>
20 <property name="leftMargin">
21 <number>0</number>
22 </property>
23 <property name="topMargin">
24 <number>0</number>
25 </property>
26 <property name="rightMargin">
27 <number>0</number>
28 </property>
29 <property name="bottomMargin">
30 <number>0</number>
31 </property>
32 <item>
33 <widget class="QListWidget" name="listWidget">
34 <property name="selectionMode">
35 <enum>QAbstractItemView::ExtendedSelection</enum>
36 </property>
37 </widget>
38 </item>
39 <item>
40 <widget class="QDialogButtonBox" name="buttonBox">
41 <property name="standardButtons">
42 <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
43 </property>
44 </widget>
45 </item>
46 </layout>
47 </widget>
48 <resources/>
49 <connections/>
50 </ui>
@@ -1,75 +1,75
1 #ifndef SCIQLOP_DRAGDROPHELPER_H
2 #define SCIQLOP_DRAGDROPHELPER_H
1 #ifndef SCIQLOP_DRAGDROPGUICONTROLLER_H
2 #define SCIQLOP_DRAGDROPGUICONTROLLER_H
3 3
4 4 #include <Common/spimpl.h>
5 5 #include <QLoggingCategory>
6 6 #include <QWidget>
7 7
8 8 class QVBoxLayout;
9 9 class QScrollArea;
10 10 class QTabBar;
11 11 class VisualizationDragWidget;
12 12 class VisualizationDragDropContainer;
13 13 class QMimeData;
14 14
15 Q_DECLARE_LOGGING_CATEGORY(LOG_DragDropHelper)
15 Q_DECLARE_LOGGING_CATEGORY(LOG_DragDropGuiController)
16 16
17 17 /**
18 18 * @brief Helper class for drag&drop operations.
19 19 * @note The helper is accessible from the sqpApp singleton and has the same life as the whole
20 20 * application (like a controller). But contrary to a controller, it doesn't live in a thread and
21 21 * can interect with the gui.
22 22 * @see SqpApplication
23 23 */
24 class DragDropHelper {
24 class DragDropGuiController {
25 25 public:
26 26 static const QString MIME_TYPE_GRAPH;
27 27 static const QString MIME_TYPE_ZONE;
28 28
29 29 enum class PlaceHolderType { Default, Graph, Zone };
30 30
31 DragDropHelper();
32 virtual ~DragDropHelper();
31 DragDropGuiController();
32 virtual ~DragDropGuiController();
33 33
34 34 /// Resets some internal variables. Must be called before any new drag&drop operation.
35 35 void resetDragAndDrop();
36 36
37 37 /// Sets the visualization widget currently being drag on the visualization.
38 38 void setCurrentDragWidget(VisualizationDragWidget *dragWidget);
39 39
40 40 /// Returns the visualization widget currently being drag on the visualization.
41 41 /// Can be null if a new visualization widget is intended to be created by the drag&drop
42 42 /// operation.
43 43 VisualizationDragWidget *getCurrentDragWidget() const;
44 44
45 45 QWidget &placeHolder() const;
46 46 void insertPlaceHolder(QVBoxLayout *layout, int index, PlaceHolderType type,
47 47 const QString &topLabelText);
48 48 void removePlaceHolder();
49 49 bool isPlaceHolderSet() const;
50 50
51 51 /// Checks if the specified mime data is valid for a drop in the visualization
52 52 bool checkMimeDataForVisualization(const QMimeData *mimeData,
53 53 VisualizationDragDropContainer *dropContainer);
54 54
55 55 void addDragDropScrollArea(QScrollArea *scrollArea);
56 56 void removeDragDropScrollArea(QScrollArea *scrollArea);
57 57
58 58 void addDragDropTabBar(QTabBar *tabBar);
59 59 void removeDragDropTabBar(QTabBar *tabBar);
60 60
61 61 QUrl imageTemporaryUrl(const QImage &image) const;
62 62
63 63 void setHightlightedDragWidget(VisualizationDragWidget *dragWidget);
64 64 VisualizationDragWidget *getHightlightedDragWidget() const;
65 65
66 66 /// Delays the closing of a widget during a drag&drop operation
67 67 void delayedCloseWidget(QWidget *widget);
68 68 void doCloseWidgets();
69 69
70 70 private:
71 class DragDropHelperPrivate;
72 spimpl::unique_impl_ptr<DragDropHelperPrivate> impl;
71 class DragDropGuiControllerPrivate;
72 spimpl::unique_impl_ptr<DragDropGuiControllerPrivate> impl;
73 73 };
74 74
75 #endif // SCIQLOP_DRAGDROPHELPER_H
75 #endif // SCIQLOP_DRAGDROPGUICONTROLLER_H
@@ -1,67 +1,69
1 1 #ifndef SCIQLOP_SQPAPPLICATION_H
2 2 #define SCIQLOP_SQPAPPLICATION_H
3 3
4 4 #include "SqpApplication.h"
5 5
6 6 #include <QApplication>
7 7 #include <QLoggingCategory>
8 8
9 9 #include <Common/spimpl.h>
10 10
11 11 Q_DECLARE_LOGGING_CATEGORY(LOG_SqpApplication)
12 12
13 13 #if defined(sqpApp)
14 14 #undef sqpApp
15 15 #endif
16 16 #define sqpApp (static_cast<SqpApplication *>(QCoreApplication::instance()))
17 17
18 18 class DataSourceController;
19 19 class NetworkController;
20 20 class TimeController;
21 21 class VariableController;
22 22 class VisualizationController;
23 class DragDropHelper;
23 class DragDropGuiController;
24 class ActionsGuiController;
24 25
25 26 /**
26 27 * @brief The SqpApplication class aims to make the link between SciQlop
27 28 * and its plugins. This is the intermediate class that SciQlop has to use
28 29 * in the way to connect a data source. Please first use load method to initialize
29 30 * a plugin specified by its metadata name (JSON plugin source) then others specifics
30 31 * method will be able to access it.
31 32 * You can load a data source driver plugin then create a data source.
32 33 */
33 34
34 35 class SqpApplication : public QApplication {
35 36 Q_OBJECT
36 37 public:
37 38 explicit SqpApplication(int &argc, char **argv);
38 39 virtual ~SqpApplication();
39 40 void initialize();
40 41
41 42 /// Accessors for the differents sciqlop controllers
42 43 DataSourceController &dataSourceController() noexcept;
43 44 NetworkController &networkController() noexcept;
44 45 TimeController &timeController() noexcept;
45 46 VariableController &variableController() noexcept;
46 47 VisualizationController &visualizationController() noexcept;
47 48
48 49 /// Accessors for the differents sciqlop helpers, these helpers classes are like controllers but
49 50 /// doesn't live in a thread and access gui
50 DragDropHelper &dragDropHelper() noexcept;
51 DragDropGuiController &dragDropGuiController() noexcept;
52 ActionsGuiController &actionsGuiController() noexcept;
51 53
52 54 enum class PlotsInteractionMode { None, ZoomBox, DragAndDrop, SelectionZones };
53 55
54 56 enum class PlotsCursorMode { NoCursor, Vertical, Temporal, Horizontal, Cross };
55 57
56 58 PlotsInteractionMode plotsInteractionMode() const;
57 59 void setPlotsInteractionMode(PlotsInteractionMode mode);
58 60
59 61 PlotsCursorMode plotsCursorMode() const;
60 62 void setPlotsCursorMode(PlotsCursorMode mode);
61 63
62 64 private:
63 65 class SqpApplicationPrivate;
64 66 spimpl::unique_impl_ptr<SqpApplicationPrivate> impl;
65 67 };
66 68
67 69 #endif // SCIQLOP_SQPAPPLICATION_H
@@ -1,61 +1,61
1 1 #ifndef SCIQLOP_VISUALIZATIONDRAGDROPCONTAINER_H
2 2 #define SCIQLOP_VISUALIZATIONDRAGDROPCONTAINER_H
3 3
4 4 #include <Common/spimpl.h>
5 5 #include <QFrame>
6 6 #include <QLoggingCategory>
7 7 #include <QMimeData>
8 8 #include <QVBoxLayout>
9 9
10 10 #include <functional>
11 11
12 #include <DragAndDrop/DragDropHelper.h>
12 #include <DragAndDrop/DragDropGuiController.h>
13 13
14 14 Q_DECLARE_LOGGING_CATEGORY(LOG_VisualizationDragDropContainer)
15 15
16 16 class VisualizationDragWidget;
17 17
18 18 class VisualizationDragDropContainer : public QFrame {
19 19 Q_OBJECT
20 20
21 21 signals:
22 22 void dropOccuredInContainer(int dropIndex, const QMimeData *mimeData);
23 23 void dropOccuredOnWidget(VisualizationDragWidget *dragWidget, const QMimeData *mimeData);
24 24
25 25 public:
26 26 enum class DropBehavior { Inserted, Merged, InsertedAndMerged, Forbidden };
27 27 using AcceptMimeDataFunction = std::function<bool(const QMimeData *mimeData)>;
28 28 using AcceptDragWidgetFunction
29 29 = std::function<bool(const VisualizationDragWidget *dragWidget, const QMimeData *mimeData)>;
30 30
31 31 VisualizationDragDropContainer(QWidget *parent = nullptr);
32 32
33 33 void addDragWidget(VisualizationDragWidget *dragWidget);
34 34 void insertDragWidget(int index, VisualizationDragWidget *dragWidget);
35 35
36 36 void setMimeType(const QString &mimeType, DropBehavior behavior);
37 37
38 38 int countDragWidget() const;
39 39
40 40 void setAcceptMimeDataFunction(AcceptMimeDataFunction fun);
41 41
42 42 void setAcceptDragWidgetFunction(AcceptDragWidgetFunction fun);
43 43
44 void setPlaceHolderType(DragDropHelper::PlaceHolderType type,
44 void setPlaceHolderType(DragDropGuiController::PlaceHolderType type,
45 45 const QString &placeHolderText = QString());
46 46
47 47 protected:
48 48 void dragEnterEvent(QDragEnterEvent *event);
49 49 void dragLeaveEvent(QDragLeaveEvent *event);
50 50 void dragMoveEvent(QDragMoveEvent *event);
51 51 void dropEvent(QDropEvent *event);
52 52
53 53 private:
54 54 class VisualizationDragDropContainerPrivate;
55 55 spimpl::unique_impl_ptr<VisualizationDragDropContainerPrivate> impl;
56 56
57 57 private slots:
58 58 void startDrag(VisualizationDragWidget *dragWidget, const QPoint &dragPosition);
59 59 };
60 60
61 61 #endif // SCIQLOP_VISUALIZATIONDRAGDROPCONTAINER_H
@@ -1,138 +1,141
1 1 #ifndef SCIQLOP_VISUALIZATIONGRAPHWIDGET_H
2 2 #define SCIQLOP_VISUALIZATIONGRAPHWIDGET_H
3 3
4 4 #include "Visualization/IVisualizationWidget.h"
5 5 #include "Visualization/VisualizationDragWidget.h"
6 6
7 7 #include <QLoggingCategory>
8 8 #include <QWidget>
9 9
10 10 #include <memory>
11 11
12 12 #include <Common/spimpl.h>
13 13
14 14 Q_DECLARE_LOGGING_CATEGORY(LOG_VisualizationGraphWidget)
15 15
16 16 class QCPRange;
17 17 class QCustomPlot;
18 18 class SqpRange;
19 19 class Variable;
20 20 class VisualizationWidget;
21 21 class VisualizationZoneWidget;
22 class VisualizationSelectionZoneItem;
22 23
23 24 namespace Ui {
24 25 class VisualizationGraphWidget;
25 26 } // namespace Ui
26 27
27 28 class VisualizationGraphWidget : public VisualizationDragWidget, public IVisualizationWidget {
28 29 Q_OBJECT
29 30
30 31 friend class QCustomPlotSynchronizer;
31 32 friend class VisualizationGraphRenderingDelegate;
32 33
33 34 public:
34 35 explicit VisualizationGraphWidget(const QString &name = {}, QWidget *parent = 0);
35 36 virtual ~VisualizationGraphWidget();
36 37
37 38 /// Returns the VisualizationZoneWidget which contains the graph or nullptr
38 39 VisualizationZoneWidget *parentZoneWidget() const noexcept;
39 40
40 41 /// Returns the main VisualizationWidget which contains the graph or nullptr
41 42 VisualizationWidget *parentVisualizationWidget() const;
42 43
43 44 /// If acquisition isn't enable, requestDataLoading signal cannot be emit
44 45 void enableAcquisition(bool enable);
45 46
46 47 void addVariable(std::shared_ptr<Variable> variable, SqpRange range);
47 48
48 49 /// Removes a variable from the graph
49 50 void removeVariable(std::shared_ptr<Variable> variable) noexcept;
50 51
51 52 /// Returns the list of all variables used in the graph
52 53 QList<std::shared_ptr<Variable> > variables() const;
53 54
54 55 /// Sets the y-axis range based on the data of a variable
55 56 void setYRange(std::shared_ptr<Variable> variable);
56 57 SqpRange graphRange() const noexcept;
57 58 void setGraphRange(const SqpRange &range);
58 59
60 // Zones
59 61 /// Returns the ranges of all the selection zones on the graph
60 62 QVector<SqpRange> selectionZoneRanges() const;
61
62 63 /// Adds new selection zones in the graph
63 64 void addSelectionZones(const QVector<SqpRange> &ranges);
65 /// Removes the specified selection zone
66 void removeSelectionZone(VisualizationSelectionZoneItem *selectionZone);
64 67
65 68 /// Undo the last zoom done with a zoom box
66 69 void undoZoom();
67 70
68 71 // IVisualizationWidget interface
69 72 void accept(IVisualizationWidgetVisitor *visitor) override;
70 73 bool canDrop(const Variable &variable) const override;
71 74 bool contains(const Variable &variable) const override;
72 75 QString name() const override;
73 76
74 77 // VisualisationDragWidget
75 78 QMimeData *mimeData(const QPoint &position) const override;
76 79 QPixmap customDragPixmap(const QPoint &dragPosition) override;
77 80 bool isDragAllowed() const override;
78 81 void highlightForMerge(bool highlighted) override;
79 82
80 83 // Cursors
81 84 /// Adds or moves the vertical cursor at the specified value on the x-axis
82 85 void addVerticalCursor(double time);
83 86 /// Adds or moves the vertical cursor at the specified value on the x-axis
84 87 void addVerticalCursorAtViewportPosition(double position);
85 88 void removeVerticalCursor();
86 89 /// Adds or moves the vertical cursor at the specified value on the y-axis
87 90 void addHorizontalCursor(double value);
88 91 /// Adds or moves the vertical cursor at the specified value on the y-axis
89 92 void addHorizontalCursorAtViewportPosition(double position);
90 93 void removeHorizontalCursor();
91 94
92 95 signals:
93 96 void synchronize(const SqpRange &range, const SqpRange &oldRange);
94 97 void requestDataLoading(QVector<std::shared_ptr<Variable> > variable, const SqpRange &range,
95 98 bool synchronise);
96 99
97 100 /// Signal emitted when the variable is about to be removed from the graph
98 101 void variableAboutToBeRemoved(std::shared_ptr<Variable> var);
99 102 /// Signal emitted when the variable has been added to the graph
100 103 void variableAdded(std::shared_ptr<Variable> var);
101 104
102 105 protected:
103 106 void closeEvent(QCloseEvent *event) override;
104 107 void enterEvent(QEvent *event) override;
105 108 void leaveEvent(QEvent *event) override;
106 109
107 110 QCustomPlot &plot() const noexcept;
108 111
109 112 private:
110 113 Ui::VisualizationGraphWidget *ui;
111 114
112 115 class VisualizationGraphWidgetPrivate;
113 116 spimpl::unique_impl_ptr<VisualizationGraphWidgetPrivate> impl;
114 117
115 118 private slots:
116 119 /// Slot called when right clicking on the graph (displays a menu)
117 120 void onGraphMenuRequested(const QPoint &pos) noexcept;
118 121
119 122 /// Rescale the X axe to range parameter
120 123 void onRangeChanged(const QCPRange &t1, const QCPRange &t2);
121 124
122 125 /// Slot called when a mouse double click was made
123 126 void onMouseDoubleClick(QMouseEvent *event) noexcept;
124 127 /// Slot called when a mouse move was made
125 128 void onMouseMove(QMouseEvent *event) noexcept;
126 129 /// Slot called when a mouse wheel was made, to perform some processing before the zoom is done
127 130 void onMouseWheel(QWheelEvent *event) noexcept;
128 131 /// Slot called when a mouse press was made, to activate the calibration of a graph
129 132 void onMousePress(QMouseEvent *event) noexcept;
130 133 /// Slot called when a mouse release was made, to deactivate the calibration of a graph
131 134 void onMouseRelease(QMouseEvent *event) noexcept;
132 135
133 136 void onDataCacheVariableUpdated();
134 137
135 138 void onUpdateVarDisplaying(std::shared_ptr<Variable> variable, const SqpRange &range);
136 139 };
137 140
138 141 #endif // SCIQLOP_VISUALIZATIONGRAPHWIDGET_H
@@ -1,47 +1,68
1 1 #ifndef SCIQLOP_VISUALIZATIONSELECTIONZONEITEM_H
2 2 #define SCIQLOP_VISUALIZATIONSELECTIONZONEITEM_H
3 3
4 4 #include <Common/spimpl.h>
5 5 #include <Data/SqpRange.h>
6 6 #include <Visualization/qcustomplot.h>
7 7
8 class VisualizationGraphWidget;
9
8 10 class VisualizationSelectionZoneItem : public QCPItemRect {
9 11
10 12 public:
11 13 VisualizationSelectionZoneItem(QCustomPlot *plot);
12 14 virtual ~VisualizationSelectionZoneItem();
13 15
16 VisualizationGraphWidget *parentGraphWidget() const noexcept;
17
14 18 void setName(const QString &name);
15 19 QString name() const;
16 20
17 21 SqpRange range() const;
18 22 void setRange(double tstart, double tend);
19 23 void setStart(double tstart);
20 24 void setEnd(double tend);
21 25
22 26 void setColor(const QColor &color);
23 27
24 28 void setEditionEnabled(bool value);
25 29 bool isEditionEnabled() const;
26 30
31 /// Moves the item at the top of its QCPLayer. It will then receive the mouse events if multiple
32 /// items are stacked on top of each others.
33 void moveToTop();
34
27 35 Qt::CursorShape curshorShapeForPosition(const QPoint &position) const;
28 36 void setHovered(bool value);
29 37
38 /// Sets the zones which should be moved or reisized together with this zone
30 39 void setAssociatedEditedZones(const QVector<VisualizationSelectionZoneItem *> &associatedZones);
31 40
41 /// Align the specified zones with this one, vertically with the left border
42 bool alignZonesVerticallyOnLeft(const QVector<VisualizationSelectionZoneItem *> &zonesToAlign,
43 bool allowResize);
44 /// Align the specified zones with this one, vertically with the right border
45 bool alignZonesVerticallyOnRight(const QVector<VisualizationSelectionZoneItem *> &zonesToAlign,
46 bool allowResize);
47 /// Align the specified zones with this one, temporally with the left border
48 bool alignZonesTemporallyOnLeft(const QVector<VisualizationSelectionZoneItem *> &zonesToAlign,
49 bool allowResize);
50 /// Align the specified zones with this one, temporally with the right border
51 bool alignZonesTemporallyOnRight(const QVector<VisualizationSelectionZoneItem *> &zonesToAlign,
52 bool allowResize);
53
32 54 protected:
33 55 void mousePressEvent(QMouseEvent *event, const QVariant &details) override;
34 56 void mouseMoveEvent(QMouseEvent *event, const QPointF &startPos) override;
35 57 void mouseReleaseEvent(QMouseEvent *event, const QPointF &startPos) override;
36 58
37 59 void resizeLeft(double pixelDiff);
38 60 void resizeRight(double pixelDiff);
39 61 void move(double pixelDiff);
40 62
41
42 63 private:
43 64 class VisualizationSelectionZoneItemPrivate;
44 65 spimpl::unique_impl_ptr<VisualizationSelectionZoneItemPrivate> impl;
45 66 };
46 67
47 68 #endif // SCIQLOP_VISUALIZATIONSELECTIONZONEITEM_H
@@ -1,103 +1,110
1 1
2 2 gui_moc_headers = [
3 3 'include/DataSource/DataSourceWidget.h',
4 4 'include/Settings/SqpSettingsDialog.h',
5 5 'include/Settings/SqpSettingsGeneralWidget.h',
6 6 'include/SidePane/SqpSidePane.h',
7 7 'include/SqpApplication.h',
8 8 'include/DragAndDrop/DragDropScroller.h',
9 9 'include/DragAndDrop/DragDropTabSwitcher.h',
10 10 'include/TimeWidget/TimeWidget.h',
11 11 'include/Variable/VariableInspectorWidget.h',
12 12 'include/Variable/RenameVariableDialog.h',
13 13 'include/Visualization/qcustomplot.h',
14 14 'include/Visualization/VisualizationGraphWidget.h',
15 15 'include/Visualization/VisualizationTabWidget.h',
16 16 'include/Visualization/VisualizationWidget.h',
17 17 'include/Visualization/VisualizationZoneWidget.h',
18 18 'include/Visualization/VisualizationDragDropContainer.h',
19 19 'include/Visualization/VisualizationDragWidget.h',
20 'include/Visualization/ColorScaleEditor.h'
20 'include/Visualization/ColorScaleEditor.h',
21 'include/Actions/SelectionZoneAction.h',
22 'include/Visualization/VisualizationMultiZoneSelectionDialog.h'
21 23 ]
22 24
23 25 gui_ui_files = [
24 26 'ui/DataSource/DataSourceWidget.ui',
25 27 'ui/Settings/SqpSettingsDialog.ui',
26 28 'ui/Settings/SqpSettingsGeneralWidget.ui',
27 29 'ui/SidePane/SqpSidePane.ui',
28 30 'ui/TimeWidget/TimeWidget.ui',
29 31 'ui/Variable/VariableInspectorWidget.ui',
30 32 'ui/Variable/RenameVariableDialog.ui',
31 33 'ui/Variable/VariableMenuHeaderWidget.ui',
32 34 'ui/Visualization/VisualizationGraphWidget.ui',
33 35 'ui/Visualization/VisualizationTabWidget.ui',
34 36 'ui/Visualization/VisualizationWidget.ui',
35 37 'ui/Visualization/VisualizationZoneWidget.ui',
36 'ui/Visualization/ColorScaleEditor.ui'
38 'ui/Visualization/ColorScaleEditor.ui',
39 'ui/Visualization/VisualizationMultiZoneSelectionDialog.ui'
37 40 ]
38 41
39 42 gui_qresources = ['resources/sqpguiresources.qrc']
40 43
41 44 gui_moc_files = qt5.preprocess(moc_headers : gui_moc_headers,
42 45 ui_files : gui_ui_files,
43 46 qresources : gui_qresources)
44 47
45 48 gui_sources = [
46 49 'src/SqpApplication.cpp',
47 'src/DragAndDrop/DragDropHelper.cpp',
50 'src/DragAndDrop/DragDropGuiController.cpp',
48 51 'src/DragAndDrop/DragDropScroller.cpp',
49 52 'src/DragAndDrop/DragDropTabSwitcher.cpp',
50 53 'src/Common/ColorUtils.cpp',
51 54 'src/Common/VisualizationDef.cpp',
52 55 'src/DataSource/DataSourceTreeWidgetItem.cpp',
53 56 'src/DataSource/DataSourceTreeWidgetHelper.cpp',
54 57 'src/DataSource/DataSourceWidget.cpp',
55 58 'src/DataSource/DataSourceTreeWidget.cpp',
56 59 'src/Settings/SqpSettingsDialog.cpp',
57 60 'src/Settings/SqpSettingsGeneralWidget.cpp',
58 61 'src/SidePane/SqpSidePane.cpp',
59 62 'src/TimeWidget/TimeWidget.cpp',
60 63 'src/Variable/VariableInspectorWidget.cpp',
61 64 'src/Variable/VariableInspectorTableView.cpp',
62 65 'src/Variable/VariableMenuHeaderWidget.cpp',
63 66 'src/Variable/RenameVariableDialog.cpp',
64 67 'src/Visualization/VisualizationGraphHelper.cpp',
65 68 'src/Visualization/VisualizationGraphRenderingDelegate.cpp',
66 69 'src/Visualization/VisualizationGraphWidget.cpp',
67 70 'src/Visualization/VisualizationTabWidget.cpp',
68 71 'src/Visualization/VisualizationWidget.cpp',
69 72 'src/Visualization/VisualizationZoneWidget.cpp',
70 73 'src/Visualization/qcustomplot.cpp',
71 74 'src/Visualization/QCustomPlotSynchronizer.cpp',
72 75 'src/Visualization/operations/FindVariableOperation.cpp',
73 76 'src/Visualization/operations/GenerateVariableMenuOperation.cpp',
74 77 'src/Visualization/operations/MenuBuilder.cpp',
75 78 'src/Visualization/operations/RemoveVariableOperation.cpp',
76 79 'src/Visualization/operations/RescaleAxeOperation.cpp',
77 80 'src/Visualization/VisualizationDragDropContainer.cpp',
78 81 'src/Visualization/VisualizationDragWidget.cpp',
79 82 'src/Visualization/AxisRenderingUtils.cpp',
80 83 'src/Visualization/PlottablesRenderingUtils.cpp',
81 84 'src/Visualization/MacScrollBarStyle.cpp',
82 85 'src/Visualization/VisualizationCursorItem.cpp',
83 86 'src/Visualization/ColorScaleEditor.cpp',
84 87 'src/Visualization/SqpColorScale.cpp',
85 88 'src/Visualization/QCPColorMapIterator.cpp',
86 89 'src/Visualization/VisualizationSelectionZoneItem.cpp',
87 'src/Visualization/VisualizationSelectionZoneManager.cpp'
90 'src/Visualization/VisualizationSelectionZoneManager.cpp',
91 'src/Actions/SelectionZoneAction.cpp',
92 'src/Actions/ActionsGuiController.cpp',
93 'src/Visualization/VisualizationActionManager.cpp',
94 'src/Visualization/VisualizationMultiZoneSelectionDialog.cpp'
88 95 ]
89 96
90 97 gui_inc = include_directories(['include'])
91 98
92 99 sciqlop_gui_lib = library('sciqlopgui',
93 100 gui_sources,
94 101 gui_moc_files,
95 102 include_directories : [gui_inc],
96 103 dependencies : [ qt5printsupport, qt5gui, qt5widgets, qt5svg, sciqlop_core],
97 104 install : true
98 105 )
99 106
100 107 sciqlop_gui = declare_dependency(link_with : sciqlop_gui_lib,
101 108 include_directories : gui_inc,
102 109 dependencies : [qt5printsupport, qt5gui, qt5widgets, qt5svg, sciqlop_core])
103 110
@@ -1,47 +1,47
1 1 #include "DataSource/DataSourceTreeWidget.h"
2 2 #include "Common/MimeTypesDef.h"
3 3 #include "DataSource/DataSourceController.h"
4 4 #include "DataSource/DataSourceItem.h"
5 5 #include "DataSource/DataSourceTreeWidgetItem.h"
6 6
7 #include "DragAndDrop/DragDropHelper.h"
7 #include "DragAndDrop/DragDropGuiController.h"
8 8 #include "SqpApplication.h"
9 9
10 10 #include <QMimeData>
11 11
12 12 DataSourceTreeWidget::DataSourceTreeWidget(QWidget *parent) : QTreeWidget(parent)
13 13 {
14 14 }
15 15
16 16 QMimeData *DataSourceTreeWidget::mimeData(const QList<QTreeWidgetItem *> items) const
17 17 {
18 18 auto mimeData = new QMimeData;
19 19
20 20 // Basic check to ensure the item are correctly typed
21 21 Q_ASSERT(items.isEmpty() || dynamic_cast<DataSourceTreeWidgetItem *>(items.first()) != nullptr);
22 22
23 23 QVariantList productData;
24 24
25 25 for (auto item : items) {
26 26 auto dataSourceTreeItem = static_cast<DataSourceTreeWidgetItem *>(item);
27 27 auto dataSource = dataSourceTreeItem->data();
28 28
29 29 if (dataSource->type() == DataSourceItemType::COMPONENT
30 30 || dataSource->type() == DataSourceItemType::PRODUCT) {
31 31 auto metaData = dataSource->data();
32 32 productData << metaData;
33 33 }
34 34 }
35 35
36 36 auto encodedData = sqpApp->dataSourceController().mimeDataForProductsData(productData);
37 37 mimeData->setData(MIME_TYPE_PRODUCT_LIST, encodedData);
38 38
39 39 return mimeData;
40 40 }
41 41
42 42 void DataSourceTreeWidget::startDrag(Qt::DropActions supportedActions)
43 43 {
44 44 // Resets the drag&drop operations before it's starting
45 sqpApp->dragDropHelper().resetDragAndDrop();
45 sqpApp->dragDropGuiController().resetDragAndDrop();
46 46 QTreeWidget::startDrag(supportedActions);
47 47 }
@@ -1,294 +1,296
1 #include "DragAndDrop/DragDropHelper.h"
1 #include "DragAndDrop/DragDropGuiController.h"
2 2 #include "DragAndDrop/DragDropScroller.h"
3 3 #include "DragAndDrop/DragDropTabSwitcher.h"
4 4 #include "SqpApplication.h"
5 5 #include "Visualization/VisualizationDragDropContainer.h"
6 6 #include "Visualization/VisualizationDragWidget.h"
7 7 #include "Visualization/VisualizationWidget.h"
8 8 #include "Visualization/operations/FindVariableOperation.h"
9 9
10 10 #include "Variable/Variable.h"
11 11 #include "Variable/VariableController.h"
12 12
13 13 #include "Common/MimeTypesDef.h"
14 14 #include "Common/VisualizationDef.h"
15 15
16 16 #include <QDir>
17 17 #include <QLabel>
18 18 #include <QUrl>
19 19 #include <QVBoxLayout>
20 20
21 21
22 Q_LOGGING_CATEGORY(LOG_DragDropHelper, "DragDropHelper")
22 Q_LOGGING_CATEGORY(LOG_DragDropGuiController, "DragDropGuiController")
23 23
24 24
25 struct DragDropHelper::DragDropHelperPrivate {
25 struct DragDropGuiController::DragDropGuiControllerPrivate {
26 26
27 27 VisualizationDragWidget *m_CurrentDragWidget = nullptr;
28 28 std::unique_ptr<QWidget> m_PlaceHolder = nullptr;
29 29 QLabel *m_PlaceHolderLabel;
30 30 QWidget *m_PlaceBackground;
31 31 std::unique_ptr<DragDropScroller> m_DragDropScroller = nullptr;
32 32 std::unique_ptr<DragDropTabSwitcher> m_DragDropTabSwitcher = nullptr;
33 33 QString m_ImageTempUrl; // Temporary file for image url generated by the drag & drop. Not using
34 34 // QTemporaryFile to have a name which is not generated.
35 35
36 36 VisualizationDragWidget *m_HighlightedDragWidget = nullptr;
37 37
38 38 QMetaObject::Connection m_DragWidgetDestroyedConnection;
39 39 QMetaObject::Connection m_HighlightedWidgetDestroyedConnection;
40 40
41 41 QList<QWidget *> m_WidgetToClose;
42 42
43 explicit DragDropHelperPrivate()
43 explicit DragDropGuiControllerPrivate()
44 44 : m_PlaceHolder{std::make_unique<QWidget>()},
45 45 m_DragDropScroller{std::make_unique<DragDropScroller>()},
46 46 m_DragDropTabSwitcher{std::make_unique<DragDropTabSwitcher>()}
47 47 {
48 48
49 49 auto layout = new QVBoxLayout{m_PlaceHolder.get()};
50 50 layout->setSpacing(0);
51 51 layout->setContentsMargins(0, 0, 0, 0);
52 52
53 53 m_PlaceHolderLabel = new QLabel{"", m_PlaceHolder.get()};
54 54 m_PlaceHolderLabel->setMinimumHeight(25);
55 55 layout->addWidget(m_PlaceHolderLabel);
56 56
57 57 m_PlaceBackground = new QWidget{m_PlaceHolder.get()};
58 58 m_PlaceBackground->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
59 59 layout->addWidget(m_PlaceBackground);
60 60
61 61 sqpApp->installEventFilter(m_DragDropScroller.get());
62 62 sqpApp->installEventFilter(m_DragDropTabSwitcher.get());
63 63
64 64 m_ImageTempUrl = QDir::temp().absoluteFilePath("Sciqlop_graph.png");
65 65 }
66 66
67 void preparePlaceHolder(DragDropHelper::PlaceHolderType type, const QString &topLabelText) const
67 void preparePlaceHolder(DragDropGuiController::PlaceHolderType type,
68 const QString &topLabelText) const
68 69 {
69 70 if (m_CurrentDragWidget) {
70 71 m_PlaceHolder->setMinimumSize(m_CurrentDragWidget->size());
71 72 m_PlaceHolder->setSizePolicy(m_CurrentDragWidget->sizePolicy());
72 73 }
73 74 else {
74 75 // Configuration of the placeHolder when there is no dragWidget
75 76 // (for instance with a drag from a variable)
76 77
77 78 m_PlaceHolder->setMinimumSize(0, GRAPH_MINIMUM_HEIGHT);
78 79 m_PlaceHolder->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
79 80 }
80 81
81 82 switch (type) {
82 case DragDropHelper::PlaceHolderType::Graph:
83 case DragDropGuiController::PlaceHolderType::Graph:
83 84 m_PlaceBackground->setStyleSheet(
84 85 "background-color: #BBD5EE; border: 1px solid #2A7FD4");
85 86 break;
86 case DragDropHelper::PlaceHolderType::Zone:
87 case DragDropHelper::PlaceHolderType::Default:
87 case DragDropGuiController::PlaceHolderType::Zone:
88 case DragDropGuiController::PlaceHolderType::Default:
88 89 m_PlaceBackground->setStyleSheet(
89 90 "background-color: #BBD5EE; border: 2px solid #2A7FD4");
90 91 m_PlaceHolderLabel->setStyleSheet("color: #2A7FD4");
91 92 break;
92 93 }
93 94
94 95 m_PlaceHolderLabel->setText(topLabelText);
95 96 m_PlaceHolderLabel->setVisible(!topLabelText.isEmpty());
96 97 }
97 98 };
98 99
99 100
100 DragDropHelper::DragDropHelper() : impl{spimpl::make_unique_impl<DragDropHelperPrivate>()}
101 DragDropGuiController::DragDropGuiController()
102 : impl{spimpl::make_unique_impl<DragDropGuiControllerPrivate>()}
101 103 {
102 104 }
103 105
104 DragDropHelper::~DragDropHelper()
106 DragDropGuiController::~DragDropGuiController()
105 107 {
106 108 QFile::remove(impl->m_ImageTempUrl);
107 109 }
108 110
109 void DragDropHelper::resetDragAndDrop()
111 void DragDropGuiController::resetDragAndDrop()
110 112 {
111 113 setCurrentDragWidget(nullptr);
112 114 impl->m_HighlightedDragWidget = nullptr;
113 115 }
114 116
115 void DragDropHelper::setCurrentDragWidget(VisualizationDragWidget *dragWidget)
117 void DragDropGuiController::setCurrentDragWidget(VisualizationDragWidget *dragWidget)
116 118 {
117 119 if (impl->m_CurrentDragWidget) {
118 120
119 121 QObject::disconnect(impl->m_DragWidgetDestroyedConnection);
120 122 }
121 123
122 124 if (dragWidget) {
123 125 // ensures the impl->m_CurrentDragWidget is reset when the widget is destroyed
124 126 impl->m_DragWidgetDestroyedConnection
125 127 = QObject::connect(dragWidget, &VisualizationDragWidget::destroyed,
126 128 [this]() { impl->m_CurrentDragWidget = nullptr; });
127 129 }
128 130
129 131 impl->m_CurrentDragWidget = dragWidget;
130 132 }
131 133
132 VisualizationDragWidget *DragDropHelper::getCurrentDragWidget() const
134 VisualizationDragWidget *DragDropGuiController::getCurrentDragWidget() const
133 135 {
134 136 return impl->m_CurrentDragWidget;
135 137 }
136 138
137 QWidget &DragDropHelper::placeHolder() const
139 QWidget &DragDropGuiController::placeHolder() const
138 140 {
139 141 return *impl->m_PlaceHolder;
140 142 }
141 143
142 void DragDropHelper::insertPlaceHolder(QVBoxLayout *layout, int index, PlaceHolderType type,
143 const QString &topLabelText)
144 void DragDropGuiController::insertPlaceHolder(QVBoxLayout *layout, int index, PlaceHolderType type,
145 const QString &topLabelText)
144 146 {
145 147 removePlaceHolder();
146 148 impl->preparePlaceHolder(type, topLabelText);
147 149 layout->insertWidget(index, impl->m_PlaceHolder.get());
148 150 impl->m_PlaceHolder->show();
149 151 }
150 152
151 void DragDropHelper::removePlaceHolder()
153 void DragDropGuiController::removePlaceHolder()
152 154 {
153 155 auto parentWidget = impl->m_PlaceHolder->parentWidget();
154 156 if (parentWidget) {
155 157 parentWidget->layout()->removeWidget(impl->m_PlaceHolder.get());
156 158 impl->m_PlaceHolder->setParent(nullptr);
157 159 impl->m_PlaceHolder->hide();
158 160 }
159 161 }
160 162
161 bool DragDropHelper::isPlaceHolderSet() const
163 bool DragDropGuiController::isPlaceHolderSet() const
162 164 {
163 165 return impl->m_PlaceHolder->parentWidget();
164 166 }
165 167
166 void DragDropHelper::addDragDropScrollArea(QScrollArea *scrollArea)
168 void DragDropGuiController::addDragDropScrollArea(QScrollArea *scrollArea)
167 169 {
168 170 impl->m_DragDropScroller->addScrollArea(scrollArea);
169 171 }
170 172
171 void DragDropHelper::removeDragDropScrollArea(QScrollArea *scrollArea)
173 void DragDropGuiController::removeDragDropScrollArea(QScrollArea *scrollArea)
172 174 {
173 175 impl->m_DragDropScroller->removeScrollArea(scrollArea);
174 176 }
175 177
176 void DragDropHelper::addDragDropTabBar(QTabBar *tabBar)
178 void DragDropGuiController::addDragDropTabBar(QTabBar *tabBar)
177 179 {
178 180 impl->m_DragDropTabSwitcher->addTabBar(tabBar);
179 181 }
180 182
181 void DragDropHelper::removeDragDropTabBar(QTabBar *tabBar)
183 void DragDropGuiController::removeDragDropTabBar(QTabBar *tabBar)
182 184 {
183 185 impl->m_DragDropTabSwitcher->removeTabBar(tabBar);
184 186 }
185 187
186 QUrl DragDropHelper::imageTemporaryUrl(const QImage &image) const
188 QUrl DragDropGuiController::imageTemporaryUrl(const QImage &image) const
187 189 {
188 190 image.save(impl->m_ImageTempUrl);
189 191 return QUrl::fromLocalFile(impl->m_ImageTempUrl);
190 192 }
191 193
192 void DragDropHelper::setHightlightedDragWidget(VisualizationDragWidget *dragWidget)
194 void DragDropGuiController::setHightlightedDragWidget(VisualizationDragWidget *dragWidget)
193 195 {
194 196 if (impl->m_HighlightedDragWidget) {
195 197 impl->m_HighlightedDragWidget->highlightForMerge(false);
196 198 QObject::disconnect(impl->m_HighlightedWidgetDestroyedConnection);
197 199 }
198 200
199 201 if (dragWidget) {
200 202 dragWidget->highlightForMerge(true);
201 203
202 204 // ensures the impl->m_HighlightedDragWidget is reset when the widget is destroyed
203 205 impl->m_DragWidgetDestroyedConnection
204 206 = QObject::connect(dragWidget, &VisualizationDragWidget::destroyed,
205 207 [this]() { impl->m_HighlightedDragWidget = nullptr; });
206 208 }
207 209
208 210 impl->m_HighlightedDragWidget = dragWidget;
209 211 }
210 212
211 VisualizationDragWidget *DragDropHelper::getHightlightedDragWidget() const
213 VisualizationDragWidget *DragDropGuiController::getHightlightedDragWidget() const
212 214 {
213 215 return impl->m_HighlightedDragWidget;
214 216 }
215 217
216 void DragDropHelper::delayedCloseWidget(QWidget *widget)
218 void DragDropGuiController::delayedCloseWidget(QWidget *widget)
217 219 {
218 220 widget->hide();
219 221 impl->m_WidgetToClose << widget;
220 222 }
221 223
222 void DragDropHelper::doCloseWidgets()
224 void DragDropGuiController::doCloseWidgets()
223 225 {
224 226 for (auto widget : impl->m_WidgetToClose) {
225 227 widget->close();
226 228 }
227 229
228 230 impl->m_WidgetToClose.clear();
229 231 }
230 232
231 bool DragDropHelper::checkMimeDataForVisualization(const QMimeData *mimeData,
232 VisualizationDragDropContainer *dropContainer)
233 bool DragDropGuiController::checkMimeDataForVisualization(
234 const QMimeData *mimeData, VisualizationDragDropContainer *dropContainer)
233 235 {
234 236 if (!mimeData || !dropContainer) {
235 qCWarning(LOG_DragDropHelper()) << QObject::tr(
236 "DragDropHelper::checkMimeDataForVisualization, invalid input parameters.");
237 qCWarning(LOG_DragDropGuiController()) << QObject::tr(
238 "DragDropGuiController::checkMimeDataForVisualization, invalid input parameters.");
237 239 Q_ASSERT(false);
238 240 return false;
239 241 }
240 242
241 243 auto result = false;
242 244
243 245 if (mimeData->hasFormat(MIME_TYPE_VARIABLE_LIST)) {
244 246 auto variables = sqpApp->variableController().variablesForMimeData(
245 247 mimeData->data(MIME_TYPE_VARIABLE_LIST));
246 248
247 249 if (variables.count() == 1) {
248 250
249 251 auto variable = variables.first();
250 252 if (variable->dataSeries() != nullptr) {
251 253
252 254 // Check that the variable is not already in a graph
253 255
254 256 auto parent = dropContainer->parentWidget();
255 257 while (parent && qobject_cast<VisualizationWidget *>(parent) == nullptr) {
256 258 parent = parent->parentWidget(); // Search for the top level VisualizationWidget
257 259 }
258 260
259 261 if (parent) {
260 262 auto visualizationWidget = static_cast<VisualizationWidget *>(parent);
261 263
262 264 FindVariableOperation findVariableOperation{variable};
263 265 visualizationWidget->accept(&findVariableOperation);
264 266 auto variableContainers = findVariableOperation.result();
265 267 if (variableContainers.empty()) {
266 268 result = true;
267 269 }
268 270 else {
269 271 // result = false: the variable already exist in the visualisation
270 272 }
271 273 }
272 274 else {
273 qCWarning(LOG_DragDropHelper()) << QObject::tr(
274 "DragDropHelper::checkMimeDataForVisualization, the parent "
275 qCWarning(LOG_DragDropGuiController()) << QObject::tr(
276 "DragDropGuiController::checkMimeDataForVisualization, the parent "
275 277 "VisualizationWidget cannot be found. Cannot check if the variable is "
276 278 "already used or not.");
277 279 }
278 280 }
279 281 else {
280 282 // result = false: the variable is not fully loaded
281 283 }
282 284 }
283 285 else {
284 286 // result = false: cannot drop multiple variables in the visualisation
285 287 }
286 288 }
287 289 else {
288 290 // Other MIME data
289 291 // no special rules, accepted by default
290 292 result = true;
291 293 }
292 294
293 295 return result;
294 296 }
@@ -1,203 +1,212
1 1 #include "SqpApplication.h"
2 2
3 #include <Actions/ActionsGuiController.h>
3 4 #include <Catalogue/CatalogueController.h>
4 5 #include <Data/IDataProvider.h>
5 6 #include <DataSource/DataSourceController.h>
6 #include <DragAndDrop/DragDropHelper.h>
7 #include <DragAndDrop/DragDropGuiController.h>
7 8 #include <Network/NetworkController.h>
8 9 #include <QThread>
9 10 #include <Time/TimeController.h>
10 11 #include <Variable/Variable.h>
11 12 #include <Variable/VariableController.h>
12 13 #include <Variable/VariableModel.h>
13 14 #include <Visualization/VisualizationController.h>
14 15
15 16 Q_LOGGING_CATEGORY(LOG_SqpApplication, "SqpApplication")
16 17
17 18 class SqpApplication::SqpApplicationPrivate {
18 19 public:
19 20 SqpApplicationPrivate()
20 21 : m_DataSourceController{std::make_unique<DataSourceController>()},
21 22 m_VariableController{std::make_unique<VariableController>()},
22 23 m_TimeController{std::make_unique<TimeController>()},
23 24 m_NetworkController{std::make_unique<NetworkController>()},
24 25 m_VisualizationController{std::make_unique<VisualizationController>()},
25 m_DragDropHelper{std::make_unique<DragDropHelper>()},
26 m_DragDropGuiController{std::make_unique<DragDropGuiController>()},
26 27 m_CatalogueController{std::make_unique<CatalogueController>()},
28 m_ActionsGuiController{std::make_unique<ActionsGuiController>()},
27 29 m_PlotInterractionMode(SqpApplication::PlotsInteractionMode::None),
28 30 m_PlotCursorMode(SqpApplication::PlotsCursorMode::NoCursor)
29 31 {
30 32 // /////////////////////////////// //
31 33 // Connections between controllers //
32 34 // /////////////////////////////// //
33 35
34 36 // VariableController <-> DataSourceController
35 37 connect(m_DataSourceController.get(),
36 38 SIGNAL(variableCreationRequested(const QString &, const QVariantHash &,
37 39 std::shared_ptr<IDataProvider>)),
38 40 m_VariableController.get(),
39 41 SLOT(createVariable(const QString &, const QVariantHash &,
40 42 std::shared_ptr<IDataProvider>)));
41 43
42 44 connect(m_VariableController->variableModel(), &VariableModel::requestVariable,
43 45 m_DataSourceController.get(), &DataSourceController::requestVariable);
44 46
45 47 // VariableController <-> VisualizationController
46 48 connect(m_VariableController.get(),
47 49 SIGNAL(variableAboutToBeDeleted(std::shared_ptr<Variable>)),
48 50 m_VisualizationController.get(),
49 51 SIGNAL(variableAboutToBeDeleted(std::shared_ptr<Variable>)), Qt::DirectConnection);
50 52
51 53 connect(m_VariableController.get(),
52 54 SIGNAL(rangeChanged(std::shared_ptr<Variable>, const SqpRange &)),
53 55 m_VisualizationController.get(),
54 56 SIGNAL(rangeChanged(std::shared_ptr<Variable>, const SqpRange &)));
55 57
56 58
57 59 m_DataSourceController->moveToThread(&m_DataSourceControllerThread);
58 60 m_DataSourceControllerThread.setObjectName("DataSourceControllerThread");
59 61 m_NetworkController->moveToThread(&m_NetworkControllerThread);
60 62 m_NetworkControllerThread.setObjectName("NetworkControllerThread");
61 63 m_VariableController->moveToThread(&m_VariableControllerThread);
62 64 m_VariableControllerThread.setObjectName("VariableControllerThread");
63 65 m_VisualizationController->moveToThread(&m_VisualizationControllerThread);
64 66 m_VisualizationControllerThread.setObjectName("VsualizationControllerThread");
65 67 m_CatalogueController->moveToThread(&m_CatalogueControllerThread);
66 68 m_CatalogueControllerThread.setObjectName("CatalogueControllerThread");
67 69
68 70
69 71 // Additionnal init
70 72 m_VariableController->setTimeController(m_TimeController.get());
71 73 }
72 74
73 75 virtual ~SqpApplicationPrivate()
74 76 {
75 77 m_DataSourceControllerThread.quit();
76 78 m_DataSourceControllerThread.wait();
77 79
78 80 m_NetworkControllerThread.quit();
79 81 m_NetworkControllerThread.wait();
80 82
81 83 m_VariableControllerThread.quit();
82 84 m_VariableControllerThread.wait();
83 85
84 86 m_VisualizationControllerThread.quit();
85 87 m_VisualizationControllerThread.wait();
86 88
87 89 m_CatalogueControllerThread.quit();
88 90 m_CatalogueControllerThread.wait();
89 91 }
90 92
91 93 std::unique_ptr<DataSourceController> m_DataSourceController;
92 94 std::unique_ptr<VariableController> m_VariableController;
93 95 std::unique_ptr<TimeController> m_TimeController;
94 96 std::unique_ptr<NetworkController> m_NetworkController;
95 97 std::unique_ptr<VisualizationController> m_VisualizationController;
96 98 std::unique_ptr<CatalogueController> m_CatalogueController;
99
97 100 QThread m_DataSourceControllerThread;
98 101 QThread m_NetworkControllerThread;
99 102 QThread m_VariableControllerThread;
100 103 QThread m_VisualizationControllerThread;
101 104 QThread m_CatalogueControllerThread;
102 105
103 std::unique_ptr<DragDropHelper> m_DragDropHelper;
106 std::unique_ptr<DragDropGuiController> m_DragDropGuiController;
107 std::unique_ptr<ActionsGuiController> m_ActionsGuiController;
104 108
105 109 SqpApplication::PlotsInteractionMode m_PlotInterractionMode;
106 110 SqpApplication::PlotsCursorMode m_PlotCursorMode;
107 111 };
108 112
109 113
110 114 SqpApplication::SqpApplication(int &argc, char **argv)
111 115 : QApplication{argc, argv}, impl{spimpl::make_unique_impl<SqpApplicationPrivate>()}
112 116 {
113 117 qCDebug(LOG_SqpApplication()) << tr("SqpApplication construction") << QThread::currentThread();
114 118
115 119 connect(&impl->m_DataSourceControllerThread, &QThread::started,
116 120 impl->m_DataSourceController.get(), &DataSourceController::initialize);
117 121 connect(&impl->m_DataSourceControllerThread, &QThread::finished,
118 122 impl->m_DataSourceController.get(), &DataSourceController::finalize);
119 123
120 124 connect(&impl->m_NetworkControllerThread, &QThread::started, impl->m_NetworkController.get(),
121 125 &NetworkController::initialize);
122 126 connect(&impl->m_NetworkControllerThread, &QThread::finished, impl->m_NetworkController.get(),
123 127 &NetworkController::finalize);
124 128
125 129 connect(&impl->m_VariableControllerThread, &QThread::started, impl->m_VariableController.get(),
126 130 &VariableController::initialize);
127 131 connect(&impl->m_VariableControllerThread, &QThread::finished, impl->m_VariableController.get(),
128 132 &VariableController::finalize);
129 133
130 134 connect(&impl->m_VisualizationControllerThread, &QThread::started,
131 135 impl->m_VisualizationController.get(), &VisualizationController::initialize);
132 136 connect(&impl->m_VisualizationControllerThread, &QThread::finished,
133 137 impl->m_VisualizationController.get(), &VisualizationController::finalize);
134 138
135 139 connect(&impl->m_CatalogueControllerThread, &QThread::started,
136 140 impl->m_CatalogueController.get(), &CatalogueController::initialize);
137 141 connect(&impl->m_CatalogueControllerThread, &QThread::finished,
138 142 impl->m_CatalogueController.get(), &CatalogueController::finalize);
139 143
140 144 impl->m_DataSourceControllerThread.start();
141 145 impl->m_NetworkControllerThread.start();
142 146 impl->m_VariableControllerThread.start();
143 147 impl->m_VisualizationControllerThread.start();
144 148 impl->m_CatalogueControllerThread.start();
145 149 }
146 150
147 151 SqpApplication::~SqpApplication()
148 152 {
149 153 }
150 154
151 155 void SqpApplication::initialize()
152 156 {
153 157 }
154 158
155 159 DataSourceController &SqpApplication::dataSourceController() noexcept
156 160 {
157 161 return *impl->m_DataSourceController;
158 162 }
159 163
160 164 NetworkController &SqpApplication::networkController() noexcept
161 165 {
162 166 return *impl->m_NetworkController;
163 167 }
164 168
165 169 TimeController &SqpApplication::timeController() noexcept
166 170 {
167 171 return *impl->m_TimeController;
168 172 }
169 173
170 174 VariableController &SqpApplication::variableController() noexcept
171 175 {
172 176 return *impl->m_VariableController;
173 177 }
174 178
175 179 VisualizationController &SqpApplication::visualizationController() noexcept
176 180 {
177 181 return *impl->m_VisualizationController;
178 182 }
179 183
180 DragDropHelper &SqpApplication::dragDropHelper() noexcept
184 DragDropGuiController &SqpApplication::dragDropGuiController() noexcept
185 {
186 return *impl->m_DragDropGuiController;
187 }
188
189 ActionsGuiController &SqpApplication::actionsGuiController() noexcept
181 190 {
182 return *impl->m_DragDropHelper;
191 return *impl->m_ActionsGuiController;
183 192 }
184 193
185 194 SqpApplication::PlotsInteractionMode SqpApplication::plotsInteractionMode() const
186 195 {
187 196 return impl->m_PlotInterractionMode;
188 197 }
189 198
190 199 void SqpApplication::setPlotsInteractionMode(SqpApplication::PlotsInteractionMode mode)
191 200 {
192 201 impl->m_PlotInterractionMode = mode;
193 202 }
194 203
195 204 SqpApplication::PlotsCursorMode SqpApplication::plotsCursorMode() const
196 205 {
197 206 return impl->m_PlotCursorMode;
198 207 }
199 208
200 209 void SqpApplication::setPlotsCursorMode(SqpApplication::PlotsCursorMode mode)
201 210 {
202 211 impl->m_PlotCursorMode = mode;
203 212 }
@@ -1,154 +1,154
1 1 #include "TimeWidget/TimeWidget.h"
2 2 #include "ui_TimeWidget.h"
3 3
4 4 #include <Common/DateUtils.h>
5 5 #include <Common/MimeTypesDef.h>
6 6
7 #include <DragAndDrop/DragDropHelper.h>
7 #include <DragAndDrop/DragDropGuiController.h>
8 8 #include <SqpApplication.h>
9 9 #include <Time/TimeController.h>
10 10
11 11 #include <QDrag>
12 12 #include <QDragEnterEvent>
13 13 #include <QDropEvent>
14 14 #include <QMimeData>
15 15
16 16
17 17 struct TimeWidget::TimeWidgetPrivate {
18 18
19 19 explicit TimeWidgetPrivate() {}
20 20
21 21 QPoint m_DragStartPosition;
22 22 };
23 23
24 24 TimeWidget::TimeWidget(QWidget *parent)
25 25 : QWidget{parent},
26 26 ui{new Ui::TimeWidget},
27 27 impl{spimpl::make_unique_impl<TimeWidgetPrivate>()}
28 28 {
29 29 ui->setupUi(this);
30 30
31 31 ui->applyToolButton->setIcon(sqpApp->style()->standardIcon(QStyle::SP_DialogApplyButton));
32 32
33 33 // Connection
34 34 connect(ui->startDateTimeEdit, &QDateTimeEdit::dateTimeChanged, this,
35 35 &TimeWidget::onTimeUpdateRequested);
36 36
37 37 connect(ui->endDateTimeEdit, &QDateTimeEdit::dateTimeChanged, this,
38 38 &TimeWidget::onTimeUpdateRequested);
39 39
40 40
41 41 connect(ui->applyToolButton, &QToolButton::clicked, &sqpApp->timeController(),
42 42 &TimeController::onTimeNotify);
43 43
44 44 // Initialisation
45 45 auto endDateTime = QDateTime::currentDateTimeUtc();
46 46 auto startDateTime = endDateTime.addSecs(-3600); // one hour before
47 47
48 48 ui->startDateTimeEdit->setDateTime(startDateTime);
49 49 ui->endDateTimeEdit->setDateTime(endDateTime);
50 50
51 51 auto dateTime = SqpRange{DateUtils::secondsSinceEpoch(startDateTime),
52 52 DateUtils::secondsSinceEpoch(endDateTime)};
53 53
54 54 sqpApp->timeController().onTimeToUpdate(dateTime);
55 55 }
56 56
57 57
58 58 TimeWidget::~TimeWidget()
59 59 {
60 60 delete ui;
61 61 }
62 62
63 63 void TimeWidget::setTimeRange(SqpRange time)
64 64 {
65 65 auto startDateTime = DateUtils::dateTime(time.m_TStart);
66 66 auto endDateTime = DateUtils::dateTime(time.m_TEnd);
67 67
68 68 ui->startDateTimeEdit->setDateTime(startDateTime);
69 69 ui->endDateTimeEdit->setDateTime(endDateTime);
70 70 }
71 71
72 72 SqpRange TimeWidget::timeRange() const
73 73 {
74 74 return SqpRange{DateUtils::secondsSinceEpoch(ui->startDateTimeEdit->dateTime()),
75 75 DateUtils::secondsSinceEpoch(ui->endDateTimeEdit->dateTime())};
76 76 }
77 77
78 78 void TimeWidget::onTimeUpdateRequested()
79 79 {
80 80 auto dateTime = timeRange();
81 81 emit timeUpdated(std::move(dateTime));
82 82 }
83 83
84 84 void TimeWidget::dragEnterEvent(QDragEnterEvent *event)
85 85 {
86 86 if (event->mimeData()->hasFormat(MIME_TYPE_TIME_RANGE)) {
87 87 event->acceptProposedAction();
88 88 setStyleSheet("QDateTimeEdit{background-color: #BBD5EE; border:2px solid #2A7FD4}");
89 89 }
90 90 else {
91 91 event->ignore();
92 92 }
93 93 }
94 94
95 95 void TimeWidget::dragLeaveEvent(QDragLeaveEvent *event)
96 96 {
97 97 setStyleSheet(QString{});
98 98 }
99 99
100 100 void TimeWidget::dropEvent(QDropEvent *event)
101 101 {
102 102 if (event->mimeData()->hasFormat(MIME_TYPE_TIME_RANGE)) {
103 103 auto mimeData = event->mimeData()->data(MIME_TYPE_TIME_RANGE);
104 104 auto timeRange = TimeController::timeRangeForMimeData(mimeData);
105 105
106 106 setTimeRange(timeRange);
107 107 }
108 108 else {
109 109 event->ignore();
110 110 }
111 111
112 112 setStyleSheet(QString{});
113 113 }
114 114
115 115
116 116 void TimeWidget::mousePressEvent(QMouseEvent *event)
117 117 {
118 118 if (event->button() == Qt::LeftButton) {
119 119 impl->m_DragStartPosition = event->pos();
120 120 }
121 121
122 122 QWidget::mousePressEvent(event);
123 123 }
124 124
125 125 void TimeWidget::mouseMoveEvent(QMouseEvent *event)
126 126 {
127 127 if (!(event->buttons() & Qt::LeftButton)) {
128 128 return;
129 129 }
130 130
131 131 if ((event->pos() - impl->m_DragStartPosition).manhattanLength()
132 132 < QApplication::startDragDistance()) {
133 133 return;
134 134 }
135 135
136 136 // Note: The management of the drag object is done by Qt
137 137 auto drag = new QDrag{this};
138 138
139 139 auto mimeData = new QMimeData;
140 140 auto timeData = TimeController::mimeDataForTimeRange(timeRange());
141 141 mimeData->setData(MIME_TYPE_TIME_RANGE, timeData);
142 142
143 143 drag->setMimeData(mimeData);
144 144
145 145 auto pixmap = QPixmap{":/icones/time.png"};
146 146 drag->setPixmap(pixmap.scaledToWidth(22));
147 147
148 sqpApp->dragDropHelper().resetDragAndDrop();
148 sqpApp->dragDropGuiController().resetDragAndDrop();
149 149
150 150 // Note: The exec() is blocking on windows but not on linux and macOS
151 151 drag->exec(Qt::MoveAction | Qt::CopyAction);
152 152
153 153 QWidget::mouseMoveEvent(event);
154 154 }
@@ -1,15 +1,15
1 1 #include "Variable/VariableInspectorTableView.h"
2 2
3 #include "DragAndDrop/DragDropHelper.h"
3 #include "DragAndDrop/DragDropGuiController.h"
4 4 #include "SqpApplication.h"
5 5
6 6 VariableInspectorTableView::VariableInspectorTableView(QWidget *parent) : QTableView(parent)
7 7 {
8 8 }
9 9
10 10 void VariableInspectorTableView::startDrag(Qt::DropActions supportedActions)
11 11 {
12 12 // Resets the drag&drop operations before it's starting
13 sqpApp->dragDropHelper().resetDragAndDrop();
13 sqpApp->dragDropGuiController().resetDragAndDrop();
14 14 QTableView::startDrag(supportedActions);
15 15 }
@@ -1,240 +1,240
1 1 #include <Variable/RenameVariableDialog.h>
2 2 #include <Variable/Variable.h>
3 3 #include <Variable/VariableController.h>
4 4 #include <Variable/VariableInspectorWidget.h>
5 5 #include <Variable/VariableMenuHeaderWidget.h>
6 6 #include <Variable/VariableModel.h>
7 7
8 8 #include <ui_VariableInspectorWidget.h>
9 9
10 10 #include <QMouseEvent>
11 11 #include <QSortFilterProxyModel>
12 12 #include <QStyledItemDelegate>
13 13 #include <QWidgetAction>
14 14
15 #include <DragAndDrop/DragDropHelper.h>
15 #include <DragAndDrop/DragDropGuiController.h>
16 16 #include <SqpApplication.h>
17 17
18 18 Q_LOGGING_CATEGORY(LOG_VariableInspectorWidget, "VariableInspectorWidget")
19 19
20 20
21 21 class QProgressBarItemDelegate : public QStyledItemDelegate {
22 22
23 23 public:
24 24 QProgressBarItemDelegate(QObject *parent) : QStyledItemDelegate{parent} {}
25 25
26 26 void paint(QPainter *painter, const QStyleOptionViewItem &option,
27 27 const QModelIndex &index) const
28 28 {
29 29 auto data = index.data(Qt::DisplayRole);
30 30 auto progressData = index.data(VariableRoles::ProgressRole);
31 31 if (data.isValid() && progressData.isValid()) {
32 32 auto name = data.value<QString>();
33 33 auto progress = progressData.value<double>();
34 34 if (progress > 0) {
35 35 auto cancelButtonWidth = 20;
36 36 auto progressBarOption = QStyleOptionProgressBar{};
37 37 auto progressRect = option.rect;
38 38 progressRect.setWidth(progressRect.width() - cancelButtonWidth);
39 39 progressBarOption.rect = progressRect;
40 40 progressBarOption.minimum = 0;
41 41 progressBarOption.maximum = 100;
42 42 progressBarOption.progress = progress;
43 43 progressBarOption.text
44 44 = QString("%1 %2").arg(name).arg(QString::number(progress, 'f', 2) + "%");
45 45 progressBarOption.textVisible = true;
46 46 progressBarOption.textAlignment = Qt::AlignCenter;
47 47
48 48
49 49 QApplication::style()->drawControl(QStyle::CE_ProgressBar, &progressBarOption,
50 50 painter);
51 51
52 52 // Cancel button
53 53 auto buttonRect = QRect(progressRect.right(), option.rect.top(), cancelButtonWidth,
54 54 option.rect.height());
55 55 auto buttonOption = QStyleOptionButton{};
56 56 buttonOption.rect = buttonRect;
57 57 buttonOption.text = "X";
58 58
59 59 QApplication::style()->drawControl(QStyle::CE_PushButton, &buttonOption, painter);
60 60 }
61 61 else {
62 62 QStyledItemDelegate::paint(painter, option, index);
63 63 }
64 64 }
65 65 else {
66 66 QStyledItemDelegate::paint(painter, option, index);
67 67 }
68 68 }
69 69
70 70 bool editorEvent(QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option,
71 71 const QModelIndex &index)
72 72 {
73 73 if (event->type() == QEvent::MouseButtonRelease) {
74 74 auto data = index.data(Qt::DisplayRole);
75 75 auto progressData = index.data(VariableRoles::ProgressRole);
76 76 if (data.isValid() && progressData.isValid()) {
77 77 auto cancelButtonWidth = 20;
78 78 auto progressRect = option.rect;
79 79 progressRect.setWidth(progressRect.width() - cancelButtonWidth);
80 80 // Cancel button
81 81 auto buttonRect = QRect(progressRect.right(), option.rect.top(), cancelButtonWidth,
82 82 option.rect.height());
83 83
84 84 auto e = (QMouseEvent *)event;
85 85 auto clickX = e->x();
86 86 auto clickY = e->y();
87 87
88 88 auto x = buttonRect.left(); // the X coordinate
89 89 auto y = buttonRect.top(); // the Y coordinate
90 90 auto w = buttonRect.width(); // button width
91 91 auto h = buttonRect.height(); // button height
92 92
93 93 if (clickX > x && clickX < x + w) {
94 94 if (clickY > y && clickY < y + h) {
95 95 auto variableModel = sqpApp->variableController().variableModel();
96 96 variableModel->abortProgress(index);
97 97 }
98 98 return true;
99 99 }
100 100 else {
101 101 return QStyledItemDelegate::editorEvent(event, model, option, index);
102 102 }
103 103 }
104 104 else {
105 105 return QStyledItemDelegate::editorEvent(event, model, option, index);
106 106 }
107 107 }
108 108 else {
109 109 return QStyledItemDelegate::editorEvent(event, model, option, index);
110 110 }
111 111
112 112
113 113 return QStyledItemDelegate::editorEvent(event, model, option, index);
114 114 }
115 115 };
116 116
117 117 VariableInspectorWidget::VariableInspectorWidget(QWidget *parent)
118 118 : QWidget{parent},
119 119 ui{new Ui::VariableInspectorWidget},
120 120 m_ProgressBarItemDelegate{new QProgressBarItemDelegate{this}}
121 121 {
122 122 ui->setupUi(this);
123 123
124 124 // Sets model for table
125 125 // auto sortFilterModel = new QSortFilterProxyModel{this};
126 126 // sortFilterModel->setSourceModel(sqpApp->variableController().variableModel());
127 127
128 128 auto variableModel = sqpApp->variableController().variableModel();
129 129 ui->tableView->setModel(variableModel);
130 130
131 131 // Adds extra signal/slot between view and model, so the view can be updated instantly when
132 132 // there is a change of data in the model
133 133 connect(variableModel, SIGNAL(dataChanged(const QModelIndex &, const QModelIndex &)), this,
134 134 SLOT(refresh()));
135 135
136 136 ui->tableView->setSelectionModel(sqpApp->variableController().variableSelectionModel());
137 137 ui->tableView->setItemDelegateForColumn(0, m_ProgressBarItemDelegate);
138 138
139 139 // Fixes column sizes
140 140 auto model = ui->tableView->model();
141 141 const auto count = model->columnCount();
142 142 for (auto i = 0; i < count; ++i) {
143 143 ui->tableView->setColumnWidth(
144 144 i, model->headerData(i, Qt::Horizontal, Qt::SizeHintRole).toSize().width());
145 145 }
146 146
147 147 // Sets selection options
148 148 ui->tableView->setSelectionBehavior(QTableView::SelectRows);
149 149 ui->tableView->setSelectionMode(QTableView::ExtendedSelection);
150 150
151 151 // Connection to show a menu when right clicking on the tree
152 152 ui->tableView->setContextMenuPolicy(Qt::CustomContextMenu);
153 153 connect(ui->tableView, &QTableView::customContextMenuRequested, this,
154 154 &VariableInspectorWidget::onTableMenuRequested);
155 155 }
156 156
157 157 VariableInspectorWidget::~VariableInspectorWidget()
158 158 {
159 159 delete ui;
160 160 }
161 161
162 162 void VariableInspectorWidget::onTableMenuRequested(const QPoint &pos) noexcept
163 163 {
164 164 auto selectedRows = ui->tableView->selectionModel()->selectedRows();
165 165
166 166 // Gets the model to retrieve the underlying selected variables
167 167 auto model = sqpApp->variableController().variableModel();
168 168 auto selectedVariables = QVector<std::shared_ptr<Variable> >{};
169 169 for (const auto &selectedRow : qAsConst(selectedRows)) {
170 170 if (auto selectedVariable = model->variable(selectedRow.row())) {
171 171 selectedVariables.push_back(selectedVariable);
172 172 }
173 173 }
174 174
175 175 QMenu tableMenu{};
176 176
177 177 // Emits a signal so that potential receivers can populate the menu before displaying it
178 178 emit tableMenuAboutToBeDisplayed(&tableMenu, selectedVariables);
179 179
180 180 // Adds menu-specific actions
181 181 if (!selectedVariables.isEmpty()) {
182 182 tableMenu.addSeparator();
183 183
184 184 // 'Rename' and 'Duplicate' actions (only if one variable selected)
185 185 if (selectedVariables.size() == 1) {
186 186 auto selectedVariable = selectedVariables.front();
187 187
188 188 auto duplicateFun = [varW = std::weak_ptr<Variable>(selectedVariable)]()
189 189 {
190 190 if (auto var = varW.lock()) {
191 191 sqpApp->variableController().cloneVariable(var);
192 192 }
193 193 };
194 194
195 195 tableMenu.addAction(tr("Duplicate"), duplicateFun);
196 196
197 197 auto renameFun = [ varW = std::weak_ptr<Variable>(selectedVariable), &model, this ]()
198 198 {
199 199 if (auto var = varW.lock()) {
200 200 // Generates forbidden names (names associated to existing variables)
201 201 auto allVariables = model->variables();
202 202 auto forbiddenNames = QVector<QString>(allVariables.size());
203 203 std::transform(allVariables.cbegin(), allVariables.cend(),
204 204 forbiddenNames.begin(),
205 205 [](const auto &variable) { return variable->name(); });
206 206
207 207 RenameVariableDialog dialog{var->name(), forbiddenNames, this};
208 208 if (dialog.exec() == QDialog::Accepted) {
209 209 var->setName(dialog.name());
210 210 }
211 211 }
212 212 };
213 213
214 214 tableMenu.addAction(tr("Rename..."), renameFun);
215 215 }
216 216
217 217 // 'Delete' action
218 218 auto deleteFun = [&selectedVariables]() {
219 219 sqpApp->variableController().deleteVariables(selectedVariables);
220 220 };
221 221
222 222 tableMenu.addAction(QIcon{":/icones/delete.png"}, tr("Delete"), deleteFun);
223 223 }
224 224
225 225 if (!tableMenu.isEmpty()) {
226 226 // Generates menu header (inserted before first action)
227 227 auto firstAction = tableMenu.actions().first();
228 228 auto headerAction = new QWidgetAction{&tableMenu};
229 229 headerAction->setDefaultWidget(new VariableMenuHeaderWidget{selectedVariables, &tableMenu});
230 230 tableMenu.insertAction(firstAction, headerAction);
231 231
232 232 // Displays menu
233 233 tableMenu.exec(QCursor::pos());
234 234 }
235 235 }
236 236
237 237 void VariableInspectorWidget::refresh() noexcept
238 238 {
239 239 ui->tableView->viewport()->update();
240 240 }
@@ -1,499 +1,496
1 1 #include "Visualization/VisualizationDragDropContainer.h"
2 #include "DragAndDrop/DragDropHelper.h"
2 #include "DragAndDrop/DragDropGuiController.h"
3 3 #include "SqpApplication.h"
4 4 #include "Visualization/VisualizationDragWidget.h"
5 5
6 6 #include "Common/VisualizationDef.h"
7 7
8 8 #include <QDrag>
9 9 #include <QDragEnterEvent>
10 10 #include <QVBoxLayout>
11 11
12 12 #include <cmath>
13 13 #include <memory>
14 14
15 15 Q_LOGGING_CATEGORY(LOG_VisualizationDragDropContainer, "VisualizationDragDropContainer")
16 16
17 17 auto DRAGGED_MINIATURE_WIDTH = 200; // in pixels
18 18
19 19 struct VisualizationDragDropContainer::VisualizationDragDropContainerPrivate {
20 20
21 21 QVBoxLayout *m_Layout;
22 22 QHash<QString, VisualizationDragDropContainer::DropBehavior> m_AcceptedMimeTypes;
23 23 QString m_PlaceHolderText;
24 DragDropHelper::PlaceHolderType m_PlaceHolderType;
24 DragDropGuiController::PlaceHolderType m_PlaceHolderType;
25 25
26 26 VisualizationDragDropContainer::AcceptMimeDataFunction m_AcceptMimeDataFun
27 27 = [](auto mimeData) { return true; };
28 28 VisualizationDragDropContainer::AcceptDragWidgetFunction m_AcceptDragWidgetFun
29 29 = [](auto dragWidget, auto mimeData) { return true; };
30 30
31 31 int m_MinContainerHeight = 0;
32 32
33 33 explicit VisualizationDragDropContainerPrivate(QWidget *widget)
34 : m_PlaceHolderType(DragDropHelper::PlaceHolderType::Graph)
34 : m_PlaceHolderType(DragDropGuiController::PlaceHolderType::Graph)
35 35 {
36 36 m_Layout = new QVBoxLayout(widget);
37 37 m_Layout->setContentsMargins(0, 0, 0, 0);
38 38 }
39 39
40 40 bool acceptMimeData(const QMimeData *data) const
41 41 {
42 42 auto accepted = false;
43 43 for (auto it = m_AcceptedMimeTypes.constBegin(); it != m_AcceptedMimeTypes.constEnd();
44 44 ++it) {
45 45 const auto &type = it.key();
46 46 const auto &behavior = it.value();
47 47
48 48 if (data->hasFormat(type)) {
49 49 if (behavior != DropBehavior::Forbidden) {
50 50 accepted = true;
51 51 }
52 52 else {
53 53 accepted = false;
54 54 break;
55 55 }
56 56 }
57 57 }
58 58
59 59 if (accepted) {
60 60 accepted = m_AcceptMimeDataFun(data);
61 61 }
62 62
63 63 return accepted;
64 64 }
65 65
66 66 bool allowMergeForMimeData(const QMimeData *data) const
67 67 {
68 68 auto result = false;
69 69 for (auto it = m_AcceptedMimeTypes.constBegin(); it != m_AcceptedMimeTypes.constEnd();
70 70 ++it) {
71 71
72 72 if (data->hasFormat(it.key())
73 73 && (it.value() == VisualizationDragDropContainer::DropBehavior::Merged
74 74 || it.value()
75 75 == VisualizationDragDropContainer::DropBehavior::InsertedAndMerged)) {
76 76 result = true;
77 77 }
78 78 else if (data->hasFormat(it.key())
79 79 && it.value() == VisualizationDragDropContainer::DropBehavior::Inserted) {
80 80 // Merge is forbidden if the mime data contain an acceptable type which cannot be
81 81 // merged
82 82 result = false;
83 83 break;
84 84 }
85 85 }
86 86
87 87 return result;
88 88 }
89 89
90 90 bool allowInsertForMimeData(const QMimeData *data) const
91 91 {
92 92 for (auto it = m_AcceptedMimeTypes.constBegin(); it != m_AcceptedMimeTypes.constEnd();
93 93 ++it) {
94 94 if (data->hasFormat(it.key())
95 95 && (it.value() == VisualizationDragDropContainer::DropBehavior::Inserted
96 96 || it.value()
97 97 == VisualizationDragDropContainer::DropBehavior::InsertedAndMerged)) {
98 98 return true;
99 99 }
100 100 }
101 101
102 102 return false;
103 103 }
104 104
105 105 bool hasPlaceHolder() const
106 106 {
107 return sqpApp->dragDropHelper().placeHolder().parentWidget() == m_Layout->parentWidget();
107 return sqpApp->dragDropGuiController().placeHolder().parentWidget()
108 == m_Layout->parentWidget();
108 109 }
109 110
110 111 VisualizationDragWidget *getChildDragWidgetAt(const QWidget *parent, const QPoint &pos) const
111 112 {
112 113 VisualizationDragWidget *dragWidget = nullptr;
113 114
114 115 for (auto child : parent->children()) {
115 116 auto widget = qobject_cast<VisualizationDragWidget *>(child);
116 117 if (widget && widget->isVisible()) {
117 118 if (widget->frameGeometry().contains(pos)) {
118 119 dragWidget = widget;
119 120 break;
120 121 }
121 122 }
122 123 }
123 124
124 125 return dragWidget;
125 126 }
126 127
127 128 bool cursorIsInContainer(QWidget *container) const
128 129 {
129 130 auto widgetUnderMouse = sqpApp->widgetAt(QCursor::pos());
130 131 return container->isAncestorOf(widgetUnderMouse) && widgetUnderMouse != container
131 && sqpApp->dragDropHelper().placeHolder().isAncestorOf(widgetUnderMouse);
132 && sqpApp->dragDropGuiController().placeHolder().isAncestorOf(widgetUnderMouse);
132 133 }
133 134
134 135 int countDragWidget(const QWidget *parent, bool onlyVisible = false) const
135 136 {
136 137 auto nbGraph = 0;
137 138 for (auto child : parent->children()) {
138 139 if (qobject_cast<VisualizationDragWidget *>(child)) {
139 140 if (!onlyVisible || qobject_cast<VisualizationDragWidget *>(child)->isVisible()) {
140 141 nbGraph += 1;
141 142 }
142 143 }
143 144 }
144 145
145 146 return nbGraph;
146 147 }
147 148
148 149 bool findPlaceHolderPosition(const QPoint &pos, const QMimeData *mimeData, bool canInsert,
149 150 bool canMerge, const VisualizationDragDropContainer *container);
150 151 };
151 152
152 153 VisualizationDragDropContainer::VisualizationDragDropContainer(QWidget *parent)
153 154 : QFrame{parent},
154 155 impl{spimpl::make_unique_impl<VisualizationDragDropContainerPrivate>(this)}
155 156 {
156 157 setAcceptDrops(true);
157 158 }
158 159
159 160 void VisualizationDragDropContainer::addDragWidget(VisualizationDragWidget *dragWidget)
160 161 {
161 162 impl->m_Layout->addWidget(dragWidget);
162 163 disconnect(dragWidget, &VisualizationDragWidget::dragDetected, nullptr, nullptr);
163 164 connect(dragWidget, &VisualizationDragWidget::dragDetected, this,
164 165 &VisualizationDragDropContainer::startDrag);
165 166 }
166 167
167 168 void VisualizationDragDropContainer::insertDragWidget(int index,
168 169 VisualizationDragWidget *dragWidget)
169 170 {
170 171 impl->m_Layout->insertWidget(index, dragWidget);
171 172 disconnect(dragWidget, &VisualizationDragWidget::dragDetected, nullptr, nullptr);
172 173 connect(dragWidget, &VisualizationDragWidget::dragDetected, this,
173 174 &VisualizationDragDropContainer::startDrag);
174 175 }
175 176
176 177 void VisualizationDragDropContainer::setMimeType(
177 178 const QString &mimeType, VisualizationDragDropContainer::DropBehavior behavior)
178 179 {
179 180 impl->m_AcceptedMimeTypes[mimeType] = behavior;
180 181 }
181 182
182 183 int VisualizationDragDropContainer::countDragWidget() const
183 184 {
184 185 return impl->countDragWidget(this);
185 186 }
186 187
187 188 void VisualizationDragDropContainer::setAcceptMimeDataFunction(
188 189 VisualizationDragDropContainer::AcceptMimeDataFunction fun)
189 190 {
190 191 impl->m_AcceptMimeDataFun = fun;
191 192 }
192 193
193 194 void VisualizationDragDropContainer::setAcceptDragWidgetFunction(
194 195 VisualizationDragDropContainer::AcceptDragWidgetFunction fun)
195 196 {
196 197 impl->m_AcceptDragWidgetFun = fun;
197 198 }
198 199
199 void VisualizationDragDropContainer::setPlaceHolderType(DragDropHelper::PlaceHolderType type,
200 void VisualizationDragDropContainer::setPlaceHolderType(DragDropGuiController::PlaceHolderType type,
200 201 const QString &placeHolderText)
201 202 {
202 203 impl->m_PlaceHolderType = type;
203 204 impl->m_PlaceHolderText = placeHolderText;
204 205 }
205 206
206 207 void VisualizationDragDropContainer::startDrag(VisualizationDragWidget *dragWidget,
207 208 const QPoint &dragPosition)
208 209 {
209 auto &helper = sqpApp->dragDropHelper();
210 auto &helper = sqpApp->dragDropGuiController();
210 211 helper.resetDragAndDrop();
211 212
212 213 // Note: The management of the drag object is done by Qt
213 214 auto drag = new QDrag{dragWidget};
214 215
215 216 auto mimeData = dragWidget->mimeData(dragPosition);
216 217 drag->setMimeData(mimeData);
217 218
218 219 auto pixmap = dragWidget->customDragPixmap(dragPosition);
219 220 if (pixmap.isNull()) {
220 221 pixmap = QPixmap{dragWidget->size()};
221 222 dragWidget->render(&pixmap);
222 223 }
223 224
224 225 drag->setPixmap(pixmap.scaled(DRAGGED_MINIATURE_WIDTH, DRAGGED_MINIATURE_WIDTH,
225 226 Qt::KeepAspectRatio, Qt::SmoothTransformation));
226 227
227 228 auto image = pixmap.toImage();
228 229 mimeData->setImageData(image);
229 230 mimeData->setUrls({helper.imageTemporaryUrl(image)});
230 231
231 232 if (impl->m_Layout->indexOf(dragWidget) >= 0) {
232 233
233 234 if (impl->acceptMimeData(mimeData) && impl->allowInsertForMimeData(mimeData)) {
234 235 helper.setCurrentDragWidget(dragWidget);
235 236
236 237 if (impl->cursorIsInContainer(this)) {
237 238 auto dragWidgetIndex = impl->m_Layout->indexOf(dragWidget);
238 239 helper.insertPlaceHolder(impl->m_Layout, dragWidgetIndex, impl->m_PlaceHolderType,
239 240 impl->m_PlaceHolderText);
240 241 dragWidget->setVisible(false);
241 242 }
242 243 else {
243 244 // The drag starts directly outside the drop zone
244 245 // do not add the placeHolder
245 246 }
246 247 }
247 248
248 249 drag->exec(Qt::MoveAction | Qt::CopyAction, Qt::MoveAction);
249 250
250 251 helper.doCloseWidgets();
251 252 }
252 253 else {
253 254 qCWarning(LOG_VisualizationDragDropContainer())
254 255 << tr("VisualizationDragDropContainer::startDrag, drag aborted, the specified "
255 256 "VisualizationDragWidget is not found in this container.");
256 257 }
257 258 }
258 259
259 260 void VisualizationDragDropContainer::dragEnterEvent(QDragEnterEvent *event)
260 261 {
261 262 if (impl->acceptMimeData(event->mimeData())) {
262 263 event->acceptProposedAction();
263 264
264 auto &helper = sqpApp->dragDropHelper();
265 auto &helper = sqpApp->dragDropGuiController();
265 266
266 267 if (!impl->hasPlaceHolder()) {
267 268 auto dragWidget = helper.getCurrentDragWidget();
268 269
269 270 if (dragWidget) {
270 271 // If the drag&drop is internal to the visualization, entering the container hide
271 272 // the dragWidget which was made visible by the dragLeaveEvent
272 273 auto parentWidget
273 274 = qobject_cast<VisualizationDragDropContainer *>(dragWidget->parentWidget());
274 275 if (parentWidget) {
275 276 dragWidget->setVisible(false);
276 277 }
277 278 }
278 279
279 280 auto canMerge = impl->allowMergeForMimeData(event->mimeData());
280 281 auto canInsert = impl->allowInsertForMimeData(event->mimeData());
281 282 if (!impl->findPlaceHolderPosition(event->pos(), event->mimeData(), canInsert, canMerge,
282 283 this)) {
283 284 event->ignore();
284 285 }
285 286 }
286 287 else {
287 288 // do nothing
288 289 }
289 290 }
290 291 else {
291 292 event->ignore();
292 293 }
293 294
294 295 QWidget::dragEnterEvent(event);
295 296 }
296 297
297 298 void VisualizationDragDropContainer::dragLeaveEvent(QDragLeaveEvent *event)
298 299 {
299 300 Q_UNUSED(event);
300 301
301 auto &helper = sqpApp->dragDropHelper();
302 auto &helper = sqpApp->dragDropGuiController();
302 303
303 304 if (!impl->cursorIsInContainer(this)) {
304 305 helper.removePlaceHolder();
305 306 helper.setHightlightedDragWidget(nullptr);
306 307 impl->m_MinContainerHeight = 0;
307 308
308 309 auto dragWidget = helper.getCurrentDragWidget();
309 310 if (dragWidget) {
310 311 // dragWidget has a value only if the drag is started from the visualization
311 312 // In that case, shows the drag widget at its original place
312 313 // So the drag widget doesn't stay hidden if the drop occurs outside the visualization
313 314 // drop zone (It is not possible to catch a drop event outside of the application)
314 315
315 316 if (dragWidget) {
316 317 dragWidget->setVisible(true);
317 318 }
318 319 }
319 320 }
320 321 else {
321 322 // Leave event probably received for a child widget.
322 323 // Do nothing.
323 324 // Note: The DragLeave event, doesn't have any mean to determine who sent it.
324 325 }
325 326
326 327 QWidget::dragLeaveEvent(event);
327 328 }
328 329
329 330 void VisualizationDragDropContainer::dragMoveEvent(QDragMoveEvent *event)
330 331 {
331 332 if (impl->acceptMimeData(event->mimeData())) {
332 333 auto canMerge = impl->allowMergeForMimeData(event->mimeData());
333 334 auto canInsert = impl->allowInsertForMimeData(event->mimeData());
334 335 impl->findPlaceHolderPosition(event->pos(), event->mimeData(), canInsert, canMerge, this);
335 336 }
336 337 else {
337 338 event->ignore();
338 339 }
339 340
340 341 QWidget::dragMoveEvent(event);
341 342 }
342 343
343 344 void VisualizationDragDropContainer::dropEvent(QDropEvent *event)
344 345 {
345 auto &helper = sqpApp->dragDropHelper();
346 auto &helper = sqpApp->dragDropGuiController();
346 347
347 348 if (impl->acceptMimeData(event->mimeData())) {
348 349 auto dragWidget = helper.getCurrentDragWidget();
349 350 if (impl->hasPlaceHolder()) {
350 351 // drop where the placeHolder is located
351 352
352 353 auto canInsert = impl->allowInsertForMimeData(event->mimeData());
353 354 if (canInsert) {
354 355 auto droppedIndex = impl->m_Layout->indexOf(&helper.placeHolder());
355 356
356 357 if (dragWidget) {
357 358 auto dragWidgetIndex = impl->m_Layout->indexOf(dragWidget);
358 359 if (dragWidgetIndex >= 0 && dragWidgetIndex < droppedIndex) {
359 360 // Correction of the index if the drop occurs in the same container
360 361 // and if the drag is started from the visualization (in that case, the
361 362 // dragWidget is hidden)
362 363 droppedIndex -= 1;
363 364 }
364 365
365 366 dragWidget->setVisible(true);
366 367 }
367 368
368 369 event->acceptProposedAction();
369 370
370 371 helper.removePlaceHolder();
371 372
372 373 emit dropOccuredInContainer(droppedIndex, event->mimeData());
373 374 }
374 375 else {
375 376 qCWarning(LOG_VisualizationDragDropContainer()) << tr(
376 377 "VisualizationDragDropContainer::dropEvent, dropping on the placeHolder, but "
377 378 "the insertion is forbidden.");
378 379 Q_ASSERT(false);
379 380 }
380 381 }
381 382 else if (helper.getHightlightedDragWidget()) {
382 383 // drop on the highlighted widget
383 384
384 385 auto canMerge = impl->allowMergeForMimeData(event->mimeData());
385 386 if (canMerge) {
386 387 event->acceptProposedAction();
387 388 emit dropOccuredOnWidget(helper.getHightlightedDragWidget(), event->mimeData());
388 389 }
389 390 else {
390 391 qCWarning(LOG_VisualizationDragDropContainer())
391 392 << tr("VisualizationDragDropContainer::dropEvent, dropping on a widget, but "
392 393 "the merge is forbidden.");
393 394 Q_ASSERT(false);
394 395 }
395 396 }
396 397 }
397 398 else {
398 399 event->ignore();
399 400 }
400 401
401 sqpApp->dragDropHelper().setHightlightedDragWidget(nullptr);
402 sqpApp->dragDropGuiController().setHightlightedDragWidget(nullptr);
402 403 impl->m_MinContainerHeight = 0;
403 404
404 405 QWidget::dropEvent(event);
405 406 }
406 407
407 408
408 409 bool VisualizationDragDropContainer::VisualizationDragDropContainerPrivate::findPlaceHolderPosition(
409 410 const QPoint &pos, const QMimeData *mimeData, bool canInsert, bool canMerge,
410 411 const VisualizationDragDropContainer *container)
411 412 {
412 auto &helper = sqpApp->dragDropHelper();
413 auto &helper = sqpApp->dragDropGuiController();
413 414
414 415 auto absPos = container->mapToGlobal(pos);
415 416 auto isOnPlaceHolder = helper.placeHolder().isAncestorOf(sqpApp->widgetAt(absPos));
416 417
417 418 if (countDragWidget(container, true) == 0) {
418 419 // Drop on an empty container, just add the placeHolder at the top
419 420 helper.insertPlaceHolder(m_Layout, 0, m_PlaceHolderType, m_PlaceHolderText);
420 421 }
421 422 else if (!isOnPlaceHolder) {
422 423 auto nbDragWidget = countDragWidget(container);
423 424 if (nbDragWidget > 0) {
424 425
425 426 if (m_MinContainerHeight == 0) {
426 427 m_MinContainerHeight = container->size().height();
427 428 }
428 429
429 430 m_MinContainerHeight = qMin(m_MinContainerHeight, container->size().height());
430 431 auto graphHeight = qMax(m_MinContainerHeight / nbDragWidget, GRAPH_MINIMUM_HEIGHT);
431 432
432 433 auto posY = pos.y();
433 434 auto dropIndex = floor(posY / graphHeight);
434 auto zoneSize = qMin(graphHeight / 4.0, 75.0);
435 auto zoneSize = graphHeight / 4.0;
435 436
436 437
437 438 auto isOnTop = posY < dropIndex * graphHeight + zoneSize;
438 439 auto isOnBottom = posY > (dropIndex + 1) * graphHeight - zoneSize;
439 440
440 441 auto placeHolderIndex = m_Layout->indexOf(&(helper.placeHolder()));
441 442
442 443 auto dragWidgetHovered = getChildDragWidgetAt(container, pos);
443 444
444 if (canInsert && (isOnTop || isOnBottom || !canMerge)) {
445 if (isOnBottom) {
445 auto acceptMerge = m_AcceptDragWidgetFun(dragWidgetHovered, mimeData);
446
447 if (canInsert && (isOnTop || isOnBottom || !canMerge || !acceptMerge)) {
448 if (posY > (dropIndex + 1) * graphHeight - graphHeight / 2.0) {
446 449 dropIndex += 1;
447 450 }
448 451
449 452 if (helper.getCurrentDragWidget()) {
450 453 auto dragWidgetIndex = m_Layout->indexOf(helper.getCurrentDragWidget());
451 454 if (dragWidgetIndex >= 0 && dragWidgetIndex <= dropIndex) {
452 455 // Correction of the index if the drop occurs in the same container
453 456 // and if the drag is started from the visualization (in that case, the
454 457 // dragWidget is hidden)
455 458 dropIndex += 1;
456 459 }
457 460 }
458 461
459 462 if (dropIndex != placeHolderIndex) {
460 463 helper.insertPlaceHolder(m_Layout, dropIndex, m_PlaceHolderType,
461 464 m_PlaceHolderText);
462 465 }
463 466
464 467 helper.setHightlightedDragWidget(nullptr);
465 468 }
466 469 else if (canMerge && dragWidgetHovered) {
467 470 // drop on the middle -> merge
468 471 if (hasPlaceHolder()) {
469 472 helper.removePlaceHolder();
470 473 }
471 474
472 if (m_AcceptDragWidgetFun(dragWidgetHovered, mimeData)) {
473 helper.setHightlightedDragWidget(dragWidgetHovered);
474 return true;
475 }
476 else {
477 return false;
478 }
475 helper.setHightlightedDragWidget(dragWidgetHovered);
479 476 }
480 477 else {
481 478 qCWarning(LOG_VisualizationDragDropContainer())
482 479 << tr("VisualizationDragDropContainer::findPlaceHolderPosition, no valid drop "
483 480 "action.");
484 481 }
485 482 }
486 483 else {
487 qCWarning(LOG_VisualizationDragDropContainer())
484 qCInfo(LOG_VisualizationDragDropContainer())
488 485 << tr("VisualizationDragDropContainer::findPlaceHolderPosition, no widget "
489 486 "found in the "
490 487 "container");
491 488 }
492 489 }
493 490 else {
494 491 // the mouse is hover the placeHolder
495 492 // Do nothing
496 493 }
497 494
498 495 return true;
499 496 }
@@ -1,876 +1,1004
1 1 #include "Visualization/VisualizationGraphWidget.h"
2 2 #include "Visualization/IVisualizationWidgetVisitor.h"
3 3 #include "Visualization/VisualizationCursorItem.h"
4 4 #include "Visualization/VisualizationDefs.h"
5 5 #include "Visualization/VisualizationGraphHelper.h"
6 6 #include "Visualization/VisualizationGraphRenderingDelegate.h"
7 #include "Visualization/VisualizationMultiZoneSelectionDialog.h"
7 8 #include "Visualization/VisualizationSelectionZoneItem.h"
8 9 #include "Visualization/VisualizationSelectionZoneManager.h"
9 10 #include "Visualization/VisualizationWidget.h"
10 11 #include "Visualization/VisualizationZoneWidget.h"
11 12 #include "ui_VisualizationGraphWidget.h"
12 13
14 #include <Actions/ActionsGuiController.h>
13 15 #include <Common/MimeTypesDef.h>
14 16 #include <Data/ArrayData.h>
15 17 #include <Data/IDataSeries.h>
16 18 #include <Data/SpectrogramSeries.h>
17 #include <DragAndDrop/DragDropHelper.h>
19 #include <DragAndDrop/DragDropGuiController.h>
18 20 #include <Settings/SqpSettingsDefs.h>
19 21 #include <SqpApplication.h>
20 22 #include <Time/TimeController.h>
21 23 #include <Variable/Variable.h>
22 24 #include <Variable/VariableController.h>
23 25
24 26 #include <unordered_map>
25 27
26 28 Q_LOGGING_CATEGORY(LOG_VisualizationGraphWidget, "VisualizationGraphWidget")
27 29
28 30 namespace {
29 31
30 32 /// Key pressed to enable drag&drop in all modes
31 33 const auto DRAG_DROP_MODIFIER = Qt::AltModifier;
32 34
33 35 /// Key pressed to enable zoom on horizontal axis
34 36 const auto HORIZONTAL_ZOOM_MODIFIER = Qt::ControlModifier;
35 37
36 38 /// Key pressed to enable zoom on vertical axis
37 39 const auto VERTICAL_ZOOM_MODIFIER = Qt::ShiftModifier;
38 40
39 41 /// Speed of a step of a wheel event for a pan, in percentage of the axis range
40 42 const auto PAN_SPEED = 5;
41 43
42 44 /// Key pressed to enable a calibration pan
43 45 const auto VERTICAL_PAN_MODIFIER = Qt::AltModifier;
44 46
45 47 /// Key pressed to enable multi selection of selection zones
46 48 const auto MULTI_ZONE_SELECTION_MODIFIER = Qt::ControlModifier;
47 49
48 50 /// Minimum size for the zoom box, in percentage of the axis range
49 51 const auto ZOOM_BOX_MIN_SIZE = 0.8;
50 52
51 53 /// Format of the dates appearing in the label of a cursor
52 54 const auto CURSOR_LABELS_DATETIME_FORMAT = QStringLiteral("yyyy/MM/dd\nhh:mm:ss:zzz");
53 55
54 56 } // namespace
55 57
56 58 struct VisualizationGraphWidget::VisualizationGraphWidgetPrivate {
57 59
58 60 explicit VisualizationGraphWidgetPrivate(const QString &name)
59 61 : m_Name{name},
60 62 m_DoAcquisition{true},
61 63 m_IsCalibration{false},
62 64 m_RenderingDelegate{nullptr}
63 65 {
64 66 }
65 67
66 68 void updateData(PlottablesMap &plottables, std::shared_ptr<IDataSeries> dataSeries,
67 69 const SqpRange &range)
68 70 {
69 71 VisualizationGraphHelper::updateData(plottables, dataSeries, range);
70 72
71 73 // Prevents that data has changed to update rendering
72 74 m_RenderingDelegate->onPlotUpdated();
73 75 }
74 76
75 77 QString m_Name;
76 78 // 1 variable -> n qcpplot
77 79 std::map<std::shared_ptr<Variable>, PlottablesMap> m_VariableToPlotMultiMap;
78 80 bool m_DoAcquisition;
79 81 bool m_IsCalibration;
80 82 /// Delegate used to attach rendering features to the plot
81 83 std::unique_ptr<VisualizationGraphRenderingDelegate> m_RenderingDelegate;
82 84
83 85 QCPItemRect *m_DrawingZoomRect = nullptr;
84 86 QStack<QPair<QCPRange, QCPRange> > m_ZoomStack;
85 87
86 88 std::unique_ptr<VisualizationCursorItem> m_HorizontalCursor = nullptr;
87 89 std::unique_ptr<VisualizationCursorItem> m_VerticalCursor = nullptr;
88 90
89 91 VisualizationSelectionZoneItem *m_DrawingZone = nullptr;
90 92 VisualizationSelectionZoneItem *m_HoveredZone = nullptr;
91 93 QVector<VisualizationSelectionZoneItem *> m_SelectionZones;
92 94
93 95 bool m_HasMovedMouse = false; // Indicates if the mouse moved in a releaseMouse even
94 96
95 97 void startDrawingRect(const QPoint &pos, QCustomPlot &plot)
96 98 {
97 99 removeDrawingRect(plot);
98 100
99 101 auto axisPos = posToAxisPos(pos, plot);
100 102
101 103 m_DrawingZoomRect = new QCPItemRect{&plot};
102 104 QPen p;
103 105 p.setWidth(2);
104 106 m_DrawingZoomRect->setPen(p);
105 107
106 108 m_DrawingZoomRect->topLeft->setCoords(axisPos);
107 109 m_DrawingZoomRect->bottomRight->setCoords(axisPos);
108 110 }
109 111
110 112 void removeDrawingRect(QCustomPlot &plot)
111 113 {
112 114 if (m_DrawingZoomRect) {
113 115 plot.removeItem(m_DrawingZoomRect); // the item is deleted by QCustomPlot
114 116 m_DrawingZoomRect = nullptr;
115 117 plot.replot(QCustomPlot::rpQueuedReplot);
116 118 }
117 119 }
118 120
119 121 void startDrawingZone(const QPoint &pos, VisualizationGraphWidget *graph)
120 122 {
121 123 endDrawingZone(graph);
122 124
123 125 auto axisPos = posToAxisPos(pos, graph->plot());
124 126
125 127 m_DrawingZone = new VisualizationSelectionZoneItem{&graph->plot()};
126 128 m_DrawingZone->setRange(axisPos.x(), axisPos.x());
127 129 m_DrawingZone->setEditionEnabled(false);
128 130 }
129 131
130 132 void endDrawingZone(VisualizationGraphWidget *graph)
131 133 {
132 134 if (m_DrawingZone) {
133 135 auto drawingZoneRange = m_DrawingZone->range();
134 136 if (qAbs(drawingZoneRange.m_TEnd - drawingZoneRange.m_TStart) > 0) {
135 137 m_DrawingZone->setEditionEnabled(true);
136 138 addSelectionZone(m_DrawingZone);
137 139 }
138 140 else {
139 141 graph->plot().removeItem(m_DrawingZone); // the item is deleted by QCustomPlot
140 142 }
141 143
142 144 graph->plot().replot(QCustomPlot::rpQueuedReplot);
143 145 m_DrawingZone = nullptr;
144 146 }
145 147 }
146 148
147 149 void setSelectionZonesEditionEnabled(bool value)
148 150 {
149 151 for (auto s : m_SelectionZones) {
150 152 s->setEditionEnabled(value);
151 153 }
152 154 }
153 155
154 156 void addSelectionZone(VisualizationSelectionZoneItem *zone) { m_SelectionZones << zone; }
155 157
156 158 VisualizationSelectionZoneItem *selectionZoneAt(const QPoint &pos,
157 159 const QCustomPlot &plot) const
158 160 {
159 161 VisualizationSelectionZoneItem *selectionZoneItemUnderCursor = nullptr;
160 162 auto minDistanceToZone = -1;
161 163 for (auto zone : m_SelectionZones) {
162 164 auto distanceToZone = zone->selectTest(pos, false);
163 165 if ((minDistanceToZone < 0 || distanceToZone <= minDistanceToZone)
164 166 && distanceToZone >= 0 && distanceToZone < plot.selectionTolerance()) {
165 167 selectionZoneItemUnderCursor = zone;
166 168 }
167 169 }
168 170
169 171 return selectionZoneItemUnderCursor;
170 172 }
171 173
174 QVector<VisualizationSelectionZoneItem *> selectionZonesAt(const QPoint &pos,
175 const QCustomPlot &plot) const
176 {
177 QVector<VisualizationSelectionZoneItem *> zones;
178 for (auto zone : m_SelectionZones) {
179 auto distanceToZone = zone->selectTest(pos, false);
180 if (distanceToZone >= 0 && distanceToZone < plot.selectionTolerance()) {
181 zones << zone;
182 }
183 }
184
185 return zones;
186 }
187
188 void moveSelectionZoneOnTop(VisualizationSelectionZoneItem *zone, QCustomPlot &plot)
189 {
190 if (!m_SelectionZones.isEmpty() && m_SelectionZones.last() != zone) {
191 zone->moveToTop();
192 m_SelectionZones.removeAll(zone);
193 m_SelectionZones.append(zone);
194 }
195 }
196
172 197 QPointF posToAxisPos(const QPoint &pos, QCustomPlot &plot) const
173 198 {
174 199 auto axisX = plot.axisRect()->axis(QCPAxis::atBottom);
175 200 auto axisY = plot.axisRect()->axis(QCPAxis::atLeft);
176 201 return QPointF{axisX->pixelToCoord(pos.x()), axisY->pixelToCoord(pos.y())};
177 202 }
178 203
179 204 bool pointIsInAxisRect(const QPointF &axisPoint, QCustomPlot &plot) const
180 205 {
181 206 auto axisX = plot.axisRect()->axis(QCPAxis::atBottom);
182 207 auto axisY = plot.axisRect()->axis(QCPAxis::atLeft);
183 208 return axisX->range().contains(axisPoint.x()) && axisY->range().contains(axisPoint.y());
184 209 }
185 210 };
186 211
187 212 VisualizationGraphWidget::VisualizationGraphWidget(const QString &name, QWidget *parent)
188 213 : VisualizationDragWidget{parent},
189 214 ui{new Ui::VisualizationGraphWidget},
190 215 impl{spimpl::make_unique_impl<VisualizationGraphWidgetPrivate>(name)}
191 216 {
192 217 ui->setupUi(this);
193 218
194 219 // 'Close' options : widget is deleted when closed
195 220 setAttribute(Qt::WA_DeleteOnClose);
196 221
197 222 // Set qcpplot properties :
198 223 // - zoom is enabled
199 224 // - Mouse wheel on qcpplot is intercepted to determine the zoom orientation
200 225 ui->widget->setInteractions(QCP::iRangeZoom);
201 226 ui->widget->axisRect()->setRangeDrag(Qt::Horizontal | Qt::Vertical);
202 227
203 228 // The delegate must be initialized after the ui as it uses the plot
204 229 impl->m_RenderingDelegate = std::make_unique<VisualizationGraphRenderingDelegate>(*this);
205 230
206 231 // Init the cursors
207 232 impl->m_HorizontalCursor = std::make_unique<VisualizationCursorItem>(&plot());
208 233 impl->m_HorizontalCursor->setOrientation(Qt::Horizontal);
209 234 impl->m_VerticalCursor = std::make_unique<VisualizationCursorItem>(&plot());
210 235 impl->m_VerticalCursor->setOrientation(Qt::Vertical);
211 236
212 237 connect(ui->widget, &QCustomPlot::mousePress, this, &VisualizationGraphWidget::onMousePress);
213 238 connect(ui->widget, &QCustomPlot::mouseRelease, this,
214 239 &VisualizationGraphWidget::onMouseRelease);
215 240 connect(ui->widget, &QCustomPlot::mouseMove, this, &VisualizationGraphWidget::onMouseMove);
216 241 connect(ui->widget, &QCustomPlot::mouseWheel, this, &VisualizationGraphWidget::onMouseWheel);
217 242 connect(ui->widget, &QCustomPlot::mouseDoubleClick, this,
218 243 &VisualizationGraphWidget::onMouseDoubleClick);
219 244 connect(
220 245 ui->widget->xAxis,
221 246 static_cast<void (QCPAxis::*)(const QCPRange &, const QCPRange &)>(&QCPAxis::rangeChanged),
222 247 this, &VisualizationGraphWidget::onRangeChanged, Qt::DirectConnection);
223 248
224 249 // Activates menu when right clicking on the graph
225 250 ui->widget->setContextMenuPolicy(Qt::CustomContextMenu);
226 251 connect(ui->widget, &QCustomPlot::customContextMenuRequested, this,
227 252 &VisualizationGraphWidget::onGraphMenuRequested);
228 253
229 254 connect(this, &VisualizationGraphWidget::requestDataLoading, &sqpApp->variableController(),
230 255 &VariableController::onRequestDataLoading);
231 256
232 257 connect(&sqpApp->variableController(), &VariableController::updateVarDisplaying, this,
233 258 &VisualizationGraphWidget::onUpdateVarDisplaying);
234 259
235 260 #ifdef Q_OS_MAC
236 261 plot().setPlottingHint(QCP::phFastPolylines, true);
237 262 #endif
238 263 }
239 264
240 265
241 266 VisualizationGraphWidget::~VisualizationGraphWidget()
242 267 {
243 268 delete ui;
244 269 }
245 270
246 271 VisualizationZoneWidget *VisualizationGraphWidget::parentZoneWidget() const noexcept
247 272 {
248 273 auto parent = parentWidget();
249 274 while (parent != nullptr && !qobject_cast<VisualizationZoneWidget *>(parent)) {
250 275 parent = parent->parentWidget();
251 276 }
252 277
253 278 return qobject_cast<VisualizationZoneWidget *>(parent);
254 279 }
255 280
256 281 VisualizationWidget *VisualizationGraphWidget::parentVisualizationWidget() const
257 282 {
258 283 auto parent = parentWidget();
259 284 while (parent != nullptr && !qobject_cast<VisualizationWidget *>(parent)) {
260 285 parent = parent->parentWidget();
261 286 }
262 287
263 288 return qobject_cast<VisualizationWidget *>(parent);
264 289 }
265 290
266 291 void VisualizationGraphWidget::enableAcquisition(bool enable)
267 292 {
268 293 impl->m_DoAcquisition = enable;
269 294 }
270 295
271 296 void VisualizationGraphWidget::addVariable(std::shared_ptr<Variable> variable, SqpRange range)
272 297 {
273 298 // Uses delegate to create the qcpplot components according to the variable
274 299 auto createdPlottables = VisualizationGraphHelper::create(variable, *ui->widget);
275 300
276 301 if (auto dataSeries = variable->dataSeries()) {
277 302 // Set axes properties according to the units of the data series
278 303 impl->m_RenderingDelegate->setAxesProperties(dataSeries);
279 304
280 305 // Sets rendering properties for the new plottables
281 306 // Warning: this method must be called after setAxesProperties(), as it can access to some
282 307 // axes properties that have to be initialized
283 308 impl->m_RenderingDelegate->setPlottablesProperties(dataSeries, createdPlottables);
284 309 }
285 310
286 311 impl->m_VariableToPlotMultiMap.insert({variable, std::move(createdPlottables)});
287 312
288 313 connect(variable.get(), SIGNAL(updated()), this, SLOT(onDataCacheVariableUpdated()));
289 314
290 315 this->enableAcquisition(false);
291 316 this->setGraphRange(range);
292 317 this->enableAcquisition(true);
293 318
294 319 emit requestDataLoading(QVector<std::shared_ptr<Variable> >() << variable, range, false);
295 320
296 321 emit variableAdded(variable);
297 322 }
298 323
299 324 void VisualizationGraphWidget::removeVariable(std::shared_ptr<Variable> variable) noexcept
300 325 {
301 326 // Each component associated to the variable :
302 327 // - is removed from qcpplot (which deletes it)
303 328 // - is no longer referenced in the map
304 329 auto variableIt = impl->m_VariableToPlotMultiMap.find(variable);
305 330 if (variableIt != impl->m_VariableToPlotMultiMap.cend()) {
306 331 emit variableAboutToBeRemoved(variable);
307 332
308 333 auto &plottablesMap = variableIt->second;
309 334
310 335 for (auto plottableIt = plottablesMap.cbegin(), plottableEnd = plottablesMap.cend();
311 336 plottableIt != plottableEnd;) {
312 337 ui->widget->removePlottable(plottableIt->second);
313 338 plottableIt = plottablesMap.erase(plottableIt);
314 339 }
315 340
316 341 impl->m_VariableToPlotMultiMap.erase(variableIt);
317 342 }
318 343
319 344 // Updates graph
320 345 ui->widget->replot();
321 346 }
322 347
323 348 QList<std::shared_ptr<Variable> > VisualizationGraphWidget::variables() const
324 349 {
325 350 auto variables = QList<std::shared_ptr<Variable> >{};
326 351 for (auto it = std::cbegin(impl->m_VariableToPlotMultiMap);
327 352 it != std::cend(impl->m_VariableToPlotMultiMap); ++it) {
328 353 variables << it->first;
329 354 }
330 355
331 356 return variables;
332 357 }
333 358
334 359 void VisualizationGraphWidget::setYRange(std::shared_ptr<Variable> variable)
335 360 {
336 361 if (!variable) {
337 362 qCCritical(LOG_VisualizationGraphWidget()) << "Can't set y-axis range: variable is null";
338 363 return;
339 364 }
340 365
341 366 VisualizationGraphHelper::setYAxisRange(variable, *ui->widget);
342 367 }
343 368
344 369 SqpRange VisualizationGraphWidget::graphRange() const noexcept
345 370 {
346 371 auto graphRange = ui->widget->xAxis->range();
347 372 return SqpRange{graphRange.lower, graphRange.upper};
348 373 }
349 374
350 375 void VisualizationGraphWidget::setGraphRange(const SqpRange &range)
351 376 {
352 377 qCDebug(LOG_VisualizationGraphWidget()) << tr("VisualizationGraphWidget::setGraphRange START");
353 378 ui->widget->xAxis->setRange(range.m_TStart, range.m_TEnd);
354 379 ui->widget->replot();
355 380 qCDebug(LOG_VisualizationGraphWidget()) << tr("VisualizationGraphWidget::setGraphRange END");
356 381 }
357 382
358 383 QVector<SqpRange> VisualizationGraphWidget::selectionZoneRanges() const
359 384 {
360 385 QVector<SqpRange> ranges;
361 386 for (auto zone : impl->m_SelectionZones) {
362 387 ranges << zone->range();
363 388 }
364 389
365 390 return ranges;
366 391 }
367 392
368 393 void VisualizationGraphWidget::addSelectionZones(const QVector<SqpRange> &ranges)
369 394 {
370 395 for (const auto &range : ranges) {
371 396 // note: ownership is transfered to QCustomPlot
372 397 auto zone = new VisualizationSelectionZoneItem(&plot());
373 398 zone->setRange(range.m_TStart, range.m_TEnd);
374 399 impl->addSelectionZone(zone);
375 400 }
376 401
377 402 plot().replot(QCustomPlot::rpQueuedReplot);
378 403 }
379 404
405 void VisualizationGraphWidget::removeSelectionZone(VisualizationSelectionZoneItem *selectionZone)
406 {
407 parentVisualizationWidget()->selectionZoneManager().setSelected(selectionZone, false);
408
409 if (impl->m_HoveredZone == selectionZone) {
410 impl->m_HoveredZone = nullptr;
411 setCursor(Qt::ArrowCursor);
412 }
413
414 impl->m_SelectionZones.removeAll(selectionZone);
415 plot().removeItem(selectionZone);
416 plot().replot(QCustomPlot::rpQueuedReplot);
417 }
418
380 419 void VisualizationGraphWidget::undoZoom()
381 420 {
382 421 auto zoom = impl->m_ZoomStack.pop();
383 422 auto axisX = plot().axisRect()->axis(QCPAxis::atBottom);
384 423 auto axisY = plot().axisRect()->axis(QCPAxis::atLeft);
385 424
386 425 axisX->setRange(zoom.first);
387 426 axisY->setRange(zoom.second);
388 427
389 428 plot().replot(QCustomPlot::rpQueuedReplot);
390 429 }
391 430
392 431 void VisualizationGraphWidget::accept(IVisualizationWidgetVisitor *visitor)
393 432 {
394 433 if (visitor) {
395 434 visitor->visit(this);
396 435 }
397 436 else {
398 437 qCCritical(LOG_VisualizationGraphWidget())
399 438 << tr("Can't visit widget : the visitor is null");
400 439 }
401 440 }
402 441
403 442 bool VisualizationGraphWidget::canDrop(const Variable &variable) const
404 443 {
405 444 auto isSpectrogram = [](const auto &variable) {
406 445 return std::dynamic_pointer_cast<SpectrogramSeries>(variable.dataSeries()) != nullptr;
407 446 };
408 447
409 448 // - A spectrogram series can't be dropped on graph with existing plottables
410 449 // - No data series can be dropped on graph with existing spectrogram series
411 450 return isSpectrogram(variable)
412 451 ? impl->m_VariableToPlotMultiMap.empty()
413 452 : std::none_of(
414 453 impl->m_VariableToPlotMultiMap.cbegin(), impl->m_VariableToPlotMultiMap.cend(),
415 454 [isSpectrogram](const auto &entry) { return isSpectrogram(*entry.first); });
416 455 }
417 456
418 457 bool VisualizationGraphWidget::contains(const Variable &variable) const
419 458 {
420 459 // Finds the variable among the keys of the map
421 460 auto variablePtr = &variable;
422 461 auto findVariable
423 462 = [variablePtr](const auto &entry) { return variablePtr == entry.first.get(); };
424 463
425 464 auto end = impl->m_VariableToPlotMultiMap.cend();
426 465 auto it = std::find_if(impl->m_VariableToPlotMultiMap.cbegin(), end, findVariable);
427 466 return it != end;
428 467 }
429 468
430 469 QString VisualizationGraphWidget::name() const
431 470 {
432 471 return impl->m_Name;
433 472 }
434 473
435 474 QMimeData *VisualizationGraphWidget::mimeData(const QPoint &position) const
436 475 {
437 476 auto mimeData = new QMimeData;
438 477
439 478 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(position, plot());
440 479 if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones
441 480 && selectionZoneItemUnderCursor) {
442 481 mimeData->setData(MIME_TYPE_TIME_RANGE, TimeController::mimeDataForTimeRange(
443 482 selectionZoneItemUnderCursor->range()));
444 483 mimeData->setData(MIME_TYPE_SELECTION_ZONE, TimeController::mimeDataForTimeRange(
445 484 selectionZoneItemUnderCursor->range()));
446 485 }
447 486 else {
448 487 mimeData->setData(MIME_TYPE_GRAPH, QByteArray{});
449 488
450 489 auto timeRangeData = TimeController::mimeDataForTimeRange(graphRange());
451 490 mimeData->setData(MIME_TYPE_TIME_RANGE, timeRangeData);
452 491 }
453 492
454 493 return mimeData;
455 494 }
456 495
457 496 QPixmap VisualizationGraphWidget::customDragPixmap(const QPoint &dragPosition)
458 497 {
459 498 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(dragPosition, plot());
460 499 if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones
461 500 && selectionZoneItemUnderCursor) {
462 501
463 502 auto zoneTopLeft = selectionZoneItemUnderCursor->topLeft->pixelPosition();
464 503 auto zoneBottomRight = selectionZoneItemUnderCursor->bottomRight->pixelPosition();
465 504
466 505 auto zoneSize = QSizeF{qAbs(zoneBottomRight.x() - zoneTopLeft.x()),
467 506 qAbs(zoneBottomRight.y() - zoneTopLeft.y())}
468 507 .toSize();
469 508
470 509 auto pixmap = QPixmap(zoneSize);
471 510 render(&pixmap, QPoint(), QRegion{QRect{zoneTopLeft.toPoint(), zoneSize}});
472 511
473 512 return pixmap;
474 513 }
475 514
476 515 return QPixmap();
477 516 }
478 517
479 518 bool VisualizationGraphWidget::isDragAllowed() const
480 519 {
481 520 return true;
482 521 }
483 522
484 523 void VisualizationGraphWidget::highlightForMerge(bool highlighted)
485 524 {
486 525 if (highlighted) {
487 526 plot().setBackground(QBrush(QColor("#BBD5EE")));
488 527 }
489 528 else {
490 529 plot().setBackground(QBrush(Qt::white));
491 530 }
492 531
493 532 plot().update();
494 533 }
495 534
496 535 void VisualizationGraphWidget::addVerticalCursor(double time)
497 536 {
498 537 impl->m_VerticalCursor->setPosition(time);
499 538 impl->m_VerticalCursor->setVisible(true);
500 539
501 540 auto text
502 541 = DateUtils::dateTime(time).toString(CURSOR_LABELS_DATETIME_FORMAT).replace(' ', '\n');
503 542 impl->m_VerticalCursor->setLabelText(text);
504 543 }
505 544
506 545 void VisualizationGraphWidget::addVerticalCursorAtViewportPosition(double position)
507 546 {
508 547 impl->m_VerticalCursor->setAbsolutePosition(position);
509 548 impl->m_VerticalCursor->setVisible(true);
510 549
511 550 auto axis = plot().axisRect()->axis(QCPAxis::atBottom);
512 551 auto text
513 552 = DateUtils::dateTime(axis->pixelToCoord(position)).toString(CURSOR_LABELS_DATETIME_FORMAT);
514 553 impl->m_VerticalCursor->setLabelText(text);
515 554 }
516 555
517 556 void VisualizationGraphWidget::removeVerticalCursor()
518 557 {
519 558 impl->m_VerticalCursor->setVisible(false);
520 559 plot().replot(QCustomPlot::rpQueuedReplot);
521 560 }
522 561
523 562 void VisualizationGraphWidget::addHorizontalCursor(double value)
524 563 {
525 564 impl->m_HorizontalCursor->setPosition(value);
526 565 impl->m_HorizontalCursor->setVisible(true);
527 566 impl->m_HorizontalCursor->setLabelText(QString::number(value));
528 567 }
529 568
530 569 void VisualizationGraphWidget::addHorizontalCursorAtViewportPosition(double position)
531 570 {
532 571 impl->m_HorizontalCursor->setAbsolutePosition(position);
533 572 impl->m_HorizontalCursor->setVisible(true);
534 573
535 574 auto axis = plot().axisRect()->axis(QCPAxis::atLeft);
536 575 impl->m_HorizontalCursor->setLabelText(QString::number(axis->pixelToCoord(position)));
537 576 }
538 577
539 578 void VisualizationGraphWidget::removeHorizontalCursor()
540 579 {
541 580 impl->m_HorizontalCursor->setVisible(false);
542 581 plot().replot(QCustomPlot::rpQueuedReplot);
543 582 }
544 583
545 584 void VisualizationGraphWidget::closeEvent(QCloseEvent *event)
546 585 {
547 586 Q_UNUSED(event);
548 587
549 588 // Prevents that all variables will be removed from graph when it will be closed
550 589 for (auto &variableEntry : impl->m_VariableToPlotMultiMap) {
551 590 emit variableAboutToBeRemoved(variableEntry.first);
552 591 }
553 592 }
554 593
555 594 void VisualizationGraphWidget::enterEvent(QEvent *event)
556 595 {
557 596 Q_UNUSED(event);
558 597 impl->m_RenderingDelegate->showGraphOverlay(true);
559 598 }
560 599
561 600 void VisualizationGraphWidget::leaveEvent(QEvent *event)
562 601 {
563 602 Q_UNUSED(event);
564 603 impl->m_RenderingDelegate->showGraphOverlay(false);
565 604
566 605 if (auto parentZone = parentZoneWidget()) {
567 606 parentZone->notifyMouseLeaveGraph(this);
568 607 }
569 608 else {
570 609 qCWarning(LOG_VisualizationGraphWidget()) << "leaveEvent: No parent zone widget";
571 610 }
572 611
573 612 if (impl->m_HoveredZone) {
574 613 impl->m_HoveredZone->setHovered(false);
575 614 impl->m_HoveredZone = nullptr;
576 615 }
577 616 }
578 617
579 618 QCustomPlot &VisualizationGraphWidget::plot() const noexcept
580 619 {
581 620 return *ui->widget;
582 621 }
583 622
584 623 void VisualizationGraphWidget::onGraphMenuRequested(const QPoint &pos) noexcept
585 624 {
586 625 QMenu graphMenu{};
587 626
588 627 // Iterates on variables (unique keys)
589 628 for (auto it = impl->m_VariableToPlotMultiMap.cbegin(),
590 629 end = impl->m_VariableToPlotMultiMap.cend();
591 630 it != end; it = impl->m_VariableToPlotMultiMap.upper_bound(it->first)) {
592 631 // 'Remove variable' action
593 632 graphMenu.addAction(tr("Remove variable %1").arg(it->first->name()),
594 633 [ this, var = it->first ]() { removeVariable(var); });
595 634 }
596 635
597 636 if (!impl->m_ZoomStack.isEmpty()) {
598 637 if (!graphMenu.isEmpty()) {
599 638 graphMenu.addSeparator();
600 639 }
601 640
602 641 graphMenu.addAction(tr("Undo Zoom"), [this]() { undoZoom(); });
603 642 }
604 643
644 // Selection Zone Actions
645 auto selectionZoneItem = impl->selectionZoneAt(pos, plot());
646 if (selectionZoneItem) {
647 auto selectedItems = parentVisualizationWidget()->selectionZoneManager().selectedItems();
648 selectedItems.removeAll(selectionZoneItem);
649 selectedItems.prepend(selectionZoneItem); // Put the current selection zone first
650
651 auto zoneActions = sqpApp->actionsGuiController().selectionZoneActions();
652 if (!zoneActions.isEmpty() && !graphMenu.isEmpty()) {
653 graphMenu.addSeparator();
654 }
655
656 QHash<QString, QMenu *> subMenus;
657 QHash<QString, bool> subMenusEnabled;
658
659 for (auto zoneAction : zoneActions) {
660
661 auto isEnabled = zoneAction->isEnabled(selectedItems);
662
663 auto menu = &graphMenu;
664 for (auto subMenuName : zoneAction->subMenuList()) {
665 if (!subMenus.contains(subMenuName)) {
666 menu = menu->addMenu(subMenuName);
667 subMenus[subMenuName] = menu;
668 subMenusEnabled[subMenuName] = isEnabled;
669 }
670 else {
671 menu = subMenus.value(subMenuName);
672 if (isEnabled) {
673 // The sub menu is enabled if at least one of its actions is enabled
674 subMenusEnabled[subMenuName] = true;
675 }
676 }
677 }
678
679 auto action = menu->addAction(zoneAction->name());
680 action->setEnabled(isEnabled);
681 action->setShortcut(zoneAction->displayedShortcut());
682 QObject::connect(action, &QAction::triggered,
683 [zoneAction, selectedItems]() { zoneAction->execute(selectedItems); });
684 }
685
686 for (auto it = subMenus.cbegin(); it != subMenus.cend(); ++it) {
687 it.value()->setEnabled(subMenusEnabled[it.key()]);
688 }
689 }
690
605 691 if (!graphMenu.isEmpty()) {
606 692 graphMenu.exec(QCursor::pos());
607 693 }
608 694 }
609 695
610 696 void VisualizationGraphWidget::onRangeChanged(const QCPRange &t1, const QCPRange &t2)
611 697 {
612 698 qCDebug(LOG_VisualizationGraphWidget())
613 699 << tr("TORM: VisualizationGraphWidget::onRangeChanged")
614 700 << QThread::currentThread()->objectName() << "DoAcqui" << impl->m_DoAcquisition;
615 701
616 702 auto graphRange = SqpRange{t1.lower, t1.upper};
617 703 auto oldGraphRange = SqpRange{t2.lower, t2.upper};
618 704
619 705 if (impl->m_DoAcquisition) {
620 706 QVector<std::shared_ptr<Variable> > variableUnderGraphVector;
621 707
622 708 for (auto it = impl->m_VariableToPlotMultiMap.begin(),
623 709 end = impl->m_VariableToPlotMultiMap.end();
624 710 it != end; it = impl->m_VariableToPlotMultiMap.upper_bound(it->first)) {
625 711 variableUnderGraphVector.push_back(it->first);
626 712 }
627 713 emit requestDataLoading(std::move(variableUnderGraphVector), graphRange,
628 714 !impl->m_IsCalibration);
629 715
630 716 if (!impl->m_IsCalibration) {
631 717 qCDebug(LOG_VisualizationGraphWidget())
632 718 << tr("TORM: VisualizationGraphWidget::Synchronize notify !!")
633 719 << QThread::currentThread()->objectName() << graphRange << oldGraphRange;
634 720 emit synchronize(graphRange, oldGraphRange);
635 721 }
636 722 }
637 723
638 724 auto pos = mapFromGlobal(QCursor::pos());
639 725 auto axisPos = impl->posToAxisPos(pos, plot());
640 726 if (auto parentZone = parentZoneWidget()) {
641 727 if (impl->pointIsInAxisRect(axisPos, plot())) {
642 728 parentZone->notifyMouseMoveInGraph(pos, axisPos, this);
643 729 }
644 730 else {
645 731 parentZone->notifyMouseLeaveGraph(this);
646 732 }
647 733 }
648 734 else {
649 735 qCWarning(LOG_VisualizationGraphWidget()) << "onMouseMove: No parent zone widget";
650 736 }
651 737 }
652 738
653 739 void VisualizationGraphWidget::onMouseDoubleClick(QMouseEvent *event) noexcept
654 740 {
655 741 impl->m_RenderingDelegate->onMouseDoubleClick(event);
656 742 }
657 743
658 744 void VisualizationGraphWidget::onMouseMove(QMouseEvent *event) noexcept
659 745 {
660 746 // Handles plot rendering when mouse is moving
661 747 impl->m_RenderingDelegate->onMouseMove(event);
662 748
663 749 auto axisPos = impl->posToAxisPos(event->pos(), plot());
664 750
665 751 // Zoom box and zone drawing
666 752 if (impl->m_DrawingZoomRect) {
667 753 impl->m_DrawingZoomRect->bottomRight->setCoords(axisPos);
668 754 }
669 755 else if (impl->m_DrawingZone) {
670 756 impl->m_DrawingZone->setEnd(axisPos.x());
671 757 }
672 758
673 759 // Cursor
674 760 if (auto parentZone = parentZoneWidget()) {
675 761 if (impl->pointIsInAxisRect(axisPos, plot())) {
676 762 parentZone->notifyMouseMoveInGraph(event->pos(), axisPos, this);
677 763 }
678 764 else {
679 765 parentZone->notifyMouseLeaveGraph(this);
680 766 }
681 767 }
682 768 else {
683 769 qCWarning(LOG_VisualizationGraphWidget()) << "onMouseMove: No parent zone widget";
684 770 }
685 771
686 772 // Search for the selection zone under the mouse
687 773 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(event->pos(), plot());
688 774 if (selectionZoneItemUnderCursor && !impl->m_DrawingZone
689 775 && sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones) {
690 776
691 777 // Sets the appropriate cursor shape
692 778 auto cursorShape = selectionZoneItemUnderCursor->curshorShapeForPosition(event->pos());
693 779 setCursor(cursorShape);
694 780
695 781 // Manages the hovered zone
696 782 if (selectionZoneItemUnderCursor != impl->m_HoveredZone) {
697 783 if (impl->m_HoveredZone) {
698 784 impl->m_HoveredZone->setHovered(false);
699 785 }
700 786 selectionZoneItemUnderCursor->setHovered(true);
701 787 impl->m_HoveredZone = selectionZoneItemUnderCursor;
702 788 plot().replot(QCustomPlot::rpQueuedReplot);
703 789 }
704 790 }
705 791 else {
706 792 // There is no zone under the mouse or the interaction mode is not "selection zones"
707 793 if (impl->m_HoveredZone) {
708 794 impl->m_HoveredZone->setHovered(false);
709 795 impl->m_HoveredZone = nullptr;
710 796 }
711 797
712 798 setCursor(Qt::ArrowCursor);
713 799 }
714 800
715 801 impl->m_HasMovedMouse = true;
716 802 VisualizationDragWidget::mouseMoveEvent(event);
717 803 }
718 804
719 805 void VisualizationGraphWidget::onMouseWheel(QWheelEvent *event) noexcept
720 806 {
721 807 auto value = event->angleDelta().x() + event->angleDelta().y();
722 808 if (value != 0) {
723 809
724 810 auto direction = value > 0 ? 1.0 : -1.0;
725 811 auto isZoomX = event->modifiers().testFlag(HORIZONTAL_ZOOM_MODIFIER);
726 812 auto isZoomY = event->modifiers().testFlag(VERTICAL_ZOOM_MODIFIER);
727 813 impl->m_IsCalibration = event->modifiers().testFlag(VERTICAL_PAN_MODIFIER);
728 814
729 815 auto zoomOrientations = QFlags<Qt::Orientation>{};
730 816 zoomOrientations.setFlag(Qt::Horizontal, isZoomX);
731 817 zoomOrientations.setFlag(Qt::Vertical, isZoomY);
732 818
733 819 ui->widget->axisRect()->setRangeZoom(zoomOrientations);
734 820
735 821 if (!isZoomX && !isZoomY) {
736 822 auto axis = plot().axisRect()->axis(QCPAxis::atBottom);
737 823 auto diff = direction * (axis->range().size() * (PAN_SPEED / 100.0));
738 824
739 825 axis->setRange(axis->range() + diff);
740 826
741 827 if (plot().noAntialiasingOnDrag()) {
742 828 plot().setNotAntialiasedElements(QCP::aeAll);
743 829 }
744 830
745 831 plot().replot(QCustomPlot::rpQueuedReplot);
746 832 }
747 833 }
748 834 }
749 835
750 836 void VisualizationGraphWidget::onMousePress(QMouseEvent *event) noexcept
751 837 {
752 838 auto isDragDropClick = event->modifiers().testFlag(DRAG_DROP_MODIFIER);
753 839 auto isSelectionZoneMode
754 840 = sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones;
755 841 auto isLeftClick = event->buttons().testFlag(Qt::LeftButton);
756 842
757 843 if (!isDragDropClick && isLeftClick) {
758 844 if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::ZoomBox) {
759 845 // Starts a zoom box
760 846 impl->startDrawingRect(event->pos(), plot());
761 847 }
762 848 else if (isSelectionZoneMode && impl->m_DrawingZone == nullptr) {
763 849 // Starts a new selection zone
764 850 auto zoneAtPos = impl->selectionZoneAt(event->pos(), plot());
765 851 if (!zoneAtPos) {
766 852 impl->startDrawingZone(event->pos(), this);
767 853 }
768 854 }
769 855 }
770 856
771 857 // Allows mouse panning only in default mode
772 858 plot().setInteraction(QCP::iRangeDrag, sqpApp->plotsInteractionMode()
773 859 == SqpApplication::PlotsInteractionMode::None
774 860 && !isDragDropClick);
775 861
776 862 // Allows zone edition only in selection zone mode without drag&drop
777 863 impl->setSelectionZonesEditionEnabled(isSelectionZoneMode && !isDragDropClick);
778 864
779 865 // Selection / Deselection
780 866 if (isSelectionZoneMode) {
781 867 auto isMultiSelectionClick = event->modifiers().testFlag(MULTI_ZONE_SELECTION_MODIFIER);
782 868 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(event->pos(), plot());
783 if (selectionZoneItemUnderCursor && isLeftClick) {
784 selectionZoneItemUnderCursor->setAssociatedEditedZones(
785 parentVisualizationWidget()->selectionZoneManager().selectedItems());
869
870
871 if (selectionZoneItemUnderCursor && !selectionZoneItemUnderCursor->selected()
872 && !isMultiSelectionClick) {
873 parentVisualizationWidget()->selectionZoneManager().select(
874 {selectionZoneItemUnderCursor});
786 875 }
787 else if (!isMultiSelectionClick && isLeftClick) {
876 else if (!selectionZoneItemUnderCursor && !isMultiSelectionClick && isLeftClick) {
788 877 parentVisualizationWidget()->selectionZoneManager().clearSelection();
789 878 }
790 879 else {
791 880 // No selection change
792 881 }
882
883 if (selectionZoneItemUnderCursor && isLeftClick) {
884 selectionZoneItemUnderCursor->setAssociatedEditedZones(
885 parentVisualizationWidget()->selectionZoneManager().selectedItems());
886 }
793 887 }
794 888
795 889
796 890 impl->m_HasMovedMouse = false;
797 891 VisualizationDragWidget::mousePressEvent(event);
798 892 }
799 893
800 894 void VisualizationGraphWidget::onMouseRelease(QMouseEvent *event) noexcept
801 895 {
802 896 if (impl->m_DrawingZoomRect) {
803 897
804 898 auto axisX = plot().axisRect()->axis(QCPAxis::atBottom);
805 899 auto axisY = plot().axisRect()->axis(QCPAxis::atLeft);
806 900
807 901 auto newAxisXRange = QCPRange{impl->m_DrawingZoomRect->topLeft->coords().x(),
808 902 impl->m_DrawingZoomRect->bottomRight->coords().x()};
809 903
810 904 auto newAxisYRange = QCPRange{impl->m_DrawingZoomRect->topLeft->coords().y(),
811 905 impl->m_DrawingZoomRect->bottomRight->coords().y()};
812 906
813 907 impl->removeDrawingRect(plot());
814 908
815 909 if (newAxisXRange.size() > axisX->range().size() * (ZOOM_BOX_MIN_SIZE / 100.0)
816 910 && newAxisYRange.size() > axisY->range().size() * (ZOOM_BOX_MIN_SIZE / 100.0)) {
817 911 impl->m_ZoomStack.push(qMakePair(axisX->range(), axisY->range()));
818 912 axisX->setRange(newAxisXRange);
819 913 axisY->setRange(newAxisYRange);
820 914
821 915 plot().replot(QCustomPlot::rpQueuedReplot);
822 916 }
823 917 }
824 918
825 919 impl->endDrawingZone(this);
826 920
827 921 impl->m_IsCalibration = false;
828 922
829 923 // Selection / Deselection
830 924 auto isSelectionZoneMode
831 925 = sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones;
832 926 if (isSelectionZoneMode) {
833 927 auto isMultiSelectionClick = event->modifiers().testFlag(MULTI_ZONE_SELECTION_MODIFIER);
834 928 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(event->pos(), plot());
835 if (selectionZoneItemUnderCursor && event->button() == Qt::LeftButton) {
836 if (!isMultiSelectionClick && !impl->m_HasMovedMouse) {
837 parentVisualizationWidget()->selectionZoneManager().select(
838 {selectionZoneItemUnderCursor});
929 if (selectionZoneItemUnderCursor && event->button() == Qt::LeftButton
930 && !impl->m_HasMovedMouse) {
931
932 auto zonesUnderCursor = impl->selectionZonesAt(event->pos(), plot());
933 if (zonesUnderCursor.count() > 1) {
934 // There are multiple zones under the mouse.
935 // Performs the selection with a selection dialog.
936 VisualizationMultiZoneSelectionDialog dialog{this};
937 dialog.setZones(zonesUnderCursor);
938 dialog.move(mapToGlobal(event->pos() - QPoint(dialog.width() / 2, 20)));
939 dialog.activateWindow();
940 dialog.raise();
941 if (dialog.exec() == QDialog::Accepted) {
942 auto selection = dialog.selectedZones();
943
944 if (!isMultiSelectionClick) {
945 parentVisualizationWidget()->selectionZoneManager().clearSelection();
946 }
947
948 for (auto it = selection.cbegin(); it != selection.cend(); ++it) {
949 auto zone = it.key();
950 auto isSelected = it.value();
951 parentVisualizationWidget()->selectionZoneManager().setSelected(zone,
952 isSelected);
953
954 if (isSelected) {
955 // Puts the zone on top of the stack so it can be moved or resized
956 impl->moveSelectionZoneOnTop(zone, plot());
957 }
958 }
959 }
839 960 }
840 else if (!impl->m_HasMovedMouse) {
841 parentVisualizationWidget()->selectionZoneManager().setSelected(
842 selectionZoneItemUnderCursor, !selectionZoneItemUnderCursor->selected()
843 || event->button() == Qt::RightButton);
961 else {
962 if (!isMultiSelectionClick) {
963 parentVisualizationWidget()->selectionZoneManager().select(
964 {selectionZoneItemUnderCursor});
965 impl->moveSelectionZoneOnTop(selectionZoneItemUnderCursor, plot());
966 }
967 else {
968 parentVisualizationWidget()->selectionZoneManager().setSelected(
969 selectionZoneItemUnderCursor, !selectionZoneItemUnderCursor->selected()
970 || event->button() == Qt::RightButton);
971 }
844 972 }
845 973 }
846 974 else {
847 975 // No selection change
848 976 }
849 977 }
850 978 }
851 979
852 980 void VisualizationGraphWidget::onDataCacheVariableUpdated()
853 981 {
854 982 auto graphRange = ui->widget->xAxis->range();
855 983 auto dateTime = SqpRange{graphRange.lower, graphRange.upper};
856 984
857 985 for (auto &variableEntry : impl->m_VariableToPlotMultiMap) {
858 986 auto variable = variableEntry.first;
859 987 qCDebug(LOG_VisualizationGraphWidget())
860 988 << "TORM: VisualizationGraphWidget::onDataCacheVariableUpdated S" << variable->range();
861 989 qCDebug(LOG_VisualizationGraphWidget())
862 990 << "TORM: VisualizationGraphWidget::onDataCacheVariableUpdated E" << dateTime;
863 991 if (dateTime.contains(variable->range()) || dateTime.intersect(variable->range())) {
864 992 impl->updateData(variableEntry.second, variable->dataSeries(), variable->range());
865 993 }
866 994 }
867 995 }
868 996
869 997 void VisualizationGraphWidget::onUpdateVarDisplaying(std::shared_ptr<Variable> variable,
870 998 const SqpRange &range)
871 999 {
872 1000 auto it = impl->m_VariableToPlotMultiMap.find(variable);
873 1001 if (it != impl->m_VariableToPlotMultiMap.end()) {
874 1002 impl->updateData(it->second, variable->dataSeries(), range);
875 1003 }
876 1004 }
@@ -1,343 +1,440
1 1 #include "Visualization/VisualizationSelectionZoneItem.h"
2 #include "Visualization/VisualizationGraphWidget.h"
3 #include "Visualization/VisualizationSelectionZoneManager.h"
4 #include "Visualization/VisualizationWidget.h"
2 5
3 6 const QString &DEFAULT_COLOR = QStringLiteral("#E79D41");
4 7
5 8 struct VisualizationSelectionZoneItem::VisualizationSelectionZoneItemPrivate {
6 9
7 10 QCustomPlot *m_Plot;
8 11 double m_T1 = 0;
9 12 double m_T2 = 0;
10 13 QColor m_Color;
11 14
12 15 bool m_IsEditionEnabled = true;
13 16 double m_MovedOrinalT1 = 0;
14 17 double m_MovedOrinalT2 = 0;
15 18
16 19 QCPItemStraightLine *m_LeftLine;
17 20 QCPItemStraightLine *m_RightLine;
18 21 QCPItemText *m_NameLabelItem = nullptr;
19 22
20 23 enum class EditionMode { NoEdition, ResizeLeft, ResizeRight, Move };
21 24 EditionMode m_CurrentEditionMode;
22 25
23 26 QVector<VisualizationSelectionZoneItem *> m_AssociatedEditedZones;
24 27
25 28 VisualizationSelectionZoneItemPrivate(QCustomPlot *plot)
26 29 : m_Plot(plot), m_Color(Qt::blue), m_CurrentEditionMode(EditionMode::NoEdition)
27 30 {
28 31 }
29 32
30 33 void updatePosition(VisualizationSelectionZoneItem *item)
31 34 {
32 35 item->topLeft->setCoords(m_T1, 0);
33 36 item->bottomRight->setCoords(m_T2, 1);
34 37 }
35 38
36 39 EditionMode getEditionMode(const QPoint &pos, const VisualizationSelectionZoneItem *zoneItem)
37 40 {
38 41 auto distanceLeft = m_LeftLine->selectTest(pos, false);
39 42 auto distanceRight = m_RightLine->selectTest(pos, false);
40 43 auto distance = zoneItem->selectTest(pos, false);
41 44
42 45 if (distanceRight <= distance) {
43 46 return VisualizationSelectionZoneItemPrivate::EditionMode::ResizeRight;
44 47 }
45 48 else if (distanceLeft <= distance) {
46 49 return VisualizationSelectionZoneItemPrivate::EditionMode::ResizeLeft;
47 50 }
48 51
49 52 return VisualizationSelectionZoneItemPrivate::EditionMode::Move;
50 53 }
51 54
52 55 double pixelSizeToAxisXSize(double pixels)
53 56 {
54 57 auto axis = m_Plot->axisRect()->axis(QCPAxis::atBottom);
55 58 return axis->pixelToCoord(pixels) - axis->pixelToCoord(0);
56 59 }
60
61 bool alignZones(VisualizationSelectionZoneItem *referenceZone,
62 const QVector<VisualizationSelectionZoneItem *> &zonesToAlign, bool alignOnLeft,
63 bool allowResize, bool vertically)
64 {
65 auto result = false;
66
67 auto referenceTime
68 = alignOnLeft ? referenceZone->range().m_TStart : referenceZone->range().m_TEnd;
69
70 auto referenceBottomAxis = m_Plot->axisRect()->axis(QCPAxis::atBottom);
71 auto referenceVerticalPosition = referenceBottomAxis->coordToPixel(referenceTime);
72
73 for (auto otherZone : zonesToAlign) {
74
75 auto otherZoneRange = otherZone->range();
76 auto newZoneStart = otherZoneRange.m_TStart;
77 auto newZoneEnd = otherZoneRange.m_TEnd;
78
79 auto alignedTime = referenceTime;
80 if (vertically) {
81 auto otherZoneAxis = otherZone->parentPlot()->axisRect()->axis(QCPAxis::atBottom);
82 alignedTime = otherZoneAxis->pixelToCoord(referenceVerticalPosition);
83 }
84
85 if (alignOnLeft) {
86 newZoneStart = alignedTime;
87 if (!allowResize) {
88 newZoneEnd = alignedTime + (otherZoneRange.m_TEnd - otherZoneRange.m_TStart);
89 }
90 }
91 else { // align on right
92 newZoneEnd = alignedTime;
93 if (!allowResize) {
94 newZoneStart = alignedTime - (otherZoneRange.m_TEnd - otherZoneRange.m_TStart);
95 }
96 }
97
98 if (newZoneStart < newZoneEnd) {
99 result = true;
100 otherZone->setRange(newZoneStart, newZoneEnd);
101 otherZone->parentPlot()->replot();
102 }
103 }
104
105 return result;
106 }
57 107 };
58 108
59 109 VisualizationSelectionZoneItem::VisualizationSelectionZoneItem(QCustomPlot *plot)
60 110 : QCPItemRect(plot),
61 111 impl{spimpl::make_unique_impl<VisualizationSelectionZoneItemPrivate>(plot)}
62 112 {
63 113 topLeft->setTypeX(QCPItemPosition::ptPlotCoords);
64 114 topLeft->setTypeY(QCPItemPosition::ptAxisRectRatio);
65 115 bottomRight->setTypeX(QCPItemPosition::ptPlotCoords);
66 116 bottomRight->setTypeY(QCPItemPosition::ptAxisRectRatio);
67 117 setSelectable(false);
68 118
69 119 impl->m_RightLine = new QCPItemStraightLine(plot);
70 120 impl->m_RightLine->point1->setParentAnchor(topRight);
71 121 impl->m_RightLine->point2->setParentAnchor(bottomRight);
72 122 impl->m_RightLine->point1->setTypeX(QCPItemPosition::ptAbsolute);
73 123 impl->m_RightLine->point1->setTypeY(QCPItemPosition::ptAbsolute);
74 124 impl->m_RightLine->point2->setTypeX(QCPItemPosition::ptAbsolute);
75 125 impl->m_RightLine->point2->setTypeY(QCPItemPosition::ptAbsolute);
76 126 impl->m_RightLine->setSelectable(false);
77 127
78 128 impl->m_LeftLine = new QCPItemStraightLine(plot);
79 129 impl->m_LeftLine->point1->setParentAnchor(topLeft);
80 130 impl->m_LeftLine->point2->setParentAnchor(bottomLeft);
81 131 impl->m_LeftLine->point1->setTypeX(QCPItemPosition::ptAbsolute);
82 132 impl->m_LeftLine->point1->setTypeY(QCPItemPosition::ptAbsolute);
83 133 impl->m_LeftLine->point2->setTypeX(QCPItemPosition::ptAbsolute);
84 134 impl->m_LeftLine->point2->setTypeY(QCPItemPosition::ptAbsolute);
85 135 impl->m_LeftLine->setSelectable(false);
86 136
87 137 connect(this, &VisualizationSelectionZoneItem::selectionChanged, impl->m_RightLine,
88 138 &QCPItemStraightLine::setSelected);
89 139 connect(this, &VisualizationSelectionZoneItem::selectionChanged, impl->m_LeftLine,
90 140 &QCPItemStraightLine::setSelected);
91 141
92 142 setColor(QColor(DEFAULT_COLOR));
93 143 }
94 144
95 145 VisualizationSelectionZoneItem::~VisualizationSelectionZoneItem()
96 146 {
97 impl->m_Plot->removeItem(impl->m_RightLine);
98 impl->m_Plot->removeItem(impl->m_LeftLine);
147 }
148
149 VisualizationGraphWidget *VisualizationSelectionZoneItem::parentGraphWidget() const noexcept
150 {
151 auto parent = impl->m_Plot->parentWidget();
152 while (parent != nullptr && !qobject_cast<VisualizationGraphWidget *>(parent)) {
153 parent = parent->parentWidget();
154 }
155
156 return qobject_cast<VisualizationGraphWidget *>(parent);
99 157 }
100 158
101 159 void VisualizationSelectionZoneItem::setName(const QString &name)
102 160 {
103 161 if (name.isEmpty() && impl->m_NameLabelItem) {
104 162 impl->m_Plot->removeItem(impl->m_NameLabelItem);
105 163 impl->m_NameLabelItem = nullptr;
106 164 }
107 165 else if (!impl->m_NameLabelItem) {
108 166 impl->m_NameLabelItem = new QCPItemText(impl->m_Plot);
109 167 impl->m_NameLabelItem->setText(name);
110 168 impl->m_NameLabelItem->setPositionAlignment(Qt::AlignHCenter | Qt::AlignTop);
111 169 impl->m_NameLabelItem->setColor(impl->m_Color);
112 170 impl->m_NameLabelItem->position->setParentAnchor(top);
113 171 }
114 172 }
115 173
116 174 QString VisualizationSelectionZoneItem::name() const
117 175 {
118 176 if (!impl->m_NameLabelItem) {
119 177 return QString();
120 178 }
121 179
122 180 return impl->m_NameLabelItem->text();
123 181 }
124 182
125 183 SqpRange VisualizationSelectionZoneItem::range() const
126 184 {
127 185 SqpRange range;
128 186 range.m_TStart = impl->m_T1 <= impl->m_T2 ? impl->m_T1 : impl->m_T2;
129 187 range.m_TEnd = impl->m_T1 > impl->m_T2 ? impl->m_T1 : impl->m_T2;
130 188 return range;
131 189 }
132 190
133 191 void VisualizationSelectionZoneItem::setRange(double tstart, double tend)
134 192 {
135 193 impl->m_T1 = tstart;
136 194 impl->m_T2 = tend;
137 195 impl->updatePosition(this);
138 196 }
139 197
140 198 void VisualizationSelectionZoneItem::setStart(double tstart)
141 199 {
142 200 impl->m_T1 = tstart;
143 201 impl->updatePosition(this);
144 202 }
145 203
146 204 void VisualizationSelectionZoneItem::setEnd(double tend)
147 205 {
148 206 impl->m_T2 = tend;
149 207 impl->updatePosition(this);
150 208 }
151 209
152 210 void VisualizationSelectionZoneItem::setColor(const QColor &color)
153 211 {
154 212 impl->m_Color = color;
155 213
156 214 auto brushColor = color;
157 215 brushColor.setAlpha(80);
158 216 setBrush(QBrush(brushColor));
159 217 setPen(QPen(Qt::NoPen));
160 218
161 219 auto selectedBrushColor = brushColor;
162 220 selectedBrushColor.setAlpha(150);
163 221 setSelectedBrush(QBrush(selectedBrushColor));
164 222 setSelectedPen(QPen(Qt::NoPen));
165 223
166 224 auto linePen = QPen(color);
167 225 linePen.setStyle(Qt::SolidLine);
168 226 linePen.setWidth(4);
169 227
170 228 auto selectedLinePen = linePen;
171 229 selectedLinePen.setColor(color.darker(120));
172 230 selectedLinePen.setWidth(4);
173 231
174 232 impl->m_LeftLine->setPen(linePen);
175 233 impl->m_RightLine->setPen(linePen);
176 234
177 235 impl->m_LeftLine->setSelectedPen(selectedLinePen);
178 236 impl->m_RightLine->setSelectedPen(selectedLinePen);
179 237 }
180 238
181 239 void VisualizationSelectionZoneItem::setEditionEnabled(bool value)
182 240 {
183 241 impl->m_IsEditionEnabled = value;
184 242 setSelectable(value);
185 243 if (!value) {
186 244 setSelected(false);
187 245 impl->m_CurrentEditionMode = VisualizationSelectionZoneItemPrivate::EditionMode::NoEdition;
188 246 }
189 247 }
190 248
191 249 bool VisualizationSelectionZoneItem::isEditionEnabled() const
192 250 {
193 251 return impl->m_IsEditionEnabled;
194 252 }
195 253
254 void VisualizationSelectionZoneItem::moveToTop()
255 {
256 moveToLayer(layer(), false);
257 }
258
196 259 Qt::CursorShape
197 260 VisualizationSelectionZoneItem::curshorShapeForPosition(const QPoint &position) const
198 261 {
199 262 auto mode = impl->m_CurrentEditionMode
200 263 == VisualizationSelectionZoneItemPrivate::EditionMode::NoEdition
201 264 ? impl->getEditionMode(position, this)
202 265 : impl->m_CurrentEditionMode;
203 266 switch (mode) {
204 267 case VisualizationSelectionZoneItemPrivate::EditionMode::Move:
205 268 return Qt::SizeAllCursor;
206 269 case VisualizationSelectionZoneItemPrivate::EditionMode::ResizeLeft:
207 270 case VisualizationSelectionZoneItemPrivate::EditionMode::ResizeRight: // fallthrough
208 271 return Qt::SizeHorCursor;
209 272 default:
210 273 return Qt::ArrowCursor;
211 274 }
212 275 }
213 276
214 277 void VisualizationSelectionZoneItem::setHovered(bool value)
215 278 {
216 279 if (value) {
217 280 auto linePen = impl->m_LeftLine->pen();
218 281 linePen.setStyle(Qt::DotLine);
219 282 linePen.setWidth(3);
220 283
221 284 auto selectedLinePen = impl->m_LeftLine->selectedPen();
222 285 ;
223 286 selectedLinePen.setStyle(Qt::DotLine);
224 287 selectedLinePen.setWidth(3);
225 288
226 289 impl->m_LeftLine->setPen(linePen);
227 290 impl->m_RightLine->setPen(linePen);
228 291
229 292 impl->m_LeftLine->setSelectedPen(selectedLinePen);
230 293 impl->m_RightLine->setSelectedPen(selectedLinePen);
231 294 }
232 295 else {
233 296 setColor(impl->m_Color);
234 297 }
235 298 }
236 299
237 300 void VisualizationSelectionZoneItem::setAssociatedEditedZones(
238 301 const QVector<VisualizationSelectionZoneItem *> &associatedZones)
239 302 {
240 303 impl->m_AssociatedEditedZones = associatedZones;
241 304 impl->m_AssociatedEditedZones.removeAll(this);
242 305 }
243 306
307 bool VisualizationSelectionZoneItem::alignZonesVerticallyOnLeft(
308 const QVector<VisualizationSelectionZoneItem *> &zonesToAlign, bool allowResize)
309 {
310 return impl->alignZones(this, zonesToAlign, true, allowResize, true);
311 }
312
313 bool VisualizationSelectionZoneItem::alignZonesVerticallyOnRight(
314 const QVector<VisualizationSelectionZoneItem *> &zonesToAlign, bool allowResize)
315 {
316 return impl->alignZones(this, zonesToAlign, false, allowResize, true);
317 }
318
319 bool VisualizationSelectionZoneItem::alignZonesTemporallyOnLeft(
320 const QVector<VisualizationSelectionZoneItem *> &zonesToAlign, bool allowResize)
321 {
322 return impl->alignZones(this, zonesToAlign, true, allowResize, false);
323 }
324
325 bool VisualizationSelectionZoneItem::alignZonesTemporallyOnRight(
326 const QVector<VisualizationSelectionZoneItem *> &zonesToAlign, bool allowResize)
327 {
328 return impl->alignZones(this, zonesToAlign, false, allowResize, false);
329 }
330
244 331 void VisualizationSelectionZoneItem::mousePressEvent(QMouseEvent *event, const QVariant &details)
245 332 {
333 Q_UNUSED(details);
334
246 335 if (isEditionEnabled() && event->button() == Qt::LeftButton) {
247 336 impl->m_CurrentEditionMode = impl->getEditionMode(event->pos(), this);
248 337
249 338 impl->m_MovedOrinalT1 = impl->m_T1;
250 339 impl->m_MovedOrinalT2 = impl->m_T2;
251 340 for (auto associatedZone : impl->m_AssociatedEditedZones) {
252 341 associatedZone->impl->m_MovedOrinalT1 = associatedZone->impl->m_T1;
253 342 associatedZone->impl->m_MovedOrinalT2 = associatedZone->impl->m_T2;
254 343 }
255 344 }
256 345 else {
257 346 impl->m_CurrentEditionMode = VisualizationSelectionZoneItemPrivate::EditionMode::NoEdition;
258 347 event->ignore();
259 348 }
260 349 }
261 350
262 351 void VisualizationSelectionZoneItem::mouseMoveEvent(QMouseEvent *event, const QPointF &startPos)
263 352 {
264 353 if (isEditionEnabled()) {
354 if (!selected()) {
355 // Force the item to be selected during the edition
356 parentGraphWidget()->parentVisualizationWidget()->selectionZoneManager().setSelected(
357 this, true);
358 }
359
265 360 auto axis = impl->m_Plot->axisRect()->axis(QCPAxis::atBottom);
266 361 auto pixelDiff = event->pos().x() - startPos.x();
267 362 auto diff = impl->pixelSizeToAxisXSize(pixelDiff);
268 363
269 364 switch (impl->m_CurrentEditionMode) {
270 365 case VisualizationSelectionZoneItemPrivate::EditionMode::Move:
271 366 setRange(impl->m_MovedOrinalT1 + diff, impl->m_MovedOrinalT2 + diff);
272 367 for (auto associatedZone : impl->m_AssociatedEditedZones) {
273 368 associatedZone->move(pixelDiff);
274 369 }
275 370 break;
276 371 case VisualizationSelectionZoneItemPrivate::EditionMode::ResizeLeft:
277 372 setStart(impl->m_MovedOrinalT1 + diff);
278 373 for (auto associatedZone : impl->m_AssociatedEditedZones) {
279 374 impl->m_MovedOrinalT1 < impl->m_MovedOrinalT2
280 375 ? associatedZone->resizeLeft(pixelDiff)
281 376 : associatedZone->resizeRight(pixelDiff);
282 377 }
283 378 break;
284 379 case VisualizationSelectionZoneItemPrivate::EditionMode::ResizeRight:
285 380 setEnd(impl->m_MovedOrinalT2 + diff);
286 381 for (auto associatedZone : impl->m_AssociatedEditedZones) {
287 382 impl->m_MovedOrinalT1 < impl->m_MovedOrinalT2
288 383 ? associatedZone->resizeRight(pixelDiff)
289 384 : associatedZone->resizeLeft(pixelDiff);
290 385 }
291 386 break;
292 387 default:
293 388 break;
294 389 }
295 390
296 391 for (auto associatedZone : impl->m_AssociatedEditedZones) {
297 392 associatedZone->parentPlot()->replot();
298 393 }
299 394 }
300 395 else {
301 396 event->ignore();
302 397 }
303 398 }
304 399
305 400 void VisualizationSelectionZoneItem::mouseReleaseEvent(QMouseEvent *event, const QPointF &startPos)
306 401 {
402 Q_UNUSED(startPos);
403
307 404 if (isEditionEnabled()) {
308 405 impl->m_CurrentEditionMode = VisualizationSelectionZoneItemPrivate::EditionMode::NoEdition;
309 406 }
310 407 else {
311 408 event->ignore();
312 409 }
313 410
314 411 impl->m_AssociatedEditedZones.clear();
315 412 }
316 413
317 414 void VisualizationSelectionZoneItem::resizeLeft(double pixelDiff)
318 415 {
319 416 auto diff = impl->pixelSizeToAxisXSize(pixelDiff);
320 417 if (impl->m_MovedOrinalT1 <= impl->m_MovedOrinalT2) {
321 418 setStart(impl->m_MovedOrinalT1 + diff);
322 419 }
323 420 else {
324 421 setEnd(impl->m_MovedOrinalT2 + diff);
325 422 }
326 423 }
327 424
328 425 void VisualizationSelectionZoneItem::resizeRight(double pixelDiff)
329 426 {
330 427 auto diff = impl->pixelSizeToAxisXSize(pixelDiff);
331 428 if (impl->m_MovedOrinalT1 > impl->m_MovedOrinalT2) {
332 429 setStart(impl->m_MovedOrinalT1 + diff);
333 430 }
334 431 else {
335 432 setEnd(impl->m_MovedOrinalT2 + diff);
336 433 }
337 434 }
338 435
339 436 void VisualizationSelectionZoneItem::move(double pixelDiff)
340 437 {
341 438 auto diff = impl->pixelSizeToAxisXSize(pixelDiff);
342 439 setRange(impl->m_MovedOrinalT1 + diff, impl->m_MovedOrinalT2 + diff);
343 440 }
@@ -1,51 +1,51
1 1 #include "Visualization/VisualizationSelectionZoneManager.h"
2 2 #include "Visualization/VisualizationSelectionZoneItem.h"
3 3
4 4 struct VisualizationSelectionZoneManager::VisualizationSelectionZoneManagerPrivate {
5 5 QVector<VisualizationSelectionZoneItem *> m_SelectedItems;
6 6 };
7 7
8 8 VisualizationSelectionZoneManager::VisualizationSelectionZoneManager()
9 9 : impl{spimpl::make_unique_impl<VisualizationSelectionZoneManagerPrivate>()}
10 10 {
11 11 }
12 12
13 13 void VisualizationSelectionZoneManager::select(
14 14 const QVector<VisualizationSelectionZoneItem *> &items)
15 15 {
16 16 clearSelection();
17 17 for (auto item : items) {
18 18 setSelected(item, true);
19 19 }
20 20 }
21 21
22 22 void VisualizationSelectionZoneManager::setSelected(VisualizationSelectionZoneItem *item,
23 23 bool value)
24 24 {
25 25 if (value != item->selected()) {
26 26 item->setSelected(value);
27 item->parentPlot()->replot();
27 item->parentPlot()->replot(QCustomPlot::rpQueuedReplot);
28 28 }
29 29
30 30 if (!value && impl->m_SelectedItems.contains(item)) {
31 31 impl->m_SelectedItems.removeAll(item);
32 32 }
33 33 else if (value) {
34 34 impl->m_SelectedItems << item;
35 35 }
36 36 }
37 37
38 38 void VisualizationSelectionZoneManager::clearSelection()
39 39 {
40 40 for (auto item : impl->m_SelectedItems) {
41 41 item->setSelected(false);
42 item->parentPlot()->replot();
42 item->parentPlot()->replot(QCustomPlot::rpQueuedReplot);
43 43 }
44 44
45 45 impl->m_SelectedItems.clear();
46 46 }
47 47
48 48 QVector<VisualizationSelectionZoneItem *> VisualizationSelectionZoneManager::selectedItems() const
49 49 {
50 50 return impl->m_SelectedItems;
51 51 }
@@ -1,329 +1,329
1 1 #include "Visualization/VisualizationTabWidget.h"
2 2 #include "Visualization/IVisualizationWidgetVisitor.h"
3 3 #include "ui_VisualizationTabWidget.h"
4 4
5 5 #include "Visualization/VisualizationGraphWidget.h"
6 6 #include "Visualization/VisualizationZoneWidget.h"
7 7
8 8 #include "Visualization/MacScrollBarStyle.h"
9 9
10 10 #include "Variable/VariableController.h"
11 11
12 12 #include "Common/MimeTypesDef.h"
13 13
14 #include "DragAndDrop/DragDropHelper.h"
14 #include "DragAndDrop/DragDropGuiController.h"
15 15 #include "SqpApplication.h"
16 16
17 17 Q_LOGGING_CATEGORY(LOG_VisualizationTabWidget, "VisualizationTabWidget")
18 18
19 19 namespace {
20 20
21 21 /// Generates a default name for a new zone, according to the number of zones already displayed in
22 22 /// the tab
23 23 QString defaultZoneName(const QLayout &layout)
24 24 {
25 25 auto count = 0;
26 26 for (auto i = 0; i < layout.count(); ++i) {
27 27 if (dynamic_cast<VisualizationZoneWidget *>(layout.itemAt(i)->widget())) {
28 28 count++;
29 29 }
30 30 }
31 31
32 32 return QObject::tr("Zone %1").arg(count + 1);
33 33 }
34 34
35 35 /**
36 36 * Applies a function to all zones of the tab represented by its layout
37 37 * @param layout the layout that contains zones
38 38 * @param fun the function to apply to each zone
39 39 */
40 40 template <typename Fun>
41 41 void processZones(QLayout &layout, Fun fun)
42 42 {
43 43 for (auto i = 0; i < layout.count(); ++i) {
44 44 if (auto item = layout.itemAt(i)) {
45 45 if (auto visualizationZoneWidget
46 46 = dynamic_cast<VisualizationZoneWidget *>(item->widget())) {
47 47 fun(*visualizationZoneWidget);
48 48 }
49 49 }
50 50 }
51 51 }
52 52
53 53 } // namespace
54 54
55 55 struct VisualizationTabWidget::VisualizationTabWidgetPrivate {
56 56 explicit VisualizationTabWidgetPrivate(const QString &name) : m_Name{name} {}
57 57
58 58 QString m_Name;
59 59
60 60 #ifdef Q_OS_MAC
61 61 std::unique_ptr<MacScrollBarStyle> m_MacScrollBarStyle = std::make_unique<MacScrollBarStyle>();
62 62 #endif
63 63
64 64 void dropGraph(int index, VisualizationTabWidget *tabWidget);
65 65 void dropZone(int index, VisualizationTabWidget *tabWidget);
66 66 void dropVariables(const QList<std::shared_ptr<Variable> > &variables, int index,
67 67 VisualizationTabWidget *tabWidget);
68 68 };
69 69
70 70 VisualizationTabWidget::VisualizationTabWidget(const QString &name, QWidget *parent)
71 71 : QWidget{parent},
72 72 ui{new Ui::VisualizationTabWidget},
73 73 impl{spimpl::make_unique_impl<VisualizationTabWidgetPrivate>(name)}
74 74 {
75 75 ui->setupUi(this);
76 76
77 77 #ifdef Q_OS_MAC
78 78 impl->m_MacScrollBarStyle->selfInstallOn(ui->scrollArea, true);
79 79 #endif
80 80
81 ui->dragDropContainer->setPlaceHolderType(DragDropHelper::PlaceHolderType::Zone, "Zone");
81 ui->dragDropContainer->setPlaceHolderType(DragDropGuiController::PlaceHolderType::Zone, "Zone");
82 82 ui->dragDropContainer->layout()->setContentsMargins(0, 0, 0, 12);
83 83 ui->dragDropContainer->layout()->setSpacing(0);
84 84 ui->dragDropContainer->setMimeType(MIME_TYPE_GRAPH,
85 85 VisualizationDragDropContainer::DropBehavior::Inserted);
86 86 ui->dragDropContainer->setMimeType(MIME_TYPE_ZONE,
87 87 VisualizationDragDropContainer::DropBehavior::Inserted);
88 88 ui->dragDropContainer->setMimeType(MIME_TYPE_VARIABLE_LIST,
89 89 VisualizationDragDropContainer::DropBehavior::Inserted);
90 90
91 91 ui->dragDropContainer->setAcceptMimeDataFunction([this](auto mimeData) {
92 return sqpApp->dragDropHelper().checkMimeDataForVisualization(mimeData,
93 ui->dragDropContainer);
92 return sqpApp->dragDropGuiController().checkMimeDataForVisualization(mimeData,
93 ui->dragDropContainer);
94 94 });
95 95
96 96 connect(ui->dragDropContainer, &VisualizationDragDropContainer::dropOccuredInContainer, this,
97 97 &VisualizationTabWidget::dropMimeData);
98 98
99 sqpApp->dragDropHelper().addDragDropScrollArea(ui->scrollArea);
99 sqpApp->dragDropGuiController().addDragDropScrollArea(ui->scrollArea);
100 100
101 101 // Widget is deleted when closed
102 102 setAttribute(Qt::WA_DeleteOnClose);
103 103 }
104 104
105 105 VisualizationTabWidget::~VisualizationTabWidget()
106 106 {
107 sqpApp->dragDropHelper().removeDragDropScrollArea(ui->scrollArea);
107 sqpApp->dragDropGuiController().removeDragDropScrollArea(ui->scrollArea);
108 108 delete ui;
109 109 }
110 110
111 111 void VisualizationTabWidget::addZone(VisualizationZoneWidget *zoneWidget)
112 112 {
113 113 ui->dragDropContainer->addDragWidget(zoneWidget);
114 114 }
115 115
116 116 void VisualizationTabWidget::insertZone(int index, VisualizationZoneWidget *zoneWidget)
117 117 {
118 118 ui->dragDropContainer->insertDragWidget(index, zoneWidget);
119 119 }
120 120
121 121 VisualizationZoneWidget *VisualizationTabWidget::createZone(std::shared_ptr<Variable> variable)
122 122 {
123 123 return createZone({variable}, -1);
124 124 }
125 125
126 126 VisualizationZoneWidget *
127 127 VisualizationTabWidget::createZone(const QList<std::shared_ptr<Variable> > &variables, int index)
128 128 {
129 129 auto zoneWidget = createEmptyZone(index);
130 130
131 131 // Creates a new graph into the zone
132 132 zoneWidget->createGraph(variables, index);
133 133
134 134 return zoneWidget;
135 135 }
136 136
137 137 VisualizationZoneWidget *VisualizationTabWidget::createEmptyZone(int index)
138 138 {
139 139 auto zoneWidget
140 140 = new VisualizationZoneWidget{defaultZoneName(*ui->dragDropContainer->layout()), this};
141 141 this->insertZone(index, zoneWidget);
142 142
143 143 return zoneWidget;
144 144 }
145 145
146 146 void VisualizationTabWidget::accept(IVisualizationWidgetVisitor *visitor)
147 147 {
148 148 if (visitor) {
149 149 visitor->visitEnter(this);
150 150
151 151 // Apply visitor to zone children: widgets different from zones are not visited (no action)
152 152 processZones(tabLayout(), [visitor](VisualizationZoneWidget &zoneWidget) {
153 153 zoneWidget.accept(visitor);
154 154 });
155 155
156 156 visitor->visitLeave(this);
157 157 }
158 158 else {
159 159 qCCritical(LOG_VisualizationTabWidget()) << tr("Can't visit widget : the visitor is null");
160 160 }
161 161 }
162 162
163 163 bool VisualizationTabWidget::canDrop(const Variable &variable) const
164 164 {
165 165 // A tab can always accomodate a variable
166 166 Q_UNUSED(variable);
167 167 return true;
168 168 }
169 169
170 170 bool VisualizationTabWidget::contains(const Variable &variable) const
171 171 {
172 172 Q_UNUSED(variable);
173 173 return false;
174 174 }
175 175
176 176 QString VisualizationTabWidget::name() const
177 177 {
178 178 return impl->m_Name;
179 179 }
180 180
181 181 void VisualizationTabWidget::closeEvent(QCloseEvent *event)
182 182 {
183 183 // Closes zones in the tab
184 184 processZones(tabLayout(), [](VisualizationZoneWidget &zoneWidget) { zoneWidget.close(); });
185 185
186 186 QWidget::closeEvent(event);
187 187 }
188 188
189 189 QLayout &VisualizationTabWidget::tabLayout() const noexcept
190 190 {
191 191 return *ui->dragDropContainer->layout();
192 192 }
193 193
194 194 void VisualizationTabWidget::dropMimeData(int index, const QMimeData *mimeData)
195 195 {
196 196 if (mimeData->hasFormat(MIME_TYPE_GRAPH)) {
197 197 impl->dropGraph(index, this);
198 198 }
199 199 else if (mimeData->hasFormat(MIME_TYPE_ZONE)) {
200 200 impl->dropZone(index, this);
201 201 }
202 202 else if (mimeData->hasFormat(MIME_TYPE_VARIABLE_LIST)) {
203 203 auto variables = sqpApp->variableController().variablesForMimeData(
204 204 mimeData->data(MIME_TYPE_VARIABLE_LIST));
205 205 impl->dropVariables(variables, index, this);
206 206 }
207 207 else {
208 208 qCWarning(LOG_VisualizationZoneWidget())
209 209 << tr("VisualizationTabWidget::dropMimeData, unknown MIME data received.");
210 210 }
211 211 }
212 212
213 213 void VisualizationTabWidget::VisualizationTabWidgetPrivate::dropGraph(
214 214 int index, VisualizationTabWidget *tabWidget)
215 215 {
216 auto &helper = sqpApp->dragDropHelper();
216 auto &helper = sqpApp->dragDropGuiController();
217 217
218 218 auto graphWidget = qobject_cast<VisualizationGraphWidget *>(helper.getCurrentDragWidget());
219 219 if (!graphWidget) {
220 220 qCWarning(LOG_VisualizationZoneWidget())
221 221 << tr("VisualizationTabWidget::dropGraph, drop aborted, the dropped graph is not "
222 222 "found or invalid.");
223 223 Q_ASSERT(false);
224 224 return;
225 225 }
226 226
227 227 auto parentDragDropContainer
228 228 = qobject_cast<VisualizationDragDropContainer *>(graphWidget->parentWidget());
229 229 if (!parentDragDropContainer) {
230 230 qCWarning(LOG_VisualizationZoneWidget())
231 231 << tr("VisualizationTabWidget::dropGraph, drop aborted, the parent container of "
232 232 "the dropped graph is not found.");
233 233 Q_ASSERT(false);
234 234 return;
235 235 }
236 236
237 237 auto nbGraph = parentDragDropContainer->countDragWidget();
238 238
239 239 const auto &variables = graphWidget->variables();
240 240
241 241 if (!variables.isEmpty()) {
242 242 // Abort the requests for the variables (if any)
243 243 // Commented, because it's not sure if it's needed or not
244 244 // for (const auto& var : variables)
245 245 //{
246 246 // sqpApp->variableController().onAbortProgressRequested(var);
247 247 //}
248 248
249 249 if (nbGraph == 1) {
250 250 // This is the only graph in the previous zone, close the zone
251 251 helper.delayedCloseWidget(graphWidget->parentZoneWidget());
252 252 }
253 253 else {
254 254 // Close the graph
255 255 helper.delayedCloseWidget(graphWidget);
256 256 }
257 257
258 258 auto zoneWidget = tabWidget->createZone(variables, index);
259 259 auto firstGraph = zoneWidget->firstGraph();
260 260 if (firstGraph) {
261 261 firstGraph->addSelectionZones(graphWidget->selectionZoneRanges());
262 262 }
263 263 else {
264 264 qCWarning(LOG_VisualizationZoneWidget())
265 265 << tr("VisualizationTabWidget::dropGraph, no graph added in the widget.");
266 266 Q_ASSERT(false);
267 267 }
268 268 }
269 269 else {
270 270 // The graph is empty, create an empty zone and move the graph inside
271 271
272 272 auto parentZoneWidget = graphWidget->parentZoneWidget();
273 273
274 274 parentDragDropContainer->layout()->removeWidget(graphWidget);
275 275
276 276 auto zoneWidget = tabWidget->createEmptyZone(index);
277 277 zoneWidget->addGraph(graphWidget);
278 278
279 279 // Close the old zone if it was the only graph inside
280 280 if (nbGraph == 1) {
281 281 helper.delayedCloseWidget(parentZoneWidget);
282 282 }
283 283 }
284 284 }
285 285
286 286 void VisualizationTabWidget::VisualizationTabWidgetPrivate::dropZone(
287 287 int index, VisualizationTabWidget *tabWidget)
288 288 {
289 auto &helper = sqpApp->dragDropHelper();
289 auto &helper = sqpApp->dragDropGuiController();
290 290
291 291 auto zoneWidget = qobject_cast<VisualizationZoneWidget *>(helper.getCurrentDragWidget());
292 292 if (!zoneWidget) {
293 293 qCWarning(LOG_VisualizationZoneWidget())
294 294 << tr("VisualizationTabWidget::dropZone, drop aborted, the dropped zone is not "
295 295 "found or invalid.");
296 296 Q_ASSERT(false);
297 297 return;
298 298 }
299 299
300 300 auto parentDragDropContainer
301 301 = qobject_cast<VisualizationDragDropContainer *>(zoneWidget->parentWidget());
302 302 if (!parentDragDropContainer) {
303 303 qCWarning(LOG_VisualizationZoneWidget())
304 304 << tr("VisualizationTabWidget::dropZone, drop aborted, the parent container of "
305 305 "the dropped zone is not found.");
306 306 Q_ASSERT(false);
307 307 return;
308 308 }
309 309
310 310 // Simple move of the zone, no variable operation associated
311 311 parentDragDropContainer->layout()->removeWidget(zoneWidget);
312 312 tabWidget->ui->dragDropContainer->insertDragWidget(index, zoneWidget);
313 313 }
314 314
315 315 void VisualizationTabWidget::VisualizationTabWidgetPrivate::dropVariables(
316 316 const QList<std::shared_ptr<Variable> > &variables, int index,
317 317 VisualizationTabWidget *tabWidget)
318 318 {
319 319 // Note: the AcceptMimeDataFunction (set on the drop container) ensure there is a single and
320 320 // compatible variable here
321 321 if (variables.count() > 1) {
322 322 qCWarning(LOG_VisualizationZoneWidget())
323 323 << tr("VisualizationTabWidget::dropVariables, dropping multiple variables, operation "
324 324 "aborted.");
325 325 return;
326 326 }
327 327
328 328 tabWidget->createZone(variables, index);
329 329 }
@@ -1,197 +1,215
1 1 #include "Visualization/VisualizationWidget.h"
2 2 #include "Visualization/IVisualizationWidgetVisitor.h"
3 #include "Visualization/VisualizationActionManager.h"
3 4 #include "Visualization/VisualizationGraphWidget.h"
5 #include "Visualization/VisualizationSelectionZoneItem.h"
4 6 #include "Visualization/VisualizationSelectionZoneManager.h"
5 7 #include "Visualization/VisualizationTabWidget.h"
6 8 #include "Visualization/VisualizationZoneWidget.h"
7 9 #include "Visualization/operations/FindVariableOperation.h"
8 10 #include "Visualization/operations/GenerateVariableMenuOperation.h"
9 11 #include "Visualization/operations/RemoveVariableOperation.h"
10 12 #include "Visualization/operations/RescaleAxeOperation.h"
11 13 #include "Visualization/qcustomplot.h"
12 14
13 15 #include "ui_VisualizationWidget.h"
14 16
15 #include "DragAndDrop/DragDropHelper.h"
17 #include "DragAndDrop/DragDropGuiController.h"
16 18 #include "SqpApplication.h"
17 19
18 20 #include <QToolButton>
19 21
20 22 #include <memory>
21 23
22 24 Q_LOGGING_CATEGORY(LOG_VisualizationWidget, "VisualizationWidget")
23 25
24 26 struct VisualizationWidget::VisualizationWidgetPrivate {
25 27 std::unique_ptr<VisualizationSelectionZoneManager> m_ZoneSelectionManager = nullptr;
28 VisualizationActionManager m_ActionManager;
26 29
27 30 VisualizationWidgetPrivate()
28 31 : m_ZoneSelectionManager(std::make_unique<VisualizationSelectionZoneManager>())
29 32 {
30 33 }
31 34 };
32 35
33 36 VisualizationWidget::VisualizationWidget(QWidget *parent)
34 37 : QWidget{parent},
35 38 ui{new Ui::VisualizationWidget},
36 39 impl{spimpl::make_unique_impl<VisualizationWidgetPrivate>()}
37 40 {
38 41 ui->setupUi(this);
39 42
40 43 auto addTabViewButton = new QToolButton{ui->tabWidget};
41 44 addTabViewButton->setText(tr("Add View"));
42 45 addTabViewButton->setCursor(Qt::ArrowCursor);
43 46 ui->tabWidget->setCornerWidget(addTabViewButton, Qt::TopRightCorner);
44 47
45 48 auto enableMinimumCornerWidgetSize = [this](bool enable) {
46 49
47 50 auto tabViewCornerWidget = ui->tabWidget->cornerWidget();
48 51 auto width = enable ? tabViewCornerWidget->width() : 0;
49 52 auto height = enable ? tabViewCornerWidget->height() : 0;
50 53 tabViewCornerWidget->setMinimumHeight(height);
51 54 tabViewCornerWidget->setMinimumWidth(width);
52 55 ui->tabWidget->setMinimumHeight(height);
53 56 ui->tabWidget->setMinimumWidth(width);
54 57 };
55 58
56 59 auto addTabView = [this, enableMinimumCornerWidgetSize]() {
57 60 auto widget = new VisualizationTabWidget{QString{"View %1"}.arg(ui->tabWidget->count() + 1),
58 61 ui->tabWidget};
59 62 auto index = ui->tabWidget->addTab(widget, widget->name());
60 63 if (ui->tabWidget->count() > 0) {
61 64 enableMinimumCornerWidgetSize(false);
62 65 }
63 66 qCInfo(LOG_VisualizationWidget()) << tr("add the tab of index %1").arg(index);
64 67 };
65 68
66 69 auto removeTabView = [this, enableMinimumCornerWidgetSize](int index) {
67 70 if (ui->tabWidget->count() == 1) {
68 71 enableMinimumCornerWidgetSize(true);
69 72 }
70 73
71 74 // Removes widget from tab and closes it
72 75 auto widget = ui->tabWidget->widget(index);
73 76 ui->tabWidget->removeTab(index);
74 77 if (widget) {
75 78 widget->close();
76 79 }
77 80
78 81 qCInfo(LOG_VisualizationWidget()) << tr("remove the tab of index %1").arg(index);
79 82
80 83 };
81 84
82 85 ui->tabWidget->setTabsClosable(true);
83 86
84 87 connect(addTabViewButton, &QToolButton::clicked, addTabView);
85 88 connect(ui->tabWidget, &QTabWidget::tabCloseRequested, removeTabView);
86 89
87 sqpApp->dragDropHelper().addDragDropTabBar(ui->tabWidget->tabBar());
90 sqpApp->dragDropGuiController().addDragDropTabBar(ui->tabWidget->tabBar());
91
92 // Actions
93 impl->m_ActionManager.installSelectionZoneActions();
94
95 auto removeZoneAction = new QAction("Remove selected zone(s)");
96 removeZoneAction->setShortcut(QKeySequence::Delete);
97 connect(removeZoneAction, &QAction::triggered, [this]() {
98 auto selection = impl->m_ZoneSelectionManager->selectedItems();
99 for (auto selectionZone : selection) {
100 if (auto graph = selectionZone->parentGraphWidget()) {
101 graph->removeSelectionZone(selectionZone);
102 }
103 }
104 });
105 addAction(removeZoneAction);
88 106
89 107 // Adds default tab
90 108 addTabView();
91 109 }
92 110
93 111 VisualizationWidget::~VisualizationWidget()
94 112 {
95 sqpApp->dragDropHelper().removeDragDropTabBar(ui->tabWidget->tabBar());
113 sqpApp->dragDropGuiController().removeDragDropTabBar(ui->tabWidget->tabBar());
96 114 delete ui;
97 115 }
98 116
99 117 VisualizationSelectionZoneManager &VisualizationWidget::selectionZoneManager() const
100 118 {
101 119 return *impl->m_ZoneSelectionManager.get();
102 120 }
103 121
104 122 void VisualizationWidget::accept(IVisualizationWidgetVisitor *visitor)
105 123 {
106 124 if (visitor) {
107 125 visitor->visitEnter(this);
108 126
109 127 // Apply visitor for tab children
110 128 for (auto i = 0; i < ui->tabWidget->count(); ++i) {
111 129 // Widgets different from tabs are not visited (no action)
112 130 if (auto visualizationTabWidget
113 131 = dynamic_cast<VisualizationTabWidget *>(ui->tabWidget->widget(i))) {
114 132 visualizationTabWidget->accept(visitor);
115 133 }
116 134 }
117 135
118 136 visitor->visitLeave(this);
119 137 }
120 138 else {
121 139 qCCritical(LOG_VisualizationWidget()) << tr("Can't visit widget : the visitor is null");
122 140 }
123 141 }
124 142
125 143 bool VisualizationWidget::canDrop(const Variable &variable) const
126 144 {
127 145 // The main widget can never accomodate a variable
128 146 Q_UNUSED(variable);
129 147 return false;
130 148 }
131 149
132 150 bool VisualizationWidget::contains(const Variable &variable) const
133 151 {
134 152 Q_UNUSED(variable);
135 153 return false;
136 154 }
137 155
138 156 QString VisualizationWidget::name() const
139 157 {
140 158 return QStringLiteral("MainView");
141 159 }
142 160
143 161 void VisualizationWidget::attachVariableMenu(
144 162 QMenu *menu, const QVector<std::shared_ptr<Variable> > &variables) noexcept
145 163 {
146 164 // Menu is generated only if there is a single variable
147 165 if (variables.size() == 1) {
148 166 if (auto variable = variables.first()) {
149 167 // Gets the containers of the variable
150 168 FindVariableOperation findVariableOperation{variable};
151 169 accept(&findVariableOperation);
152 170 auto variableContainers = findVariableOperation.result();
153 171
154 172 // Generates the actions that make it possible to visualize the variable
155 173 GenerateVariableMenuOperation generateVariableMenuOperation{
156 174 menu, variable, std::move(variableContainers)};
157 175 accept(&generateVariableMenuOperation);
158 176 }
159 177 else {
160 178 qCCritical(LOG_VisualizationWidget()) << tr(
161 179 "Can't generate the menu relative to the visualization: the variable is null");
162 180 }
163 181 }
164 182 else {
165 183 qCDebug(LOG_VisualizationWidget())
166 184 << tr("No generation of the menu related to the visualization: several variables are "
167 185 "selected");
168 186 }
169 187 }
170 188
171 189 void VisualizationWidget::onVariableAboutToBeDeleted(std::shared_ptr<Variable> variable) noexcept
172 190 {
173 191 // Calls the operation of removing all references to the variable in the visualization
174 192 auto removeVariableOperation = RemoveVariableOperation{variable};
175 193 accept(&removeVariableOperation);
176 194 }
177 195
178 196 void VisualizationWidget::onRangeChanged(std::shared_ptr<Variable> variable,
179 197 const SqpRange &range) noexcept
180 198 {
181 199 // Calls the operation of rescaling all graph that contrains variable in the visualization
182 200 auto rescaleVariableOperation = RescaleAxeOperation{variable, range};
183 201 accept(&rescaleVariableOperation);
184 202 }
185 203
186 204 void VisualizationWidget::closeEvent(QCloseEvent *event)
187 205 {
188 206 // Closes tabs in the widget
189 207 for (auto i = 0; i < ui->tabWidget->count(); ++i) {
190 208 if (auto visualizationTabWidget
191 209 = dynamic_cast<VisualizationTabWidget *>(ui->tabWidget->widget(i))) {
192 210 visualizationTabWidget->close();
193 211 }
194 212 }
195 213
196 214 QWidget::closeEvent(event);
197 215 }
@@ -1,587 +1,587
1 1 #include "Visualization/VisualizationZoneWidget.h"
2 2
3 3 #include "Visualization/IVisualizationWidgetVisitor.h"
4 4 #include "Visualization/QCustomPlotSynchronizer.h"
5 5 #include "Visualization/VisualizationGraphWidget.h"
6 6 #include "Visualization/VisualizationWidget.h"
7 7 #include "ui_VisualizationZoneWidget.h"
8 8
9 9 #include "Common/MimeTypesDef.h"
10 10 #include "Common/VisualizationDef.h"
11 11
12 12 #include <Data/SqpRange.h>
13 13 #include <Time/TimeController.h>
14 14 #include <Variable/Variable.h>
15 15 #include <Variable/VariableController.h>
16 16
17 17 #include <Visualization/operations/FindVariableOperation.h>
18 18
19 #include <DragAndDrop/DragDropHelper.h>
19 #include <DragAndDrop/DragDropGuiController.h>
20 20 #include <QUuid>
21 21 #include <SqpApplication.h>
22 22 #include <cmath>
23 23
24 24 #include <QLayout>
25 25
26 26 Q_LOGGING_CATEGORY(LOG_VisualizationZoneWidget, "VisualizationZoneWidget")
27 27
28 28 namespace {
29 29
30 30
31 31 /// Generates a default name for a new graph, according to the number of graphs already displayed in
32 32 /// the zone
33 33 QString defaultGraphName(const QLayout &layout)
34 34 {
35 35 auto count = 0;
36 36 for (auto i = 0; i < layout.count(); ++i) {
37 37 if (dynamic_cast<VisualizationGraphWidget *>(layout.itemAt(i)->widget())) {
38 38 count++;
39 39 }
40 40 }
41 41
42 42 return QObject::tr("Graph %1").arg(count + 1);
43 43 }
44 44
45 45 /**
46 46 * Applies a function to all graphs of the zone represented by its layout
47 47 * @param layout the layout that contains graphs
48 48 * @param fun the function to apply to each graph
49 49 */
50 50 template <typename Fun>
51 51 void processGraphs(QLayout &layout, Fun fun)
52 52 {
53 53 for (auto i = 0; i < layout.count(); ++i) {
54 54 if (auto item = layout.itemAt(i)) {
55 55 if (auto visualizationGraphWidget
56 56 = qobject_cast<VisualizationGraphWidget *>(item->widget())) {
57 57 fun(*visualizationGraphWidget);
58 58 }
59 59 }
60 60 }
61 61 }
62 62
63 63 } // namespace
64 64
65 65 struct VisualizationZoneWidget::VisualizationZoneWidgetPrivate {
66 66
67 67 explicit VisualizationZoneWidgetPrivate()
68 68 : m_SynchronisationGroupId{QUuid::createUuid()},
69 69 m_Synchronizer{std::make_unique<QCustomPlotSynchronizer>()}
70 70 {
71 71 }
72 72 QUuid m_SynchronisationGroupId;
73 73 std::unique_ptr<IGraphSynchronizer> m_Synchronizer;
74 74
75 75 void dropGraph(int index, VisualizationZoneWidget *zoneWidget);
76 76 void dropVariables(const QList<std::shared_ptr<Variable> > &variables, int index,
77 77 VisualizationZoneWidget *zoneWidget);
78 78 };
79 79
80 80 VisualizationZoneWidget::VisualizationZoneWidget(const QString &name, QWidget *parent)
81 81 : VisualizationDragWidget{parent},
82 82 ui{new Ui::VisualizationZoneWidget},
83 83 impl{spimpl::make_unique_impl<VisualizationZoneWidgetPrivate>()}
84 84 {
85 85 ui->setupUi(this);
86 86
87 87 ui->zoneNameLabel->setText(name);
88 88
89 ui->dragDropContainer->setPlaceHolderType(DragDropHelper::PlaceHolderType::Graph);
89 ui->dragDropContainer->setPlaceHolderType(DragDropGuiController::PlaceHolderType::Graph);
90 90 ui->dragDropContainer->setMimeType(MIME_TYPE_GRAPH,
91 91 VisualizationDragDropContainer::DropBehavior::Inserted);
92 92 ui->dragDropContainer->setMimeType(
93 93 MIME_TYPE_VARIABLE_LIST, VisualizationDragDropContainer::DropBehavior::InsertedAndMerged);
94 94 ui->dragDropContainer->setMimeType(MIME_TYPE_TIME_RANGE,
95 95 VisualizationDragDropContainer::DropBehavior::Merged);
96 96 ui->dragDropContainer->setMimeType(MIME_TYPE_ZONE,
97 97 VisualizationDragDropContainer::DropBehavior::Forbidden);
98 98 ui->dragDropContainer->setMimeType(MIME_TYPE_SELECTION_ZONE,
99 99 VisualizationDragDropContainer::DropBehavior::Forbidden);
100 100 ui->dragDropContainer->setAcceptMimeDataFunction([this](auto mimeData) {
101 return sqpApp->dragDropHelper().checkMimeDataForVisualization(mimeData,
102 ui->dragDropContainer);
101 return sqpApp->dragDropGuiController().checkMimeDataForVisualization(mimeData,
102 ui->dragDropContainer);
103 103 });
104 104
105 105 auto acceptDragWidgetFun = [](auto dragWidget, auto mimeData) {
106 106 if (!mimeData) {
107 107 return false;
108 108 }
109 109
110 110 if (mimeData->hasFormat(MIME_TYPE_VARIABLE_LIST)) {
111 111 auto variables = sqpApp->variableController().variablesForMimeData(
112 112 mimeData->data(MIME_TYPE_VARIABLE_LIST));
113 113
114 114 if (variables.count() != 1) {
115 115 return false;
116 116 }
117 117 auto variable = variables.first();
118 118
119 119 if (auto graphWidget = dynamic_cast<const VisualizationGraphWidget *>(dragWidget)) {
120 120 return graphWidget->canDrop(*variable);
121 121 }
122 122 }
123 123
124 124 return true;
125 125 };
126 126 ui->dragDropContainer->setAcceptDragWidgetFunction(acceptDragWidgetFun);
127 127
128 128 connect(ui->dragDropContainer, &VisualizationDragDropContainer::dropOccuredInContainer, this,
129 129 &VisualizationZoneWidget::dropMimeData);
130 130 connect(ui->dragDropContainer, &VisualizationDragDropContainer::dropOccuredOnWidget, this,
131 131 &VisualizationZoneWidget::dropMimeDataOnGraph);
132 132
133 133 // 'Close' options : widget is deleted when closed
134 134 setAttribute(Qt::WA_DeleteOnClose);
135 135 connect(ui->closeButton, &QToolButton::clicked, this, &VisualizationZoneWidget::close);
136 136 ui->closeButton->setIcon(sqpApp->style()->standardIcon(QStyle::SP_TitleBarCloseButton));
137 137
138 138 // Synchronisation id
139 139 QMetaObject::invokeMethod(&sqpApp->variableController(), "onAddSynchronizationGroupId",
140 140 Qt::QueuedConnection, Q_ARG(QUuid, impl->m_SynchronisationGroupId));
141 141 }
142 142
143 143 VisualizationZoneWidget::~VisualizationZoneWidget()
144 144 {
145 145 delete ui;
146 146 }
147 147
148 148 void VisualizationZoneWidget::addGraph(VisualizationGraphWidget *graphWidget)
149 149 {
150 150 // Synchronize new graph with others in the zone
151 151 impl->m_Synchronizer->addGraph(*graphWidget);
152 152
153 153 ui->dragDropContainer->addDragWidget(graphWidget);
154 154 }
155 155
156 156 void VisualizationZoneWidget::insertGraph(int index, VisualizationGraphWidget *graphWidget)
157 157 {
158 158 // Synchronize new graph with others in the zone
159 159 impl->m_Synchronizer->addGraph(*graphWidget);
160 160
161 161 ui->dragDropContainer->insertDragWidget(index, graphWidget);
162 162 }
163 163
164 164 VisualizationGraphWidget *VisualizationZoneWidget::createGraph(std::shared_ptr<Variable> variable)
165 165 {
166 166 return createGraph(variable, -1);
167 167 }
168 168
169 169 VisualizationGraphWidget *VisualizationZoneWidget::createGraph(std::shared_ptr<Variable> variable,
170 170 int index)
171 171 {
172 172 auto graphWidget
173 173 = new VisualizationGraphWidget{defaultGraphName(*ui->dragDropContainer->layout()), this};
174 174
175 175
176 176 // Set graph properties
177 177 graphWidget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::MinimumExpanding);
178 178 graphWidget->setMinimumHeight(GRAPH_MINIMUM_HEIGHT);
179 179
180 180
181 181 // Lambda to synchronize zone widget
182 182 auto synchronizeZoneWidget = [this, graphWidget](const SqpRange &graphRange,
183 183 const SqpRange &oldGraphRange) {
184 184
185 185 auto zoomType = VariableController::getZoomType(graphRange, oldGraphRange);
186 186 auto frameLayout = ui->dragDropContainer->layout();
187 187 for (auto i = 0; i < frameLayout->count(); ++i) {
188 188 auto graphChild
189 189 = dynamic_cast<VisualizationGraphWidget *>(frameLayout->itemAt(i)->widget());
190 190 if (graphChild && (graphChild != graphWidget)) {
191 191
192 192 auto graphChildRange = graphChild->graphRange();
193 193 switch (zoomType) {
194 194 case AcquisitionZoomType::ZoomIn: {
195 195 auto deltaLeft = graphRange.m_TStart - oldGraphRange.m_TStart;
196 196 auto deltaRight = oldGraphRange.m_TEnd - graphRange.m_TEnd;
197 197 graphChildRange.m_TStart += deltaLeft;
198 198 graphChildRange.m_TEnd -= deltaRight;
199 199 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: ZoomIn");
200 200 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: deltaLeft")
201 201 << deltaLeft;
202 202 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: deltaRight")
203 203 << deltaRight;
204 204 qCDebug(LOG_VisualizationZoneWidget())
205 205 << tr("TORM: dt") << graphRange.m_TEnd - graphRange.m_TStart;
206 206
207 207 break;
208 208 }
209 209
210 210 case AcquisitionZoomType::ZoomOut: {
211 211 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: ZoomOut");
212 212 auto deltaLeft = oldGraphRange.m_TStart - graphRange.m_TStart;
213 213 auto deltaRight = graphRange.m_TEnd - oldGraphRange.m_TEnd;
214 214 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: deltaLeft")
215 215 << deltaLeft;
216 216 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: deltaRight")
217 217 << deltaRight;
218 218 qCDebug(LOG_VisualizationZoneWidget())
219 219 << tr("TORM: dt") << graphRange.m_TEnd - graphRange.m_TStart;
220 220 graphChildRange.m_TStart -= deltaLeft;
221 221 graphChildRange.m_TEnd += deltaRight;
222 222 break;
223 223 }
224 224 case AcquisitionZoomType::PanRight: {
225 225 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: PanRight");
226 226 auto deltaLeft = graphRange.m_TStart - oldGraphRange.m_TStart;
227 227 auto deltaRight = graphRange.m_TEnd - oldGraphRange.m_TEnd;
228 228 graphChildRange.m_TStart += deltaLeft;
229 229 graphChildRange.m_TEnd += deltaRight;
230 230 qCDebug(LOG_VisualizationZoneWidget())
231 231 << tr("TORM: dt") << graphRange.m_TEnd - graphRange.m_TStart;
232 232 break;
233 233 }
234 234 case AcquisitionZoomType::PanLeft: {
235 235 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: PanLeft");
236 236 auto deltaLeft = oldGraphRange.m_TStart - graphRange.m_TStart;
237 237 auto deltaRight = oldGraphRange.m_TEnd - graphRange.m_TEnd;
238 238 graphChildRange.m_TStart -= deltaLeft;
239 239 graphChildRange.m_TEnd -= deltaRight;
240 240 break;
241 241 }
242 242 case AcquisitionZoomType::Unknown: {
243 243 qCDebug(LOG_VisualizationZoneWidget())
244 244 << tr("Impossible to synchronize: zoom type unknown");
245 245 break;
246 246 }
247 247 default:
248 248 qCCritical(LOG_VisualizationZoneWidget())
249 249 << tr("Impossible to synchronize: zoom type not take into account");
250 250 // No action
251 251 break;
252 252 }
253 253 graphChild->enableAcquisition(false);
254 254 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: Range before: ")
255 255 << graphChild->graphRange();
256 256 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: Range after : ")
257 257 << graphChildRange;
258 258 qCDebug(LOG_VisualizationZoneWidget())
259 259 << tr("TORM: child dt") << graphChildRange.m_TEnd - graphChildRange.m_TStart;
260 260 graphChild->setGraphRange(graphChildRange);
261 261 graphChild->enableAcquisition(true);
262 262 }
263 263 }
264 264 };
265 265
266 266 // connection for synchronization
267 267 connect(graphWidget, &VisualizationGraphWidget::synchronize, synchronizeZoneWidget);
268 268 connect(graphWidget, &VisualizationGraphWidget::variableAdded, this,
269 269 &VisualizationZoneWidget::onVariableAdded);
270 270 connect(graphWidget, &VisualizationGraphWidget::variableAboutToBeRemoved, this,
271 271 &VisualizationZoneWidget::onVariableAboutToBeRemoved);
272 272
273 273 auto range = SqpRange{};
274 274 if (auto firstGraph = this->firstGraph()) {
275 275 // Case of a new graph in a existant zone
276 276 range = firstGraph->graphRange();
277 277 }
278 278 else {
279 279 // Case of a new graph as the first of the zone
280 280 range = variable->range();
281 281 }
282 282
283 283 this->insertGraph(index, graphWidget);
284 284
285 285 graphWidget->addVariable(variable, range);
286 286 graphWidget->setYRange(variable);
287 287
288 288 return graphWidget;
289 289 }
290 290
291 291 VisualizationGraphWidget *
292 292 VisualizationZoneWidget::createGraph(const QList<std::shared_ptr<Variable> > variables, int index)
293 293 {
294 294 if (variables.isEmpty()) {
295 295 return nullptr;
296 296 }
297 297
298 298 auto graphWidget = createGraph(variables.first(), index);
299 299 for (auto variableIt = variables.cbegin() + 1; variableIt != variables.cend(); ++variableIt) {
300 300 graphWidget->addVariable(*variableIt, graphWidget->graphRange());
301 301 }
302 302
303 303 return graphWidget;
304 304 }
305 305
306 306 VisualizationGraphWidget *VisualizationZoneWidget::firstGraph() const
307 307 {
308 308 VisualizationGraphWidget *firstGraph = nullptr;
309 309 auto layout = ui->dragDropContainer->layout();
310 310 if (layout->count() > 0) {
311 311 if (auto visualizationGraphWidget
312 312 = qobject_cast<VisualizationGraphWidget *>(layout->itemAt(0)->widget())) {
313 313 firstGraph = visualizationGraphWidget;
314 314 }
315 315 }
316 316
317 317 return firstGraph;
318 318 }
319 319
320 320 void VisualizationZoneWidget::accept(IVisualizationWidgetVisitor *visitor)
321 321 {
322 322 if (visitor) {
323 323 visitor->visitEnter(this);
324 324
325 325 // Apply visitor to graph children: widgets different from graphs are not visited (no
326 326 // action)
327 327 processGraphs(
328 328 *ui->dragDropContainer->layout(),
329 329 [visitor](VisualizationGraphWidget &graphWidget) { graphWidget.accept(visitor); });
330 330
331 331 visitor->visitLeave(this);
332 332 }
333 333 else {
334 334 qCCritical(LOG_VisualizationZoneWidget()) << tr("Can't visit widget : the visitor is null");
335 335 }
336 336 }
337 337
338 338 bool VisualizationZoneWidget::canDrop(const Variable &variable) const
339 339 {
340 340 // A tab can always accomodate a variable
341 341 Q_UNUSED(variable);
342 342 return true;
343 343 }
344 344
345 345 bool VisualizationZoneWidget::contains(const Variable &variable) const
346 346 {
347 347 Q_UNUSED(variable);
348 348 return false;
349 349 }
350 350
351 351 QString VisualizationZoneWidget::name() const
352 352 {
353 353 return ui->zoneNameLabel->text();
354 354 }
355 355
356 356 QMimeData *VisualizationZoneWidget::mimeData(const QPoint &position) const
357 357 {
358 358 Q_UNUSED(position);
359 359
360 360 auto mimeData = new QMimeData;
361 361 mimeData->setData(MIME_TYPE_ZONE, QByteArray{});
362 362
363 363 if (auto firstGraph = this->firstGraph()) {
364 364 auto timeRangeData = TimeController::mimeDataForTimeRange(firstGraph->graphRange());
365 365 mimeData->setData(MIME_TYPE_TIME_RANGE, timeRangeData);
366 366 }
367 367
368 368 return mimeData;
369 369 }
370 370
371 371 bool VisualizationZoneWidget::isDragAllowed() const
372 372 {
373 373 return true;
374 374 }
375 375
376 376 void VisualizationZoneWidget::notifyMouseMoveInGraph(const QPointF &graphPosition,
377 377 const QPointF &plotPosition,
378 378 VisualizationGraphWidget *graphWidget)
379 379 {
380 380 processGraphs(*ui->dragDropContainer->layout(), [&graphPosition, &plotPosition, &graphWidget](
381 381 VisualizationGraphWidget &processedGraph) {
382 382
383 383 switch (sqpApp->plotsCursorMode()) {
384 384 case SqpApplication::PlotsCursorMode::Vertical:
385 385 processedGraph.removeHorizontalCursor();
386 386 processedGraph.addVerticalCursorAtViewportPosition(graphPosition.x());
387 387 break;
388 388 case SqpApplication::PlotsCursorMode::Temporal:
389 389 processedGraph.addVerticalCursor(plotPosition.x());
390 390 processedGraph.removeHorizontalCursor();
391 391 break;
392 392 case SqpApplication::PlotsCursorMode::Horizontal:
393 393 processedGraph.removeVerticalCursor();
394 394 if (&processedGraph == graphWidget) {
395 395 processedGraph.addHorizontalCursorAtViewportPosition(graphPosition.y());
396 396 }
397 397 else {
398 398 processedGraph.removeHorizontalCursor();
399 399 }
400 400 break;
401 401 case SqpApplication::PlotsCursorMode::Cross:
402 402 if (&processedGraph == graphWidget) {
403 403 processedGraph.addVerticalCursorAtViewportPosition(graphPosition.x());
404 404 processedGraph.addHorizontalCursorAtViewportPosition(graphPosition.y());
405 405 }
406 406 else {
407 407 processedGraph.removeHorizontalCursor();
408 408 processedGraph.removeVerticalCursor();
409 409 }
410 410 break;
411 411 case SqpApplication::PlotsCursorMode::NoCursor:
412 412 processedGraph.removeHorizontalCursor();
413 413 processedGraph.removeVerticalCursor();
414 414 break;
415 415 }
416 416
417 417
418 418 });
419 419 }
420 420
421 421 void VisualizationZoneWidget::notifyMouseLeaveGraph(VisualizationGraphWidget *graphWidget)
422 422 {
423 423 processGraphs(*ui->dragDropContainer->layout(), [](VisualizationGraphWidget &processedGraph) {
424 424 processedGraph.removeHorizontalCursor();
425 425 processedGraph.removeVerticalCursor();
426 426 });
427 427 }
428 428
429 429 void VisualizationZoneWidget::closeEvent(QCloseEvent *event)
430 430 {
431 431 // Closes graphs in the zone
432 432 processGraphs(*ui->dragDropContainer->layout(),
433 433 [](VisualizationGraphWidget &graphWidget) { graphWidget.close(); });
434 434
435 435 // Delete synchronization group from variable controller
436 436 QMetaObject::invokeMethod(&sqpApp->variableController(), "onRemoveSynchronizationGroupId",
437 437 Qt::QueuedConnection, Q_ARG(QUuid, impl->m_SynchronisationGroupId));
438 438
439 439 QWidget::closeEvent(event);
440 440 }
441 441
442 442 void VisualizationZoneWidget::onVariableAdded(std::shared_ptr<Variable> variable)
443 443 {
444 444 QMetaObject::invokeMethod(&sqpApp->variableController(), "onAddSynchronized",
445 445 Qt::QueuedConnection, Q_ARG(std::shared_ptr<Variable>, variable),
446 446 Q_ARG(QUuid, impl->m_SynchronisationGroupId));
447 447 }
448 448
449 449 void VisualizationZoneWidget::onVariableAboutToBeRemoved(std::shared_ptr<Variable> variable)
450 450 {
451 451 QMetaObject::invokeMethod(&sqpApp->variableController(), "desynchronize", Qt::QueuedConnection,
452 452 Q_ARG(std::shared_ptr<Variable>, variable),
453 453 Q_ARG(QUuid, impl->m_SynchronisationGroupId));
454 454 }
455 455
456 456 void VisualizationZoneWidget::dropMimeData(int index, const QMimeData *mimeData)
457 457 {
458 458 if (mimeData->hasFormat(MIME_TYPE_GRAPH)) {
459 459 impl->dropGraph(index, this);
460 460 }
461 461 else if (mimeData->hasFormat(MIME_TYPE_VARIABLE_LIST)) {
462 462 auto variables = sqpApp->variableController().variablesForMimeData(
463 463 mimeData->data(MIME_TYPE_VARIABLE_LIST));
464 464 impl->dropVariables(variables, index, this);
465 465 }
466 466 else {
467 467 qCWarning(LOG_VisualizationZoneWidget())
468 468 << tr("VisualizationZoneWidget::dropMimeData, unknown MIME data received.");
469 469 }
470 470 }
471 471
472 472 void VisualizationZoneWidget::dropMimeDataOnGraph(VisualizationDragWidget *dragWidget,
473 473 const QMimeData *mimeData)
474 474 {
475 475 auto graphWidget = qobject_cast<VisualizationGraphWidget *>(dragWidget);
476 476 if (!graphWidget) {
477 477 qCWarning(LOG_VisualizationZoneWidget())
478 478 << tr("VisualizationZoneWidget::dropMimeDataOnGraph, dropping in an unknown widget, "
479 479 "drop aborted");
480 480 Q_ASSERT(false);
481 481 return;
482 482 }
483 483
484 484 if (mimeData->hasFormat(MIME_TYPE_VARIABLE_LIST)) {
485 485 auto variables = sqpApp->variableController().variablesForMimeData(
486 486 mimeData->data(MIME_TYPE_VARIABLE_LIST));
487 487 for (const auto &var : variables) {
488 488 graphWidget->addVariable(var, graphWidget->graphRange());
489 489 }
490 490 }
491 491 else if (mimeData->hasFormat(MIME_TYPE_TIME_RANGE)) {
492 492 auto range = TimeController::timeRangeForMimeData(mimeData->data(MIME_TYPE_TIME_RANGE));
493 493 graphWidget->setGraphRange(range);
494 494 }
495 495 else {
496 496 qCWarning(LOG_VisualizationZoneWidget())
497 497 << tr("VisualizationZoneWidget::dropMimeDataOnGraph, unknown MIME data received.");
498 498 }
499 499 }
500 500
501 501 void VisualizationZoneWidget::VisualizationZoneWidgetPrivate::dropGraph(
502 502 int index, VisualizationZoneWidget *zoneWidget)
503 503 {
504 auto &helper = sqpApp->dragDropHelper();
504 auto &helper = sqpApp->dragDropGuiController();
505 505
506 506 auto graphWidget = qobject_cast<VisualizationGraphWidget *>(helper.getCurrentDragWidget());
507 507 if (!graphWidget) {
508 508 qCWarning(LOG_VisualizationZoneWidget())
509 509 << tr("VisualizationZoneWidget::dropGraph, drop aborted, the dropped graph is not "
510 510 "found or invalid.");
511 511 Q_ASSERT(false);
512 512 return;
513 513 }
514 514
515 515 auto parentDragDropContainer
516 516 = qobject_cast<VisualizationDragDropContainer *>(graphWidget->parentWidget());
517 517 if (!parentDragDropContainer) {
518 518 qCWarning(LOG_VisualizationZoneWidget())
519 519 << tr("VisualizationZoneWidget::dropGraph, drop aborted, the parent container of "
520 520 "the dropped graph is not found.");
521 521 Q_ASSERT(false);
522 522 return;
523 523 }
524 524
525 525 const auto &variables = graphWidget->variables();
526 526
527 527 if (parentDragDropContainer != zoneWidget->ui->dragDropContainer && !variables.isEmpty()) {
528 528 // The drop didn't occur in the same zone
529 529
530 530 // Abort the requests for the variables (if any)
531 531 // Commented, because it's not sure if it's needed or not
532 532 // for (const auto& var : variables)
533 533 //{
534 534 // sqpApp->variableController().onAbortProgressRequested(var);
535 535 //}
536 536
537 537 auto previousParentZoneWidget = graphWidget->parentZoneWidget();
538 538 auto nbGraph = parentDragDropContainer->countDragWidget();
539 539 if (nbGraph == 1) {
540 540 // This is the only graph in the previous zone, close the zone
541 541 helper.delayedCloseWidget(previousParentZoneWidget);
542 542 }
543 543 else {
544 544 // Close the graph
545 545 helper.delayedCloseWidget(graphWidget);
546 546 }
547 547
548 548 // Creates the new graph in the zone
549 549 auto newGraphWidget = zoneWidget->createGraph(variables, index);
550 550 newGraphWidget->addSelectionZones(graphWidget->selectionZoneRanges());
551 551 }
552 552 else {
553 553 // The drop occurred in the same zone or the graph is empty
554 554 // Simple move of the graph, no variable operation associated
555 555 parentDragDropContainer->layout()->removeWidget(graphWidget);
556 556
557 557 if (variables.isEmpty() && parentDragDropContainer != zoneWidget->ui->dragDropContainer) {
558 558 // The graph is empty and dropped in a different zone.
559 559 // Take the range of the first graph in the zone (if existing).
560 560 auto layout = zoneWidget->ui->dragDropContainer->layout();
561 561 if (layout->count() > 0) {
562 562 if (auto visualizationGraphWidget
563 563 = qobject_cast<VisualizationGraphWidget *>(layout->itemAt(0)->widget())) {
564 564 graphWidget->setGraphRange(visualizationGraphWidget->graphRange());
565 565 }
566 566 }
567 567 }
568 568
569 569 zoneWidget->ui->dragDropContainer->insertDragWidget(index, graphWidget);
570 570 }
571 571 }
572 572
573 573 void VisualizationZoneWidget::VisualizationZoneWidgetPrivate::dropVariables(
574 574 const QList<std::shared_ptr<Variable> > &variables, int index,
575 575 VisualizationZoneWidget *zoneWidget)
576 576 {
577 577 // Note: the AcceptMimeDataFunction (set on the drop container) ensure there is a single and
578 578 // compatible variable here
579 579 if (variables.count() > 1) {
580 580 qCWarning(LOG_VisualizationZoneWidget())
581 581 << tr("VisualizationZoneWidget::dropVariables, dropping multiple variables, operation "
582 582 "aborted.");
583 583 return;
584 584 }
585 585
586 586 zoneWidget->createGraph(variables, index);
587 587 }
General Comments 0
You need to be logged in to leave comments. Login now