##// END OF EJS Templates
Drag of the time widget on a graph
trabillard -
r885:be7e4409e91c
parent child
Show More
@@ -1,39 +1,47
1 1 #ifndef SCIQLOP_TIMEWIDGET_H
2 2 #define SCIQLOP_TIMEWIDGET_H
3 3
4 4 #include <QWidget>
5 5
6 6 #include <Data/SqpRange.h>
7 7
8 #include <Common/spimpl.h>
9
8 10 namespace Ui {
9 11 class TimeWidget;
10 12 } // Ui
11 13
12 14 class TimeWidget : public QWidget {
13 15 Q_OBJECT
14 16
15 17 public:
16 18 explicit TimeWidget(QWidget *parent = 0);
17 19 virtual ~TimeWidget();
18 20
19 21 void setTimeRange(SqpRange time);
22 SqpRange timeRange() const;
20 23
21 24 signals:
22 25 /// Signal emitted when the time parameters has beed updated
23 26 void timeUpdated(SqpRange time);
24 27
25 28 public slots:
26 29 /// slot called when time parameters update has ben requested
27 30 void onTimeUpdateRequested();
28 31
29 32 protected:
30 33 void dragEnterEvent(QDragEnterEvent *event) override;
31 34 void dragLeaveEvent(QDragLeaveEvent *event) override;
32 35 void dropEvent(QDropEvent *event) override;
33 36
37 void mousePressEvent(QMouseEvent *event) override;
38 void mouseMoveEvent(QMouseEvent *event) override;
34 39
35 40 private:
36 41 Ui::TimeWidget *ui;
42
43 class TimeWidgetPrivate;
44 spimpl::unique_impl_ptr<TimeWidgetPrivate> impl;
37 45 };
38 46
39 47 #endif // SCIQLOP_ SQPSIDEPANE_H
@@ -1,52 +1,52
1 1 #ifndef SCIQLOP_VISUALIZATIONDRAGDROPCONTAINER_H
2 2 #define SCIQLOP_VISUALIZATIONDRAGDROPCONTAINER_H
3 3
4 4 #include <Common/spimpl.h>
5 5 #include <QLoggingCategory>
6 6 #include <QMimeData>
7 7 #include <QVBoxLayout>
8 8 #include <QWidget>
9 9
10 10 #include <functional>
11 11
12 12 Q_DECLARE_LOGGING_CATEGORY(LOG_VisualizationDragDropContainer)
13 13
14 14 class VisualizationDragWidget;
15 15
16 16 class VisualizationDragDropContainer : public QWidget {
17 17 Q_OBJECT
18 18
19 19 signals:
20 20 void dropOccuredInContainer(int dropIndex, const QMimeData *mimeData);
21 21 void dropOccuredOnWidget(VisualizationDragWidget *dragWidget, const QMimeData *mimeData);
22 22
23 23 public:
24 enum class DropBehavior { Inserted, Merged, InsertedAndMerged };
24 25 using AcceptMimeDataFunction = std::function<bool(const QMimeData *mimeData)>;
25 26
26 27 VisualizationDragDropContainer(QWidget *parent = nullptr);
27 28
28 29 void addDragWidget(VisualizationDragWidget *dragWidget);
29 30 void insertDragWidget(int index, VisualizationDragWidget *dragWidget);
30 31
31 void setAcceptedMimeTypes(const QStringList &mimeTypes);
32 void setMergeAllowedMimeTypes(const QStringList &mimeTypes);
32 void addAcceptedMimeType(const QString &mimeType, DropBehavior behavior);
33 33
34 34 int countDragWidget() const;
35 35
36 36 void setAcceptMimeDataFunction(AcceptMimeDataFunction fun);
37 37
38 38 protected:
39 39 void dragEnterEvent(QDragEnterEvent *event);
40 40 void dragLeaveEvent(QDragLeaveEvent *event);
41 41 void dragMoveEvent(QDragMoveEvent *event);
42 42 void dropEvent(QDropEvent *event);
43 43
44 44 private:
45 45 class VisualizationDragDropContainerPrivate;
46 46 spimpl::unique_impl_ptr<VisualizationDragDropContainerPrivate> impl;
47 47
48 48 private slots:
49 49 void startDrag(VisualizationDragWidget *dragWidget, const QPoint &dragPosition);
50 50 };
51 51
52 52 #endif // SCIQLOP_VISUALIZATIONDRAGDROPCONTAINER_H
@@ -1,96 +1,156
1 1 #include "TimeWidget/TimeWidget.h"
2 2 #include "ui_TimeWidget.h"
3 3
4 4 #include <Common/DateUtils.h>
5 5 #include <Common/MimeTypesDef.h>
6 6
7 #include <DragDropHelper.h>
7 8 #include <SqpApplication.h>
8 9 #include <Time/TimeController.h>
9 10
11 #include <QDrag>
10 12 #include <QDragEnterEvent>
11 13 #include <QDropEvent>
12 14 #include <QMimeData>
13 15
14 TimeWidget::TimeWidget(QWidget *parent) : QWidget{parent}, ui{new Ui::TimeWidget}
16
17 struct TimeWidget::TimeWidgetPrivate {
18
19 explicit TimeWidgetPrivate() {}
20
21 QPoint m_DragStartPosition;
22 };
23
24 TimeWidget::TimeWidget(QWidget *parent)
25 : QWidget{parent},
26 ui{new Ui::TimeWidget},
27 impl{spimpl::make_unique_impl<TimeWidgetPrivate>()}
15 28 {
16 29 ui->setupUi(this);
17 30
18 31 ui->applyToolButton->setIcon(sqpApp->style()->standardIcon(QStyle::SP_DialogApplyButton));
19 32
20 33 // Connection
21 34 connect(ui->startDateTimeEdit, &QDateTimeEdit::dateTimeChanged, this,
22 35 &TimeWidget::onTimeUpdateRequested);
23 36
24 37 connect(ui->endDateTimeEdit, &QDateTimeEdit::dateTimeChanged, this,
25 38 &TimeWidget::onTimeUpdateRequested);
26 39
27 40
28 41 connect(ui->applyToolButton, &QToolButton::clicked, &sqpApp->timeController(),
29 42 &TimeController::onTimeNotify);
30 43
31 44 // Initialisation
32 45 auto endDateTime = QDateTime::currentDateTimeUtc();
33 46 auto startDateTime = endDateTime.addSecs(-3600); // one hour before
34 47
35 48 ui->startDateTimeEdit->setDateTime(startDateTime);
36 49 ui->endDateTimeEdit->setDateTime(endDateTime);
37 50
38 51 auto dateTime = SqpRange{DateUtils::secondsSinceEpoch(startDateTime),
39 52 DateUtils::secondsSinceEpoch(endDateTime)};
40 53
41 54 sqpApp->timeController().onTimeToUpdate(dateTime);
42 55 }
43 56
44 57
45 58 TimeWidget::~TimeWidget()
46 59 {
47 60 delete ui;
48 61 }
49 62
50 63 void TimeWidget::setTimeRange(SqpRange time)
51 64 {
52 65 auto startDateTime = DateUtils::dateTime(time.m_TStart);
53 66 auto endDateTime = DateUtils::dateTime(time.m_TEnd);
54 67
55 68 ui->startDateTimeEdit->setDateTime(startDateTime);
56 69 ui->endDateTimeEdit->setDateTime(endDateTime);
57 70 }
58 71
59 void TimeWidget::onTimeUpdateRequested()
72 SqpRange TimeWidget::timeRange() const
60 73 {
61 auto dateTime = SqpRange{DateUtils::secondsSinceEpoch(ui->startDateTimeEdit->dateTime()),
62 DateUtils::secondsSinceEpoch(ui->endDateTimeEdit->dateTime())};
74 return SqpRange{DateUtils::secondsSinceEpoch(ui->startDateTimeEdit->dateTime()),
75 DateUtils::secondsSinceEpoch(ui->endDateTimeEdit->dateTime())};
76 }
63 77
78 void TimeWidget::onTimeUpdateRequested()
79 {
80 auto dateTime = timeRange();
64 81 emit timeUpdated(std::move(dateTime));
65 82 }
66 83
67 84 void TimeWidget::dragEnterEvent(QDragEnterEvent *event)
68 85 {
69 86 if (event->mimeData()->hasFormat(MIME_TYPE_TIME_RANGE)) {
70 87 event->acceptProposedAction();
71 88 setStyleSheet("QDateTimeEdit{background-color: #BBD5EE; border:2px solid #2A7FD4}");
72 89 }
73 90 else {
74 91 event->ignore();
75 92 }
76 93 }
77 94
78 95 void TimeWidget::dragLeaveEvent(QDragLeaveEvent *event)
79 96 {
80 97 setStyleSheet(QString());
81 98 }
82 99
83 100 void TimeWidget::dropEvent(QDropEvent *event)
84 101 {
85 102 if (event->mimeData()->hasFormat(MIME_TYPE_TIME_RANGE)) {
86 103 auto mimeData = event->mimeData()->data(MIME_TYPE_TIME_RANGE);
87 104 auto timeRange = TimeController::timeRangeForMimeData(mimeData);
88 105
89 106 setTimeRange(timeRange);
90 107 }
91 108 else {
92 109 event->ignore();
93 110 }
94 111
95 112 setStyleSheet(QString());
96 113 }
114
115
116 void TimeWidget::mousePressEvent(QMouseEvent *event)
117 {
118 if (event->button() == Qt::LeftButton) {
119 impl->m_DragStartPosition = event->pos();
120 }
121
122 QWidget::mousePressEvent(event);
123 }
124
125 void TimeWidget::mouseMoveEvent(QMouseEvent *event)
126 {
127 if (!(event->buttons() & Qt::LeftButton)) {
128 return;
129 }
130
131 if ((event->pos() - impl->m_DragStartPosition).manhattanLength()
132 < QApplication::startDragDistance()) {
133 return;
134 }
135
136 // Note: The management of the drag object is done by Qt
137 auto drag = new QDrag{this};
138
139 auto mimeData = new QMimeData;
140 auto timeData = TimeController::mimeDataForTimeRange(timeRange());
141 mimeData->setData(MIME_TYPE_TIME_RANGE, timeData);
142
143 drag->setMimeData(mimeData);
144
145 auto pixmap = QPixmap(size());
146 render(&pixmap);
147 drag->setPixmap(pixmap);
148 drag->setHotSpot(impl->m_DragStartPosition);
149
150 sqpApp->dragDropHelper().resetDragAndDrop();
151
152 // Note: The exec() is blocking on windows but not on linux and macOS
153 drag->exec(Qt::MoveAction | Qt::CopyAction);
154
155 QWidget::mouseMoveEvent(event);
156 }
@@ -1,392 +1,435
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 QStringList m_AcceptedMimeTypes;
21 QStringList m_MergeAllowedMimeTypes;
20 QHash<QString, VisualizationDragDropContainer::DropBehavior> m_AcceptedMimeTypes;
21
22 22 VisualizationDragDropContainer::AcceptMimeDataFunction m_AcceptMimeDataFun
23 23 = [](auto mimeData) { return true; };
24
24 25 int m_MinContainerHeight = 0;
25 26
26 27 explicit VisualizationDragDropContainerPrivate(QWidget *widget)
27 28 {
28 29 m_Layout = new QVBoxLayout(widget);
29 30 m_Layout->setContentsMargins(0, 0, 0, 0);
30 31 }
31 32
32 33 bool acceptMimeData(const QMimeData *data) const
33 34 {
34 for (const auto &type : m_AcceptedMimeTypes) {
35 for (const auto &type : m_AcceptedMimeTypes.keys()) {
35 36 if (data->hasFormat(type) && m_AcceptMimeDataFun(data)) {
36 37 return true;
37 38 }
38 39 }
39 40
40 41 return false;
41 42 }
42 43
43 bool allowMergeMimeData(const QMimeData *data) const
44 bool allowMergeForMimeData(const QMimeData *data) const
44 45 {
45 for (const auto &type : m_MergeAllowedMimeTypes) {
46 if (data->hasFormat(type)) {
46 bool result = false;
47 for (auto it = m_AcceptedMimeTypes.constBegin(); it != m_AcceptedMimeTypes.constEnd();
48 ++it) {
49
50 if (data->hasFormat(it.key())
51 && (it.value() == VisualizationDragDropContainer::DropBehavior::Merged
52 || it.value()
53 == VisualizationDragDropContainer::DropBehavior::InsertedAndMerged)) {
54 result = true;
55 }
56 else if (data->hasFormat(it.key())
57 && it.value() == VisualizationDragDropContainer::DropBehavior::Inserted) {
58 // Merge is forbidden if the mime data contain an acceptable type which cannot be
59 // merged
60 result = false;
61 break;
62 }
63 }
64
65 return result;
66 }
67
68 bool allowInsertForMimeData(const QMimeData *data) const
69 {
70 for (auto it = m_AcceptedMimeTypes.constBegin(); it != m_AcceptedMimeTypes.constEnd();
71 ++it) {
72 if (data->hasFormat(it.key())
73 && (it.value() == VisualizationDragDropContainer::DropBehavior::Inserted
74 || it.value()
75 == VisualizationDragDropContainer::DropBehavior::InsertedAndMerged)) {
47 76 return true;
48 77 }
49 78 }
50 79
51 80 return false;
52 81 }
53 82
54 83 bool hasPlaceHolder() const
55 84 {
56 85 return sqpApp->dragDropHelper().placeHolder().parentWidget() == m_Layout->parentWidget();
57 86 }
58 87
59 88 VisualizationDragWidget *getChildDragWidgetAt(const QWidget *parent, const QPoint &pos) const
60 89 {
61 90 VisualizationDragWidget *dragWidget = nullptr;
62 91
63 92 for (auto child : parent->children()) {
64 93 auto widget = qobject_cast<VisualizationDragWidget *>(child);
65 94 if (widget && widget->isVisible()) {
66 95 if (widget->frameGeometry().contains(pos)) {
67 96 dragWidget = widget;
68 97 break;
69 98 }
70 99 }
71 100 }
72 101
73 102 return dragWidget;
74 103 }
75 104
76 105 bool cursorIsInContainer(QWidget *container) const
77 106 {
78 107 return container->isAncestorOf(sqpApp->widgetAt(QCursor::pos()));
79 108 }
80 109
81 110 int countDragWidget(const QWidget *parent) const
82 111 {
83 112 auto nbGraph = 0;
84 113 for (auto child : parent->children()) {
85 114 if (qobject_cast<VisualizationDragWidget *>(child)) {
86 115 nbGraph += 1;
87 116 }
88 117 }
89 118
90 119 return nbGraph;
91 120 }
92 121
93 void findPlaceHolderPosition(const QPoint &pos, bool canMerge,
122 void findPlaceHolderPosition(const QPoint &pos, bool canInsert, bool canMerge,
94 123 const VisualizationDragDropContainer *container);
95 124 };
96 125
97 126 VisualizationDragDropContainer::VisualizationDragDropContainer(QWidget *parent)
98 127 : QWidget{parent},
99 128 impl{spimpl::make_unique_impl<VisualizationDragDropContainerPrivate>(this)}
100 129 {
101 130 setAcceptDrops(true);
102 131 }
103 132
104 133 void VisualizationDragDropContainer::addDragWidget(VisualizationDragWidget *dragWidget)
105 134 {
106 135 impl->m_Layout->addWidget(dragWidget);
107 136 disconnect(dragWidget, &VisualizationDragWidget::dragDetected, nullptr, nullptr);
108 137 connect(dragWidget, &VisualizationDragWidget::dragDetected, this,
109 138 &VisualizationDragDropContainer::startDrag);
110 139 }
111 140
112 141 void VisualizationDragDropContainer::insertDragWidget(int index,
113 142 VisualizationDragWidget *dragWidget)
114 143 {
115 144 impl->m_Layout->insertWidget(index, dragWidget);
116 145 disconnect(dragWidget, &VisualizationDragWidget::dragDetected, nullptr, nullptr);
117 146 connect(dragWidget, &VisualizationDragWidget::dragDetected, this,
118 147 &VisualizationDragDropContainer::startDrag);
119 148 }
120 149
121 void VisualizationDragDropContainer::setAcceptedMimeTypes(const QStringList &mimeTypes)
122 {
123 impl->m_AcceptedMimeTypes = mimeTypes;
124 }
125
126 void VisualizationDragDropContainer::setMergeAllowedMimeTypes(const QStringList &mimeTypes)
150 void VisualizationDragDropContainer::addAcceptedMimeType(
151 const QString &mimeType, VisualizationDragDropContainer::DropBehavior behavior)
127 152 {
128 impl->m_MergeAllowedMimeTypes = mimeTypes;
153 impl->m_AcceptedMimeTypes[mimeType] = behavior;
129 154 }
130 155
131 156 int VisualizationDragDropContainer::countDragWidget() const
132 157 {
133 158 return impl->countDragWidget(this);
134 159 }
135 160
136 161 void VisualizationDragDropContainer::setAcceptMimeDataFunction(
137 162 VisualizationDragDropContainer::AcceptMimeDataFunction fun)
138 163 {
139 164 impl->m_AcceptMimeDataFun = fun;
140 165 }
141 166
142 167 void VisualizationDragDropContainer::startDrag(VisualizationDragWidget *dragWidget,
143 168 const QPoint &dragPosition)
144 169 {
145 170 auto &helper = sqpApp->dragDropHelper();
146 171 helper.resetDragAndDrop();
147 172
148 173 // Note: The management of the drag object is done by Qt
149 174 auto drag = new QDrag{dragWidget};
150 175 drag->setHotSpot(dragPosition);
151 176
152 177 auto mimeData = dragWidget->mimeData();
153 178 drag->setMimeData(mimeData);
154 179
155 180 auto pixmap = QPixmap(dragWidget->size());
156 181 dragWidget->render(&pixmap);
157 182 drag->setPixmap(pixmap);
158 183
159 184 auto image = pixmap.toImage();
160 185 mimeData->setImageData(image);
161 186 mimeData->setUrls({helper.imageTemporaryUrl(image)});
162 187
163 188 if (impl->m_Layout->indexOf(dragWidget) >= 0) {
164 189 helper.setCurrentDragWidget(dragWidget);
165 190
166 191 if (impl->cursorIsInContainer(this)) {
167 192 auto dragWidgetIndex = impl->m_Layout->indexOf(dragWidget);
168 193 helper.insertPlaceHolder(impl->m_Layout, dragWidgetIndex);
169 194 dragWidget->setVisible(false);
170 195 }
171 196 else {
172 197 // The drag starts directly outside the drop zone
173 198 // do not add the placeHolder
174 199 }
175 200
176 201 // Note: The exec() is blocking on windows but not on linux and macOS
177 202 drag->exec(Qt::MoveAction | Qt::CopyAction);
178 203 }
179 204 else {
180 205 qCWarning(LOG_VisualizationDragDropContainer())
181 206 << tr("VisualizationDragDropContainer::startDrag, drag aborted, the specified "
182 207 "VisualizationDragWidget is not found in this container.");
183 208 }
184 209 }
185 210
186 211 void VisualizationDragDropContainer::dragEnterEvent(QDragEnterEvent *event)
187 212 {
188 213 if (impl->acceptMimeData(event->mimeData())) {
189 214 event->acceptProposedAction();
190 215
191 216 auto &helper = sqpApp->dragDropHelper();
192 217
193 218 if (!impl->hasPlaceHolder()) {
194 219 auto dragWidget = helper.getCurrentDragWidget();
195 220
196 221 if (dragWidget) {
197 222 // If the drag&drop is internal to the visualization, entering the container hide
198 223 // the dragWidget which was made visible by the dragLeaveEvent
199 224 auto parentWidget
200 225 = qobject_cast<VisualizationDragDropContainer *>(dragWidget->parentWidget());
201 226 if (parentWidget) {
202 227 dragWidget->setVisible(false);
203 228 }
204 229 }
205 230
206 auto canMerge = impl->allowMergeMimeData(event->mimeData());
207 impl->findPlaceHolderPosition(event->pos(), canMerge, this);
231 auto canMerge = impl->allowMergeForMimeData(event->mimeData());
232 auto canInsert = impl->allowInsertForMimeData(event->mimeData());
233 impl->findPlaceHolderPosition(event->pos(), canInsert, canMerge, this);
208 234 }
209 235 else {
210 236 // do nothing
211 237 }
212 238 }
213 239 else {
214 240 event->ignore();
215 241 }
216 242
217 243 QWidget::dragEnterEvent(event);
218 244 }
219 245
220 246 void VisualizationDragDropContainer::dragLeaveEvent(QDragLeaveEvent *event)
221 247 {
222 248 Q_UNUSED(event);
223 249
224 250 auto &helper = sqpApp->dragDropHelper();
225 251
226 252 if (!impl->cursorIsInContainer(this)) {
227 253 helper.removePlaceHolder();
228 254 helper.setHightlightedDragWidget(nullptr);
229 255 impl->m_MinContainerHeight = 0;
230 256
231 257 auto dragWidget = helper.getCurrentDragWidget();
232 258 if (dragWidget) {
233 259 // dragWidget has a value only if the drag is started from the visualization
234 260 // In that case, shows the drag widget at its original place
235 261 // So the drag widget doesn't stay hidden if the drop occurs outside the visualization
236 262 // drop zone (It is not possible to catch a drop event outside of the application)
237 263
238 264 if (dragWidget) {
239 265 dragWidget->setVisible(true);
240 266 }
241 267 }
242 268 }
243 269 else {
244 270 // Leave event probably received for a child widget.
245 271 // Do nothing.
246 272 // Note: The DragLeave event, doesn't have any mean to determine who sent it.
247 273 }
248 274
249 275 QWidget::dragLeaveEvent(event);
250 276 }
251 277
252 278 void VisualizationDragDropContainer::dragMoveEvent(QDragMoveEvent *event)
253 279 {
254 280 if (impl->acceptMimeData(event->mimeData())) {
255 auto canMerge = impl->allowMergeMimeData(event->mimeData());
256 impl->findPlaceHolderPosition(event->pos(), canMerge, this);
281 auto canMerge = impl->allowMergeForMimeData(event->mimeData());
282 auto canInsert = impl->allowInsertForMimeData(event->mimeData());
283 impl->findPlaceHolderPosition(event->pos(), canInsert, canMerge, this);
257 284 }
258 285 else {
259 286 event->ignore();
260 287 }
261 288
262 289 QWidget::dragMoveEvent(event);
263 290 }
264 291
265 292 void VisualizationDragDropContainer::dropEvent(QDropEvent *event)
266 293 {
267 294 auto &helper = sqpApp->dragDropHelper();
268 295
269 296 if (impl->acceptMimeData(event->mimeData())) {
270 297 auto dragWidget = helper.getCurrentDragWidget();
271 298 if (impl->hasPlaceHolder()) {
272 299 // drop where the placeHolder is located
273 300
274 auto droppedIndex = impl->m_Layout->indexOf(&helper.placeHolder());
301 auto canInsert = impl->allowInsertForMimeData(event->mimeData());
302 if (canInsert) {
303 auto droppedIndex = impl->m_Layout->indexOf(&helper.placeHolder());
275 304
276 if (dragWidget) {
277 auto dragWidgetIndex = impl->m_Layout->indexOf(dragWidget);
278 if (dragWidgetIndex >= 0 && dragWidgetIndex < droppedIndex) {
279 // Correction of the index if the drop occurs in the same container
280 // and if the drag is started from the visualization (in that case, the
281 // dragWidget is hidden)
282 droppedIndex -= 1;
283 }
305 if (dragWidget) {
306 auto dragWidgetIndex = impl->m_Layout->indexOf(dragWidget);
307 if (dragWidgetIndex >= 0 && dragWidgetIndex < droppedIndex) {
308 // Correction of the index if the drop occurs in the same container
309 // and if the drag is started from the visualization (in that case, the
310 // dragWidget is hidden)
311 droppedIndex -= 1;
312 }
284 313
285 dragWidget->setVisible(true);
286 }
314 dragWidget->setVisible(true);
315 }
287 316
288 event->acceptProposedAction();
317 event->acceptProposedAction();
289 318
290 helper.removePlaceHolder();
319 helper.removePlaceHolder();
291 320
292 emit dropOccuredInContainer(droppedIndex, event->mimeData());
321 emit dropOccuredInContainer(droppedIndex, event->mimeData());
322 }
323 else {
324 qCWarning(LOG_VisualizationDragDropContainer()) << tr(
325 "VisualizationDragDropContainer::dropEvent, dropping on the placeHolder, but "
326 "the insertion is forbidden.");
327 Q_ASSERT(false);
328 }
293 329 }
294 330 else if (helper.getHightlightedDragWidget()) {
295 331 // drop on the highlighted widget
296 332
297 auto canMerge = impl->allowMergeMimeData(event->mimeData());
333 auto canMerge = impl->allowMergeForMimeData(event->mimeData());
298 334 if (canMerge) {
299 335 event->acceptProposedAction();
300 336 emit dropOccuredOnWidget(helper.getHightlightedDragWidget(), event->mimeData());
301 337 }
302 338 else {
303 339 qCWarning(LOG_VisualizationDragDropContainer())
304 340 << tr("VisualizationDragDropContainer::dropEvent, dropping on a widget, but "
305 341 "the merge is forbidden.");
306 342 Q_ASSERT(false);
307 343 }
308 344 }
309 345 }
310 346 else {
311 347 event->ignore();
312 348 }
313 349
314 350 sqpApp->dragDropHelper().setHightlightedDragWidget(nullptr);
315 351 impl->m_MinContainerHeight = 0;
316 352
317 353 QWidget::dropEvent(event);
318 354 }
319 355
320 356
321 357 void VisualizationDragDropContainer::VisualizationDragDropContainerPrivate::findPlaceHolderPosition(
322 const QPoint &pos, bool canMerge, const VisualizationDragDropContainer *container)
358 const QPoint &pos, bool canInsert, bool canMerge,
359 const VisualizationDragDropContainer *container)
323 360 {
324 361 auto &helper = sqpApp->dragDropHelper();
325 362
326 363 auto dragWidgetHovered = getChildDragWidgetAt(container, pos);
327 364 if (dragWidgetHovered) {
328 365 auto nbDragWidget = countDragWidget(container);
329 366 if (nbDragWidget > 0) {
330 367
331 368 if (m_MinContainerHeight == 0) {
332 369 m_MinContainerHeight = container->size().height();
333 370 }
334 371
335 372 m_MinContainerHeight = qMin(m_MinContainerHeight, container->size().height());
336 373 auto graphHeight = qMax(m_MinContainerHeight / nbDragWidget, GRAPH_MINIMUM_HEIGHT);
337 374
338 375 auto posY = pos.y();
339 376 auto dropIndex = floor(posY / graphHeight);
340 377 auto zoneSize = qMin(graphHeight / 4.0, 75.0);
341 378
342 379
343 380 auto isOnTop = posY < dropIndex * graphHeight + zoneSize;
344 381 auto isOnBottom = posY > (dropIndex + 1) * graphHeight - zoneSize;
345 382
346 383 auto placeHolderIndex = m_Layout->indexOf(&(helper.placeHolder()));
347 384
348 if (isOnTop || isOnBottom || !canMerge) {
385 if (canInsert && (isOnTop || isOnBottom || !canMerge)) {
349 386 if (isOnBottom) {
350 387 dropIndex += 1;
351 388 }
352 389
353 390 if (helper.getCurrentDragWidget()) {
354 391 auto dragWidgetIndex = m_Layout->indexOf(helper.getCurrentDragWidget());
355 392 if (dragWidgetIndex >= 0 && dragWidgetIndex <= dropIndex) {
356 393 // Correction of the index if the drop occurs in the same container
357 394 // and if the drag is started from the visualization (in that case, the
358 395 // dragWidget is hidden)
359 396 dropIndex += 1;
360 397 }
361 398 }
362 399
363 400 if (dropIndex != placeHolderIndex) {
364 401 helper.insertPlaceHolder(m_Layout, dropIndex);
365 402 }
366 403
367 404 helper.setHightlightedDragWidget(nullptr);
368 405 }
369 406 else if (canMerge) {
370 407 // drop on the middle -> merge
371 408 if (hasPlaceHolder()) {
372 409 helper.removePlaceHolder();
373 410 }
374 411
375 412 helper.setHightlightedDragWidget(dragWidgetHovered);
376 413 }
414 else {
415 qCWarning(LOG_VisualizationDragDropContainer())
416 << tr("VisualizationDragDropContainer::findPlaceHolderPosition, no valid drop "
417 "action.");
418 Q_ASSERT(false);
419 }
377 420 }
378 421 else {
379 qCWarning(LOG_VisualizationDragDropContainer())
380 << tr("VisualizationDragDropContainer::dragMoveEvent, no widget found in the "
381 "container");
422 qCWarning(LOG_VisualizationDragDropContainer()) << tr(
423 "VisualizationDragDropContainer::findPlaceHolderPosition, no widget found in the "
424 "container");
382 425 }
383 426 }
384 else if (!hasPlaceHolder()) {
427 else if (!hasPlaceHolder() && canInsert) {
385 428 // Drop on an empty container, just add the placeHolder at the top
386 429 helper.insertPlaceHolder(m_Layout, 0);
387 430 }
388 431 else {
389 432 // No hovered drag widget, the mouse is probably hover the placeHolder
390 433 // Do nothing
391 434 }
392 435 }
@@ -1,300 +1,307
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->setAcceptedMimeTypes(
72 {MIME_TYPE_GRAPH, MIME_TYPE_ZONE, MIME_TYPE_VARIABLE_LIST});
73 connect(ui->dragDropContainer, &VisualizationDragDropContainer::dropOccuredInContainer, this,
74 &VisualizationTabWidget::dropMimeData);
71 ui->dragDropContainer->addAcceptedMimeType(
72 MIME_TYPE_GRAPH, VisualizationDragDropContainer::DropBehavior::Inserted);
73 ui->dragDropContainer->addAcceptedMimeType(
74 MIME_TYPE_ZONE, VisualizationDragDropContainer::DropBehavior::Inserted);
75 ui->dragDropContainer->addAcceptedMimeType(
76 MIME_TYPE_VARIABLE_LIST, VisualizationDragDropContainer::DropBehavior::Inserted);
77
75 78 ui->dragDropContainer->setAcceptMimeDataFunction([this](auto mimeData) {
76 79 return sqpApp->dragDropHelper().checkMimeDataForVisualization(mimeData,
77 80 ui->dragDropContainer);
78 81 });
82
83 connect(ui->dragDropContainer, &VisualizationDragDropContainer::dropOccuredInContainer, this,
84 &VisualizationTabWidget::dropMimeData);
85
79 86 sqpApp->dragDropHelper().addDragDropScrollArea(ui->scrollArea);
80 87
81 88 // Widget is deleted when closed
82 89 setAttribute(Qt::WA_DeleteOnClose);
83 90 }
84 91
85 92 VisualizationTabWidget::~VisualizationTabWidget()
86 93 {
87 94 sqpApp->dragDropHelper().removeDragDropScrollArea(ui->scrollArea);
88 95 delete ui;
89 96 }
90 97
91 98 void VisualizationTabWidget::addZone(VisualizationZoneWidget *zoneWidget)
92 99 {
93 100 ui->dragDropContainer->addDragWidget(zoneWidget);
94 101 }
95 102
96 103 void VisualizationTabWidget::insertZone(int index, VisualizationZoneWidget *zoneWidget)
97 104 {
98 105 ui->dragDropContainer->insertDragWidget(index, zoneWidget);
99 106 }
100 107
101 108 VisualizationZoneWidget *VisualizationTabWidget::createZone(std::shared_ptr<Variable> variable)
102 109 {
103 110 return createZone({variable}, -1);
104 111 }
105 112
106 113 VisualizationZoneWidget *
107 114 VisualizationTabWidget::createZone(const QList<std::shared_ptr<Variable> > &variables, int index)
108 115 {
109 116 auto zoneWidget = createEmptyZone(index);
110 117
111 118 // Creates a new graph into the zone
112 119 zoneWidget->createGraph(variables, index);
113 120
114 121 return zoneWidget;
115 122 }
116 123
117 124 VisualizationZoneWidget *VisualizationTabWidget::createEmptyZone(int index)
118 125 {
119 126 auto zoneWidget
120 127 = new VisualizationZoneWidget{defaultZoneName(*ui->dragDropContainer->layout()), this};
121 128 this->insertZone(index, zoneWidget);
122 129
123 130 return zoneWidget;
124 131 }
125 132
126 133 void VisualizationTabWidget::accept(IVisualizationWidgetVisitor *visitor)
127 134 {
128 135 if (visitor) {
129 136 visitor->visitEnter(this);
130 137
131 138 // Apply visitor to zone children: widgets different from zones are not visited (no action)
132 139 processZones(tabLayout(), [visitor](VisualizationZoneWidget &zoneWidget) {
133 140 zoneWidget.accept(visitor);
134 141 });
135 142
136 143 visitor->visitLeave(this);
137 144 }
138 145 else {
139 146 qCCritical(LOG_VisualizationTabWidget()) << tr("Can't visit widget : the visitor is null");
140 147 }
141 148 }
142 149
143 150 bool VisualizationTabWidget::canDrop(const Variable &variable) const
144 151 {
145 152 // A tab can always accomodate a variable
146 153 Q_UNUSED(variable);
147 154 return true;
148 155 }
149 156
150 157 bool VisualizationTabWidget::contains(const Variable &variable) const
151 158 {
152 159 Q_UNUSED(variable);
153 160 return false;
154 161 }
155 162
156 163 QString VisualizationTabWidget::name() const
157 164 {
158 165 return impl->m_Name;
159 166 }
160 167
161 168 void VisualizationTabWidget::closeEvent(QCloseEvent *event)
162 169 {
163 170 // Closes zones in the tab
164 171 processZones(tabLayout(), [](VisualizationZoneWidget &zoneWidget) { zoneWidget.close(); });
165 172
166 173 QWidget::closeEvent(event);
167 174 }
168 175
169 176 QLayout &VisualizationTabWidget::tabLayout() const noexcept
170 177 {
171 178 return *ui->dragDropContainer->layout();
172 179 }
173 180
174 181 void VisualizationTabWidget::dropMimeData(int index, const QMimeData *mimeData)
175 182 {
176 183 if (mimeData->hasFormat(MIME_TYPE_GRAPH)) {
177 184 impl->dropGraph(index, this);
178 185 }
179 186 else if (mimeData->hasFormat(MIME_TYPE_ZONE)) {
180 187 impl->dropZone(index, this);
181 188 }
182 189 else if (mimeData->hasFormat(MIME_TYPE_VARIABLE_LIST)) {
183 190 auto variables = sqpApp->variableController().variablesForMimeData(
184 191 mimeData->data(MIME_TYPE_VARIABLE_LIST));
185 192 impl->dropVariables(variables, index, this);
186 193 }
187 194 else {
188 195 qCWarning(LOG_VisualizationZoneWidget())
189 196 << tr("VisualizationTabWidget::dropMimeData, unknown MIME data received.");
190 197 }
191 198 }
192 199
193 200 void VisualizationTabWidget::VisualizationTabWidgetPrivate::dropGraph(
194 201 int index, VisualizationTabWidget *tabWidget)
195 202 {
196 203 auto &helper = sqpApp->dragDropHelper();
197 204
198 205 auto graphWidget = qobject_cast<VisualizationGraphWidget *>(helper.getCurrentDragWidget());
199 206 if (!graphWidget) {
200 207 qCWarning(LOG_VisualizationZoneWidget())
201 208 << tr("VisualizationTabWidget::dropGraph, drop aborted, the dropped graph is not "
202 209 "found or invalid.");
203 210 Q_ASSERT(false);
204 211 return;
205 212 }
206 213
207 214 auto parentDragDropContainer
208 215 = qobject_cast<VisualizationDragDropContainer *>(graphWidget->parentWidget());
209 216 if (!parentDragDropContainer) {
210 217 qCWarning(LOG_VisualizationZoneWidget())
211 218 << tr("VisualizationTabWidget::dropGraph, drop aborted, the parent container of "
212 219 "the dropped graph is not found.");
213 220 Q_ASSERT(false);
214 221 return;
215 222 }
216 223
217 224 auto nbGraph = parentDragDropContainer->countDragWidget();
218 225
219 226 const auto &variables = graphWidget->variables();
220 227
221 228 if (!variables.isEmpty()) {
222 229 // Abort the requests for the variables (if any)
223 230 // Commented, because it's not sure if it's needed or not
224 231 // for (const auto& var : variables)
225 232 //{
226 233 // sqpApp->variableController().onAbortProgressRequested(var);
227 234 //}
228 235
229 236 if (nbGraph == 1) {
230 237 // This is the only graph in the previous zone, close the zone
231 238 graphWidget->parentZoneWidget()->close();
232 239 }
233 240 else {
234 241 // Close the graph
235 242 graphWidget->close();
236 243 }
237 244
238 245 tabWidget->createZone(variables, index);
239 246 }
240 247 else {
241 248 // The graph is empty, create an empty zone and move the graph inside
242 249
243 250 auto parentZoneWidget = graphWidget->parentZoneWidget();
244 251
245 252 parentDragDropContainer->layout()->removeWidget(graphWidget);
246 253
247 254 auto zoneWidget = tabWidget->createEmptyZone(index);
248 255 zoneWidget->addGraph(graphWidget);
249 256
250 257 // Close the old zone if it was the only graph inside
251 258 if (nbGraph == 1) {
252 259 parentZoneWidget->close();
253 260 }
254 261 }
255 262 }
256 263
257 264 void VisualizationTabWidget::VisualizationTabWidgetPrivate::dropZone(
258 265 int index, VisualizationTabWidget *tabWidget)
259 266 {
260 267 auto &helper = sqpApp->dragDropHelper();
261 268
262 269 auto zoneWidget = qobject_cast<VisualizationZoneWidget *>(helper.getCurrentDragWidget());
263 270 if (!zoneWidget) {
264 271 qCWarning(LOG_VisualizationZoneWidget())
265 272 << tr("VisualizationTabWidget::dropZone, drop aborted, the dropped zone is not "
266 273 "found or invalid.");
267 274 Q_ASSERT(false);
268 275 return;
269 276 }
270 277
271 278 auto parentDragDropContainer
272 279 = qobject_cast<VisualizationDragDropContainer *>(zoneWidget->parentWidget());
273 280 if (!parentDragDropContainer) {
274 281 qCWarning(LOG_VisualizationZoneWidget())
275 282 << tr("VisualizationTabWidget::dropZone, drop aborted, the parent container of "
276 283 "the dropped zone is not found.");
277 284 Q_ASSERT(false);
278 285 return;
279 286 }
280 287
281 288 // Simple move of the zone, no variable operation associated
282 289 parentDragDropContainer->layout()->removeWidget(zoneWidget);
283 290 tabWidget->ui->dragDropContainer->insertDragWidget(index, zoneWidget);
284 291 }
285 292
286 293 void VisualizationTabWidget::VisualizationTabWidgetPrivate::dropVariables(
287 294 const QList<std::shared_ptr<Variable> > &variables, int index,
288 295 VisualizationTabWidget *tabWidget)
289 296 {
290 297 // Note: the AcceptMimeDataFunction (set on the drop container) ensure there is a single and
291 298 // compatible variable here
292 299 if (variables.count() > 1) {
293 300 qCWarning(LOG_VisualizationZoneWidget())
294 301 << tr("VisualizationTabWidget::dropVariables, dropping multiple variables, operation "
295 302 "aborted.");
296 303 return;
297 304 }
298 305
299 306 tabWidget->createZone(variables, index);
300 307 }
@@ -1,512 +1,515
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->setAcceptedMimeTypes({MIME_TYPE_GRAPH, MIME_TYPE_VARIABLE_LIST});
105 ui->dragDropContainer->setMergeAllowedMimeTypes({MIME_TYPE_VARIABLE_LIST});
104 ui->dragDropContainer->addAcceptedMimeType(
105 MIME_TYPE_GRAPH, VisualizationDragDropContainer::DropBehavior::Inserted);
106 ui->dragDropContainer->addAcceptedMimeType(
107 MIME_TYPE_VARIABLE_LIST, VisualizationDragDropContainer::DropBehavior::InsertedAndMerged);
108 ui->dragDropContainer->addAcceptedMimeType(
109 MIME_TYPE_TIME_RANGE, VisualizationDragDropContainer::DropBehavior::Merged);
106 110 ui->dragDropContainer->setAcceptMimeDataFunction([this](auto mimeData) {
107 111 return sqpApp->dragDropHelper().checkMimeDataForVisualization(mimeData,
108 112 ui->dragDropContainer);
109 113 });
110 114
111 115 connect(ui->dragDropContainer, &VisualizationDragDropContainer::dropOccuredInContainer, this,
112 116 &VisualizationZoneWidget::dropMimeData);
113 117 connect(ui->dragDropContainer, &VisualizationDragDropContainer::dropOccuredOnWidget, this,
114 118 &VisualizationZoneWidget::dropMimeDataOnGraph);
115 119
116 120 // 'Close' options : widget is deleted when closed
117 121 setAttribute(Qt::WA_DeleteOnClose);
118 122 connect(ui->closeButton, &QToolButton::clicked, this, &VisualizationZoneWidget::close);
119 123 ui->closeButton->setIcon(sqpApp->style()->standardIcon(QStyle::SP_TitleBarCloseButton));
120 124
121 125 // Synchronisation id
122 126 QMetaObject::invokeMethod(&sqpApp->variableController(), "onAddSynchronizationGroupId",
123 127 Qt::QueuedConnection, Q_ARG(QUuid, impl->m_SynchronisationGroupId));
124 128 }
125 129
126 130 VisualizationZoneWidget::~VisualizationZoneWidget()
127 131 {
128 132 delete ui;
129 133 }
130 134
131 135 void VisualizationZoneWidget::addGraph(VisualizationGraphWidget *graphWidget)
132 136 {
133 137 // Synchronize new graph with others in the zone
134 138 impl->m_Synchronizer->addGraph(*graphWidget);
135 139
136 140 ui->dragDropContainer->addDragWidget(graphWidget);
137 141 }
138 142
139 143 void VisualizationZoneWidget::insertGraph(int index, VisualizationGraphWidget *graphWidget)
140 144 {
141 145 // Synchronize new graph with others in the zone
142 146 impl->m_Synchronizer->addGraph(*graphWidget);
143 147
144 148 ui->dragDropContainer->insertDragWidget(index, graphWidget);
145 149 }
146 150
147 151 VisualizationGraphWidget *VisualizationZoneWidget::createGraph(std::shared_ptr<Variable> variable)
148 152 {
149 153 return createGraph(variable, -1);
150 154 }
151 155
152 156 VisualizationGraphWidget *VisualizationZoneWidget::createGraph(std::shared_ptr<Variable> variable,
153 157 int index)
154 158 {
155 159 auto graphWidget
156 160 = new VisualizationGraphWidget{defaultGraphName(*ui->dragDropContainer->layout()), this};
157 161
158 162
159 163 // Set graph properties
160 164 graphWidget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::MinimumExpanding);
161 165 graphWidget->setMinimumHeight(GRAPH_MINIMUM_HEIGHT);
162 166
163 167
164 168 // Lambda to synchronize zone widget
165 169 auto synchronizeZoneWidget = [this, graphWidget](const SqpRange &graphRange,
166 170 const SqpRange &oldGraphRange) {
167 171
168 172 auto zoomType = VariableController::getZoomType(graphRange, oldGraphRange);
169 173 auto frameLayout = ui->dragDropContainer->layout();
170 174 for (auto i = 0; i < frameLayout->count(); ++i) {
171 175 auto graphChild
172 176 = dynamic_cast<VisualizationGraphWidget *>(frameLayout->itemAt(i)->widget());
173 177 if (graphChild && (graphChild != graphWidget)) {
174 178
175 179 auto graphChildRange = graphChild->graphRange();
176 180 switch (zoomType) {
177 181 case AcquisitionZoomType::ZoomIn: {
178 182 auto deltaLeft = graphRange.m_TStart - oldGraphRange.m_TStart;
179 183 auto deltaRight = oldGraphRange.m_TEnd - graphRange.m_TEnd;
180 184 graphChildRange.m_TStart += deltaLeft;
181 185 graphChildRange.m_TEnd -= deltaRight;
182 186 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: ZoomIn");
183 187 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: deltaLeft")
184 188 << deltaLeft;
185 189 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: deltaRight")
186 190 << deltaRight;
187 191 qCDebug(LOG_VisualizationZoneWidget())
188 192 << tr("TORM: dt") << graphRange.m_TEnd - graphRange.m_TStart;
189 193
190 194 break;
191 195 }
192 196
193 197 case AcquisitionZoomType::ZoomOut: {
194 198 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: ZoomOut");
195 199 auto deltaLeft = oldGraphRange.m_TStart - graphRange.m_TStart;
196 200 auto deltaRight = graphRange.m_TEnd - oldGraphRange.m_TEnd;
197 201 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: deltaLeft")
198 202 << deltaLeft;
199 203 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: deltaRight")
200 204 << deltaRight;
201 205 qCDebug(LOG_VisualizationZoneWidget())
202 206 << tr("TORM: dt") << graphRange.m_TEnd - graphRange.m_TStart;
203 207 graphChildRange.m_TStart -= deltaLeft;
204 208 graphChildRange.m_TEnd += deltaRight;
205 209 break;
206 210 }
207 211 case AcquisitionZoomType::PanRight: {
208 212 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: PanRight");
209 213 auto deltaLeft = graphRange.m_TStart - oldGraphRange.m_TStart;
210 214 auto deltaRight = graphRange.m_TEnd - oldGraphRange.m_TEnd;
211 215 graphChildRange.m_TStart += deltaLeft;
212 216 graphChildRange.m_TEnd += deltaRight;
213 217 qCDebug(LOG_VisualizationZoneWidget())
214 218 << tr("TORM: dt") << graphRange.m_TEnd - graphRange.m_TStart;
215 219 break;
216 220 }
217 221 case AcquisitionZoomType::PanLeft: {
218 222 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: PanLeft");
219 223 auto deltaLeft = oldGraphRange.m_TStart - graphRange.m_TStart;
220 224 auto deltaRight = oldGraphRange.m_TEnd - graphRange.m_TEnd;
221 225 graphChildRange.m_TStart -= deltaLeft;
222 226 graphChildRange.m_TEnd -= deltaRight;
223 227 break;
224 228 }
225 229 case AcquisitionZoomType::Unknown: {
226 230 qCDebug(LOG_VisualizationZoneWidget())
227 231 << tr("Impossible to synchronize: zoom type unknown");
228 232 break;
229 233 }
230 234 default:
231 235 qCCritical(LOG_VisualizationZoneWidget())
232 236 << tr("Impossible to synchronize: zoom type not take into account");
233 237 // No action
234 238 break;
235 239 }
236 240 graphChild->enableAcquisition(false);
237 241 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: Range before: ")
238 242 << graphChild->graphRange();
239 243 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: Range after : ")
240 244 << graphChildRange;
241 245 qCDebug(LOG_VisualizationZoneWidget())
242 246 << tr("TORM: child dt") << graphChildRange.m_TEnd - graphChildRange.m_TStart;
243 247 graphChild->setGraphRange(graphChildRange);
244 248 graphChild->enableAcquisition(true);
245 249 }
246 250 }
247 251 };
248 252
249 253 // connection for synchronization
250 254 connect(graphWidget, &VisualizationGraphWidget::synchronize, synchronizeZoneWidget);
251 255 connect(graphWidget, &VisualizationGraphWidget::variableAdded, this,
252 256 &VisualizationZoneWidget::onVariableAdded);
253 257 connect(graphWidget, &VisualizationGraphWidget::variableAboutToBeRemoved, this,
254 258 &VisualizationZoneWidget::onVariableAboutToBeRemoved);
255 259
256 260 auto range = SqpRange{};
257 261 if (auto firstGraph = impl->firstGraph(this)) {
258 262 // Case of a new graph in a existant zone
259 263 range = firstGraph->graphRange();
260 264 }
261 265 else {
262 266 // Case of a new graph as the first of the zone
263 267 range = variable->range();
264 268 }
265 269
266 270 this->insertGraph(index, graphWidget);
267 271
268 272 graphWidget->addVariable(variable, range);
269 273
270 274 // get y using variable range
271 275 if (auto dataSeries = variable->dataSeries()) {
272 276 dataSeries->lockRead();
273 277 auto valuesBounds
274 278 = dataSeries->valuesBounds(variable->range().m_TStart, variable->range().m_TEnd);
275 279 auto end = dataSeries->cend();
276 280 if (valuesBounds.first != end && valuesBounds.second != end) {
277 281 auto rangeValue = [](const auto &value) { return std::isnan(value) ? 0. : value; };
278 282
279 283 auto minValue = rangeValue(valuesBounds.first->minValue());
280 284 auto maxValue = rangeValue(valuesBounds.second->maxValue());
281 285
282 286 graphWidget->setYRange(SqpRange{minValue, maxValue});
283 287 }
284 288 dataSeries->unlock();
285 289 }
286 290
287 291 return graphWidget;
288 292 }
289 293
290 294 VisualizationGraphWidget *
291 295 VisualizationZoneWidget::createGraph(const QList<std::shared_ptr<Variable> > variables, int index)
292 296 {
293 297 if (variables.isEmpty()) {
294 298 return nullptr;
295 299 }
296 300
297 301 auto graphWidget = createGraph(variables.first(), index);
298 302 for (auto variableIt = variables.cbegin() + 1; variableIt != variables.cend(); ++variableIt) {
299 303 graphWidget->addVariable(*variableIt, graphWidget->graphRange());
300 304 }
301 305
302 306 return graphWidget;
303 307 }
304 308
305 309 void VisualizationZoneWidget::accept(IVisualizationWidgetVisitor *visitor)
306 310 {
307 311 if (visitor) {
308 312 visitor->visitEnter(this);
309 313
310 314 // Apply visitor to graph children: widgets different from graphs are not visited (no
311 315 // action)
312 316 processGraphs(
313 317 *ui->dragDropContainer->layout(),
314 318 [visitor](VisualizationGraphWidget &graphWidget) { graphWidget.accept(visitor); });
315 319
316 320 visitor->visitLeave(this);
317 321 }
318 322 else {
319 323 qCCritical(LOG_VisualizationZoneWidget()) << tr("Can't visit widget : the visitor is null");
320 324 }
321 325 }
322 326
323 327 bool VisualizationZoneWidget::canDrop(const Variable &variable) const
324 328 {
325 329 // A tab can always accomodate a variable
326 330 Q_UNUSED(variable);
327 331 return true;
328 332 }
329 333
330 334 bool VisualizationZoneWidget::contains(const Variable &variable) const
331 335 {
332 336 Q_UNUSED(variable);
333 337 return false;
334 338 }
335 339
336 340 QString VisualizationZoneWidget::name() const
337 341 {
338 342 return ui->zoneNameLabel->text();
339 343 }
340 344
341 345 QMimeData *VisualizationZoneWidget::mimeData() const
342 346 {
343 347 auto mimeData = new QMimeData;
344 348 mimeData->setData(MIME_TYPE_ZONE, QByteArray{});
345 349
346 if (const auto firstGraph = impl->firstGraph(this)) {
347 auto timeRangeData = TimeController::mimeDataForTimeRange(firstGraph->graphRange());
348 mimeData->setData(MIME_TYPE_TIME_RANGE, timeRangeData);
349 }
350
351 350 return mimeData;
352 351 }
353 352
354 353 bool VisualizationZoneWidget::isDragAllowed() const
355 354 {
356 355 return true;
357 356 }
358 357
359 358 void VisualizationZoneWidget::closeEvent(QCloseEvent *event)
360 359 {
361 360 // Closes graphs in the zone
362 361 processGraphs(*ui->dragDropContainer->layout(),
363 362 [](VisualizationGraphWidget &graphWidget) { graphWidget.close(); });
364 363
365 364 // Delete synchronization group from variable controller
366 365 QMetaObject::invokeMethod(&sqpApp->variableController(), "onRemoveSynchronizationGroupId",
367 366 Qt::QueuedConnection, Q_ARG(QUuid, impl->m_SynchronisationGroupId));
368 367
369 368 QWidget::closeEvent(event);
370 369 }
371 370
372 371 void VisualizationZoneWidget::onVariableAdded(std::shared_ptr<Variable> variable)
373 372 {
374 373 QMetaObject::invokeMethod(&sqpApp->variableController(), "onAddSynchronized",
375 374 Qt::QueuedConnection, Q_ARG(std::shared_ptr<Variable>, variable),
376 375 Q_ARG(QUuid, impl->m_SynchronisationGroupId));
377 376 }
378 377
379 378 void VisualizationZoneWidget::onVariableAboutToBeRemoved(std::shared_ptr<Variable> variable)
380 379 {
381 380 QMetaObject::invokeMethod(&sqpApp->variableController(), "desynchronize", Qt::QueuedConnection,
382 381 Q_ARG(std::shared_ptr<Variable>, variable),
383 382 Q_ARG(QUuid, impl->m_SynchronisationGroupId));
384 383 }
385 384
386 385 void VisualizationZoneWidget::dropMimeData(int index, const QMimeData *mimeData)
387 386 {
388 387 if (mimeData->hasFormat(MIME_TYPE_GRAPH)) {
389 388 impl->dropGraph(index, this);
390 389 }
391 390 else if (mimeData->hasFormat(MIME_TYPE_VARIABLE_LIST)) {
392 391 auto variables = sqpApp->variableController().variablesForMimeData(
393 392 mimeData->data(MIME_TYPE_VARIABLE_LIST));
394 393 impl->dropVariables(variables, index, this);
395 394 }
396 395 else {
397 396 qCWarning(LOG_VisualizationZoneWidget())
398 397 << tr("VisualizationZoneWidget::dropMimeData, unknown MIME data received.");
399 398 }
400 399 }
401 400
402 401 void VisualizationZoneWidget::dropMimeDataOnGraph(VisualizationDragWidget *dragWidget,
403 402 const QMimeData *mimeData)
404 403 {
405 404 auto graphWidget = qobject_cast<VisualizationGraphWidget *>(dragWidget);
406 405 if (!graphWidget) {
407 406 qCWarning(LOG_VisualizationZoneWidget())
408 407 << tr("VisualizationZoneWidget::dropMimeDataOnGraph, dropping in an unknown widget, "
409 408 "drop aborted");
410 409 Q_ASSERT(false);
411 410 return;
412 411 }
413 412
414 413 if (mimeData->hasFormat(MIME_TYPE_VARIABLE_LIST)) {
415 414 auto variables = sqpApp->variableController().variablesForMimeData(
416 415 mimeData->data(MIME_TYPE_VARIABLE_LIST));
417 416 for (const auto &var : variables) {
418 417 graphWidget->addVariable(var, graphWidget->graphRange());
419 418 }
420 419 }
420 else if (mimeData->hasFormat(MIME_TYPE_TIME_RANGE)) {
421 auto range = TimeController::timeRangeForMimeData(mimeData->data(MIME_TYPE_TIME_RANGE));
422 graphWidget->setGraphRange(range);
423 }
421 424 else {
422 425 qCWarning(LOG_VisualizationZoneWidget())
423 426 << tr("VisualizationZoneWidget::dropMimeDataOnGraph, unknown MIME data received.");
424 427 }
425 428 }
426 429
427 430 void VisualizationZoneWidget::VisualizationZoneWidgetPrivate::dropGraph(
428 431 int index, VisualizationZoneWidget *zoneWidget)
429 432 {
430 433 auto &helper = sqpApp->dragDropHelper();
431 434
432 435 auto graphWidget = qobject_cast<VisualizationGraphWidget *>(helper.getCurrentDragWidget());
433 436 if (!graphWidget) {
434 437 qCWarning(LOG_VisualizationZoneWidget())
435 438 << tr("VisualizationZoneWidget::dropGraph, drop aborted, the dropped graph is not "
436 439 "found or invalid.");
437 440 Q_ASSERT(false);
438 441 return;
439 442 }
440 443
441 444 auto parentDragDropContainer
442 445 = qobject_cast<VisualizationDragDropContainer *>(graphWidget->parentWidget());
443 446 if (!parentDragDropContainer) {
444 447 qCWarning(LOG_VisualizationZoneWidget())
445 448 << tr("VisualizationZoneWidget::dropGraph, drop aborted, the parent container of "
446 449 "the dropped graph is not found.");
447 450 Q_ASSERT(false);
448 451 return;
449 452 }
450 453
451 454 const auto &variables = graphWidget->variables();
452 455
453 456 if (parentDragDropContainer != zoneWidget->ui->dragDropContainer && !variables.isEmpty()) {
454 457 // The drop didn't occur in the same zone
455 458
456 459 // Abort the requests for the variables (if any)
457 460 // Commented, because it's not sure if it's needed or not
458 461 // for (const auto& var : variables)
459 462 //{
460 463 // sqpApp->variableController().onAbortProgressRequested(var);
461 464 //}
462 465
463 466 auto previousParentZoneWidget = graphWidget->parentZoneWidget();
464 467 auto nbGraph = parentDragDropContainer->countDragWidget();
465 468 if (nbGraph == 1) {
466 469 // This is the only graph in the previous zone, close the zone
467 470 previousParentZoneWidget->close();
468 471 }
469 472 else {
470 473 // Close the graph
471 474 graphWidget->close();
472 475 }
473 476
474 477 // Creates the new graph in the zone
475 478 zoneWidget->createGraph(variables, index);
476 479 }
477 480 else {
478 481 // The drop occurred in the same zone or the graph is empty
479 482 // Simple move of the graph, no variable operation associated
480 483 parentDragDropContainer->layout()->removeWidget(graphWidget);
481 484
482 485 if (variables.isEmpty() && parentDragDropContainer != zoneWidget->ui->dragDropContainer) {
483 486 // The graph is empty and dropped in a different zone.
484 487 // Take the range of the first graph in the zone (if existing).
485 488 auto layout = zoneWidget->ui->dragDropContainer->layout();
486 489 if (layout->count() > 0) {
487 490 if (auto visualizationGraphWidget
488 491 = qobject_cast<VisualizationGraphWidget *>(layout->itemAt(0)->widget())) {
489 492 graphWidget->setGraphRange(visualizationGraphWidget->graphRange());
490 493 }
491 494 }
492 495 }
493 496
494 497 zoneWidget->ui->dragDropContainer->insertDragWidget(index, graphWidget);
495 498 }
496 499 }
497 500
498 501 void VisualizationZoneWidget::VisualizationZoneWidgetPrivate::dropVariables(
499 502 const QList<std::shared_ptr<Variable> > &variables, int index,
500 503 VisualizationZoneWidget *zoneWidget)
501 504 {
502 505 // Note: the AcceptMimeDataFunction (set on the drop container) ensure there is a single and
503 506 // compatible variable here
504 507 if (variables.count() > 1) {
505 508 qCWarning(LOG_VisualizationZoneWidget())
506 509 << tr("VisualizationZoneWidget::dropVariables, dropping multiple variables, operation "
507 510 "aborted.");
508 511 return;
509 512 }
510 513
511 514 zoneWidget->createGraph(variables, index);
512 515 }
General Comments 3
Under Review
author

Pull request updated. Auto status change to "Under Review"

Changed commits:
  * 1 added
  * 0 removed

Changed files:
  * A core/tests/meson.build
You need to be logged in to leave comments. Login now