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

Auto status change to "Under Review"

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