##// END OF EJS Templates
Add an empty area at the bottom of the tab where a new zone can be created from a drop. Differentiate graph and zone placeHolders.
trabillard -
r881:f5851ce3ff09
parent child
Show More
@@ -1,88 +1,91
1 1 #ifndef SCIQLOP_DRAGDROPHELPER_H
2 2 #define SCIQLOP_DRAGDROPHELPER_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 VisualizationDragWidget;
11 11 class VisualizationDragDropContainer;
12 12 class QMimeData;
13 13
14 14 Q_DECLARE_LOGGING_CATEGORY(LOG_DragDropHelper)
15 15
16 16 /**
17 17 * @brief Helper class for drag&drop operations.
18 18 * @note The helper is accessible from the sqpApp singleton and has the same life as the whole
19 19 * application (like a controller). But contrary to a controller, it doesn't live in a thread and
20 20 * can interect with the gui.
21 21 * @see SqpApplication
22 22 */
23 23 class DragDropHelper {
24 24 public:
25 25 static const QString MIME_TYPE_GRAPH;
26 26 static const QString MIME_TYPE_ZONE;
27 27
28 enum class PlaceHolderType { Default, Graph, Zone };
29
28 30 DragDropHelper();
29 31 virtual ~DragDropHelper();
30 32
31 33 /// Resets some internal variables. Must be called before any new drag&drop operation.
32 34 void resetDragAndDrop();
33 35
34 36 /// Sets the visualization widget currently being drag on the visualization.
35 37 void setCurrentDragWidget(VisualizationDragWidget *dragWidget);
36 38
37 39 /// Returns the visualization widget currently being drag on the visualization.
38 40 /// Can be null if a new visualization widget is intended to be created by the drag&drop
39 41 /// operation.
40 42 VisualizationDragWidget *getCurrentDragWidget() const;
41 43
42 44 QWidget &placeHolder() const;
43 void insertPlaceHolder(QVBoxLayout *layout, int index);
45 void insertPlaceHolder(QVBoxLayout *layout, int index, PlaceHolderType type,
46 const QString &topLabelText);
44 47 void removePlaceHolder();
45 48 bool isPlaceHolderSet() const;
46 49
47 50 /// Checks if the specified mime data is valid for a drop in the visualization
48 51 bool checkMimeDataForVisualization(const QMimeData *mimeData,
49 52 VisualizationDragDropContainer *dropContainer);
50 53
51 54 void addDragDropScrollArea(QScrollArea *scrollArea);
52 55 void removeDragDropScrollArea(QScrollArea *scrollArea);
53 56
54 57 QUrl imageTemporaryUrl(const QImage &image) const;
55 58
56 59 void setHightlightedDragWidget(VisualizationDragWidget *dragWidget);
57 60 VisualizationDragWidget *getHightlightedDragWidget() const;
58 61
59 62 private:
60 63 class DragDropHelperPrivate;
61 64 spimpl::unique_impl_ptr<DragDropHelperPrivate> impl;
62 65 };
63 66
64 67 /**
65 68 * @brief Event filter class which manage the scroll of QScrollArea during a drag&drop operation.
66 69 * @note A QScrollArea inside an other QScrollArea is not fully supported.
67 70 */
68 71 class DragDropScroller : public QObject {
69 72 Q_OBJECT
70 73
71 74 public:
72 75 DragDropScroller(QObject *parent = nullptr);
73 76
74 77 void addScrollArea(QScrollArea *scrollArea);
75 78 void removeScrollArea(QScrollArea *scrollArea);
76 79
77 80 protected:
78 81 bool eventFilter(QObject *obj, QEvent *event);
79 82
80 83 private:
81 84 class DragDropScrollerPrivate;
82 85 spimpl::unique_impl_ptr<DragDropScrollerPrivate> impl;
83 86
84 87 private slots:
85 88 void onTimer();
86 89 };
87 90
88 91 #endif // SCIQLOP_DRAGDROPHELPER_H
@@ -1,52 +1,57
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 <DragDropHelper.h>
13
12 14 Q_DECLARE_LOGGING_CATEGORY(LOG_VisualizationDragDropContainer)
13 15
14 16 class VisualizationDragWidget;
15 17
16 18 class VisualizationDragDropContainer : public QFrame {
17 19 Q_OBJECT
18 20
19 21 signals:
20 22 void dropOccuredInContainer(int dropIndex, const QMimeData *mimeData);
21 23 void dropOccuredOnWidget(VisualizationDragWidget *dragWidget, const QMimeData *mimeData);
22 24
23 25 public:
24 26 enum class DropBehavior { Inserted, Merged, InsertedAndMerged };
25 27 using AcceptMimeDataFunction = std::function<bool(const QMimeData *mimeData)>;
26 28
27 29 VisualizationDragDropContainer(QWidget *parent = nullptr);
28 30
29 31 void addDragWidget(VisualizationDragWidget *dragWidget);
30 32 void insertDragWidget(int index, VisualizationDragWidget *dragWidget);
31 33
32 34 void addAcceptedMimeType(const QString &mimeType, DropBehavior behavior);
33 35
34 36 int countDragWidget() const;
35 37
36 38 void setAcceptMimeDataFunction(AcceptMimeDataFunction fun);
37 39
40 void setPlaceHolderType(DragDropHelper::PlaceHolderType type,
41 const QString &placeHolderText = QString());
42
38 43 protected:
39 44 void dragEnterEvent(QDragEnterEvent *event);
40 45 void dragLeaveEvent(QDragLeaveEvent *event);
41 46 void dragMoveEvent(QDragMoveEvent *event);
42 47 void dropEvent(QDropEvent *event);
43 48
44 49 private:
45 50 class VisualizationDragDropContainerPrivate;
46 51 spimpl::unique_impl_ptr<VisualizationDragDropContainerPrivate> impl;
47 52
48 53 private slots:
49 54 void startDrag(VisualizationDragWidget *dragWidget, const QPoint &dragPosition);
50 55 };
51 56
52 57 #endif // SCIQLOP_VISUALIZATIONDRAGDROPCONTAINER_H
@@ -1,353 +1,383
1 1 #include "DragDropHelper.h"
2 2 #include "SqpApplication.h"
3 3 #include "Visualization/VisualizationDragDropContainer.h"
4 4 #include "Visualization/VisualizationDragWidget.h"
5 5 #include "Visualization/VisualizationWidget.h"
6 6 #include "Visualization/operations/FindVariableOperation.h"
7 7
8 8 #include "Variable/Variable.h"
9 9 #include "Variable/VariableController.h"
10 10
11 11 #include "Common/MimeTypesDef.h"
12 12 #include "Common/VisualizationDef.h"
13 13
14 14 #include <QDir>
15 15 #include <QDragEnterEvent>
16 16 #include <QDragMoveEvent>
17 #include <QLabel>
17 18 #include <QScrollArea>
18 19 #include <QScrollBar>
19 20 #include <QTimer>
20 21 #include <QVBoxLayout>
21 22
22 23 const int SCROLL_SPEED = 5;
23 24 const int SCROLL_ZONE_SIZE = 50;
24 25
25 26 Q_LOGGING_CATEGORY(LOG_DragDropHelper, "DragDrophelper")
26 27
27 28 struct DragDropScroller::DragDropScrollerPrivate {
28 29
29 30 QList<QScrollArea *> m_ScrollAreas;
30 31 QScrollArea *m_CurrentScrollArea = nullptr;
31 32 std::unique_ptr<QTimer> m_Timer = nullptr;
32 33
33 34 enum class ScrollDirection { up, down, unknown };
34 35 ScrollDirection m_Direction = ScrollDirection::unknown;
35 36
36 37 explicit DragDropScrollerPrivate() : m_Timer{std::make_unique<QTimer>()}
37 38 {
38 39 m_Timer->setInterval(0);
39 40 }
40 41 };
41 42
42 43 DragDropScroller::DragDropScroller(QObject *parent)
43 44 : QObject{parent}, impl{spimpl::make_unique_impl<DragDropScrollerPrivate>()}
44 45 {
45 46 connect(impl->m_Timer.get(), &QTimer::timeout, this, &DragDropScroller::onTimer);
46 47 }
47 48
48 49 void DragDropScroller::addScrollArea(QScrollArea *scrollArea)
49 50 {
50 51 impl->m_ScrollAreas << scrollArea;
51 52 scrollArea->viewport()->setAcceptDrops(true);
52 53 }
53 54
54 55 void DragDropScroller::removeScrollArea(QScrollArea *scrollArea)
55 56 {
56 57 impl->m_ScrollAreas.removeAll(scrollArea);
57 58 scrollArea->viewport()->setAcceptDrops(false);
58 59 }
59 60
60 61 bool DragDropScroller::eventFilter(QObject *obj, QEvent *event)
61 62 {
62 63 if (event->type() == QEvent::DragMove) {
63 64 auto w = static_cast<QWidget *>(obj);
64 65
65 66 if (impl->m_CurrentScrollArea && impl->m_CurrentScrollArea->isAncestorOf(w)) {
66 67 auto moveEvent = static_cast<QDragMoveEvent *>(event);
67 68
68 69 auto pos = moveEvent->pos();
69 70 if (impl->m_CurrentScrollArea->viewport() != w) {
70 71 auto globalPos = w->mapToGlobal(moveEvent->pos());
71 72 pos = impl->m_CurrentScrollArea->viewport()->mapFromGlobal(globalPos);
72 73 }
73 74
74 75 auto isInTopZone = pos.y() > impl->m_CurrentScrollArea->viewport()->size().height()
75 76 - SCROLL_ZONE_SIZE;
76 77 auto isInBottomZone = pos.y() < SCROLL_ZONE_SIZE;
77 78
78 79 if (!isInTopZone && !isInBottomZone) {
79 80 impl->m_Direction = DragDropScrollerPrivate::ScrollDirection::unknown;
80 81 impl->m_Timer->stop();
81 82 }
82 83 else if (!impl->m_Timer->isActive()) {
83 84 impl->m_Direction = isInTopZone ? DragDropScrollerPrivate::ScrollDirection::up
84 85 : DragDropScrollerPrivate::ScrollDirection::down;
85 86 impl->m_Timer->start();
86 87 }
87 88 }
88 89 }
89 90 else if (event->type() == QEvent::DragEnter) {
90 91 auto w = static_cast<QWidget *>(obj);
91 92
92 93 for (auto scrollArea : impl->m_ScrollAreas) {
93 94 if (impl->m_CurrentScrollArea != scrollArea && scrollArea->isAncestorOf(w)) {
94 95 auto enterEvent = static_cast<QDragEnterEvent *>(event);
95 96 enterEvent->acceptProposedAction();
96 97 enterEvent->setDropAction(Qt::IgnoreAction);
97 98 impl->m_CurrentScrollArea = scrollArea;
98 99 break;
99 100 }
100 101 }
101 102 }
102 103 else if (event->type() == QEvent::DragLeave) {
103 104 if (impl->m_CurrentScrollArea) {
104 105 if (!QRect(QPoint(), impl->m_CurrentScrollArea->size())
105 106 .contains(impl->m_CurrentScrollArea->mapFromGlobal(QCursor::pos()))) {
106 107 impl->m_CurrentScrollArea = nullptr;
107 108 impl->m_Direction = DragDropScrollerPrivate::ScrollDirection::unknown;
108 109 impl->m_Timer->stop();
109 110 }
110 111 }
111 112 }
112 113 else if (event->type() == QEvent::Drop) {
113 114 if (impl->m_CurrentScrollArea) {
114 115 impl->m_CurrentScrollArea = nullptr;
115 116 impl->m_Direction = DragDropScrollerPrivate::ScrollDirection::unknown;
116 117 impl->m_Timer->stop();
117 118 }
118 119 }
119 120
120 121 return false;
121 122 }
122 123
123 124 void DragDropScroller::onTimer()
124 125 {
125 126 if (impl->m_CurrentScrollArea) {
126 127 auto mvt = 0;
127 128 switch (impl->m_Direction) {
128 129 case DragDropScrollerPrivate::ScrollDirection::up:
129 130 mvt = SCROLL_SPEED;
130 131 break;
131 132 case DragDropScrollerPrivate::ScrollDirection::down:
132 133 mvt = -SCROLL_SPEED;
133 134 break;
134 135 default:
135 136 break;
136 137 }
137 138
138 139 impl->m_CurrentScrollArea->verticalScrollBar()->setValue(
139 140 impl->m_CurrentScrollArea->verticalScrollBar()->value() + mvt);
140 141 }
141 142 }
142 143
143 144 struct DragDropHelper::DragDropHelperPrivate {
144 145
145 146 VisualizationDragWidget *m_CurrentDragWidget = nullptr;
146 147 std::unique_ptr<QWidget> m_PlaceHolder = nullptr;
148 QLabel *m_PlaceHolderLabel;
149 QWidget *m_PlaceBackground;
147 150 std::unique_ptr<DragDropScroller> m_DragDropScroller = nullptr;
148 151 QString m_ImageTempUrl; // Temporary file for image url generated by the drag & drop. Not using
149 152 // QTemporaryFile to have a name which is not generated.
150 153
151 154 VisualizationDragWidget *m_HighlightedDragWidget = nullptr;
152 155
153 156 QMetaObject::Connection m_DragWidgetDestroyedConnection;
154 157 QMetaObject::Connection m_HighlightedWidgetDestroyedConnection;
155 158
156 159 explicit DragDropHelperPrivate()
157 160 : m_PlaceHolder{std::make_unique<QWidget>()},
158 161 m_DragDropScroller{std::make_unique<DragDropScroller>()}
159 162 {
160 m_PlaceHolder->setStyleSheet("background-color: #BBD5EE; border:2px solid #2A7FD4");
161 sqpApp->installEventFilter(m_DragDropScroller.get());
162 163
164 auto layout = new QVBoxLayout{m_PlaceHolder.get()};
165 layout->setSpacing(0);
166 layout->setContentsMargins(0, 0, 0, 0);
167
168 m_PlaceHolderLabel = new QLabel{"", m_PlaceHolder.get()};
169 m_PlaceHolderLabel->setMinimumHeight(25);
170 layout->addWidget(m_PlaceHolderLabel);
171
172 m_PlaceBackground = new QWidget{m_PlaceHolder.get()};
173 m_PlaceBackground->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
174 layout->addWidget(m_PlaceBackground);
175
176 sqpApp->installEventFilter(m_DragDropScroller.get());
163 177
164 178 m_ImageTempUrl = QDir::temp().absoluteFilePath("Sciqlop_graph.png");
165 179 }
166 180
167 void preparePlaceHolder() const
181 void preparePlaceHolder(DragDropHelper::PlaceHolderType type, const QString &topLabelText) const
168 182 {
169 183 if (m_CurrentDragWidget) {
170 184 m_PlaceHolder->setMinimumSize(m_CurrentDragWidget->size());
171 185 m_PlaceHolder->setSizePolicy(m_CurrentDragWidget->sizePolicy());
172 186 }
173 187 else {
174 188 // Configuration of the placeHolder when there is no dragWidget
175 189 // (for instance with a drag from a variable)
176 190
177 191 m_PlaceHolder->setMinimumSize(0, GRAPH_MINIMUM_HEIGHT);
178 192 m_PlaceHolder->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
179 193 }
194
195 switch (type) {
196 case DragDropHelper::PlaceHolderType::Graph:
197 m_PlaceBackground->setStyleSheet(
198 "background-color: #BBD5EE; border: 1px solid #2A7FD4");
199 break;
200 case DragDropHelper::PlaceHolderType::Zone:
201 case DragDropHelper::PlaceHolderType::Default:
202 m_PlaceBackground->setStyleSheet(
203 "background-color: #BBD5EE; border: 2px solid #2A7FD4");
204 m_PlaceHolderLabel->setStyleSheet("color: #2A7FD4");
205 break;
206 }
207
208 m_PlaceHolderLabel->setText(topLabelText);
209 m_PlaceHolderLabel->setVisible(!topLabelText.isEmpty());
180 210 }
181 211 };
182 212
183 213
184 214 DragDropHelper::DragDropHelper() : impl{spimpl::make_unique_impl<DragDropHelperPrivate>()}
185 215 {
186 216 }
187 217
188 218 DragDropHelper::~DragDropHelper()
189 219 {
190 220 QFile::remove(impl->m_ImageTempUrl);
191 221 }
192 222
193 223 void DragDropHelper::resetDragAndDrop()
194 224 {
195 225 setCurrentDragWidget(nullptr);
196 226 impl->m_HighlightedDragWidget = nullptr;
197 227 }
198 228
199 229 void DragDropHelper::setCurrentDragWidget(VisualizationDragWidget *dragWidget)
200 230 {
201 231 if (impl->m_CurrentDragWidget) {
202 232
203 233 QObject::disconnect(impl->m_DragWidgetDestroyedConnection);
204 234 }
205 235
206 236 if (dragWidget) {
207 237 // ensures the impl->m_CurrentDragWidget is reset when the widget is destroyed
208 238 impl->m_DragWidgetDestroyedConnection
209 239 = QObject::connect(dragWidget, &VisualizationDragWidget::destroyed,
210 240 [this]() { impl->m_CurrentDragWidget = nullptr; });
211 241 }
212 242
213 243 impl->m_CurrentDragWidget = dragWidget;
214 244 }
215 245
216 246 VisualizationDragWidget *DragDropHelper::getCurrentDragWidget() const
217 247 {
218 248 return impl->m_CurrentDragWidget;
219 249 }
220 250
221
222 251 QWidget &DragDropHelper::placeHolder() const
223 252 {
224 253 return *impl->m_PlaceHolder;
225 254 }
226 255
227 void DragDropHelper::insertPlaceHolder(QVBoxLayout *layout, int index)
256 void DragDropHelper::insertPlaceHolder(QVBoxLayout *layout, int index, PlaceHolderType type,
257 const QString &topLabelText)
228 258 {
229 259 removePlaceHolder();
230 impl->preparePlaceHolder();
260 impl->preparePlaceHolder(type, topLabelText);
231 261 layout->insertWidget(index, impl->m_PlaceHolder.get());
232 262 impl->m_PlaceHolder->show();
233 263 }
234 264
235 265 void DragDropHelper::removePlaceHolder()
236 266 {
237 267 auto parentWidget = impl->m_PlaceHolder->parentWidget();
238 268 if (parentWidget) {
239 269 parentWidget->layout()->removeWidget(impl->m_PlaceHolder.get());
240 270 impl->m_PlaceHolder->setParent(nullptr);
241 271 impl->m_PlaceHolder->hide();
242 272 }
243 273 }
244 274
245 275 bool DragDropHelper::isPlaceHolderSet() const
246 276 {
247 277 return impl->m_PlaceHolder->parentWidget();
248 278 }
249 279
250 280 void DragDropHelper::addDragDropScrollArea(QScrollArea *scrollArea)
251 281 {
252 282 impl->m_DragDropScroller->addScrollArea(scrollArea);
253 283 }
254 284
255 285 void DragDropHelper::removeDragDropScrollArea(QScrollArea *scrollArea)
256 286 {
257 287 impl->m_DragDropScroller->removeScrollArea(scrollArea);
258 288 }
259 289
260 290 QUrl DragDropHelper::imageTemporaryUrl(const QImage &image) const
261 291 {
262 292 image.save(impl->m_ImageTempUrl);
263 293 return QUrl::fromLocalFile(impl->m_ImageTempUrl);
264 294 }
265 295
266 296 void DragDropHelper::setHightlightedDragWidget(VisualizationDragWidget *dragWidget)
267 297 {
268 298 if (impl->m_HighlightedDragWidget) {
269 299 impl->m_HighlightedDragWidget->highlightForMerge(false);
270 300 QObject::disconnect(impl->m_HighlightedWidgetDestroyedConnection);
271 301 }
272 302
273 303 if (dragWidget) {
274 304 dragWidget->highlightForMerge(true);
275 305
276 306 // ensures the impl->m_HighlightedDragWidget is reset when the widget is destroyed
277 307 impl->m_DragWidgetDestroyedConnection
278 308 = QObject::connect(dragWidget, &VisualizationDragWidget::destroyed,
279 309 [this]() { impl->m_HighlightedDragWidget = nullptr; });
280 310 }
281 311
282 312 impl->m_HighlightedDragWidget = dragWidget;
283 313 }
284 314
285 315 VisualizationDragWidget *DragDropHelper::getHightlightedDragWidget() const
286 316 {
287 317 return impl->m_HighlightedDragWidget;
288 318 }
289 319
290 320 bool DragDropHelper::checkMimeDataForVisualization(const QMimeData *mimeData,
291 321 VisualizationDragDropContainer *dropContainer)
292 322 {
293 323 if (!mimeData || !dropContainer) {
294 324 qCWarning(LOG_DragDropHelper()) << QObject::tr(
295 325 "DragDropHelper::checkMimeDataForVisualization, invalid input parameters.");
296 326 Q_ASSERT(false);
297 327 return false;
298 328 }
299 329
300 330 auto result = false;
301 331
302 332 if (mimeData->hasFormat(MIME_TYPE_VARIABLE_LIST)) {
303 333 auto variables = sqpApp->variableController().variablesForMimeData(
304 334 mimeData->data(MIME_TYPE_VARIABLE_LIST));
305 335
306 336 if (variables.count() == 1) {
307 337
308 338 auto variable = variables.first();
309 339 if (variable->dataSeries() != nullptr) {
310 340
311 341 // Check that the variable is not already in a graph
312 342
313 343 auto parent = dropContainer->parentWidget();
314 344 while (parent && qobject_cast<VisualizationWidget *>(parent) == nullptr) {
315 345 parent = parent->parentWidget(); // Search for the top level VisualizationWidget
316 346 }
317 347
318 348 if (parent) {
319 349 auto visualizationWidget = static_cast<VisualizationWidget *>(parent);
320 350
321 351 FindVariableOperation findVariableOperation{variable};
322 352 visualizationWidget->accept(&findVariableOperation);
323 353 auto variableContainers = findVariableOperation.result();
324 354 if (variableContainers.empty()) {
325 355 result = true;
326 356 }
327 357 else {
328 358 // result = false: the variable already exist in the visualisation
329 359 }
330 360 }
331 361 else {
332 362 qCWarning(LOG_DragDropHelper()) << QObject::tr(
333 363 "DragDropHelper::checkMimeDataForVisualization, the parent "
334 364 "VisualizationWidget cannot be found. Cannot check if the variable is "
335 365 "already used or not.");
336 366 }
337 367 }
338 368 else {
339 369 // result = false: the variable is not fully loaded
340 370 }
341 371 }
342 372 else {
343 373 // result = false: cannot drop multiple variables in the visualisation
344 374 }
345 375 }
346 376 else {
347 377 // Other MIME data
348 378 // no special rules, accepted by default
349 379 result = true;
350 380 }
351 381
352 382 return result;
353 383 }
@@ -1,442 +1,453
1 1 #include "Visualization/VisualizationDragDropContainer.h"
2 2 #include "DragDropHelper.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 struct VisualizationDragDropContainer::VisualizationDragDropContainerPrivate {
18 18
19 19 QVBoxLayout *m_Layout;
20 20 QHash<QString, VisualizationDragDropContainer::DropBehavior> m_AcceptedMimeTypes;
21 QString m_PlaceHolderText;
22 DragDropHelper::PlaceHolderType m_PlaceHolderType = DragDropHelper::PlaceHolderType::Graph;
21 23
22 24 VisualizationDragDropContainer::AcceptMimeDataFunction m_AcceptMimeDataFun
23 25 = [](auto mimeData) { return true; };
24 26
25 27 int m_MinContainerHeight = 0;
26 28
27 29 explicit VisualizationDragDropContainerPrivate(QWidget *widget)
28 30 {
29 31 m_Layout = new QVBoxLayout(widget);
30 32 m_Layout->setContentsMargins(0, 0, 0, 0);
31 33 }
32 34
33 35 bool acceptMimeData(const QMimeData *data) const
34 36 {
35 37 for (const auto &type : m_AcceptedMimeTypes.keys()) {
36 38 if (data->hasFormat(type) && m_AcceptMimeDataFun(data)) {
37 39 return true;
38 40 }
39 41 }
40 42
41 43 return false;
42 44 }
43 45
44 46 bool allowMergeForMimeData(const QMimeData *data) const
45 47 {
46 48 bool result = false;
47 49 for (auto it = m_AcceptedMimeTypes.constBegin(); it != m_AcceptedMimeTypes.constEnd();
48 50 ++it) {
49 51
50 52 if (data->hasFormat(it.key())
51 53 && (it.value() == VisualizationDragDropContainer::DropBehavior::Merged
52 54 || it.value()
53 55 == VisualizationDragDropContainer::DropBehavior::InsertedAndMerged)) {
54 56 result = true;
55 57 }
56 58 else if (data->hasFormat(it.key())
57 59 && it.value() == VisualizationDragDropContainer::DropBehavior::Inserted) {
58 60 // Merge is forbidden if the mime data contain an acceptable type which cannot be
59 61 // merged
60 62 result = false;
61 63 break;
62 64 }
63 65 }
64 66
65 67 return result;
66 68 }
67 69
68 70 bool allowInsertForMimeData(const QMimeData *data) const
69 71 {
70 72 for (auto it = m_AcceptedMimeTypes.constBegin(); it != m_AcceptedMimeTypes.constEnd();
71 73 ++it) {
72 74 if (data->hasFormat(it.key())
73 75 && (it.value() == VisualizationDragDropContainer::DropBehavior::Inserted
74 76 || it.value()
75 77 == VisualizationDragDropContainer::DropBehavior::InsertedAndMerged)) {
76 78 return true;
77 79 }
78 80 }
79 81
80 82 return false;
81 83 }
82 84
83 85 bool hasPlaceHolder() const
84 86 {
85 87 return sqpApp->dragDropHelper().placeHolder().parentWidget() == m_Layout->parentWidget();
86 88 }
87 89
88 90 VisualizationDragWidget *getChildDragWidgetAt(const QWidget *parent, const QPoint &pos) const
89 91 {
90 92 VisualizationDragWidget *dragWidget = nullptr;
91 93
92 94 for (auto child : parent->children()) {
93 95 auto widget = qobject_cast<VisualizationDragWidget *>(child);
94 96 if (widget && widget->isVisible()) {
95 97 if (widget->frameGeometry().contains(pos)) {
96 98 dragWidget = widget;
97 99 break;
98 100 }
99 101 }
100 102 }
101 103
102 104 return dragWidget;
103 105 }
104 106
105 107 bool cursorIsInContainer(QWidget *container) const
106 108 {
107 109 return container->isAncestorOf(sqpApp->widgetAt(QCursor::pos()));
108 110 }
109 111
110 112 int countDragWidget(const QWidget *parent, bool onlyVisible = false) const
111 113 {
112 114 auto nbGraph = 0;
113 115 for (auto child : parent->children()) {
114 116 if (qobject_cast<VisualizationDragWidget *>(child)) {
115 117 if (!onlyVisible || qobject_cast<VisualizationDragWidget *>(child)->isVisible()) {
116 118 nbGraph += 1;
117 119 }
118 120 }
119 121 }
120 122
121 123 return nbGraph;
122 124 }
123 125
124 126 void findPlaceHolderPosition(const QPoint &pos, bool canInsert, bool canMerge,
125 127 const VisualizationDragDropContainer *container);
126 128 };
127 129
128 130 VisualizationDragDropContainer::VisualizationDragDropContainer(QWidget *parent)
129 131 : QFrame{parent},
130 132 impl{spimpl::make_unique_impl<VisualizationDragDropContainerPrivate>(this)}
131 133 {
132 134 setAcceptDrops(true);
133 135 }
134 136
135 137 void VisualizationDragDropContainer::addDragWidget(VisualizationDragWidget *dragWidget)
136 138 {
137 139 impl->m_Layout->addWidget(dragWidget);
138 140 disconnect(dragWidget, &VisualizationDragWidget::dragDetected, nullptr, nullptr);
139 141 connect(dragWidget, &VisualizationDragWidget::dragDetected, this,
140 142 &VisualizationDragDropContainer::startDrag);
141 143 }
142 144
143 145 void VisualizationDragDropContainer::insertDragWidget(int index,
144 146 VisualizationDragWidget *dragWidget)
145 147 {
146 148 impl->m_Layout->insertWidget(index, dragWidget);
147 149 disconnect(dragWidget, &VisualizationDragWidget::dragDetected, nullptr, nullptr);
148 150 connect(dragWidget, &VisualizationDragWidget::dragDetected, this,
149 151 &VisualizationDragDropContainer::startDrag);
150 152 }
151 153
152 154 void VisualizationDragDropContainer::addAcceptedMimeType(
153 155 const QString &mimeType, VisualizationDragDropContainer::DropBehavior behavior)
154 156 {
155 157 impl->m_AcceptedMimeTypes[mimeType] = behavior;
156 158 }
157 159
158 160 int VisualizationDragDropContainer::countDragWidget() const
159 161 {
160 162 return impl->countDragWidget(this);
161 163 }
162 164
163 165 void VisualizationDragDropContainer::setAcceptMimeDataFunction(
164 166 VisualizationDragDropContainer::AcceptMimeDataFunction fun)
165 167 {
166 168 impl->m_AcceptMimeDataFun = fun;
167 169 }
168 170
171 void VisualizationDragDropContainer::setPlaceHolderType(DragDropHelper::PlaceHolderType type,
172 const QString &placeHolderText)
173 {
174 impl->m_PlaceHolderType = type;
175 impl->m_PlaceHolderText = placeHolderText;
176 }
177
169 178 void VisualizationDragDropContainer::startDrag(VisualizationDragWidget *dragWidget,
170 179 const QPoint &dragPosition)
171 180 {
172 181 auto &helper = sqpApp->dragDropHelper();
173 182 helper.resetDragAndDrop();
174 183
175 184 // Note: The management of the drag object is done by Qt
176 185 auto drag = new QDrag{dragWidget};
177 186 drag->setHotSpot(dragPosition);
178 187
179 188 auto mimeData = dragWidget->mimeData();
180 189 drag->setMimeData(mimeData);
181 190
182 191 auto pixmap = QPixmap(dragWidget->size());
183 192 dragWidget->render(&pixmap);
184 193 drag->setPixmap(pixmap);
185 194
186 195 auto image = pixmap.toImage();
187 196 mimeData->setImageData(image);
188 197 mimeData->setUrls({helper.imageTemporaryUrl(image)});
189 198
190 199 if (impl->m_Layout->indexOf(dragWidget) >= 0) {
191 200 helper.setCurrentDragWidget(dragWidget);
192 201
193 202 if (impl->cursorIsInContainer(this)) {
194 203 auto dragWidgetIndex = impl->m_Layout->indexOf(dragWidget);
195 helper.insertPlaceHolder(impl->m_Layout, dragWidgetIndex);
204 helper.insertPlaceHolder(impl->m_Layout, dragWidgetIndex, impl->m_PlaceHolderType,
205 impl->m_PlaceHolderText);
196 206 dragWidget->setVisible(false);
197 207 }
198 208 else {
199 209 // The drag starts directly outside the drop zone
200 210 // do not add the placeHolder
201 211 }
202 212
203 213 // Note: The exec() is blocking on windows but not on linux and macOS
204 214 drag->exec(Qt::MoveAction | Qt::CopyAction);
205 215 }
206 216 else {
207 217 qCWarning(LOG_VisualizationDragDropContainer())
208 218 << tr("VisualizationDragDropContainer::startDrag, drag aborted, the specified "
209 219 "VisualizationDragWidget is not found in this container.");
210 220 }
211 221 }
212 222
213 223 void VisualizationDragDropContainer::dragEnterEvent(QDragEnterEvent *event)
214 224 {
215 225 if (impl->acceptMimeData(event->mimeData())) {
216 226 event->acceptProposedAction();
217 227
218 228 auto &helper = sqpApp->dragDropHelper();
219 229
220 230 if (!impl->hasPlaceHolder()) {
221 231 auto dragWidget = helper.getCurrentDragWidget();
222 232
223 233 if (dragWidget) {
224 234 // If the drag&drop is internal to the visualization, entering the container hide
225 235 // the dragWidget which was made visible by the dragLeaveEvent
226 236 auto parentWidget
227 237 = qobject_cast<VisualizationDragDropContainer *>(dragWidget->parentWidget());
228 238 if (parentWidget) {
229 239 dragWidget->setVisible(false);
230 240 }
231 241 }
232 242
233 243 auto canMerge = impl->allowMergeForMimeData(event->mimeData());
234 244 auto canInsert = impl->allowInsertForMimeData(event->mimeData());
235 245 impl->findPlaceHolderPosition(event->pos(), canInsert, canMerge, this);
236 246 }
237 247 else {
238 248 // do nothing
239 249 }
240 250 }
241 251 else {
242 252 event->ignore();
243 253 }
244 254
245 255 QWidget::dragEnterEvent(event);
246 256 }
247 257
248 258 void VisualizationDragDropContainer::dragLeaveEvent(QDragLeaveEvent *event)
249 259 {
250 260 Q_UNUSED(event);
251 261
252 262 auto &helper = sqpApp->dragDropHelper();
253 263
254 264 if (!impl->cursorIsInContainer(this)) {
255 265 helper.removePlaceHolder();
256 266 helper.setHightlightedDragWidget(nullptr);
257 267 impl->m_MinContainerHeight = 0;
258 268
259 269 auto dragWidget = helper.getCurrentDragWidget();
260 270 if (dragWidget) {
261 271 // dragWidget has a value only if the drag is started from the visualization
262 272 // In that case, shows the drag widget at its original place
263 273 // So the drag widget doesn't stay hidden if the drop occurs outside the visualization
264 274 // drop zone (It is not possible to catch a drop event outside of the application)
265 275
266 276 if (dragWidget) {
267 277 dragWidget->setVisible(true);
268 278 }
269 279 }
270 280 }
271 281 else {
272 282 // Leave event probably received for a child widget.
273 283 // Do nothing.
274 284 // Note: The DragLeave event, doesn't have any mean to determine who sent it.
275 285 }
276 286
277 287 QWidget::dragLeaveEvent(event);
278 288 }
279 289
280 290 void VisualizationDragDropContainer::dragMoveEvent(QDragMoveEvent *event)
281 291 {
282 292 if (impl->acceptMimeData(event->mimeData())) {
283 293 auto canMerge = impl->allowMergeForMimeData(event->mimeData());
284 294 auto canInsert = impl->allowInsertForMimeData(event->mimeData());
285 295 impl->findPlaceHolderPosition(event->pos(), canInsert, canMerge, this);
286 296 }
287 297 else {
288 298 event->ignore();
289 299 }
290 300
291 301 QWidget::dragMoveEvent(event);
292 302 }
293 303
294 304 void VisualizationDragDropContainer::dropEvent(QDropEvent *event)
295 305 {
296 306 auto &helper = sqpApp->dragDropHelper();
297 307
298 308 if (impl->acceptMimeData(event->mimeData())) {
299 309 auto dragWidget = helper.getCurrentDragWidget();
300 310 if (impl->hasPlaceHolder()) {
301 311 // drop where the placeHolder is located
302 312
303 313 auto canInsert = impl->allowInsertForMimeData(event->mimeData());
304 314 if (canInsert) {
305 315 auto droppedIndex = impl->m_Layout->indexOf(&helper.placeHolder());
306 316
307 317 if (dragWidget) {
308 318 auto dragWidgetIndex = impl->m_Layout->indexOf(dragWidget);
309 319 if (dragWidgetIndex >= 0 && dragWidgetIndex < droppedIndex) {
310 320 // Correction of the index if the drop occurs in the same container
311 321 // and if the drag is started from the visualization (in that case, the
312 322 // dragWidget is hidden)
313 323 droppedIndex -= 1;
314 324 }
315 325
316 326 dragWidget->setVisible(true);
317 327 }
318 328
319 329 event->acceptProposedAction();
320 330
321 331 helper.removePlaceHolder();
322 332
323 333 emit dropOccuredInContainer(droppedIndex, event->mimeData());
324 334 }
325 335 else {
326 336 qCWarning(LOG_VisualizationDragDropContainer()) << tr(
327 337 "VisualizationDragDropContainer::dropEvent, dropping on the placeHolder, but "
328 338 "the insertion is forbidden.");
329 339 Q_ASSERT(false);
330 340 }
331 341 }
332 342 else if (helper.getHightlightedDragWidget()) {
333 343 // drop on the highlighted widget
334 344
335 345 auto canMerge = impl->allowMergeForMimeData(event->mimeData());
336 346 if (canMerge) {
337 347 event->acceptProposedAction();
338 348 emit dropOccuredOnWidget(helper.getHightlightedDragWidget(), event->mimeData());
339 349 }
340 350 else {
341 351 qCWarning(LOG_VisualizationDragDropContainer())
342 352 << tr("VisualizationDragDropContainer::dropEvent, dropping on a widget, but "
343 353 "the merge is forbidden.");
344 354 Q_ASSERT(false);
345 355 }
346 356 }
347 357 }
348 358 else {
349 359 event->ignore();
350 360 }
351 361
352 362 sqpApp->dragDropHelper().setHightlightedDragWidget(nullptr);
353 363 impl->m_MinContainerHeight = 0;
354 364
355 365 QWidget::dropEvent(event);
356 366 }
357 367
358 368
359 369 void VisualizationDragDropContainer::VisualizationDragDropContainerPrivate::findPlaceHolderPosition(
360 370 const QPoint &pos, bool canInsert, bool canMerge,
361 371 const VisualizationDragDropContainer *container)
362 372 {
363 373 auto &helper = sqpApp->dragDropHelper();
364 374
365 375 auto absPos = container->mapToGlobal(pos);
366 376 auto isOnPlaceHolder = sqpApp->widgetAt(absPos) == &(helper.placeHolder());
367 377
368 378 if (countDragWidget(container, true) == 0) {
369 379 // Drop on an empty container, just add the placeHolder at the top
370 helper.insertPlaceHolder(m_Layout, 0);
380 helper.insertPlaceHolder(m_Layout, 0, m_PlaceHolderType, m_PlaceHolderText);
371 381 }
372 382 else if (!isOnPlaceHolder) {
373 383 auto nbDragWidget = countDragWidget(container);
374 384 if (nbDragWidget > 0) {
375 385
376 386 if (m_MinContainerHeight == 0) {
377 387 m_MinContainerHeight = container->size().height();
378 388 }
379 389
380 390 m_MinContainerHeight = qMin(m_MinContainerHeight, container->size().height());
381 391 auto graphHeight = qMax(m_MinContainerHeight / nbDragWidget, GRAPH_MINIMUM_HEIGHT);
382 392
383 393 auto posY = pos.y();
384 394 auto dropIndex = floor(posY / graphHeight);
385 395 auto zoneSize = qMin(graphHeight / 4.0, 75.0);
386 396
387 397
388 398 auto isOnTop = posY < dropIndex * graphHeight + zoneSize;
389 399 auto isOnBottom = posY > (dropIndex + 1) * graphHeight - zoneSize;
390 400
391 401 auto placeHolderIndex = m_Layout->indexOf(&(helper.placeHolder()));
392 402
393 403 auto dragWidgetHovered = getChildDragWidgetAt(container, pos);
394 404
395 405 if (canInsert && (isOnTop || isOnBottom || !canMerge)) {
396 406 if (isOnBottom) {
397 407 dropIndex += 1;
398 408 }
399 409
400 410 if (helper.getCurrentDragWidget()) {
401 411 auto dragWidgetIndex = m_Layout->indexOf(helper.getCurrentDragWidget());
402 412 if (dragWidgetIndex >= 0 && dragWidgetIndex <= dropIndex) {
403 413 // Correction of the index if the drop occurs in the same container
404 414 // and if the drag is started from the visualization (in that case, the
405 415 // dragWidget is hidden)
406 416 dropIndex += 1;
407 417 }
408 418 }
409 419
410 420 if (dropIndex != placeHolderIndex) {
411 helper.insertPlaceHolder(m_Layout, dropIndex);
421 helper.insertPlaceHolder(m_Layout, dropIndex, m_PlaceHolderType,
422 m_PlaceHolderText);
412 423 }
413 424
414 425 helper.setHightlightedDragWidget(nullptr);
415 426 }
416 427 else if (canMerge && dragWidgetHovered) {
417 428 // drop on the middle -> merge
418 429 if (hasPlaceHolder()) {
419 430 helper.removePlaceHolder();
420 431 }
421 432
422 433 helper.setHightlightedDragWidget(dragWidgetHovered);
423 434 }
424 435 else {
425 qCWarning(LOG_VisualizationDragDropContainer()) << tr(
426 "VisualizationDragDropContainer::findPlaceHolderPosition, no valid drop "
427 "action.");
436 qCWarning(LOG_VisualizationDragDropContainer())
437 << tr("VisualizationDragDropContainer::findPlaceHolderPosition, no valid drop "
438 "action.");
428 439 Q_ASSERT(false);
429 440 }
430 441 }
431 442 else {
432 443 qCWarning(LOG_VisualizationDragDropContainer())
433 << tr("VisualizationDragDropContainer::findPlaceHolderPosition, no widget "
434 "found in the "
435 "container");
444 << tr("VisualizationDragDropContainer::findPlaceHolderPosition, no widget "
445 "found in the "
446 "container");
436 447 }
437 448 }
438 449 else {
439 450 // the mouse is hover the placeHolder
440 451 // Do nothing
441 452 }
442 453 }
@@ -1,307 +1,309
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 "Variable/VariableController.h"
9 9
10 10 #include "Common/MimeTypesDef.h"
11 11
12 12 #include "DragDropHelper.h"
13 13 #include "SqpApplication.h"
14 14
15 15 Q_LOGGING_CATEGORY(LOG_VisualizationTabWidget, "VisualizationTabWidget")
16 16
17 17 namespace {
18 18
19 19 /// Generates a default name for a new zone, according to the number of zones already displayed in
20 20 /// the tab
21 21 QString defaultZoneName(const QLayout &layout)
22 22 {
23 23 auto count = 0;
24 24 for (auto i = 0; i < layout.count(); ++i) {
25 25 if (dynamic_cast<VisualizationZoneWidget *>(layout.itemAt(i)->widget())) {
26 26 count++;
27 27 }
28 28 }
29 29
30 30 return QObject::tr("Zone %1").arg(count + 1);
31 31 }
32 32
33 33 /**
34 34 * Applies a function to all zones of the tab represented by its layout
35 35 * @param layout the layout that contains zones
36 36 * @param fun the function to apply to each zone
37 37 */
38 38 template <typename Fun>
39 39 void processZones(QLayout &layout, Fun fun)
40 40 {
41 41 for (auto i = 0; i < layout.count(); ++i) {
42 42 if (auto item = layout.itemAt(i)) {
43 43 if (auto visualizationZoneWidget
44 44 = dynamic_cast<VisualizationZoneWidget *>(item->widget())) {
45 45 fun(*visualizationZoneWidget);
46 46 }
47 47 }
48 48 }
49 49 }
50 50
51 51 } // namespace
52 52
53 53 struct VisualizationTabWidget::VisualizationTabWidgetPrivate {
54 54 explicit VisualizationTabWidgetPrivate(const QString &name) : m_Name{name} {}
55 55
56 56 QString m_Name;
57 57
58 58 void dropGraph(int index, VisualizationTabWidget *tabWidget);
59 59 void dropZone(int index, VisualizationTabWidget *tabWidget);
60 60 void dropVariables(const QList<std::shared_ptr<Variable> > &variables, int index,
61 61 VisualizationTabWidget *tabWidget);
62 62 };
63 63
64 64 VisualizationTabWidget::VisualizationTabWidget(const QString &name, QWidget *parent)
65 65 : QWidget{parent},
66 66 ui{new Ui::VisualizationTabWidget},
67 67 impl{spimpl::make_unique_impl<VisualizationTabWidgetPrivate>(name)}
68 68 {
69 69 ui->setupUi(this);
70 70
71 ui->dragDropContainer->setPlaceHolderType(DragDropHelper::PlaceHolderType::Zone, "Zone");
72 ui->dragDropContainer->layout()->setContentsMargins(0, 0, 0, 5);
71 73 ui->dragDropContainer->addAcceptedMimeType(
72 74 MIME_TYPE_GRAPH, VisualizationDragDropContainer::DropBehavior::Inserted);
73 75 ui->dragDropContainer->addAcceptedMimeType(
74 76 MIME_TYPE_ZONE, VisualizationDragDropContainer::DropBehavior::Inserted);
75 77 ui->dragDropContainer->addAcceptedMimeType(
76 78 MIME_TYPE_VARIABLE_LIST, VisualizationDragDropContainer::DropBehavior::Inserted);
77 79
78 80 ui->dragDropContainer->setAcceptMimeDataFunction([this](auto mimeData) {
79 81 return sqpApp->dragDropHelper().checkMimeDataForVisualization(mimeData,
80 82 ui->dragDropContainer);
81 83 });
82 84
83 85 connect(ui->dragDropContainer, &VisualizationDragDropContainer::dropOccuredInContainer, this,
84 86 &VisualizationTabWidget::dropMimeData);
85 87
86 88 sqpApp->dragDropHelper().addDragDropScrollArea(ui->scrollArea);
87 89
88 90 // Widget is deleted when closed
89 91 setAttribute(Qt::WA_DeleteOnClose);
90 92 }
91 93
92 94 VisualizationTabWidget::~VisualizationTabWidget()
93 95 {
94 96 sqpApp->dragDropHelper().removeDragDropScrollArea(ui->scrollArea);
95 97 delete ui;
96 98 }
97 99
98 100 void VisualizationTabWidget::addZone(VisualizationZoneWidget *zoneWidget)
99 101 {
100 102 ui->dragDropContainer->addDragWidget(zoneWidget);
101 103 }
102 104
103 105 void VisualizationTabWidget::insertZone(int index, VisualizationZoneWidget *zoneWidget)
104 106 {
105 107 ui->dragDropContainer->insertDragWidget(index, zoneWidget);
106 108 }
107 109
108 110 VisualizationZoneWidget *VisualizationTabWidget::createZone(std::shared_ptr<Variable> variable)
109 111 {
110 112 return createZone({variable}, -1);
111 113 }
112 114
113 115 VisualizationZoneWidget *
114 116 VisualizationTabWidget::createZone(const QList<std::shared_ptr<Variable> > &variables, int index)
115 117 {
116 118 auto zoneWidget = createEmptyZone(index);
117 119
118 120 // Creates a new graph into the zone
119 121 zoneWidget->createGraph(variables, index);
120 122
121 123 return zoneWidget;
122 124 }
123 125
124 126 VisualizationZoneWidget *VisualizationTabWidget::createEmptyZone(int index)
125 127 {
126 128 auto zoneWidget
127 129 = new VisualizationZoneWidget{defaultZoneName(*ui->dragDropContainer->layout()), this};
128 130 this->insertZone(index, zoneWidget);
129 131
130 132 return zoneWidget;
131 133 }
132 134
133 135 void VisualizationTabWidget::accept(IVisualizationWidgetVisitor *visitor)
134 136 {
135 137 if (visitor) {
136 138 visitor->visitEnter(this);
137 139
138 140 // Apply visitor to zone children: widgets different from zones are not visited (no action)
139 141 processZones(tabLayout(), [visitor](VisualizationZoneWidget &zoneWidget) {
140 142 zoneWidget.accept(visitor);
141 143 });
142 144
143 145 visitor->visitLeave(this);
144 146 }
145 147 else {
146 148 qCCritical(LOG_VisualizationTabWidget()) << tr("Can't visit widget : the visitor is null");
147 149 }
148 150 }
149 151
150 152 bool VisualizationTabWidget::canDrop(const Variable &variable) const
151 153 {
152 154 // A tab can always accomodate a variable
153 155 Q_UNUSED(variable);
154 156 return true;
155 157 }
156 158
157 159 bool VisualizationTabWidget::contains(const Variable &variable) const
158 160 {
159 161 Q_UNUSED(variable);
160 162 return false;
161 163 }
162 164
163 165 QString VisualizationTabWidget::name() const
164 166 {
165 167 return impl->m_Name;
166 168 }
167 169
168 170 void VisualizationTabWidget::closeEvent(QCloseEvent *event)
169 171 {
170 172 // Closes zones in the tab
171 173 processZones(tabLayout(), [](VisualizationZoneWidget &zoneWidget) { zoneWidget.close(); });
172 174
173 175 QWidget::closeEvent(event);
174 176 }
175 177
176 178 QLayout &VisualizationTabWidget::tabLayout() const noexcept
177 179 {
178 180 return *ui->dragDropContainer->layout();
179 181 }
180 182
181 183 void VisualizationTabWidget::dropMimeData(int index, const QMimeData *mimeData)
182 184 {
183 185 if (mimeData->hasFormat(MIME_TYPE_GRAPH)) {
184 186 impl->dropGraph(index, this);
185 187 }
186 188 else if (mimeData->hasFormat(MIME_TYPE_ZONE)) {
187 189 impl->dropZone(index, this);
188 190 }
189 191 else if (mimeData->hasFormat(MIME_TYPE_VARIABLE_LIST)) {
190 192 auto variables = sqpApp->variableController().variablesForMimeData(
191 193 mimeData->data(MIME_TYPE_VARIABLE_LIST));
192 194 impl->dropVariables(variables, index, this);
193 195 }
194 196 else {
195 197 qCWarning(LOG_VisualizationZoneWidget())
196 198 << tr("VisualizationTabWidget::dropMimeData, unknown MIME data received.");
197 199 }
198 200 }
199 201
200 202 void VisualizationTabWidget::VisualizationTabWidgetPrivate::dropGraph(
201 203 int index, VisualizationTabWidget *tabWidget)
202 204 {
203 205 auto &helper = sqpApp->dragDropHelper();
204 206
205 207 auto graphWidget = qobject_cast<VisualizationGraphWidget *>(helper.getCurrentDragWidget());
206 208 if (!graphWidget) {
207 209 qCWarning(LOG_VisualizationZoneWidget())
208 210 << tr("VisualizationTabWidget::dropGraph, drop aborted, the dropped graph is not "
209 211 "found or invalid.");
210 212 Q_ASSERT(false);
211 213 return;
212 214 }
213 215
214 216 auto parentDragDropContainer
215 217 = qobject_cast<VisualizationDragDropContainer *>(graphWidget->parentWidget());
216 218 if (!parentDragDropContainer) {
217 219 qCWarning(LOG_VisualizationZoneWidget())
218 220 << tr("VisualizationTabWidget::dropGraph, drop aborted, the parent container of "
219 221 "the dropped graph is not found.");
220 222 Q_ASSERT(false);
221 223 return;
222 224 }
223 225
224 226 auto nbGraph = parentDragDropContainer->countDragWidget();
225 227
226 228 const auto &variables = graphWidget->variables();
227 229
228 230 if (!variables.isEmpty()) {
229 231 // Abort the requests for the variables (if any)
230 232 // Commented, because it's not sure if it's needed or not
231 233 // for (const auto& var : variables)
232 234 //{
233 235 // sqpApp->variableController().onAbortProgressRequested(var);
234 236 //}
235 237
236 238 if (nbGraph == 1) {
237 239 // This is the only graph in the previous zone, close the zone
238 240 graphWidget->parentZoneWidget()->close();
239 241 }
240 242 else {
241 243 // Close the graph
242 244 graphWidget->close();
243 245 }
244 246
245 247 tabWidget->createZone(variables, index);
246 248 }
247 249 else {
248 250 // The graph is empty, create an empty zone and move the graph inside
249 251
250 252 auto parentZoneWidget = graphWidget->parentZoneWidget();
251 253
252 254 parentDragDropContainer->layout()->removeWidget(graphWidget);
253 255
254 256 auto zoneWidget = tabWidget->createEmptyZone(index);
255 257 zoneWidget->addGraph(graphWidget);
256 258
257 259 // Close the old zone if it was the only graph inside
258 260 if (nbGraph == 1) {
259 261 parentZoneWidget->close();
260 262 }
261 263 }
262 264 }
263 265
264 266 void VisualizationTabWidget::VisualizationTabWidgetPrivate::dropZone(
265 267 int index, VisualizationTabWidget *tabWidget)
266 268 {
267 269 auto &helper = sqpApp->dragDropHelper();
268 270
269 271 auto zoneWidget = qobject_cast<VisualizationZoneWidget *>(helper.getCurrentDragWidget());
270 272 if (!zoneWidget) {
271 273 qCWarning(LOG_VisualizationZoneWidget())
272 274 << tr("VisualizationTabWidget::dropZone, drop aborted, the dropped zone is not "
273 275 "found or invalid.");
274 276 Q_ASSERT(false);
275 277 return;
276 278 }
277 279
278 280 auto parentDragDropContainer
279 281 = qobject_cast<VisualizationDragDropContainer *>(zoneWidget->parentWidget());
280 282 if (!parentDragDropContainer) {
281 283 qCWarning(LOG_VisualizationZoneWidget())
282 284 << tr("VisualizationTabWidget::dropZone, drop aborted, the parent container of "
283 285 "the dropped zone is not found.");
284 286 Q_ASSERT(false);
285 287 return;
286 288 }
287 289
288 290 // Simple move of the zone, no variable operation associated
289 291 parentDragDropContainer->layout()->removeWidget(zoneWidget);
290 292 tabWidget->ui->dragDropContainer->insertDragWidget(index, zoneWidget);
291 293 }
292 294
293 295 void VisualizationTabWidget::VisualizationTabWidgetPrivate::dropVariables(
294 296 const QList<std::shared_ptr<Variable> > &variables, int index,
295 297 VisualizationTabWidget *tabWidget)
296 298 {
297 299 // Note: the AcceptMimeDataFunction (set on the drop container) ensure there is a single and
298 300 // compatible variable here
299 301 if (variables.count() > 1) {
300 302 qCWarning(LOG_VisualizationZoneWidget())
301 303 << tr("VisualizationTabWidget::dropVariables, dropping multiple variables, operation "
302 304 "aborted.");
303 305 return;
304 306 }
305 307
306 308 tabWidget->createZone(variables, index);
307 309 }
@@ -1,515 +1,516
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 19 #include <DragDropHelper.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 = dynamic_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 // Returns the first graph in the zone or nullptr if there is no graph inside
76 76 VisualizationGraphWidget *firstGraph(const VisualizationZoneWidget *zoneWidget) const
77 77 {
78 78 VisualizationGraphWidget *firstGraph = nullptr;
79 79 auto layout = zoneWidget->ui->dragDropContainer->layout();
80 80 if (layout->count() > 0) {
81 81 if (auto visualizationGraphWidget
82 82 = qobject_cast<VisualizationGraphWidget *>(layout->itemAt(0)->widget())) {
83 83 firstGraph = visualizationGraphWidget;
84 84 }
85 85 }
86 86
87 87 return firstGraph;
88 88 }
89 89
90 90 void dropGraph(int index, VisualizationZoneWidget *zoneWidget);
91 91 void dropVariables(const QList<std::shared_ptr<Variable> > &variables, int index,
92 92 VisualizationZoneWidget *zoneWidget);
93 93 };
94 94
95 95 VisualizationZoneWidget::VisualizationZoneWidget(const QString &name, QWidget *parent)
96 96 : VisualizationDragWidget{parent},
97 97 ui{new Ui::VisualizationZoneWidget},
98 98 impl{spimpl::make_unique_impl<VisualizationZoneWidgetPrivate>()}
99 99 {
100 100 ui->setupUi(this);
101 101
102 102 ui->zoneNameLabel->setText(name);
103 103
104 ui->dragDropContainer->setPlaceHolderType(DragDropHelper::PlaceHolderType::Graph);
104 105 ui->dragDropContainer->addAcceptedMimeType(
105 106 MIME_TYPE_GRAPH, VisualizationDragDropContainer::DropBehavior::Inserted);
106 107 ui->dragDropContainer->addAcceptedMimeType(
107 108 MIME_TYPE_VARIABLE_LIST, VisualizationDragDropContainer::DropBehavior::InsertedAndMerged);
108 109 ui->dragDropContainer->addAcceptedMimeType(
109 110 MIME_TYPE_TIME_RANGE, VisualizationDragDropContainer::DropBehavior::Merged);
110 111 ui->dragDropContainer->setAcceptMimeDataFunction([this](auto mimeData) {
111 112 return sqpApp->dragDropHelper().checkMimeDataForVisualization(mimeData,
112 113 ui->dragDropContainer);
113 114 });
114 115
115 116 connect(ui->dragDropContainer, &VisualizationDragDropContainer::dropOccuredInContainer, this,
116 117 &VisualizationZoneWidget::dropMimeData);
117 118 connect(ui->dragDropContainer, &VisualizationDragDropContainer::dropOccuredOnWidget, this,
118 119 &VisualizationZoneWidget::dropMimeDataOnGraph);
119 120
120 121 // 'Close' options : widget is deleted when closed
121 122 setAttribute(Qt::WA_DeleteOnClose);
122 123 connect(ui->closeButton, &QToolButton::clicked, this, &VisualizationZoneWidget::close);
123 124 ui->closeButton->setIcon(sqpApp->style()->standardIcon(QStyle::SP_TitleBarCloseButton));
124 125
125 126 // Synchronisation id
126 127 QMetaObject::invokeMethod(&sqpApp->variableController(), "onAddSynchronizationGroupId",
127 128 Qt::QueuedConnection, Q_ARG(QUuid, impl->m_SynchronisationGroupId));
128 129 }
129 130
130 131 VisualizationZoneWidget::~VisualizationZoneWidget()
131 132 {
132 133 delete ui;
133 134 }
134 135
135 136 void VisualizationZoneWidget::addGraph(VisualizationGraphWidget *graphWidget)
136 137 {
137 138 // Synchronize new graph with others in the zone
138 139 impl->m_Synchronizer->addGraph(*graphWidget);
139 140
140 141 ui->dragDropContainer->addDragWidget(graphWidget);
141 142 }
142 143
143 144 void VisualizationZoneWidget::insertGraph(int index, VisualizationGraphWidget *graphWidget)
144 145 {
145 146 // Synchronize new graph with others in the zone
146 147 impl->m_Synchronizer->addGraph(*graphWidget);
147 148
148 149 ui->dragDropContainer->insertDragWidget(index, graphWidget);
149 150 }
150 151
151 152 VisualizationGraphWidget *VisualizationZoneWidget::createGraph(std::shared_ptr<Variable> variable)
152 153 {
153 154 return createGraph(variable, -1);
154 155 }
155 156
156 157 VisualizationGraphWidget *VisualizationZoneWidget::createGraph(std::shared_ptr<Variable> variable,
157 158 int index)
158 159 {
159 160 auto graphWidget
160 161 = new VisualizationGraphWidget{defaultGraphName(*ui->dragDropContainer->layout()), this};
161 162
162 163
163 164 // Set graph properties
164 165 graphWidget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::MinimumExpanding);
165 166 graphWidget->setMinimumHeight(GRAPH_MINIMUM_HEIGHT);
166 167
167 168
168 169 // Lambda to synchronize zone widget
169 170 auto synchronizeZoneWidget = [this, graphWidget](const SqpRange &graphRange,
170 171 const SqpRange &oldGraphRange) {
171 172
172 173 auto zoomType = VariableController::getZoomType(graphRange, oldGraphRange);
173 174 auto frameLayout = ui->dragDropContainer->layout();
174 175 for (auto i = 0; i < frameLayout->count(); ++i) {
175 176 auto graphChild
176 177 = dynamic_cast<VisualizationGraphWidget *>(frameLayout->itemAt(i)->widget());
177 178 if (graphChild && (graphChild != graphWidget)) {
178 179
179 180 auto graphChildRange = graphChild->graphRange();
180 181 switch (zoomType) {
181 182 case AcquisitionZoomType::ZoomIn: {
182 183 auto deltaLeft = graphRange.m_TStart - oldGraphRange.m_TStart;
183 184 auto deltaRight = oldGraphRange.m_TEnd - graphRange.m_TEnd;
184 185 graphChildRange.m_TStart += deltaLeft;
185 186 graphChildRange.m_TEnd -= deltaRight;
186 187 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: ZoomIn");
187 188 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: deltaLeft")
188 189 << deltaLeft;
189 190 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: deltaRight")
190 191 << deltaRight;
191 192 qCDebug(LOG_VisualizationZoneWidget())
192 193 << tr("TORM: dt") << graphRange.m_TEnd - graphRange.m_TStart;
193 194
194 195 break;
195 196 }
196 197
197 198 case AcquisitionZoomType::ZoomOut: {
198 199 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: ZoomOut");
199 200 auto deltaLeft = oldGraphRange.m_TStart - graphRange.m_TStart;
200 201 auto deltaRight = graphRange.m_TEnd - oldGraphRange.m_TEnd;
201 202 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: deltaLeft")
202 203 << deltaLeft;
203 204 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: deltaRight")
204 205 << deltaRight;
205 206 qCDebug(LOG_VisualizationZoneWidget())
206 207 << tr("TORM: dt") << graphRange.m_TEnd - graphRange.m_TStart;
207 208 graphChildRange.m_TStart -= deltaLeft;
208 209 graphChildRange.m_TEnd += deltaRight;
209 210 break;
210 211 }
211 212 case AcquisitionZoomType::PanRight: {
212 213 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: PanRight");
213 214 auto deltaLeft = graphRange.m_TStart - oldGraphRange.m_TStart;
214 215 auto deltaRight = graphRange.m_TEnd - oldGraphRange.m_TEnd;
215 216 graphChildRange.m_TStart += deltaLeft;
216 217 graphChildRange.m_TEnd += deltaRight;
217 218 qCDebug(LOG_VisualizationZoneWidget())
218 219 << tr("TORM: dt") << graphRange.m_TEnd - graphRange.m_TStart;
219 220 break;
220 221 }
221 222 case AcquisitionZoomType::PanLeft: {
222 223 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: PanLeft");
223 224 auto deltaLeft = oldGraphRange.m_TStart - graphRange.m_TStart;
224 225 auto deltaRight = oldGraphRange.m_TEnd - graphRange.m_TEnd;
225 226 graphChildRange.m_TStart -= deltaLeft;
226 227 graphChildRange.m_TEnd -= deltaRight;
227 228 break;
228 229 }
229 230 case AcquisitionZoomType::Unknown: {
230 231 qCDebug(LOG_VisualizationZoneWidget())
231 232 << tr("Impossible to synchronize: zoom type unknown");
232 233 break;
233 234 }
234 235 default:
235 236 qCCritical(LOG_VisualizationZoneWidget())
236 237 << tr("Impossible to synchronize: zoom type not take into account");
237 238 // No action
238 239 break;
239 240 }
240 241 graphChild->enableAcquisition(false);
241 242 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: Range before: ")
242 243 << graphChild->graphRange();
243 244 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: Range after : ")
244 245 << graphChildRange;
245 246 qCDebug(LOG_VisualizationZoneWidget())
246 247 << tr("TORM: child dt") << graphChildRange.m_TEnd - graphChildRange.m_TStart;
247 248 graphChild->setGraphRange(graphChildRange);
248 249 graphChild->enableAcquisition(true);
249 250 }
250 251 }
251 252 };
252 253
253 254 // connection for synchronization
254 255 connect(graphWidget, &VisualizationGraphWidget::synchronize, synchronizeZoneWidget);
255 256 connect(graphWidget, &VisualizationGraphWidget::variableAdded, this,
256 257 &VisualizationZoneWidget::onVariableAdded);
257 258 connect(graphWidget, &VisualizationGraphWidget::variableAboutToBeRemoved, this,
258 259 &VisualizationZoneWidget::onVariableAboutToBeRemoved);
259 260
260 261 auto range = SqpRange{};
261 262 if (auto firstGraph = impl->firstGraph(this)) {
262 263 // Case of a new graph in a existant zone
263 264 range = firstGraph->graphRange();
264 265 }
265 266 else {
266 267 // Case of a new graph as the first of the zone
267 268 range = variable->range();
268 269 }
269 270
270 271 this->insertGraph(index, graphWidget);
271 272
272 273 graphWidget->addVariable(variable, range);
273 274
274 275 // get y using variable range
275 276 if (auto dataSeries = variable->dataSeries()) {
276 277 dataSeries->lockRead();
277 278 auto valuesBounds
278 279 = dataSeries->valuesBounds(variable->range().m_TStart, variable->range().m_TEnd);
279 280 auto end = dataSeries->cend();
280 281 if (valuesBounds.first != end && valuesBounds.second != end) {
281 282 auto rangeValue = [](const auto &value) { return std::isnan(value) ? 0. : value; };
282 283
283 284 auto minValue = rangeValue(valuesBounds.first->minValue());
284 285 auto maxValue = rangeValue(valuesBounds.second->maxValue());
285 286
286 287 graphWidget->setYRange(SqpRange{minValue, maxValue});
287 288 }
288 289 dataSeries->unlock();
289 290 }
290 291
291 292 return graphWidget;
292 293 }
293 294
294 295 VisualizationGraphWidget *
295 296 VisualizationZoneWidget::createGraph(const QList<std::shared_ptr<Variable> > variables, int index)
296 297 {
297 298 if (variables.isEmpty()) {
298 299 return nullptr;
299 300 }
300 301
301 302 auto graphWidget = createGraph(variables.first(), index);
302 303 for (auto variableIt = variables.cbegin() + 1; variableIt != variables.cend(); ++variableIt) {
303 304 graphWidget->addVariable(*variableIt, graphWidget->graphRange());
304 305 }
305 306
306 307 return graphWidget;
307 308 }
308 309
309 310 void VisualizationZoneWidget::accept(IVisualizationWidgetVisitor *visitor)
310 311 {
311 312 if (visitor) {
312 313 visitor->visitEnter(this);
313 314
314 315 // Apply visitor to graph children: widgets different from graphs are not visited (no
315 316 // action)
316 317 processGraphs(
317 318 *ui->dragDropContainer->layout(),
318 319 [visitor](VisualizationGraphWidget &graphWidget) { graphWidget.accept(visitor); });
319 320
320 321 visitor->visitLeave(this);
321 322 }
322 323 else {
323 324 qCCritical(LOG_VisualizationZoneWidget()) << tr("Can't visit widget : the visitor is null");
324 325 }
325 326 }
326 327
327 328 bool VisualizationZoneWidget::canDrop(const Variable &variable) const
328 329 {
329 330 // A tab can always accomodate a variable
330 331 Q_UNUSED(variable);
331 332 return true;
332 333 }
333 334
334 335 bool VisualizationZoneWidget::contains(const Variable &variable) const
335 336 {
336 337 Q_UNUSED(variable);
337 338 return false;
338 339 }
339 340
340 341 QString VisualizationZoneWidget::name() const
341 342 {
342 343 return ui->zoneNameLabel->text();
343 344 }
344 345
345 346 QMimeData *VisualizationZoneWidget::mimeData() const
346 347 {
347 348 auto mimeData = new QMimeData;
348 349 mimeData->setData(MIME_TYPE_ZONE, QByteArray{});
349 350
350 351 return mimeData;
351 352 }
352 353
353 354 bool VisualizationZoneWidget::isDragAllowed() const
354 355 {
355 356 return true;
356 357 }
357 358
358 359 void VisualizationZoneWidget::closeEvent(QCloseEvent *event)
359 360 {
360 361 // Closes graphs in the zone
361 362 processGraphs(*ui->dragDropContainer->layout(),
362 363 [](VisualizationGraphWidget &graphWidget) { graphWidget.close(); });
363 364
364 365 // Delete synchronization group from variable controller
365 366 QMetaObject::invokeMethod(&sqpApp->variableController(), "onRemoveSynchronizationGroupId",
366 367 Qt::QueuedConnection, Q_ARG(QUuid, impl->m_SynchronisationGroupId));
367 368
368 369 QWidget::closeEvent(event);
369 370 }
370 371
371 372 void VisualizationZoneWidget::onVariableAdded(std::shared_ptr<Variable> variable)
372 373 {
373 374 QMetaObject::invokeMethod(&sqpApp->variableController(), "onAddSynchronized",
374 375 Qt::QueuedConnection, Q_ARG(std::shared_ptr<Variable>, variable),
375 376 Q_ARG(QUuid, impl->m_SynchronisationGroupId));
376 377 }
377 378
378 379 void VisualizationZoneWidget::onVariableAboutToBeRemoved(std::shared_ptr<Variable> variable)
379 380 {
380 381 QMetaObject::invokeMethod(&sqpApp->variableController(), "desynchronize", Qt::QueuedConnection,
381 382 Q_ARG(std::shared_ptr<Variable>, variable),
382 383 Q_ARG(QUuid, impl->m_SynchronisationGroupId));
383 384 }
384 385
385 386 void VisualizationZoneWidget::dropMimeData(int index, const QMimeData *mimeData)
386 387 {
387 388 if (mimeData->hasFormat(MIME_TYPE_GRAPH)) {
388 389 impl->dropGraph(index, this);
389 390 }
390 391 else if (mimeData->hasFormat(MIME_TYPE_VARIABLE_LIST)) {
391 392 auto variables = sqpApp->variableController().variablesForMimeData(
392 393 mimeData->data(MIME_TYPE_VARIABLE_LIST));
393 394 impl->dropVariables(variables, index, this);
394 395 }
395 396 else {
396 397 qCWarning(LOG_VisualizationZoneWidget())
397 398 << tr("VisualizationZoneWidget::dropMimeData, unknown MIME data received.");
398 399 }
399 400 }
400 401
401 402 void VisualizationZoneWidget::dropMimeDataOnGraph(VisualizationDragWidget *dragWidget,
402 403 const QMimeData *mimeData)
403 404 {
404 405 auto graphWidget = qobject_cast<VisualizationGraphWidget *>(dragWidget);
405 406 if (!graphWidget) {
406 407 qCWarning(LOG_VisualizationZoneWidget())
407 408 << tr("VisualizationZoneWidget::dropMimeDataOnGraph, dropping in an unknown widget, "
408 409 "drop aborted");
409 410 Q_ASSERT(false);
410 411 return;
411 412 }
412 413
413 414 if (mimeData->hasFormat(MIME_TYPE_VARIABLE_LIST)) {
414 415 auto variables = sqpApp->variableController().variablesForMimeData(
415 416 mimeData->data(MIME_TYPE_VARIABLE_LIST));
416 417 for (const auto &var : variables) {
417 418 graphWidget->addVariable(var, graphWidget->graphRange());
418 419 }
419 420 }
420 421 else if (mimeData->hasFormat(MIME_TYPE_TIME_RANGE)) {
421 422 auto range = TimeController::timeRangeForMimeData(mimeData->data(MIME_TYPE_TIME_RANGE));
422 423 graphWidget->setGraphRange(range);
423 424 }
424 425 else {
425 426 qCWarning(LOG_VisualizationZoneWidget())
426 427 << tr("VisualizationZoneWidget::dropMimeDataOnGraph, unknown MIME data received.");
427 428 }
428 429 }
429 430
430 431 void VisualizationZoneWidget::VisualizationZoneWidgetPrivate::dropGraph(
431 432 int index, VisualizationZoneWidget *zoneWidget)
432 433 {
433 434 auto &helper = sqpApp->dragDropHelper();
434 435
435 436 auto graphWidget = qobject_cast<VisualizationGraphWidget *>(helper.getCurrentDragWidget());
436 437 if (!graphWidget) {
437 438 qCWarning(LOG_VisualizationZoneWidget())
438 439 << tr("VisualizationZoneWidget::dropGraph, drop aborted, the dropped graph is not "
439 440 "found or invalid.");
440 441 Q_ASSERT(false);
441 442 return;
442 443 }
443 444
444 445 auto parentDragDropContainer
445 446 = qobject_cast<VisualizationDragDropContainer *>(graphWidget->parentWidget());
446 447 if (!parentDragDropContainer) {
447 448 qCWarning(LOG_VisualizationZoneWidget())
448 449 << tr("VisualizationZoneWidget::dropGraph, drop aborted, the parent container of "
449 450 "the dropped graph is not found.");
450 451 Q_ASSERT(false);
451 452 return;
452 453 }
453 454
454 455 const auto &variables = graphWidget->variables();
455 456
456 457 if (parentDragDropContainer != zoneWidget->ui->dragDropContainer && !variables.isEmpty()) {
457 458 // The drop didn't occur in the same zone
458 459
459 460 // Abort the requests for the variables (if any)
460 461 // Commented, because it's not sure if it's needed or not
461 462 // for (const auto& var : variables)
462 463 //{
463 464 // sqpApp->variableController().onAbortProgressRequested(var);
464 465 //}
465 466
466 467 auto previousParentZoneWidget = graphWidget->parentZoneWidget();
467 468 auto nbGraph = parentDragDropContainer->countDragWidget();
468 469 if (nbGraph == 1) {
469 470 // This is the only graph in the previous zone, close the zone
470 471 previousParentZoneWidget->close();
471 472 }
472 473 else {
473 474 // Close the graph
474 475 graphWidget->close();
475 476 }
476 477
477 478 // Creates the new graph in the zone
478 479 zoneWidget->createGraph(variables, index);
479 480 }
480 481 else {
481 482 // The drop occurred in the same zone or the graph is empty
482 483 // Simple move of the graph, no variable operation associated
483 484 parentDragDropContainer->layout()->removeWidget(graphWidget);
484 485
485 486 if (variables.isEmpty() && parentDragDropContainer != zoneWidget->ui->dragDropContainer) {
486 487 // The graph is empty and dropped in a different zone.
487 488 // Take the range of the first graph in the zone (if existing).
488 489 auto layout = zoneWidget->ui->dragDropContainer->layout();
489 490 if (layout->count() > 0) {
490 491 if (auto visualizationGraphWidget
491 492 = qobject_cast<VisualizationGraphWidget *>(layout->itemAt(0)->widget())) {
492 493 graphWidget->setGraphRange(visualizationGraphWidget->graphRange());
493 494 }
494 495 }
495 496 }
496 497
497 498 zoneWidget->ui->dragDropContainer->insertDragWidget(index, graphWidget);
498 499 }
499 500 }
500 501
501 502 void VisualizationZoneWidget::VisualizationZoneWidgetPrivate::dropVariables(
502 503 const QList<std::shared_ptr<Variable> > &variables, int index,
503 504 VisualizationZoneWidget *zoneWidget)
504 505 {
505 506 // Note: the AcceptMimeDataFunction (set on the drop container) ensure there is a single and
506 507 // compatible variable here
507 508 if (variables.count() > 1) {
508 509 qCWarning(LOG_VisualizationZoneWidget())
509 510 << tr("VisualizationZoneWidget::dropVariables, dropping multiple variables, operation "
510 511 "aborted.");
511 512 return;
512 513 }
513 514
514 515 zoneWidget->createGraph(variables, index);
515 516 }
General Comments 0
You need to be logged in to leave comments. Login now