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

Auto status change to "Under Review"

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