##// END OF EJS Templates
Rename "DragDropHelper" in "DragDropGuiController"
trabillard -
r1110:029fbf916457
parent child
Show More
@@ -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,67
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 24
25 25 /**
26 26 * @brief The SqpApplication class aims to make the link between SciQlop
27 27 * and its plugins. This is the intermediate class that SciQlop has to use
28 28 * in the way to connect a data source. Please first use load method to initialize
29 29 * a plugin specified by its metadata name (JSON plugin source) then others specifics
30 30 * method will be able to access it.
31 31 * You can load a data source driver plugin then create a data source.
32 32 */
33 33
34 34 class SqpApplication : public QApplication {
35 35 Q_OBJECT
36 36 public:
37 37 explicit SqpApplication(int &argc, char **argv);
38 38 virtual ~SqpApplication();
39 39 void initialize();
40 40
41 41 /// Accessors for the differents sciqlop controllers
42 42 DataSourceController &dataSourceController() noexcept;
43 43 NetworkController &networkController() noexcept;
44 44 TimeController &timeController() noexcept;
45 45 VariableController &variableController() noexcept;
46 46 VisualizationController &visualizationController() noexcept;
47 47
48 48 /// Accessors for the differents sciqlop helpers, these helpers classes are like controllers but
49 49 /// doesn't live in a thread and access gui
50 DragDropHelper &dragDropHelper() noexcept;
50 DragDropGuiController &dragDropGuiController() noexcept;
51 51
52 52 enum class PlotsInteractionMode { None, ZoomBox, DragAndDrop, SelectionZones };
53 53
54 54 enum class PlotsCursorMode { NoCursor, Vertical, Temporal, Horizontal, Cross };
55 55
56 56 PlotsInteractionMode plotsInteractionMode() const;
57 57 void setPlotsInteractionMode(PlotsInteractionMode mode);
58 58
59 59 PlotsCursorMode plotsCursorMode() const;
60 60 void setPlotsCursorMode(PlotsCursorMode mode);
61 61
62 62 private:
63 63 class SqpApplicationPrivate;
64 64 spimpl::unique_impl_ptr<SqpApplicationPrivate> impl;
65 65 };
66 66
67 67 #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,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,203
1 1 #include "SqpApplication.h"
2 2
3 3 #include <Catalogue/CatalogueController.h>
4 4 #include <Data/IDataProvider.h>
5 5 #include <DataSource/DataSourceController.h>
6 #include <DragAndDrop/DragDropHelper.h>
6 #include <DragAndDrop/DragDropGuiController.h>
7 7 #include <Network/NetworkController.h>
8 8 #include <QThread>
9 9 #include <Time/TimeController.h>
10 10 #include <Variable/Variable.h>
11 11 #include <Variable/VariableController.h>
12 12 #include <Variable/VariableModel.h>
13 13 #include <Visualization/VisualizationController.h>
14 14
15 15 Q_LOGGING_CATEGORY(LOG_SqpApplication, "SqpApplication")
16 16
17 17 class SqpApplication::SqpApplicationPrivate {
18 18 public:
19 19 SqpApplicationPrivate()
20 20 : m_DataSourceController{std::make_unique<DataSourceController>()},
21 21 m_VariableController{std::make_unique<VariableController>()},
22 22 m_TimeController{std::make_unique<TimeController>()},
23 23 m_NetworkController{std::make_unique<NetworkController>()},
24 24 m_VisualizationController{std::make_unique<VisualizationController>()},
25 m_DragDropHelper{std::make_unique<DragDropHelper>()},
25 m_DragDropGuiController{std::make_unique<DragDropGuiController>()},
26 26 m_CatalogueController{std::make_unique<CatalogueController>()},
27 27 m_PlotInterractionMode(SqpApplication::PlotsInteractionMode::None),
28 28 m_PlotCursorMode(SqpApplication::PlotsCursorMode::NoCursor)
29 29 {
30 30 // /////////////////////////////// //
31 31 // Connections between controllers //
32 32 // /////////////////////////////// //
33 33
34 34 // VariableController <-> DataSourceController
35 35 connect(m_DataSourceController.get(),
36 36 SIGNAL(variableCreationRequested(const QString &, const QVariantHash &,
37 37 std::shared_ptr<IDataProvider>)),
38 38 m_VariableController.get(),
39 39 SLOT(createVariable(const QString &, const QVariantHash &,
40 40 std::shared_ptr<IDataProvider>)));
41 41
42 42 connect(m_VariableController->variableModel(), &VariableModel::requestVariable,
43 43 m_DataSourceController.get(), &DataSourceController::requestVariable);
44 44
45 45 // VariableController <-> VisualizationController
46 46 connect(m_VariableController.get(),
47 47 SIGNAL(variableAboutToBeDeleted(std::shared_ptr<Variable>)),
48 48 m_VisualizationController.get(),
49 49 SIGNAL(variableAboutToBeDeleted(std::shared_ptr<Variable>)), Qt::DirectConnection);
50 50
51 51 connect(m_VariableController.get(),
52 52 SIGNAL(rangeChanged(std::shared_ptr<Variable>, const SqpRange &)),
53 53 m_VisualizationController.get(),
54 54 SIGNAL(rangeChanged(std::shared_ptr<Variable>, const SqpRange &)));
55 55
56 56
57 57 m_DataSourceController->moveToThread(&m_DataSourceControllerThread);
58 58 m_DataSourceControllerThread.setObjectName("DataSourceControllerThread");
59 59 m_NetworkController->moveToThread(&m_NetworkControllerThread);
60 60 m_NetworkControllerThread.setObjectName("NetworkControllerThread");
61 61 m_VariableController->moveToThread(&m_VariableControllerThread);
62 62 m_VariableControllerThread.setObjectName("VariableControllerThread");
63 63 m_VisualizationController->moveToThread(&m_VisualizationControllerThread);
64 64 m_VisualizationControllerThread.setObjectName("VsualizationControllerThread");
65 65 m_CatalogueController->moveToThread(&m_CatalogueControllerThread);
66 66 m_CatalogueControllerThread.setObjectName("CatalogueControllerThread");
67 67
68 68
69 69 // Additionnal init
70 70 m_VariableController->setTimeController(m_TimeController.get());
71 71 }
72 72
73 73 virtual ~SqpApplicationPrivate()
74 74 {
75 75 m_DataSourceControllerThread.quit();
76 76 m_DataSourceControllerThread.wait();
77 77
78 78 m_NetworkControllerThread.quit();
79 79 m_NetworkControllerThread.wait();
80 80
81 81 m_VariableControllerThread.quit();
82 82 m_VariableControllerThread.wait();
83 83
84 84 m_VisualizationControllerThread.quit();
85 85 m_VisualizationControllerThread.wait();
86 86
87 87 m_CatalogueControllerThread.quit();
88 88 m_CatalogueControllerThread.wait();
89 89 }
90 90
91 91 std::unique_ptr<DataSourceController> m_DataSourceController;
92 92 std::unique_ptr<VariableController> m_VariableController;
93 93 std::unique_ptr<TimeController> m_TimeController;
94 94 std::unique_ptr<NetworkController> m_NetworkController;
95 95 std::unique_ptr<VisualizationController> m_VisualizationController;
96 96 std::unique_ptr<CatalogueController> m_CatalogueController;
97 97 QThread m_DataSourceControllerThread;
98 98 QThread m_NetworkControllerThread;
99 99 QThread m_VariableControllerThread;
100 100 QThread m_VisualizationControllerThread;
101 101 QThread m_CatalogueControllerThread;
102 102
103 std::unique_ptr<DragDropHelper> m_DragDropHelper;
103 std::unique_ptr<DragDropGuiController> m_DragDropGuiController;
104 104
105 105 SqpApplication::PlotsInteractionMode m_PlotInterractionMode;
106 106 SqpApplication::PlotsCursorMode m_PlotCursorMode;
107 107 };
108 108
109 109
110 110 SqpApplication::SqpApplication(int &argc, char **argv)
111 111 : QApplication{argc, argv}, impl{spimpl::make_unique_impl<SqpApplicationPrivate>()}
112 112 {
113 113 qCDebug(LOG_SqpApplication()) << tr("SqpApplication construction") << QThread::currentThread();
114 114
115 115 connect(&impl->m_DataSourceControllerThread, &QThread::started,
116 116 impl->m_DataSourceController.get(), &DataSourceController::initialize);
117 117 connect(&impl->m_DataSourceControllerThread, &QThread::finished,
118 118 impl->m_DataSourceController.get(), &DataSourceController::finalize);
119 119
120 120 connect(&impl->m_NetworkControllerThread, &QThread::started, impl->m_NetworkController.get(),
121 121 &NetworkController::initialize);
122 122 connect(&impl->m_NetworkControllerThread, &QThread::finished, impl->m_NetworkController.get(),
123 123 &NetworkController::finalize);
124 124
125 125 connect(&impl->m_VariableControllerThread, &QThread::started, impl->m_VariableController.get(),
126 126 &VariableController::initialize);
127 127 connect(&impl->m_VariableControllerThread, &QThread::finished, impl->m_VariableController.get(),
128 128 &VariableController::finalize);
129 129
130 130 connect(&impl->m_VisualizationControllerThread, &QThread::started,
131 131 impl->m_VisualizationController.get(), &VisualizationController::initialize);
132 132 connect(&impl->m_VisualizationControllerThread, &QThread::finished,
133 133 impl->m_VisualizationController.get(), &VisualizationController::finalize);
134 134
135 135 connect(&impl->m_CatalogueControllerThread, &QThread::started,
136 136 impl->m_CatalogueController.get(), &CatalogueController::initialize);
137 137 connect(&impl->m_CatalogueControllerThread, &QThread::finished,
138 138 impl->m_CatalogueController.get(), &CatalogueController::finalize);
139 139
140 140 impl->m_DataSourceControllerThread.start();
141 141 impl->m_NetworkControllerThread.start();
142 142 impl->m_VariableControllerThread.start();
143 143 impl->m_VisualizationControllerThread.start();
144 144 impl->m_CatalogueControllerThread.start();
145 145 }
146 146
147 147 SqpApplication::~SqpApplication()
148 148 {
149 149 }
150 150
151 151 void SqpApplication::initialize()
152 152 {
153 153 }
154 154
155 155 DataSourceController &SqpApplication::dataSourceController() noexcept
156 156 {
157 157 return *impl->m_DataSourceController;
158 158 }
159 159
160 160 NetworkController &SqpApplication::networkController() noexcept
161 161 {
162 162 return *impl->m_NetworkController;
163 163 }
164 164
165 165 TimeController &SqpApplication::timeController() noexcept
166 166 {
167 167 return *impl->m_TimeController;
168 168 }
169 169
170 170 VariableController &SqpApplication::variableController() noexcept
171 171 {
172 172 return *impl->m_VariableController;
173 173 }
174 174
175 175 VisualizationController &SqpApplication::visualizationController() noexcept
176 176 {
177 177 return *impl->m_VisualizationController;
178 178 }
179 179
180 DragDropHelper &SqpApplication::dragDropHelper() noexcept
180 DragDropGuiController &SqpApplication::dragDropGuiController() noexcept
181 181 {
182 return *impl->m_DragDropHelper;
182 return *impl->m_DragDropGuiController;
183 183 }
184 184
185 185 SqpApplication::PlotsInteractionMode SqpApplication::plotsInteractionMode() const
186 186 {
187 187 return impl->m_PlotInterractionMode;
188 188 }
189 189
190 190 void SqpApplication::setPlotsInteractionMode(SqpApplication::PlotsInteractionMode mode)
191 191 {
192 192 impl->m_PlotInterractionMode = mode;
193 193 }
194 194
195 195 SqpApplication::PlotsCursorMode SqpApplication::plotsCursorMode() const
196 196 {
197 197 return impl->m_PlotCursorMode;
198 198 }
199 199
200 200 void SqpApplication::setPlotsCursorMode(SqpApplication::PlotsCursorMode mode)
201 201 {
202 202 impl->m_PlotCursorMode = mode;
203 203 }
@@ -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,500
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 435 auto zoneSize = qMin(graphHeight / 4.0, 75.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 445 if (canInsert && (isOnTop || isOnBottom || !canMerge)) {
445 446 if (isOnBottom) {
446 447 dropIndex += 1;
447 448 }
448 449
449 450 if (helper.getCurrentDragWidget()) {
450 451 auto dragWidgetIndex = m_Layout->indexOf(helper.getCurrentDragWidget());
451 452 if (dragWidgetIndex >= 0 && dragWidgetIndex <= dropIndex) {
452 453 // Correction of the index if the drop occurs in the same container
453 454 // and if the drag is started from the visualization (in that case, the
454 455 // dragWidget is hidden)
455 456 dropIndex += 1;
456 457 }
457 458 }
458 459
459 460 if (dropIndex != placeHolderIndex) {
460 461 helper.insertPlaceHolder(m_Layout, dropIndex, m_PlaceHolderType,
461 462 m_PlaceHolderText);
462 463 }
463 464
464 465 helper.setHightlightedDragWidget(nullptr);
465 466 }
466 467 else if (canMerge && dragWidgetHovered) {
467 468 // drop on the middle -> merge
468 469 if (hasPlaceHolder()) {
469 470 helper.removePlaceHolder();
470 471 }
471 472
472 473 if (m_AcceptDragWidgetFun(dragWidgetHovered, mimeData)) {
473 474 helper.setHightlightedDragWidget(dragWidgetHovered);
474 475 return true;
475 476 }
476 477 else {
477 478 return false;
478 479 }
479 480 }
480 481 else {
481 482 qCWarning(LOG_VisualizationDragDropContainer())
482 483 << tr("VisualizationDragDropContainer::findPlaceHolderPosition, no valid drop "
483 484 "action.");
484 485 }
485 486 }
486 487 else {
487 488 qCWarning(LOG_VisualizationDragDropContainer())
488 489 << tr("VisualizationDragDropContainer::findPlaceHolderPosition, no widget "
489 490 "found in the "
490 491 "container");
491 492 }
492 493 }
493 494 else {
494 495 // the mouse is hover the placeHolder
495 496 // Do nothing
496 497 }
497 498
498 499 return true;
499 500 }
@@ -1,876 +1,876
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 7 #include "Visualization/VisualizationSelectionZoneItem.h"
8 8 #include "Visualization/VisualizationSelectionZoneManager.h"
9 9 #include "Visualization/VisualizationWidget.h"
10 10 #include "Visualization/VisualizationZoneWidget.h"
11 11 #include "ui_VisualizationGraphWidget.h"
12 12
13 13 #include <Common/MimeTypesDef.h>
14 14 #include <Data/ArrayData.h>
15 15 #include <Data/IDataSeries.h>
16 16 #include <Data/SpectrogramSeries.h>
17 #include <DragAndDrop/DragDropHelper.h>
17 #include <DragAndDrop/DragDropGuiController.h>
18 18 #include <Settings/SqpSettingsDefs.h>
19 19 #include <SqpApplication.h>
20 20 #include <Time/TimeController.h>
21 21 #include <Variable/Variable.h>
22 22 #include <Variable/VariableController.h>
23 23
24 24 #include <unordered_map>
25 25
26 26 Q_LOGGING_CATEGORY(LOG_VisualizationGraphWidget, "VisualizationGraphWidget")
27 27
28 28 namespace {
29 29
30 30 /// Key pressed to enable drag&drop in all modes
31 31 const auto DRAG_DROP_MODIFIER = Qt::AltModifier;
32 32
33 33 /// Key pressed to enable zoom on horizontal axis
34 34 const auto HORIZONTAL_ZOOM_MODIFIER = Qt::ControlModifier;
35 35
36 36 /// Key pressed to enable zoom on vertical axis
37 37 const auto VERTICAL_ZOOM_MODIFIER = Qt::ShiftModifier;
38 38
39 39 /// Speed of a step of a wheel event for a pan, in percentage of the axis range
40 40 const auto PAN_SPEED = 5;
41 41
42 42 /// Key pressed to enable a calibration pan
43 43 const auto VERTICAL_PAN_MODIFIER = Qt::AltModifier;
44 44
45 45 /// Key pressed to enable multi selection of selection zones
46 46 const auto MULTI_ZONE_SELECTION_MODIFIER = Qt::ControlModifier;
47 47
48 48 /// Minimum size for the zoom box, in percentage of the axis range
49 49 const auto ZOOM_BOX_MIN_SIZE = 0.8;
50 50
51 51 /// Format of the dates appearing in the label of a cursor
52 52 const auto CURSOR_LABELS_DATETIME_FORMAT = QStringLiteral("yyyy/MM/dd\nhh:mm:ss:zzz");
53 53
54 54 } // namespace
55 55
56 56 struct VisualizationGraphWidget::VisualizationGraphWidgetPrivate {
57 57
58 58 explicit VisualizationGraphWidgetPrivate(const QString &name)
59 59 : m_Name{name},
60 60 m_DoAcquisition{true},
61 61 m_IsCalibration{false},
62 62 m_RenderingDelegate{nullptr}
63 63 {
64 64 }
65 65
66 66 void updateData(PlottablesMap &plottables, std::shared_ptr<IDataSeries> dataSeries,
67 67 const SqpRange &range)
68 68 {
69 69 VisualizationGraphHelper::updateData(plottables, dataSeries, range);
70 70
71 71 // Prevents that data has changed to update rendering
72 72 m_RenderingDelegate->onPlotUpdated();
73 73 }
74 74
75 75 QString m_Name;
76 76 // 1 variable -> n qcpplot
77 77 std::map<std::shared_ptr<Variable>, PlottablesMap> m_VariableToPlotMultiMap;
78 78 bool m_DoAcquisition;
79 79 bool m_IsCalibration;
80 80 /// Delegate used to attach rendering features to the plot
81 81 std::unique_ptr<VisualizationGraphRenderingDelegate> m_RenderingDelegate;
82 82
83 83 QCPItemRect *m_DrawingZoomRect = nullptr;
84 84 QStack<QPair<QCPRange, QCPRange> > m_ZoomStack;
85 85
86 86 std::unique_ptr<VisualizationCursorItem> m_HorizontalCursor = nullptr;
87 87 std::unique_ptr<VisualizationCursorItem> m_VerticalCursor = nullptr;
88 88
89 89 VisualizationSelectionZoneItem *m_DrawingZone = nullptr;
90 90 VisualizationSelectionZoneItem *m_HoveredZone = nullptr;
91 91 QVector<VisualizationSelectionZoneItem *> m_SelectionZones;
92 92
93 93 bool m_HasMovedMouse = false; // Indicates if the mouse moved in a releaseMouse even
94 94
95 95 void startDrawingRect(const QPoint &pos, QCustomPlot &plot)
96 96 {
97 97 removeDrawingRect(plot);
98 98
99 99 auto axisPos = posToAxisPos(pos, plot);
100 100
101 101 m_DrawingZoomRect = new QCPItemRect{&plot};
102 102 QPen p;
103 103 p.setWidth(2);
104 104 m_DrawingZoomRect->setPen(p);
105 105
106 106 m_DrawingZoomRect->topLeft->setCoords(axisPos);
107 107 m_DrawingZoomRect->bottomRight->setCoords(axisPos);
108 108 }
109 109
110 110 void removeDrawingRect(QCustomPlot &plot)
111 111 {
112 112 if (m_DrawingZoomRect) {
113 113 plot.removeItem(m_DrawingZoomRect); // the item is deleted by QCustomPlot
114 114 m_DrawingZoomRect = nullptr;
115 115 plot.replot(QCustomPlot::rpQueuedReplot);
116 116 }
117 117 }
118 118
119 119 void startDrawingZone(const QPoint &pos, VisualizationGraphWidget *graph)
120 120 {
121 121 endDrawingZone(graph);
122 122
123 123 auto axisPos = posToAxisPos(pos, graph->plot());
124 124
125 125 m_DrawingZone = new VisualizationSelectionZoneItem{&graph->plot()};
126 126 m_DrawingZone->setRange(axisPos.x(), axisPos.x());
127 127 m_DrawingZone->setEditionEnabled(false);
128 128 }
129 129
130 130 void endDrawingZone(VisualizationGraphWidget *graph)
131 131 {
132 132 if (m_DrawingZone) {
133 133 auto drawingZoneRange = m_DrawingZone->range();
134 134 if (qAbs(drawingZoneRange.m_TEnd - drawingZoneRange.m_TStart) > 0) {
135 135 m_DrawingZone->setEditionEnabled(true);
136 136 addSelectionZone(m_DrawingZone);
137 137 }
138 138 else {
139 139 graph->plot().removeItem(m_DrawingZone); // the item is deleted by QCustomPlot
140 140 }
141 141
142 142 graph->plot().replot(QCustomPlot::rpQueuedReplot);
143 143 m_DrawingZone = nullptr;
144 144 }
145 145 }
146 146
147 147 void setSelectionZonesEditionEnabled(bool value)
148 148 {
149 149 for (auto s : m_SelectionZones) {
150 150 s->setEditionEnabled(value);
151 151 }
152 152 }
153 153
154 154 void addSelectionZone(VisualizationSelectionZoneItem *zone) { m_SelectionZones << zone; }
155 155
156 156 VisualizationSelectionZoneItem *selectionZoneAt(const QPoint &pos,
157 157 const QCustomPlot &plot) const
158 158 {
159 159 VisualizationSelectionZoneItem *selectionZoneItemUnderCursor = nullptr;
160 160 auto minDistanceToZone = -1;
161 161 for (auto zone : m_SelectionZones) {
162 162 auto distanceToZone = zone->selectTest(pos, false);
163 163 if ((minDistanceToZone < 0 || distanceToZone <= minDistanceToZone)
164 164 && distanceToZone >= 0 && distanceToZone < plot.selectionTolerance()) {
165 165 selectionZoneItemUnderCursor = zone;
166 166 }
167 167 }
168 168
169 169 return selectionZoneItemUnderCursor;
170 170 }
171 171
172 172 QPointF posToAxisPos(const QPoint &pos, QCustomPlot &plot) const
173 173 {
174 174 auto axisX = plot.axisRect()->axis(QCPAxis::atBottom);
175 175 auto axisY = plot.axisRect()->axis(QCPAxis::atLeft);
176 176 return QPointF{axisX->pixelToCoord(pos.x()), axisY->pixelToCoord(pos.y())};
177 177 }
178 178
179 179 bool pointIsInAxisRect(const QPointF &axisPoint, QCustomPlot &plot) const
180 180 {
181 181 auto axisX = plot.axisRect()->axis(QCPAxis::atBottom);
182 182 auto axisY = plot.axisRect()->axis(QCPAxis::atLeft);
183 183 return axisX->range().contains(axisPoint.x()) && axisY->range().contains(axisPoint.y());
184 184 }
185 185 };
186 186
187 187 VisualizationGraphWidget::VisualizationGraphWidget(const QString &name, QWidget *parent)
188 188 : VisualizationDragWidget{parent},
189 189 ui{new Ui::VisualizationGraphWidget},
190 190 impl{spimpl::make_unique_impl<VisualizationGraphWidgetPrivate>(name)}
191 191 {
192 192 ui->setupUi(this);
193 193
194 194 // 'Close' options : widget is deleted when closed
195 195 setAttribute(Qt::WA_DeleteOnClose);
196 196
197 197 // Set qcpplot properties :
198 198 // - zoom is enabled
199 199 // - Mouse wheel on qcpplot is intercepted to determine the zoom orientation
200 200 ui->widget->setInteractions(QCP::iRangeZoom);
201 201 ui->widget->axisRect()->setRangeDrag(Qt::Horizontal | Qt::Vertical);
202 202
203 203 // The delegate must be initialized after the ui as it uses the plot
204 204 impl->m_RenderingDelegate = std::make_unique<VisualizationGraphRenderingDelegate>(*this);
205 205
206 206 // Init the cursors
207 207 impl->m_HorizontalCursor = std::make_unique<VisualizationCursorItem>(&plot());
208 208 impl->m_HorizontalCursor->setOrientation(Qt::Horizontal);
209 209 impl->m_VerticalCursor = std::make_unique<VisualizationCursorItem>(&plot());
210 210 impl->m_VerticalCursor->setOrientation(Qt::Vertical);
211 211
212 212 connect(ui->widget, &QCustomPlot::mousePress, this, &VisualizationGraphWidget::onMousePress);
213 213 connect(ui->widget, &QCustomPlot::mouseRelease, this,
214 214 &VisualizationGraphWidget::onMouseRelease);
215 215 connect(ui->widget, &QCustomPlot::mouseMove, this, &VisualizationGraphWidget::onMouseMove);
216 216 connect(ui->widget, &QCustomPlot::mouseWheel, this, &VisualizationGraphWidget::onMouseWheel);
217 217 connect(ui->widget, &QCustomPlot::mouseDoubleClick, this,
218 218 &VisualizationGraphWidget::onMouseDoubleClick);
219 219 connect(
220 220 ui->widget->xAxis,
221 221 static_cast<void (QCPAxis::*)(const QCPRange &, const QCPRange &)>(&QCPAxis::rangeChanged),
222 222 this, &VisualizationGraphWidget::onRangeChanged, Qt::DirectConnection);
223 223
224 224 // Activates menu when right clicking on the graph
225 225 ui->widget->setContextMenuPolicy(Qt::CustomContextMenu);
226 226 connect(ui->widget, &QCustomPlot::customContextMenuRequested, this,
227 227 &VisualizationGraphWidget::onGraphMenuRequested);
228 228
229 229 connect(this, &VisualizationGraphWidget::requestDataLoading, &sqpApp->variableController(),
230 230 &VariableController::onRequestDataLoading);
231 231
232 232 connect(&sqpApp->variableController(), &VariableController::updateVarDisplaying, this,
233 233 &VisualizationGraphWidget::onUpdateVarDisplaying);
234 234
235 235 #ifdef Q_OS_MAC
236 236 plot().setPlottingHint(QCP::phFastPolylines, true);
237 237 #endif
238 238 }
239 239
240 240
241 241 VisualizationGraphWidget::~VisualizationGraphWidget()
242 242 {
243 243 delete ui;
244 244 }
245 245
246 246 VisualizationZoneWidget *VisualizationGraphWidget::parentZoneWidget() const noexcept
247 247 {
248 248 auto parent = parentWidget();
249 249 while (parent != nullptr && !qobject_cast<VisualizationZoneWidget *>(parent)) {
250 250 parent = parent->parentWidget();
251 251 }
252 252
253 253 return qobject_cast<VisualizationZoneWidget *>(parent);
254 254 }
255 255
256 256 VisualizationWidget *VisualizationGraphWidget::parentVisualizationWidget() const
257 257 {
258 258 auto parent = parentWidget();
259 259 while (parent != nullptr && !qobject_cast<VisualizationWidget *>(parent)) {
260 260 parent = parent->parentWidget();
261 261 }
262 262
263 263 return qobject_cast<VisualizationWidget *>(parent);
264 264 }
265 265
266 266 void VisualizationGraphWidget::enableAcquisition(bool enable)
267 267 {
268 268 impl->m_DoAcquisition = enable;
269 269 }
270 270
271 271 void VisualizationGraphWidget::addVariable(std::shared_ptr<Variable> variable, SqpRange range)
272 272 {
273 273 // Uses delegate to create the qcpplot components according to the variable
274 274 auto createdPlottables = VisualizationGraphHelper::create(variable, *ui->widget);
275 275
276 276 if (auto dataSeries = variable->dataSeries()) {
277 277 // Set axes properties according to the units of the data series
278 278 impl->m_RenderingDelegate->setAxesProperties(dataSeries);
279 279
280 280 // Sets rendering properties for the new plottables
281 281 // Warning: this method must be called after setAxesProperties(), as it can access to some
282 282 // axes properties that have to be initialized
283 283 impl->m_RenderingDelegate->setPlottablesProperties(dataSeries, createdPlottables);
284 284 }
285 285
286 286 impl->m_VariableToPlotMultiMap.insert({variable, std::move(createdPlottables)});
287 287
288 288 connect(variable.get(), SIGNAL(updated()), this, SLOT(onDataCacheVariableUpdated()));
289 289
290 290 this->enableAcquisition(false);
291 291 this->setGraphRange(range);
292 292 this->enableAcquisition(true);
293 293
294 294 emit requestDataLoading(QVector<std::shared_ptr<Variable> >() << variable, range, false);
295 295
296 296 emit variableAdded(variable);
297 297 }
298 298
299 299 void VisualizationGraphWidget::removeVariable(std::shared_ptr<Variable> variable) noexcept
300 300 {
301 301 // Each component associated to the variable :
302 302 // - is removed from qcpplot (which deletes it)
303 303 // - is no longer referenced in the map
304 304 auto variableIt = impl->m_VariableToPlotMultiMap.find(variable);
305 305 if (variableIt != impl->m_VariableToPlotMultiMap.cend()) {
306 306 emit variableAboutToBeRemoved(variable);
307 307
308 308 auto &plottablesMap = variableIt->second;
309 309
310 310 for (auto plottableIt = plottablesMap.cbegin(), plottableEnd = plottablesMap.cend();
311 311 plottableIt != plottableEnd;) {
312 312 ui->widget->removePlottable(plottableIt->second);
313 313 plottableIt = plottablesMap.erase(plottableIt);
314 314 }
315 315
316 316 impl->m_VariableToPlotMultiMap.erase(variableIt);
317 317 }
318 318
319 319 // Updates graph
320 320 ui->widget->replot();
321 321 }
322 322
323 323 QList<std::shared_ptr<Variable> > VisualizationGraphWidget::variables() const
324 324 {
325 325 auto variables = QList<std::shared_ptr<Variable> >{};
326 326 for (auto it = std::cbegin(impl->m_VariableToPlotMultiMap);
327 327 it != std::cend(impl->m_VariableToPlotMultiMap); ++it) {
328 328 variables << it->first;
329 329 }
330 330
331 331 return variables;
332 332 }
333 333
334 334 void VisualizationGraphWidget::setYRange(std::shared_ptr<Variable> variable)
335 335 {
336 336 if (!variable) {
337 337 qCCritical(LOG_VisualizationGraphWidget()) << "Can't set y-axis range: variable is null";
338 338 return;
339 339 }
340 340
341 341 VisualizationGraphHelper::setYAxisRange(variable, *ui->widget);
342 342 }
343 343
344 344 SqpRange VisualizationGraphWidget::graphRange() const noexcept
345 345 {
346 346 auto graphRange = ui->widget->xAxis->range();
347 347 return SqpRange{graphRange.lower, graphRange.upper};
348 348 }
349 349
350 350 void VisualizationGraphWidget::setGraphRange(const SqpRange &range)
351 351 {
352 352 qCDebug(LOG_VisualizationGraphWidget()) << tr("VisualizationGraphWidget::setGraphRange START");
353 353 ui->widget->xAxis->setRange(range.m_TStart, range.m_TEnd);
354 354 ui->widget->replot();
355 355 qCDebug(LOG_VisualizationGraphWidget()) << tr("VisualizationGraphWidget::setGraphRange END");
356 356 }
357 357
358 358 QVector<SqpRange> VisualizationGraphWidget::selectionZoneRanges() const
359 359 {
360 360 QVector<SqpRange> ranges;
361 361 for (auto zone : impl->m_SelectionZones) {
362 362 ranges << zone->range();
363 363 }
364 364
365 365 return ranges;
366 366 }
367 367
368 368 void VisualizationGraphWidget::addSelectionZones(const QVector<SqpRange> &ranges)
369 369 {
370 370 for (const auto &range : ranges) {
371 371 // note: ownership is transfered to QCustomPlot
372 372 auto zone = new VisualizationSelectionZoneItem(&plot());
373 373 zone->setRange(range.m_TStart, range.m_TEnd);
374 374 impl->addSelectionZone(zone);
375 375 }
376 376
377 377 plot().replot(QCustomPlot::rpQueuedReplot);
378 378 }
379 379
380 380 void VisualizationGraphWidget::undoZoom()
381 381 {
382 382 auto zoom = impl->m_ZoomStack.pop();
383 383 auto axisX = plot().axisRect()->axis(QCPAxis::atBottom);
384 384 auto axisY = plot().axisRect()->axis(QCPAxis::atLeft);
385 385
386 386 axisX->setRange(zoom.first);
387 387 axisY->setRange(zoom.second);
388 388
389 389 plot().replot(QCustomPlot::rpQueuedReplot);
390 390 }
391 391
392 392 void VisualizationGraphWidget::accept(IVisualizationWidgetVisitor *visitor)
393 393 {
394 394 if (visitor) {
395 395 visitor->visit(this);
396 396 }
397 397 else {
398 398 qCCritical(LOG_VisualizationGraphWidget())
399 399 << tr("Can't visit widget : the visitor is null");
400 400 }
401 401 }
402 402
403 403 bool VisualizationGraphWidget::canDrop(const Variable &variable) const
404 404 {
405 405 auto isSpectrogram = [](const auto &variable) {
406 406 return std::dynamic_pointer_cast<SpectrogramSeries>(variable.dataSeries()) != nullptr;
407 407 };
408 408
409 409 // - A spectrogram series can't be dropped on graph with existing plottables
410 410 // - No data series can be dropped on graph with existing spectrogram series
411 411 return isSpectrogram(variable)
412 412 ? impl->m_VariableToPlotMultiMap.empty()
413 413 : std::none_of(
414 414 impl->m_VariableToPlotMultiMap.cbegin(), impl->m_VariableToPlotMultiMap.cend(),
415 415 [isSpectrogram](const auto &entry) { return isSpectrogram(*entry.first); });
416 416 }
417 417
418 418 bool VisualizationGraphWidget::contains(const Variable &variable) const
419 419 {
420 420 // Finds the variable among the keys of the map
421 421 auto variablePtr = &variable;
422 422 auto findVariable
423 423 = [variablePtr](const auto &entry) { return variablePtr == entry.first.get(); };
424 424
425 425 auto end = impl->m_VariableToPlotMultiMap.cend();
426 426 auto it = std::find_if(impl->m_VariableToPlotMultiMap.cbegin(), end, findVariable);
427 427 return it != end;
428 428 }
429 429
430 430 QString VisualizationGraphWidget::name() const
431 431 {
432 432 return impl->m_Name;
433 433 }
434 434
435 435 QMimeData *VisualizationGraphWidget::mimeData(const QPoint &position) const
436 436 {
437 437 auto mimeData = new QMimeData;
438 438
439 439 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(position, plot());
440 440 if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones
441 441 && selectionZoneItemUnderCursor) {
442 442 mimeData->setData(MIME_TYPE_TIME_RANGE, TimeController::mimeDataForTimeRange(
443 443 selectionZoneItemUnderCursor->range()));
444 444 mimeData->setData(MIME_TYPE_SELECTION_ZONE, TimeController::mimeDataForTimeRange(
445 445 selectionZoneItemUnderCursor->range()));
446 446 }
447 447 else {
448 448 mimeData->setData(MIME_TYPE_GRAPH, QByteArray{});
449 449
450 450 auto timeRangeData = TimeController::mimeDataForTimeRange(graphRange());
451 451 mimeData->setData(MIME_TYPE_TIME_RANGE, timeRangeData);
452 452 }
453 453
454 454 return mimeData;
455 455 }
456 456
457 457 QPixmap VisualizationGraphWidget::customDragPixmap(const QPoint &dragPosition)
458 458 {
459 459 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(dragPosition, plot());
460 460 if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones
461 461 && selectionZoneItemUnderCursor) {
462 462
463 463 auto zoneTopLeft = selectionZoneItemUnderCursor->topLeft->pixelPosition();
464 464 auto zoneBottomRight = selectionZoneItemUnderCursor->bottomRight->pixelPosition();
465 465
466 466 auto zoneSize = QSizeF{qAbs(zoneBottomRight.x() - zoneTopLeft.x()),
467 467 qAbs(zoneBottomRight.y() - zoneTopLeft.y())}
468 468 .toSize();
469 469
470 470 auto pixmap = QPixmap(zoneSize);
471 471 render(&pixmap, QPoint(), QRegion{QRect{zoneTopLeft.toPoint(), zoneSize}});
472 472
473 473 return pixmap;
474 474 }
475 475
476 476 return QPixmap();
477 477 }
478 478
479 479 bool VisualizationGraphWidget::isDragAllowed() const
480 480 {
481 481 return true;
482 482 }
483 483
484 484 void VisualizationGraphWidget::highlightForMerge(bool highlighted)
485 485 {
486 486 if (highlighted) {
487 487 plot().setBackground(QBrush(QColor("#BBD5EE")));
488 488 }
489 489 else {
490 490 plot().setBackground(QBrush(Qt::white));
491 491 }
492 492
493 493 plot().update();
494 494 }
495 495
496 496 void VisualizationGraphWidget::addVerticalCursor(double time)
497 497 {
498 498 impl->m_VerticalCursor->setPosition(time);
499 499 impl->m_VerticalCursor->setVisible(true);
500 500
501 501 auto text
502 502 = DateUtils::dateTime(time).toString(CURSOR_LABELS_DATETIME_FORMAT).replace(' ', '\n');
503 503 impl->m_VerticalCursor->setLabelText(text);
504 504 }
505 505
506 506 void VisualizationGraphWidget::addVerticalCursorAtViewportPosition(double position)
507 507 {
508 508 impl->m_VerticalCursor->setAbsolutePosition(position);
509 509 impl->m_VerticalCursor->setVisible(true);
510 510
511 511 auto axis = plot().axisRect()->axis(QCPAxis::atBottom);
512 512 auto text
513 513 = DateUtils::dateTime(axis->pixelToCoord(position)).toString(CURSOR_LABELS_DATETIME_FORMAT);
514 514 impl->m_VerticalCursor->setLabelText(text);
515 515 }
516 516
517 517 void VisualizationGraphWidget::removeVerticalCursor()
518 518 {
519 519 impl->m_VerticalCursor->setVisible(false);
520 520 plot().replot(QCustomPlot::rpQueuedReplot);
521 521 }
522 522
523 523 void VisualizationGraphWidget::addHorizontalCursor(double value)
524 524 {
525 525 impl->m_HorizontalCursor->setPosition(value);
526 526 impl->m_HorizontalCursor->setVisible(true);
527 527 impl->m_HorizontalCursor->setLabelText(QString::number(value));
528 528 }
529 529
530 530 void VisualizationGraphWidget::addHorizontalCursorAtViewportPosition(double position)
531 531 {
532 532 impl->m_HorizontalCursor->setAbsolutePosition(position);
533 533 impl->m_HorizontalCursor->setVisible(true);
534 534
535 535 auto axis = plot().axisRect()->axis(QCPAxis::atLeft);
536 536 impl->m_HorizontalCursor->setLabelText(QString::number(axis->pixelToCoord(position)));
537 537 }
538 538
539 539 void VisualizationGraphWidget::removeHorizontalCursor()
540 540 {
541 541 impl->m_HorizontalCursor->setVisible(false);
542 542 plot().replot(QCustomPlot::rpQueuedReplot);
543 543 }
544 544
545 545 void VisualizationGraphWidget::closeEvent(QCloseEvent *event)
546 546 {
547 547 Q_UNUSED(event);
548 548
549 549 // Prevents that all variables will be removed from graph when it will be closed
550 550 for (auto &variableEntry : impl->m_VariableToPlotMultiMap) {
551 551 emit variableAboutToBeRemoved(variableEntry.first);
552 552 }
553 553 }
554 554
555 555 void VisualizationGraphWidget::enterEvent(QEvent *event)
556 556 {
557 557 Q_UNUSED(event);
558 558 impl->m_RenderingDelegate->showGraphOverlay(true);
559 559 }
560 560
561 561 void VisualizationGraphWidget::leaveEvent(QEvent *event)
562 562 {
563 563 Q_UNUSED(event);
564 564 impl->m_RenderingDelegate->showGraphOverlay(false);
565 565
566 566 if (auto parentZone = parentZoneWidget()) {
567 567 parentZone->notifyMouseLeaveGraph(this);
568 568 }
569 569 else {
570 570 qCWarning(LOG_VisualizationGraphWidget()) << "leaveEvent: No parent zone widget";
571 571 }
572 572
573 573 if (impl->m_HoveredZone) {
574 574 impl->m_HoveredZone->setHovered(false);
575 575 impl->m_HoveredZone = nullptr;
576 576 }
577 577 }
578 578
579 579 QCustomPlot &VisualizationGraphWidget::plot() const noexcept
580 580 {
581 581 return *ui->widget;
582 582 }
583 583
584 584 void VisualizationGraphWidget::onGraphMenuRequested(const QPoint &pos) noexcept
585 585 {
586 586 QMenu graphMenu{};
587 587
588 588 // Iterates on variables (unique keys)
589 589 for (auto it = impl->m_VariableToPlotMultiMap.cbegin(),
590 590 end = impl->m_VariableToPlotMultiMap.cend();
591 591 it != end; it = impl->m_VariableToPlotMultiMap.upper_bound(it->first)) {
592 592 // 'Remove variable' action
593 593 graphMenu.addAction(tr("Remove variable %1").arg(it->first->name()),
594 594 [ this, var = it->first ]() { removeVariable(var); });
595 595 }
596 596
597 597 if (!impl->m_ZoomStack.isEmpty()) {
598 598 if (!graphMenu.isEmpty()) {
599 599 graphMenu.addSeparator();
600 600 }
601 601
602 602 graphMenu.addAction(tr("Undo Zoom"), [this]() { undoZoom(); });
603 603 }
604 604
605 605 if (!graphMenu.isEmpty()) {
606 606 graphMenu.exec(QCursor::pos());
607 607 }
608 608 }
609 609
610 610 void VisualizationGraphWidget::onRangeChanged(const QCPRange &t1, const QCPRange &t2)
611 611 {
612 612 qCDebug(LOG_VisualizationGraphWidget())
613 613 << tr("TORM: VisualizationGraphWidget::onRangeChanged")
614 614 << QThread::currentThread()->objectName() << "DoAcqui" << impl->m_DoAcquisition;
615 615
616 616 auto graphRange = SqpRange{t1.lower, t1.upper};
617 617 auto oldGraphRange = SqpRange{t2.lower, t2.upper};
618 618
619 619 if (impl->m_DoAcquisition) {
620 620 QVector<std::shared_ptr<Variable> > variableUnderGraphVector;
621 621
622 622 for (auto it = impl->m_VariableToPlotMultiMap.begin(),
623 623 end = impl->m_VariableToPlotMultiMap.end();
624 624 it != end; it = impl->m_VariableToPlotMultiMap.upper_bound(it->first)) {
625 625 variableUnderGraphVector.push_back(it->first);
626 626 }
627 627 emit requestDataLoading(std::move(variableUnderGraphVector), graphRange,
628 628 !impl->m_IsCalibration);
629 629
630 630 if (!impl->m_IsCalibration) {
631 631 qCDebug(LOG_VisualizationGraphWidget())
632 632 << tr("TORM: VisualizationGraphWidget::Synchronize notify !!")
633 633 << QThread::currentThread()->objectName() << graphRange << oldGraphRange;
634 634 emit synchronize(graphRange, oldGraphRange);
635 635 }
636 636 }
637 637
638 638 auto pos = mapFromGlobal(QCursor::pos());
639 639 auto axisPos = impl->posToAxisPos(pos, plot());
640 640 if (auto parentZone = parentZoneWidget()) {
641 641 if (impl->pointIsInAxisRect(axisPos, plot())) {
642 642 parentZone->notifyMouseMoveInGraph(pos, axisPos, this);
643 643 }
644 644 else {
645 645 parentZone->notifyMouseLeaveGraph(this);
646 646 }
647 647 }
648 648 else {
649 649 qCWarning(LOG_VisualizationGraphWidget()) << "onMouseMove: No parent zone widget";
650 650 }
651 651 }
652 652
653 653 void VisualizationGraphWidget::onMouseDoubleClick(QMouseEvent *event) noexcept
654 654 {
655 655 impl->m_RenderingDelegate->onMouseDoubleClick(event);
656 656 }
657 657
658 658 void VisualizationGraphWidget::onMouseMove(QMouseEvent *event) noexcept
659 659 {
660 660 // Handles plot rendering when mouse is moving
661 661 impl->m_RenderingDelegate->onMouseMove(event);
662 662
663 663 auto axisPos = impl->posToAxisPos(event->pos(), plot());
664 664
665 665 // Zoom box and zone drawing
666 666 if (impl->m_DrawingZoomRect) {
667 667 impl->m_DrawingZoomRect->bottomRight->setCoords(axisPos);
668 668 }
669 669 else if (impl->m_DrawingZone) {
670 670 impl->m_DrawingZone->setEnd(axisPos.x());
671 671 }
672 672
673 673 // Cursor
674 674 if (auto parentZone = parentZoneWidget()) {
675 675 if (impl->pointIsInAxisRect(axisPos, plot())) {
676 676 parentZone->notifyMouseMoveInGraph(event->pos(), axisPos, this);
677 677 }
678 678 else {
679 679 parentZone->notifyMouseLeaveGraph(this);
680 680 }
681 681 }
682 682 else {
683 683 qCWarning(LOG_VisualizationGraphWidget()) << "onMouseMove: No parent zone widget";
684 684 }
685 685
686 686 // Search for the selection zone under the mouse
687 687 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(event->pos(), plot());
688 688 if (selectionZoneItemUnderCursor && !impl->m_DrawingZone
689 689 && sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones) {
690 690
691 691 // Sets the appropriate cursor shape
692 692 auto cursorShape = selectionZoneItemUnderCursor->curshorShapeForPosition(event->pos());
693 693 setCursor(cursorShape);
694 694
695 695 // Manages the hovered zone
696 696 if (selectionZoneItemUnderCursor != impl->m_HoveredZone) {
697 697 if (impl->m_HoveredZone) {
698 698 impl->m_HoveredZone->setHovered(false);
699 699 }
700 700 selectionZoneItemUnderCursor->setHovered(true);
701 701 impl->m_HoveredZone = selectionZoneItemUnderCursor;
702 702 plot().replot(QCustomPlot::rpQueuedReplot);
703 703 }
704 704 }
705 705 else {
706 706 // There is no zone under the mouse or the interaction mode is not "selection zones"
707 707 if (impl->m_HoveredZone) {
708 708 impl->m_HoveredZone->setHovered(false);
709 709 impl->m_HoveredZone = nullptr;
710 710 }
711 711
712 712 setCursor(Qt::ArrowCursor);
713 713 }
714 714
715 715 impl->m_HasMovedMouse = true;
716 716 VisualizationDragWidget::mouseMoveEvent(event);
717 717 }
718 718
719 719 void VisualizationGraphWidget::onMouseWheel(QWheelEvent *event) noexcept
720 720 {
721 721 auto value = event->angleDelta().x() + event->angleDelta().y();
722 722 if (value != 0) {
723 723
724 724 auto direction = value > 0 ? 1.0 : -1.0;
725 725 auto isZoomX = event->modifiers().testFlag(HORIZONTAL_ZOOM_MODIFIER);
726 726 auto isZoomY = event->modifiers().testFlag(VERTICAL_ZOOM_MODIFIER);
727 727 impl->m_IsCalibration = event->modifiers().testFlag(VERTICAL_PAN_MODIFIER);
728 728
729 729 auto zoomOrientations = QFlags<Qt::Orientation>{};
730 730 zoomOrientations.setFlag(Qt::Horizontal, isZoomX);
731 731 zoomOrientations.setFlag(Qt::Vertical, isZoomY);
732 732
733 733 ui->widget->axisRect()->setRangeZoom(zoomOrientations);
734 734
735 735 if (!isZoomX && !isZoomY) {
736 736 auto axis = plot().axisRect()->axis(QCPAxis::atBottom);
737 737 auto diff = direction * (axis->range().size() * (PAN_SPEED / 100.0));
738 738
739 739 axis->setRange(axis->range() + diff);
740 740
741 741 if (plot().noAntialiasingOnDrag()) {
742 742 plot().setNotAntialiasedElements(QCP::aeAll);
743 743 }
744 744
745 745 plot().replot(QCustomPlot::rpQueuedReplot);
746 746 }
747 747 }
748 748 }
749 749
750 750 void VisualizationGraphWidget::onMousePress(QMouseEvent *event) noexcept
751 751 {
752 752 auto isDragDropClick = event->modifiers().testFlag(DRAG_DROP_MODIFIER);
753 753 auto isSelectionZoneMode
754 754 = sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones;
755 755 auto isLeftClick = event->buttons().testFlag(Qt::LeftButton);
756 756
757 757 if (!isDragDropClick && isLeftClick) {
758 758 if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::ZoomBox) {
759 759 // Starts a zoom box
760 760 impl->startDrawingRect(event->pos(), plot());
761 761 }
762 762 else if (isSelectionZoneMode && impl->m_DrawingZone == nullptr) {
763 763 // Starts a new selection zone
764 764 auto zoneAtPos = impl->selectionZoneAt(event->pos(), plot());
765 765 if (!zoneAtPos) {
766 766 impl->startDrawingZone(event->pos(), this);
767 767 }
768 768 }
769 769 }
770 770
771 771 // Allows mouse panning only in default mode
772 772 plot().setInteraction(QCP::iRangeDrag, sqpApp->plotsInteractionMode()
773 773 == SqpApplication::PlotsInteractionMode::None
774 774 && !isDragDropClick);
775 775
776 776 // Allows zone edition only in selection zone mode without drag&drop
777 777 impl->setSelectionZonesEditionEnabled(isSelectionZoneMode && !isDragDropClick);
778 778
779 779 // Selection / Deselection
780 780 if (isSelectionZoneMode) {
781 781 auto isMultiSelectionClick = event->modifiers().testFlag(MULTI_ZONE_SELECTION_MODIFIER);
782 782 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(event->pos(), plot());
783 783 if (selectionZoneItemUnderCursor && isLeftClick) {
784 784 selectionZoneItemUnderCursor->setAssociatedEditedZones(
785 785 parentVisualizationWidget()->selectionZoneManager().selectedItems());
786 786 }
787 787 else if (!isMultiSelectionClick && isLeftClick) {
788 788 parentVisualizationWidget()->selectionZoneManager().clearSelection();
789 789 }
790 790 else {
791 791 // No selection change
792 792 }
793 793 }
794 794
795 795
796 796 impl->m_HasMovedMouse = false;
797 797 VisualizationDragWidget::mousePressEvent(event);
798 798 }
799 799
800 800 void VisualizationGraphWidget::onMouseRelease(QMouseEvent *event) noexcept
801 801 {
802 802 if (impl->m_DrawingZoomRect) {
803 803
804 804 auto axisX = plot().axisRect()->axis(QCPAxis::atBottom);
805 805 auto axisY = plot().axisRect()->axis(QCPAxis::atLeft);
806 806
807 807 auto newAxisXRange = QCPRange{impl->m_DrawingZoomRect->topLeft->coords().x(),
808 808 impl->m_DrawingZoomRect->bottomRight->coords().x()};
809 809
810 810 auto newAxisYRange = QCPRange{impl->m_DrawingZoomRect->topLeft->coords().y(),
811 811 impl->m_DrawingZoomRect->bottomRight->coords().y()};
812 812
813 813 impl->removeDrawingRect(plot());
814 814
815 815 if (newAxisXRange.size() > axisX->range().size() * (ZOOM_BOX_MIN_SIZE / 100.0)
816 816 && newAxisYRange.size() > axisY->range().size() * (ZOOM_BOX_MIN_SIZE / 100.0)) {
817 817 impl->m_ZoomStack.push(qMakePair(axisX->range(), axisY->range()));
818 818 axisX->setRange(newAxisXRange);
819 819 axisY->setRange(newAxisYRange);
820 820
821 821 plot().replot(QCustomPlot::rpQueuedReplot);
822 822 }
823 823 }
824 824
825 825 impl->endDrawingZone(this);
826 826
827 827 impl->m_IsCalibration = false;
828 828
829 829 // Selection / Deselection
830 830 auto isSelectionZoneMode
831 831 = sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones;
832 832 if (isSelectionZoneMode) {
833 833 auto isMultiSelectionClick = event->modifiers().testFlag(MULTI_ZONE_SELECTION_MODIFIER);
834 834 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(event->pos(), plot());
835 835 if (selectionZoneItemUnderCursor && event->button() == Qt::LeftButton) {
836 836 if (!isMultiSelectionClick && !impl->m_HasMovedMouse) {
837 837 parentVisualizationWidget()->selectionZoneManager().select(
838 838 {selectionZoneItemUnderCursor});
839 839 }
840 840 else if (!impl->m_HasMovedMouse) {
841 841 parentVisualizationWidget()->selectionZoneManager().setSelected(
842 842 selectionZoneItemUnderCursor, !selectionZoneItemUnderCursor->selected()
843 843 || event->button() == Qt::RightButton);
844 844 }
845 845 }
846 846 else {
847 847 // No selection change
848 848 }
849 849 }
850 850 }
851 851
852 852 void VisualizationGraphWidget::onDataCacheVariableUpdated()
853 853 {
854 854 auto graphRange = ui->widget->xAxis->range();
855 855 auto dateTime = SqpRange{graphRange.lower, graphRange.upper};
856 856
857 857 for (auto &variableEntry : impl->m_VariableToPlotMultiMap) {
858 858 auto variable = variableEntry.first;
859 859 qCDebug(LOG_VisualizationGraphWidget())
860 860 << "TORM: VisualizationGraphWidget::onDataCacheVariableUpdated S" << variable->range();
861 861 qCDebug(LOG_VisualizationGraphWidget())
862 862 << "TORM: VisualizationGraphWidget::onDataCacheVariableUpdated E" << dateTime;
863 863 if (dateTime.contains(variable->range()) || dateTime.intersect(variable->range())) {
864 864 impl->updateData(variableEntry.second, variable->dataSeries(), variable->range());
865 865 }
866 866 }
867 867 }
868 868
869 869 void VisualizationGraphWidget::onUpdateVarDisplaying(std::shared_ptr<Variable> variable,
870 870 const SqpRange &range)
871 871 {
872 872 auto it = impl->m_VariableToPlotMultiMap.find(variable);
873 873 if (it != impl->m_VariableToPlotMultiMap.end()) {
874 874 impl->updateData(it->second, variable->dataSeries(), range);
875 875 }
876 876 }
@@ -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,197
1 1 #include "Visualization/VisualizationWidget.h"
2 2 #include "Visualization/IVisualizationWidgetVisitor.h"
3 3 #include "Visualization/VisualizationGraphWidget.h"
4 4 #include "Visualization/VisualizationSelectionZoneManager.h"
5 5 #include "Visualization/VisualizationTabWidget.h"
6 6 #include "Visualization/VisualizationZoneWidget.h"
7 7 #include "Visualization/operations/FindVariableOperation.h"
8 8 #include "Visualization/operations/GenerateVariableMenuOperation.h"
9 9 #include "Visualization/operations/RemoveVariableOperation.h"
10 10 #include "Visualization/operations/RescaleAxeOperation.h"
11 11 #include "Visualization/qcustomplot.h"
12 12
13 13 #include "ui_VisualizationWidget.h"
14 14
15 #include "DragAndDrop/DragDropHelper.h"
15 #include "DragAndDrop/DragDropGuiController.h"
16 16 #include "SqpApplication.h"
17 17
18 18 #include <QToolButton>
19 19
20 20 #include <memory>
21 21
22 22 Q_LOGGING_CATEGORY(LOG_VisualizationWidget, "VisualizationWidget")
23 23
24 24 struct VisualizationWidget::VisualizationWidgetPrivate {
25 25 std::unique_ptr<VisualizationSelectionZoneManager> m_ZoneSelectionManager = nullptr;
26 26
27 27 VisualizationWidgetPrivate()
28 28 : m_ZoneSelectionManager(std::make_unique<VisualizationSelectionZoneManager>())
29 29 {
30 30 }
31 31 };
32 32
33 33 VisualizationWidget::VisualizationWidget(QWidget *parent)
34 34 : QWidget{parent},
35 35 ui{new Ui::VisualizationWidget},
36 36 impl{spimpl::make_unique_impl<VisualizationWidgetPrivate>()}
37 37 {
38 38 ui->setupUi(this);
39 39
40 40 auto addTabViewButton = new QToolButton{ui->tabWidget};
41 41 addTabViewButton->setText(tr("Add View"));
42 42 addTabViewButton->setCursor(Qt::ArrowCursor);
43 43 ui->tabWidget->setCornerWidget(addTabViewButton, Qt::TopRightCorner);
44 44
45 45 auto enableMinimumCornerWidgetSize = [this](bool enable) {
46 46
47 47 auto tabViewCornerWidget = ui->tabWidget->cornerWidget();
48 48 auto width = enable ? tabViewCornerWidget->width() : 0;
49 49 auto height = enable ? tabViewCornerWidget->height() : 0;
50 50 tabViewCornerWidget->setMinimumHeight(height);
51 51 tabViewCornerWidget->setMinimumWidth(width);
52 52 ui->tabWidget->setMinimumHeight(height);
53 53 ui->tabWidget->setMinimumWidth(width);
54 54 };
55 55
56 56 auto addTabView = [this, enableMinimumCornerWidgetSize]() {
57 57 auto widget = new VisualizationTabWidget{QString{"View %1"}.arg(ui->tabWidget->count() + 1),
58 58 ui->tabWidget};
59 59 auto index = ui->tabWidget->addTab(widget, widget->name());
60 60 if (ui->tabWidget->count() > 0) {
61 61 enableMinimumCornerWidgetSize(false);
62 62 }
63 63 qCInfo(LOG_VisualizationWidget()) << tr("add the tab of index %1").arg(index);
64 64 };
65 65
66 66 auto removeTabView = [this, enableMinimumCornerWidgetSize](int index) {
67 67 if (ui->tabWidget->count() == 1) {
68 68 enableMinimumCornerWidgetSize(true);
69 69 }
70 70
71 71 // Removes widget from tab and closes it
72 72 auto widget = ui->tabWidget->widget(index);
73 73 ui->tabWidget->removeTab(index);
74 74 if (widget) {
75 75 widget->close();
76 76 }
77 77
78 78 qCInfo(LOG_VisualizationWidget()) << tr("remove the tab of index %1").arg(index);
79 79
80 80 };
81 81
82 82 ui->tabWidget->setTabsClosable(true);
83 83
84 84 connect(addTabViewButton, &QToolButton::clicked, addTabView);
85 85 connect(ui->tabWidget, &QTabWidget::tabCloseRequested, removeTabView);
86 86
87 sqpApp->dragDropHelper().addDragDropTabBar(ui->tabWidget->tabBar());
87 sqpApp->dragDropGuiController().addDragDropTabBar(ui->tabWidget->tabBar());
88 88
89 89 // Adds default tab
90 90 addTabView();
91 91 }
92 92
93 93 VisualizationWidget::~VisualizationWidget()
94 94 {
95 sqpApp->dragDropHelper().removeDragDropTabBar(ui->tabWidget->tabBar());
95 sqpApp->dragDropGuiController().removeDragDropTabBar(ui->tabWidget->tabBar());
96 96 delete ui;
97 97 }
98 98
99 99 VisualizationSelectionZoneManager &VisualizationWidget::selectionZoneManager() const
100 100 {
101 101 return *impl->m_ZoneSelectionManager.get();
102 102 }
103 103
104 104 void VisualizationWidget::accept(IVisualizationWidgetVisitor *visitor)
105 105 {
106 106 if (visitor) {
107 107 visitor->visitEnter(this);
108 108
109 109 // Apply visitor for tab children
110 110 for (auto i = 0; i < ui->tabWidget->count(); ++i) {
111 111 // Widgets different from tabs are not visited (no action)
112 112 if (auto visualizationTabWidget
113 113 = dynamic_cast<VisualizationTabWidget *>(ui->tabWidget->widget(i))) {
114 114 visualizationTabWidget->accept(visitor);
115 115 }
116 116 }
117 117
118 118 visitor->visitLeave(this);
119 119 }
120 120 else {
121 121 qCCritical(LOG_VisualizationWidget()) << tr("Can't visit widget : the visitor is null");
122 122 }
123 123 }
124 124
125 125 bool VisualizationWidget::canDrop(const Variable &variable) const
126 126 {
127 127 // The main widget can never accomodate a variable
128 128 Q_UNUSED(variable);
129 129 return false;
130 130 }
131 131
132 132 bool VisualizationWidget::contains(const Variable &variable) const
133 133 {
134 134 Q_UNUSED(variable);
135 135 return false;
136 136 }
137 137
138 138 QString VisualizationWidget::name() const
139 139 {
140 140 return QStringLiteral("MainView");
141 141 }
142 142
143 143 void VisualizationWidget::attachVariableMenu(
144 144 QMenu *menu, const QVector<std::shared_ptr<Variable> > &variables) noexcept
145 145 {
146 146 // Menu is generated only if there is a single variable
147 147 if (variables.size() == 1) {
148 148 if (auto variable = variables.first()) {
149 149 // Gets the containers of the variable
150 150 FindVariableOperation findVariableOperation{variable};
151 151 accept(&findVariableOperation);
152 152 auto variableContainers = findVariableOperation.result();
153 153
154 154 // Generates the actions that make it possible to visualize the variable
155 155 GenerateVariableMenuOperation generateVariableMenuOperation{
156 156 menu, variable, std::move(variableContainers)};
157 157 accept(&generateVariableMenuOperation);
158 158 }
159 159 else {
160 160 qCCritical(LOG_VisualizationWidget()) << tr(
161 161 "Can't generate the menu relative to the visualization: the variable is null");
162 162 }
163 163 }
164 164 else {
165 165 qCDebug(LOG_VisualizationWidget())
166 166 << tr("No generation of the menu related to the visualization: several variables are "
167 167 "selected");
168 168 }
169 169 }
170 170
171 171 void VisualizationWidget::onVariableAboutToBeDeleted(std::shared_ptr<Variable> variable) noexcept
172 172 {
173 173 // Calls the operation of removing all references to the variable in the visualization
174 174 auto removeVariableOperation = RemoveVariableOperation{variable};
175 175 accept(&removeVariableOperation);
176 176 }
177 177
178 178 void VisualizationWidget::onRangeChanged(std::shared_ptr<Variable> variable,
179 179 const SqpRange &range) noexcept
180 180 {
181 181 // Calls the operation of rescaling all graph that contrains variable in the visualization
182 182 auto rescaleVariableOperation = RescaleAxeOperation{variable, range};
183 183 accept(&rescaleVariableOperation);
184 184 }
185 185
186 186 void VisualizationWidget::closeEvent(QCloseEvent *event)
187 187 {
188 188 // Closes tabs in the widget
189 189 for (auto i = 0; i < ui->tabWidget->count(); ++i) {
190 190 if (auto visualizationTabWidget
191 191 = dynamic_cast<VisualizationTabWidget *>(ui->tabWidget->widget(i))) {
192 192 visualizationTabWidget->close();
193 193 }
194 194 }
195 195
196 196 QWidget::closeEvent(event);
197 197 }
@@ -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 4
Under Review
author

Auto status change to "Under Review"

Approved

Status change > Approved

Approved

Status change > Approved

You need to be logged in to leave comments. Login now