##// END OF EJS Templates
Merge branch 'feature/DragDropImprovements' into develop
trabillard -
r940:247d5dbe4c6a merge
parent child
Show More
1 NO CONTENT: new file 100644, binary diff hidden
@@ -1,135 +1,138
1 1 #ifndef SCIQLOP_VARIABLECONTROLLER_H
2 2 #define SCIQLOP_VARIABLECONTROLLER_H
3 3
4 4 #include "CoreGlobal.h"
5 5
6 6 #include <Data/AcquisitionDataPacket.h>
7 7 #include <Data/SqpRange.h>
8 8
9 9 #include <QLoggingCategory>
10 10 #include <QObject>
11 11 #include <QUuid>
12 12
13 13 #include <Common/spimpl.h>
14 14
15 15 class IDataProvider;
16 16 class QItemSelectionModel;
17 17 class TimeController;
18 18 class Variable;
19 19 class VariableModel;
20 20
21 21 Q_DECLARE_LOGGING_CATEGORY(LOG_VariableController)
22 22
23 23
24 24 /**
25 25 * Possible types of zoom operation
26 26 */
27 27 enum class AcquisitionZoomType { ZoomOut, ZoomIn, PanRight, PanLeft, Unknown };
28 28
29 29
30 30 /**
31 31 * @brief The VariableController class aims to handle the variables in SciQlop.
32 32 */
33 33 class SCIQLOP_CORE_EXPORT VariableController : public QObject {
34 34 Q_OBJECT
35 35 public:
36 36 explicit VariableController(QObject *parent = 0);
37 37 virtual ~VariableController();
38 38
39 39 VariableModel *variableModel() noexcept;
40 40 QItemSelectionModel *variableSelectionModel() noexcept;
41 41
42 42 void setTimeController(TimeController *timeController) noexcept;
43 43
44 44 /**
45 45 * Clones the variable passed in parameter and adds the duplicate to the controller
46 46 * @param variable the variable to duplicate
47 47 * @return the duplicate created, nullptr if the variable couldn't be created
48 48 */
49 49 std::shared_ptr<Variable> cloneVariable(std::shared_ptr<Variable> variable) noexcept;
50 50
51 51 /**
52 52 * Deletes from the controller the variable passed in parameter.
53 53 *
54 54 * Delete a variable includes:
55 55 * - the deletion of the various references to the variable in SciQlop
56 56 * - the deletion of the model variable
57 57 * - the deletion of the provider associated with the variable
58 58 * - removing the cache associated with the variable
59 59 *
60 60 * @param variable the variable to delete from the controller.
61 61 */
62 62 void deleteVariable(std::shared_ptr<Variable> variable) noexcept;
63 63
64 64 /**
65 65 * Deletes from the controller the variables passed in parameter.
66 66 * @param variables the variables to delete from the controller.
67 67 * @sa deleteVariable()
68 68 */
69 69 void deleteVariables(const QVector<std::shared_ptr<Variable> > &variables) noexcept;
70 70
71 71 /// Returns the MIME data associated to a list of variables
72 72 QByteArray mimeDataForVariables(const QList<std::shared_ptr<Variable> > &variables) const;
73 73
74 74 /// Returns the list of variables contained in a MIME data
75 75 QList<std::shared_ptr<Variable> > variablesForMimeData(const QByteArray &mimeData) const;
76 76
77 77 static AcquisitionZoomType getZoomType(const SqpRange &range, const SqpRange &oldRange);
78 78 signals:
79 79 /// Signal emitted when a variable is about to be deleted from the controller
80 80 void variableAboutToBeDeleted(std::shared_ptr<Variable> variable);
81 81
82 82 /// Signal emitted when a data acquisition is requested on a range for a variable
83 83 void rangeChanged(std::shared_ptr<Variable> variable, const SqpRange &range);
84 84
85 85 /// Signal emitted when a sub range of the cacheRange of the variable can be displayed
86 86 void updateVarDisplaying(std::shared_ptr<Variable> variable, const SqpRange &range);
87 87
88 88 public slots:
89 89 /// Request the data loading of the variable whithin range
90 90 void onRequestDataLoading(QVector<std::shared_ptr<Variable> > variables, const SqpRange &range,
91 91 bool synchronise);
92 92 /**
93 93 * Creates a new variable and adds it to the model
94 94 * @param name the name of the new variable
95 95 * @param metadata the metadata of the new variable
96 96 * @param provider the data provider for the new variable
97 97 * @return the pointer to the new variable or nullptr if the creation failed
98 98 */
99 99 std::shared_ptr<Variable> createVariable(const QString &name, const QVariantHash &metadata,
100 100 std::shared_ptr<IDataProvider> provider) noexcept;
101 101
102 102 /// Update the temporal parameters of every selected variable to dateTime
103 103 void onDateTimeOnSelection(const SqpRange &dateTime);
104 104
105 /// Update the temporal parameters of the specified variable
106 void onUpdateDateTime(std::shared_ptr<Variable> variable, const SqpRange &dateTime);
107
105 108
106 109 void onDataProvided(QUuid vIdentifier, const SqpRange &rangeRequested,
107 110 const SqpRange &cacheRangeRequested,
108 111 QVector<AcquisitionDataPacket> dataAcquired);
109 112
110 113 void onVariableRetrieveDataInProgress(QUuid identifier, double progress);
111 114
112 115 /// Cancel the current request for the variable
113 116 void onAbortProgressRequested(std::shared_ptr<Variable> variable);
114 117 void onAbortAcquisitionRequested(QUuid vIdentifier);
115 118
116 119 // synchronization group methods
117 120 void onAddSynchronizationGroupId(QUuid synchronizationGroupId);
118 121 void onRemoveSynchronizationGroupId(QUuid synchronizationGroupId);
119 122 void onAddSynchronized(std::shared_ptr<Variable> variable, QUuid synchronizationGroupId);
120 123
121 124 /// Desynchronizes the variable of the group whose identifier is passed in parameter
122 125 /// @remarks the method does nothing if the variable is not part of the group
123 126 void desynchronize(std::shared_ptr<Variable> variable, QUuid synchronizationGroupId);
124 127
125 128 void initialize();
126 129 void finalize();
127 130
128 131 private:
129 132 void waitForFinish();
130 133
131 134 class VariableControllerPrivate;
132 135 spimpl::unique_impl_ptr<VariableControllerPrivate> impl;
133 136 };
134 137
135 138 #endif // SCIQLOP_VARIABLECONTROLLER_H
@@ -1,110 +1,111
1 1 #ifndef SCIQLOP_VARIABLEMODEL_H
2 2 #define SCIQLOP_VARIABLEMODEL_H
3 3
4 4 #include "CoreGlobal.h"
5 5
6 6 #include <Data/SqpRange.h>
7 7
8 8 #include <QAbstractTableModel>
9 9 #include <QLoggingCategory>
10 10
11 11 #include <Common/MetaTypes.h>
12 12 #include <Common/spimpl.h>
13 13
14 14 Q_DECLARE_LOGGING_CATEGORY(LOG_VariableModel)
15 15
16 16 enum VariableRoles { ProgressRole = Qt::UserRole };
17 17
18 18
19 19 class IDataSeries;
20 20 class Variable;
21 21 class VariableController;
22 22
23 23 /**
24 24 * @brief The VariableModel class aims to hold the variables that have been created in SciQlop
25 25 */
26 26 class SCIQLOP_CORE_EXPORT VariableModel : public QAbstractTableModel {
27 27 Q_OBJECT
28 28 public:
29 29 explicit VariableModel(VariableController *parent = nullptr);
30 30
31 31 /**
32 32 * Adds an existing variable in the model.
33 33 * @param variable the variable to add.
34 34 * @remarks the variable's name is modified to avoid name duplicates
35 35 * @remarks this method does nothing if the variable already exists in the model
36 36 */
37 37 void addVariable(std::shared_ptr<Variable> variable) noexcept;
38 38
39 39 /**
40 40 * Checks that a variable is contained in the model
41 41 * @param variable the variable to check
42 42 * @return true if the variable is in the model, false otherwise
43 43 */
44 44 bool containsVariable(std::shared_ptr<Variable> variable) const noexcept;
45 45
46 46 /**
47 47 * Creates a new variable in the model
48 48 * @param name the name of the new variable
49 49 * @param metadata the metadata associated to the new variable
50 50 * @return the pointer to the new variable
51 51 */
52 52 std::shared_ptr<Variable> createVariable(const QString &name,
53 53 const QVariantHash &metadata) noexcept;
54 54
55 55 /**
56 56 * Deletes a variable from the model, if it exists
57 57 * @param variable the variable to delete
58 58 */
59 59 void deleteVariable(std::shared_ptr<Variable> variable) noexcept;
60 60
61 61
62 62 std::shared_ptr<Variable> variable(int index) const;
63 63 std::vector<std::shared_ptr<Variable> > variables() const;
64 64
65 65 void setDataProgress(std::shared_ptr<Variable> variable, double progress);
66 66
67 67
68 68 // /////////////////////////// //
69 69 // QAbstractTableModel methods //
70 70 // /////////////////////////// //
71 71
72 72 virtual int columnCount(const QModelIndex &parent = QModelIndex{}) const override;
73 73 virtual int rowCount(const QModelIndex &parent = QModelIndex{}) const override;
74 74 virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
75 75 virtual QVariant headerData(int section, Qt::Orientation orientation,
76 76 int role = Qt::DisplayRole) const override;
77 77 virtual Qt::ItemFlags flags(const QModelIndex &index) const override;
78 78
79 79 // ///////////////// //
80 80 // Drag&Drop methods //
81 81 // ///////////////// //
82 82
83 83 virtual Qt::DropActions supportedDropActions() const override;
84 84 virtual Qt::DropActions supportedDragActions() const override;
85 85 virtual QStringList mimeTypes() const override;
86 86 virtual QMimeData *mimeData(const QModelIndexList &indexes) const override;
87 87 virtual bool canDropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column,
88 88 const QModelIndex &parent) const override;
89 89 virtual bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column,
90 90 const QModelIndex &parent) override;
91 91
92 92 void abortProgress(const QModelIndex &index);
93 93
94 94 signals:
95 95 void abortProgessRequested(std::shared_ptr<Variable> variable);
96 96 void requestVariable(const QVariantHash &productData);
97 void requestVariableRangeUpdate(std::shared_ptr<Variable> variable, const SqpRange &range);
97 98
98 99 private:
99 100 class VariableModelPrivate;
100 101 spimpl::unique_impl_ptr<VariableModelPrivate> impl;
101 102
102 103 private slots:
103 104 /// Slot called when data of a variable has been updated
104 105 void onVariableUpdated() noexcept;
105 106 };
106 107
107 108 // Registers QVector<int> metatype so it can be used in VariableModel::dataChanged() signal
108 109 SCIQLOP_REGISTER_META_TYPE(QVECTOR_INT_REGISTRY, QVector<int>)
109 110
110 111 #endif // SCIQLOP_VARIABLEMODEL_H
@@ -1,1053 +1,1060
1 1 #include <Variable/Variable.h>
2 2 #include <Variable/VariableAcquisitionWorker.h>
3 3 #include <Variable/VariableCacheStrategy.h>
4 4 #include <Variable/VariableCacheStrategyFactory.h>
5 5 #include <Variable/VariableController.h>
6 6 #include <Variable/VariableModel.h>
7 7 #include <Variable/VariableSynchronizationGroup.h>
8 8
9 9 #include <Data/DataProviderParameters.h>
10 10 #include <Data/IDataProvider.h>
11 11 #include <Data/IDataSeries.h>
12 12 #include <Data/VariableRequest.h>
13 13 #include <Time/TimeController.h>
14 14
15 15 #include <QDataStream>
16 16 #include <QMutex>
17 17 #include <QThread>
18 18 #include <QUuid>
19 19 #include <QtCore/QItemSelectionModel>
20 20
21 21 #include <deque>
22 22 #include <set>
23 23 #include <unordered_map>
24 24
25 25 Q_LOGGING_CATEGORY(LOG_VariableController, "VariableController")
26 26
27 27 namespace {
28 28
29 29 SqpRange computeSynchroRangeRequested(const SqpRange &varRange, const SqpRange &graphRange,
30 30 const SqpRange &oldGraphRange)
31 31 {
32 32 auto zoomType = VariableController::getZoomType(graphRange, oldGraphRange);
33 33
34 34 auto varRangeRequested = varRange;
35 35 switch (zoomType) {
36 36 case AcquisitionZoomType::ZoomIn: {
37 37 auto deltaLeft = graphRange.m_TStart - oldGraphRange.m_TStart;
38 38 auto deltaRight = oldGraphRange.m_TEnd - graphRange.m_TEnd;
39 39 varRangeRequested.m_TStart += deltaLeft;
40 40 varRangeRequested.m_TEnd -= deltaRight;
41 41 break;
42 42 }
43 43
44 44 case AcquisitionZoomType::ZoomOut: {
45 45 auto deltaLeft = oldGraphRange.m_TStart - graphRange.m_TStart;
46 46 auto deltaRight = graphRange.m_TEnd - oldGraphRange.m_TEnd;
47 47 varRangeRequested.m_TStart -= deltaLeft;
48 48 varRangeRequested.m_TEnd += deltaRight;
49 49 break;
50 50 }
51 51 case AcquisitionZoomType::PanRight: {
52 52 auto deltaLeft = graphRange.m_TStart - oldGraphRange.m_TStart;
53 53 auto deltaRight = graphRange.m_TEnd - oldGraphRange.m_TEnd;
54 54 varRangeRequested.m_TStart += deltaLeft;
55 55 varRangeRequested.m_TEnd += deltaRight;
56 56 break;
57 57 }
58 58 case AcquisitionZoomType::PanLeft: {
59 59 auto deltaLeft = oldGraphRange.m_TStart - graphRange.m_TStart;
60 60 auto deltaRight = oldGraphRange.m_TEnd - graphRange.m_TEnd;
61 61 varRangeRequested.m_TStart -= deltaLeft;
62 62 varRangeRequested.m_TEnd -= deltaRight;
63 63 break;
64 64 }
65 65 case AcquisitionZoomType::Unknown: {
66 66 qCCritical(LOG_VariableController())
67 67 << VariableController::tr("Impossible to synchronize: zoom type unknown");
68 68 break;
69 69 }
70 70 default:
71 71 qCCritical(LOG_VariableController()) << VariableController::tr(
72 72 "Impossible to synchronize: zoom type not take into account");
73 73 // No action
74 74 break;
75 75 }
76 76
77 77 return varRangeRequested;
78 78 }
79 79 }
80 80
81 81 enum class VariableRequestHandlerState { OFF, RUNNING, PENDING };
82 82
83 83 struct VariableRequestHandler {
84 84
85 85 VariableRequestHandler()
86 86 {
87 87 m_CanUpdate = false;
88 88 m_State = VariableRequestHandlerState::OFF;
89 89 }
90 90
91 91 QUuid m_VarId;
92 92 VariableRequest m_RunningVarRequest;
93 93 VariableRequest m_PendingVarRequest;
94 94 VariableRequestHandlerState m_State;
95 95 bool m_CanUpdate;
96 96 };
97 97
98 98 struct VariableController::VariableControllerPrivate {
99 99 explicit VariableControllerPrivate(VariableController *parent)
100 100 : m_WorkingMutex{},
101 101 m_VariableModel{new VariableModel{parent}},
102 102 m_VariableSelectionModel{new QItemSelectionModel{m_VariableModel, parent}},
103 103 // m_VariableCacheStrategy{std::make_unique<VariableCacheStrategy>()},
104 104 m_VariableCacheStrategy{VariableCacheStrategyFactory::createCacheStrategy(
105 105 CacheStrategy::SingleThreshold)},
106 106 m_VariableAcquisitionWorker{std::make_unique<VariableAcquisitionWorker>()},
107 107 q{parent}
108 108 {
109 109
110 110 m_VariableAcquisitionWorker->moveToThread(&m_VariableAcquisitionWorkerThread);
111 111 m_VariableAcquisitionWorkerThread.setObjectName("VariableAcquisitionWorkerThread");
112 112 }
113 113
114 114
115 115 virtual ~VariableControllerPrivate()
116 116 {
117 117 qCDebug(LOG_VariableController()) << tr("VariableControllerPrivate destruction");
118 118 m_VariableAcquisitionWorkerThread.quit();
119 119 m_VariableAcquisitionWorkerThread.wait();
120 120 }
121 121
122 122
123 123 void processRequest(std::shared_ptr<Variable> var, const SqpRange &rangeRequested,
124 124 QUuid varRequestId);
125 125
126 126 std::shared_ptr<Variable> findVariable(QUuid vIdentifier);
127 127 std::shared_ptr<IDataSeries>
128 128 retrieveDataSeries(const QVector<AcquisitionDataPacket> acqDataPacketVector);
129 129
130 130 void registerProvider(std::shared_ptr<IDataProvider> provider);
131 131
132 132 void storeVariableRequest(QUuid varId, QUuid varRequestId, const VariableRequest &varRequest);
133 133 QUuid acceptVariableRequest(QUuid varId, std::shared_ptr<IDataSeries> dataSeries);
134 134 void updateVariables(QUuid varRequestId);
135 135 void updateVariableRequest(QUuid varRequestId);
136 136 void cancelVariableRequest(QUuid varRequestId);
137 137 void executeVarRequest(std::shared_ptr<Variable> var, VariableRequest &varRequest);
138 138
139 139 QMutex m_WorkingMutex;
140 140 /// Variable model. The VariableController has the ownership
141 141 VariableModel *m_VariableModel;
142 142 QItemSelectionModel *m_VariableSelectionModel;
143 143
144 144
145 145 TimeController *m_TimeController{nullptr};
146 146 std::unique_ptr<VariableCacheStrategy> m_VariableCacheStrategy;
147 147 std::unique_ptr<VariableAcquisitionWorker> m_VariableAcquisitionWorker;
148 148 QThread m_VariableAcquisitionWorkerThread;
149 149
150 150 std::unordered_map<std::shared_ptr<Variable>, std::shared_ptr<IDataProvider> >
151 151 m_VariableToProviderMap;
152 152 std::unordered_map<std::shared_ptr<Variable>, QUuid> m_VariableToIdentifierMap;
153 153 std::map<QUuid, std::shared_ptr<VariableSynchronizationGroup> >
154 154 m_GroupIdToVariableSynchronizationGroupMap;
155 155 std::map<QUuid, QUuid> m_VariableIdGroupIdMap;
156 156 std::set<std::shared_ptr<IDataProvider> > m_ProviderSet;
157 157
158 158 std::map<QUuid, std::list<QUuid> > m_VarGroupIdToVarIds;
159 159 std::map<QUuid, std::unique_ptr<VariableRequestHandler> > m_VarIdToVarRequestHandler;
160 160
161 161 VariableController *q;
162 162 };
163 163
164 164
165 165 VariableController::VariableController(QObject *parent)
166 166 : QObject{parent}, impl{spimpl::make_unique_impl<VariableControllerPrivate>(this)}
167 167 {
168 168 qCDebug(LOG_VariableController()) << tr("VariableController construction")
169 169 << QThread::currentThread();
170 170
171 171 connect(impl->m_VariableModel, &VariableModel::abortProgessRequested, this,
172 172 &VariableController::onAbortProgressRequested);
173 173
174 174 connect(impl->m_VariableAcquisitionWorker.get(),
175 175 &VariableAcquisitionWorker::variableCanceledRequested, this,
176 176 &VariableController::onAbortAcquisitionRequested);
177 177
178 178 connect(impl->m_VariableAcquisitionWorker.get(), &VariableAcquisitionWorker::dataProvided, this,
179 179 &VariableController::onDataProvided);
180 180 connect(impl->m_VariableAcquisitionWorker.get(),
181 181 &VariableAcquisitionWorker::variableRequestInProgress, this,
182 182 &VariableController::onVariableRetrieveDataInProgress);
183 183
184 184
185 185 connect(&impl->m_VariableAcquisitionWorkerThread, &QThread::started,
186 186 impl->m_VariableAcquisitionWorker.get(), &VariableAcquisitionWorker::initialize);
187 187 connect(&impl->m_VariableAcquisitionWorkerThread, &QThread::finished,
188 188 impl->m_VariableAcquisitionWorker.get(), &VariableAcquisitionWorker::finalize);
189 189
190 connect(impl->m_VariableModel, &VariableModel::requestVariableRangeUpdate, this,
191 &VariableController::onUpdateDateTime);
190 192
191 193 impl->m_VariableAcquisitionWorkerThread.start();
192 194 }
193 195
194 196 VariableController::~VariableController()
195 197 {
196 198 qCDebug(LOG_VariableController()) << tr("VariableController destruction")
197 199 << QThread::currentThread();
198 200 this->waitForFinish();
199 201 }
200 202
201 203 VariableModel *VariableController::variableModel() noexcept
202 204 {
203 205 return impl->m_VariableModel;
204 206 }
205 207
206 208 QItemSelectionModel *VariableController::variableSelectionModel() noexcept
207 209 {
208 210 return impl->m_VariableSelectionModel;
209 211 }
210 212
211 213 void VariableController::setTimeController(TimeController *timeController) noexcept
212 214 {
213 215 impl->m_TimeController = timeController;
214 216 }
215 217
216 218 std::shared_ptr<Variable>
217 219 VariableController::cloneVariable(std::shared_ptr<Variable> variable) noexcept
218 220 {
219 221 if (impl->m_VariableModel->containsVariable(variable)) {
220 222 // Clones variable
221 223 auto duplicate = variable->clone();
222 224
223 225 // Adds clone to model
224 226 impl->m_VariableModel->addVariable(duplicate);
225 227
226 228 // Generates clone identifier
227 229 impl->m_VariableToIdentifierMap[duplicate] = QUuid::createUuid();
228 230
229 231 // Registers provider
230 232 auto variableProvider = impl->m_VariableToProviderMap.at(variable);
231 233 auto duplicateProvider = variableProvider != nullptr ? variableProvider->clone() : nullptr;
232 234
233 235 impl->m_VariableToProviderMap[duplicate] = duplicateProvider;
234 236 if (duplicateProvider) {
235 237 impl->registerProvider(duplicateProvider);
236 238 }
237 239
238 240 return duplicate;
239 241 }
240 242 else {
241 243 qCCritical(LOG_VariableController())
242 244 << tr("Can't create duplicate of variable %1: variable not registered in the model")
243 245 .arg(variable->name());
244 246 return nullptr;
245 247 }
246 248 }
247 249
248 250 void VariableController::deleteVariable(std::shared_ptr<Variable> variable) noexcept
249 251 {
250 252 if (!variable) {
251 253 qCCritical(LOG_VariableController()) << "Can't delete variable: variable is null";
252 254 return;
253 255 }
254 256
255 257 // Spreads in SciQlop that the variable will be deleted, so that potential receivers can
256 258 // make some treatments before the deletion
257 259 emit variableAboutToBeDeleted(variable);
258 260
259 261 // Deletes identifier
260 262 impl->m_VariableToIdentifierMap.erase(variable);
261 263
262 264 // Deletes provider
263 265 auto nbProvidersDeleted = impl->m_VariableToProviderMap.erase(variable);
264 266 qCDebug(LOG_VariableController())
265 267 << tr("Number of providers deleted for variable %1: %2")
266 268 .arg(variable->name(), QString::number(nbProvidersDeleted));
267 269
268 270
269 271 // Deletes from model
270 272 impl->m_VariableModel->deleteVariable(variable);
271 273 }
272 274
273 275 void VariableController::deleteVariables(
274 276 const QVector<std::shared_ptr<Variable> > &variables) noexcept
275 277 {
276 278 for (auto variable : qAsConst(variables)) {
277 279 deleteVariable(variable);
278 280 }
279 281 }
280 282
281 283 QByteArray
282 284 VariableController::mimeDataForVariables(const QList<std::shared_ptr<Variable> > &variables) const
283 285 {
284 286 auto encodedData = QByteArray{};
285 287
286 288 QVariantList ids;
287 289 for (auto &var : variables) {
288 290 auto itVar = impl->m_VariableToIdentifierMap.find(var);
289 291 if (itVar == impl->m_VariableToIdentifierMap.cend()) {
290 292 qCCritical(LOG_VariableController())
291 293 << tr("Impossible to find the data for an unknown variable.");
292 294 }
293 295
294 296 ids << itVar->second.toByteArray();
295 297 }
296 298
297 299 QDataStream stream{&encodedData, QIODevice::WriteOnly};
298 300 stream << ids;
299 301
300 302 return encodedData;
301 303 }
302 304
303 305 QList<std::shared_ptr<Variable> >
304 306 VariableController::variablesForMimeData(const QByteArray &mimeData) const
305 307 {
306 308 auto variables = QList<std::shared_ptr<Variable> >{};
307 309 QDataStream stream{mimeData};
308 310
309 311 QVariantList ids;
310 312 stream >> ids;
311 313
312 314 for (auto id : ids) {
313 315 auto uuid = QUuid{id.toByteArray()};
314 316 auto var = impl->findVariable(uuid);
315 317 variables << var;
316 318 }
317 319
318 320 return variables;
319 321 }
320 322
321 323 std::shared_ptr<Variable>
322 324 VariableController::createVariable(const QString &name, const QVariantHash &metadata,
323 325 std::shared_ptr<IDataProvider> provider) noexcept
324 326 {
325 327 if (!impl->m_TimeController) {
326 328 qCCritical(LOG_VariableController())
327 329 << tr("Impossible to create variable: The time controller is null");
328 330 return nullptr;
329 331 }
330 332
331 333 auto range = impl->m_TimeController->dateTime();
332 334
333 335 if (auto newVariable = impl->m_VariableModel->createVariable(name, metadata)) {
334 336 auto varId = QUuid::createUuid();
335 337
336 338 // Create the handler
337 339 auto varRequestHandler = std::make_unique<VariableRequestHandler>();
338 340 varRequestHandler->m_VarId = varId;
339 341
340 342 impl->m_VarIdToVarRequestHandler.insert(
341 343 std::make_pair(varId, std::move(varRequestHandler)));
342 344
343 345 // store the provider
344 346 impl->registerProvider(provider);
345 347
346 348 // Associate the provider
347 349 impl->m_VariableToProviderMap[newVariable] = provider;
348 350 impl->m_VariableToIdentifierMap[newVariable] = varId;
349 351
350 352 this->onRequestDataLoading(QVector<std::shared_ptr<Variable> >{newVariable}, range, false);
351 353
352 354 // auto varRequestId = QUuid::createUuid();
353 355 // qCInfo(LOG_VariableController()) << "createVariable: " << varId << varRequestId;
354 356 // impl->processRequest(newVariable, range, varRequestId);
355 357 // impl->updateVariableRequest(varRequestId);
356 358
357 359 return newVariable;
358 360 }
359 361 }
360 362
361 363 void VariableController::onDateTimeOnSelection(const SqpRange &dateTime)
362 364 {
363 365 // NOTE: Even if acquisition request is aborting, the graphe range will be changed
364 366 qCDebug(LOG_VariableController()) << "VariableController::onDateTimeOnSelection"
365 367 << QThread::currentThread()->objectName();
366 368 auto selectedRows = impl->m_VariableSelectionModel->selectedRows();
367 369
368 370 // NOTE we only permit the time modification for one variable
369 371 // DEPRECATED
370 372 // auto variables = QVector<std::shared_ptr<Variable> >{};
371 373 // for (const auto &selectedRow : qAsConst(selectedRows)) {
372 374 // if (auto selectedVariable =
373 375 // impl->m_VariableModel->variable(selectedRow.row())) {
374 376 // variables << selectedVariable;
375 377
376 378 // // notify that rescale operation has to be done
377 379 // emit rangeChanged(selectedVariable, dateTime);
378 380 // }
379 381 // }
380 382 // if (!variables.isEmpty()) {
381 383 // this->onRequestDataLoading(variables, dateTime, synchro);
382 384 // }
383 385 if (selectedRows.size() == 1) {
384 386
385 387 if (auto selectedVariable
386 388 = impl->m_VariableModel->variable(qAsConst(selectedRows).first().row())) {
387 389
388 auto itVar = impl->m_VariableToIdentifierMap.find(selectedVariable);
389 if (itVar == impl->m_VariableToIdentifierMap.cend()) {
390 qCCritical(LOG_VariableController())
391 << tr("Impossible to onDateTimeOnSelection request for unknown variable");
392 return;
393 }
394
395 // notify that rescale operation has to be done
396 emit rangeChanged(selectedVariable, dateTime);
397
398 auto synchro = impl->m_VariableIdGroupIdMap.find(itVar->second)
399 != impl->m_VariableIdGroupIdMap.cend();
400
401 this->onRequestDataLoading(QVector<std::shared_ptr<Variable> >{selectedVariable},
402 dateTime, synchro);
390 onUpdateDateTime(selectedVariable, dateTime);
403 391 }
404 392 }
405 393 else if (selectedRows.size() > 1) {
406 394 qCCritical(LOG_VariableController())
407 395 << tr("Impossible to set time for more than 1 variable in the same time");
408 396 }
409 397 else {
410 398 qCWarning(LOG_VariableController())
411 399 << tr("There is no variable selected to set the time one");
412 400 }
413 401 }
414 402
403 void VariableController::onUpdateDateTime(std::shared_ptr<Variable> variable,
404 const SqpRange &dateTime)
405 {
406 auto itVar = impl->m_VariableToIdentifierMap.find(variable);
407 if (itVar == impl->m_VariableToIdentifierMap.cend()) {
408 qCCritical(LOG_VariableController())
409 << tr("Impossible to onDateTimeOnSelection request for unknown variable");
410 return;
411 }
412
413 // notify that rescale operation has to be done
414 emit rangeChanged(variable, dateTime);
415
416 auto synchro
417 = impl->m_VariableIdGroupIdMap.find(itVar->second) != impl->m_VariableIdGroupIdMap.cend();
418
419 this->onRequestDataLoading(QVector<std::shared_ptr<Variable> >{variable}, dateTime, synchro);
420 }
421
415 422 void VariableController::onDataProvided(QUuid vIdentifier, const SqpRange &rangeRequested,
416 423 const SqpRange &cacheRangeRequested,
417 424 QVector<AcquisitionDataPacket> dataAcquired)
418 425 {
419 426 qCDebug(LOG_VariableController()) << tr("onDataProvided") << QThread::currentThread();
420 427 auto retrievedDataSeries = impl->retrieveDataSeries(dataAcquired);
421 428 auto varRequestId = impl->acceptVariableRequest(vIdentifier, retrievedDataSeries);
422 429 if (!varRequestId.isNull()) {
423 430 impl->updateVariables(varRequestId);
424 431 }
425 432 }
426 433
427 434 void VariableController::onVariableRetrieveDataInProgress(QUuid identifier, double progress)
428 435 {
429 436 qCDebug(LOG_VariableController())
430 437 << "TORM: variableController::onVariableRetrieveDataInProgress"
431 438 << QThread::currentThread()->objectName() << progress;
432 439 if (auto var = impl->findVariable(identifier)) {
433 440 impl->m_VariableModel->setDataProgress(var, progress);
434 441 }
435 442 else {
436 443 qCCritical(LOG_VariableController())
437 444 << tr("Impossible to notify progression of a null variable");
438 445 }
439 446 }
440 447
441 448 void VariableController::onAbortProgressRequested(std::shared_ptr<Variable> variable)
442 449 {
443 450 qCDebug(LOG_VariableController()) << "TORM: variableController::onAbortProgressRequested"
444 451 << QThread::currentThread()->objectName() << variable->name();
445 452
446 453 auto itVar = impl->m_VariableToIdentifierMap.find(variable);
447 454 if (itVar == impl->m_VariableToIdentifierMap.cend()) {
448 455 qCCritical(LOG_VariableController())
449 456 << tr("Impossible to onAbortProgressRequested request for unknown variable");
450 457 return;
451 458 }
452 459
453 460 auto varId = itVar->second;
454 461
455 462 auto itVarHandler = impl->m_VarIdToVarRequestHandler.find(varId);
456 463 if (itVarHandler == impl->m_VarIdToVarRequestHandler.cend()) {
457 464 qCCritical(LOG_VariableController())
458 465 << tr("Impossible to onAbortProgressRequested for variable with unknown handler");
459 466 return;
460 467 }
461 468
462 469 auto varHandler = itVarHandler->second.get();
463 470
464 471 // case where a variable has a running request
465 472 if (varHandler->m_State != VariableRequestHandlerState::OFF) {
466 473 impl->cancelVariableRequest(varHandler->m_RunningVarRequest.m_VariableGroupId);
467 474 }
468 475 }
469 476
470 477 void VariableController::onAbortAcquisitionRequested(QUuid vIdentifier)
471 478 {
472 479 qCDebug(LOG_VariableController()) << "TORM: variableController::onAbortAcquisitionRequested"
473 480 << QThread::currentThread()->objectName() << vIdentifier;
474 481
475 482 if (auto var = impl->findVariable(vIdentifier)) {
476 483 this->onAbortProgressRequested(var);
477 484 }
478 485 else {
479 486 qCCritical(LOG_VariableController())
480 487 << tr("Impossible to abort Acquisition Requestof a null variable");
481 488 }
482 489 }
483 490
484 491 void VariableController::onAddSynchronizationGroupId(QUuid synchronizationGroupId)
485 492 {
486 493 qCDebug(LOG_VariableController()) << "TORM: VariableController::onAddSynchronizationGroupId"
487 494 << QThread::currentThread()->objectName()
488 495 << synchronizationGroupId;
489 496 auto vSynchroGroup = std::make_shared<VariableSynchronizationGroup>();
490 497 impl->m_GroupIdToVariableSynchronizationGroupMap.insert(
491 498 std::make_pair(synchronizationGroupId, vSynchroGroup));
492 499 }
493 500
494 501 void VariableController::onRemoveSynchronizationGroupId(QUuid synchronizationGroupId)
495 502 {
496 503 impl->m_GroupIdToVariableSynchronizationGroupMap.erase(synchronizationGroupId);
497 504 }
498 505
499 506 void VariableController::onAddSynchronized(std::shared_ptr<Variable> variable,
500 507 QUuid synchronizationGroupId)
501 508
502 509 {
503 510 qCDebug(LOG_VariableController()) << "TORM: VariableController::onAddSynchronized"
504 511 << synchronizationGroupId;
505 512 auto varToVarIdIt = impl->m_VariableToIdentifierMap.find(variable);
506 513 if (varToVarIdIt != impl->m_VariableToIdentifierMap.cend()) {
507 514 auto groupIdToVSGIt
508 515 = impl->m_GroupIdToVariableSynchronizationGroupMap.find(synchronizationGroupId);
509 516 if (groupIdToVSGIt != impl->m_GroupIdToVariableSynchronizationGroupMap.cend()) {
510 517 impl->m_VariableIdGroupIdMap.insert(
511 518 std::make_pair(varToVarIdIt->second, synchronizationGroupId));
512 519 groupIdToVSGIt->second->addVariableId(varToVarIdIt->second);
513 520 }
514 521 else {
515 522 qCCritical(LOG_VariableController())
516 523 << tr("Impossible to synchronize a variable with an unknown sycnhronization group")
517 524 << variable->name();
518 525 }
519 526 }
520 527 else {
521 528 qCCritical(LOG_VariableController())
522 529 << tr("Impossible to synchronize a variable with no identifier") << variable->name();
523 530 }
524 531 }
525 532
526 533 void VariableController::desynchronize(std::shared_ptr<Variable> variable,
527 534 QUuid synchronizationGroupId)
528 535 {
529 536 // Gets variable id
530 537 auto variableIt = impl->m_VariableToIdentifierMap.find(variable);
531 538 if (variableIt == impl->m_VariableToIdentifierMap.cend()) {
532 539 qCCritical(LOG_VariableController())
533 540 << tr("Can't desynchronize variable %1: variable identifier not found")
534 541 .arg(variable->name());
535 542 return;
536 543 }
537 544
538 545 // Gets synchronization group
539 546 auto groupIt = impl->m_GroupIdToVariableSynchronizationGroupMap.find(synchronizationGroupId);
540 547 if (groupIt == impl->m_GroupIdToVariableSynchronizationGroupMap.cend()) {
541 548 qCCritical(LOG_VariableController())
542 549 << tr("Can't desynchronize variable %1: unknown synchronization group")
543 550 .arg(variable->name());
544 551 return;
545 552 }
546 553
547 554 auto variableId = variableIt->second;
548 555
549 556 // Removes variable from synchronization group
550 557 auto synchronizationGroup = groupIt->second;
551 558 synchronizationGroup->removeVariableId(variableId);
552 559
553 560 // Removes link between variable and synchronization group
554 561 impl->m_VariableIdGroupIdMap.erase(variableId);
555 562 }
556 563
557 564 void VariableController::onRequestDataLoading(QVector<std::shared_ptr<Variable> > variables,
558 565 const SqpRange &range, bool synchronise)
559 566 {
560 567 // variables is assumed synchronized
561 568 // TODO: Asser variables synchronization
562 569 // we want to load data of the variable for the dateTime.
563 570 if (variables.isEmpty()) {
564 571 return;
565 572 }
566 573
567 574 auto varRequestId = QUuid::createUuid();
568 575 qCDebug(LOG_VariableController()) << "VariableController::onRequestDataLoading"
569 576 << QThread::currentThread()->objectName() << varRequestId
570 577 << range << synchronise;
571 578
572 579 if (!synchronise) {
573 580 auto varIds = std::list<QUuid>{};
574 581 for (const auto &var : variables) {
575 582 auto vId = impl->m_VariableToIdentifierMap.at(var);
576 583 varIds.push_back(vId);
577 584 }
578 585 impl->m_VarGroupIdToVarIds.insert(std::make_pair(varRequestId, varIds));
579 586 for (const auto &var : variables) {
580 587 qCDebug(LOG_VariableController()) << "processRequest for" << var->name() << varRequestId
581 588 << varIds.size();
582 589 impl->processRequest(var, range, varRequestId);
583 590 }
584 591 }
585 592 else {
586 593 auto vId = impl->m_VariableToIdentifierMap.at(variables.first());
587 594 auto varIdToGroupIdIt = impl->m_VariableIdGroupIdMap.find(vId);
588 595 if (varIdToGroupIdIt != impl->m_VariableIdGroupIdMap.cend()) {
589 596 auto groupId = varIdToGroupIdIt->second;
590 597
591 598 auto vSynchronizationGroup
592 599 = impl->m_GroupIdToVariableSynchronizationGroupMap.at(groupId);
593 600 auto vSyncIds = vSynchronizationGroup->getIds();
594 601
595 602 auto varIds = std::list<QUuid>{};
596 603 for (auto vId : vSyncIds) {
597 604 varIds.push_back(vId);
598 605 }
599 606 impl->m_VarGroupIdToVarIds.insert(std::make_pair(varRequestId, varIds));
600 607
601 608 for (auto vId : vSyncIds) {
602 609 auto var = impl->findVariable(vId);
603 610
604 611 // Don't process already processed var
605 612 if (var != nullptr) {
606 613 qCDebug(LOG_VariableController()) << "processRequest synchro for" << var->name()
607 614 << varRequestId;
608 615 auto vSyncRangeRequested
609 616 = variables.contains(var)
610 617 ? range
611 618 : computeSynchroRangeRequested(var->range(), range,
612 619 variables.first()->range());
613 620 qCDebug(LOG_VariableController()) << "synchro RR" << vSyncRangeRequested;
614 621 impl->processRequest(var, vSyncRangeRequested, varRequestId);
615 622 }
616 623 else {
617 624 qCCritical(LOG_VariableController())
618 625
619 626 << tr("Impossible to synchronize a null variable");
620 627 }
621 628 }
622 629 }
623 630 }
624 631
625 632 impl->updateVariables(varRequestId);
626 633 }
627 634
628 635
629 636 void VariableController::initialize()
630 637 {
631 638 qCDebug(LOG_VariableController()) << tr("VariableController init") << QThread::currentThread();
632 639 impl->m_WorkingMutex.lock();
633 640 qCDebug(LOG_VariableController()) << tr("VariableController init END");
634 641 }
635 642
636 643 void VariableController::finalize()
637 644 {
638 645 impl->m_WorkingMutex.unlock();
639 646 }
640 647
641 648 void VariableController::waitForFinish()
642 649 {
643 650 QMutexLocker locker{&impl->m_WorkingMutex};
644 651 }
645 652
646 653 AcquisitionZoomType VariableController::getZoomType(const SqpRange &range, const SqpRange &oldRange)
647 654 {
648 655 // t1.m_TStart <= t2.m_TStart && t2.m_TEnd <= t1.m_TEnd
649 656 auto zoomType = AcquisitionZoomType::Unknown;
650 657 if (range.m_TStart <= oldRange.m_TStart && oldRange.m_TEnd <= range.m_TEnd) {
651 658 qCDebug(LOG_VariableController()) << "zoomtype: ZoomOut";
652 659 zoomType = AcquisitionZoomType::ZoomOut;
653 660 }
654 661 else if (range.m_TStart > oldRange.m_TStart && range.m_TEnd > oldRange.m_TEnd) {
655 662 qCDebug(LOG_VariableController()) << "zoomtype: PanRight";
656 663 zoomType = AcquisitionZoomType::PanRight;
657 664 }
658 665 else if (range.m_TStart < oldRange.m_TStart && range.m_TEnd < oldRange.m_TEnd) {
659 666 qCDebug(LOG_VariableController()) << "zoomtype: PanLeft";
660 667 zoomType = AcquisitionZoomType::PanLeft;
661 668 }
662 669 else if (range.m_TStart > oldRange.m_TStart && oldRange.m_TEnd > range.m_TEnd) {
663 670 qCDebug(LOG_VariableController()) << "zoomtype: ZoomIn";
664 671 zoomType = AcquisitionZoomType::ZoomIn;
665 672 }
666 673 else {
667 674 qCDebug(LOG_VariableController()) << "getZoomType: Unknown type detected";
668 675 }
669 676 return zoomType;
670 677 }
671 678
672 679 void VariableController::VariableControllerPrivate::processRequest(std::shared_ptr<Variable> var,
673 680 const SqpRange &rangeRequested,
674 681 QUuid varRequestId)
675 682 {
676 683 auto itVar = m_VariableToIdentifierMap.find(var);
677 684 if (itVar == m_VariableToIdentifierMap.cend()) {
678 685 qCCritical(LOG_VariableController())
679 686 << tr("Impossible to process request for unknown variable");
680 687 return;
681 688 }
682 689
683 690 auto varId = itVar->second;
684 691
685 692 auto itVarHandler = m_VarIdToVarRequestHandler.find(varId);
686 693 if (itVarHandler == m_VarIdToVarRequestHandler.cend()) {
687 694 qCCritical(LOG_VariableController())
688 695 << tr("Impossible to process request for variable with unknown handler");
689 696 return;
690 697 }
691 698
692 699 auto oldRange = var->range();
693 700
694 701 auto varHandler = itVarHandler->second.get();
695 702
696 703 if (varHandler->m_State != VariableRequestHandlerState::OFF) {
697 704 oldRange = varHandler->m_RunningVarRequest.m_RangeRequested;
698 705 }
699 706
700 707 auto varRequest = VariableRequest{};
701 708 varRequest.m_VariableGroupId = varRequestId;
702 709 auto varStrategyRangesRequested
703 710 = m_VariableCacheStrategy->computeRange(oldRange, rangeRequested);
704 711 varRequest.m_RangeRequested = varStrategyRangesRequested.first;
705 712 varRequest.m_CacheRangeRequested = varStrategyRangesRequested.second;
706 713
707 714 switch (varHandler->m_State) {
708 715 case VariableRequestHandlerState::OFF: {
709 716 qCDebug(LOG_VariableController()) << tr("Process Request OFF")
710 717 << varRequest.m_RangeRequested
711 718 << varRequest.m_CacheRangeRequested;
712 719 varHandler->m_RunningVarRequest = varRequest;
713 720 varHandler->m_State = VariableRequestHandlerState::RUNNING;
714 721 executeVarRequest(var, varRequest);
715 722 break;
716 723 }
717 724 case VariableRequestHandlerState::RUNNING: {
718 725 qCDebug(LOG_VariableController()) << tr("Process Request RUNNING")
719 726 << varRequest.m_RangeRequested
720 727 << varRequest.m_CacheRangeRequested;
721 728 varHandler->m_State = VariableRequestHandlerState::PENDING;
722 729 varHandler->m_PendingVarRequest = varRequest;
723 730 break;
724 731 }
725 732 case VariableRequestHandlerState::PENDING: {
726 733 qCDebug(LOG_VariableController()) << tr("Process Request PENDING")
727 734 << varRequest.m_RangeRequested
728 735 << varRequest.m_CacheRangeRequested;
729 736 auto variableGroupIdToCancel = varHandler->m_PendingVarRequest.m_VariableGroupId;
730 737 cancelVariableRequest(variableGroupIdToCancel);
731 738 // Cancel variable can make state downgrade
732 739 varHandler->m_State = VariableRequestHandlerState::PENDING;
733 740 varHandler->m_PendingVarRequest = varRequest;
734 741
735 742 break;
736 743 }
737 744 default:
738 745 qCCritical(LOG_VariableController())
739 746 << QObject::tr("Unknown VariableRequestHandlerState");
740 747 }
741 748 }
742 749
743 750 std::shared_ptr<Variable>
744 751 VariableController::VariableControllerPrivate::findVariable(QUuid vIdentifier)
745 752 {
746 753 std::shared_ptr<Variable> var;
747 754 auto findReply = [vIdentifier](const auto &entry) { return vIdentifier == entry.second; };
748 755
749 756 auto end = m_VariableToIdentifierMap.cend();
750 757 auto it = std::find_if(m_VariableToIdentifierMap.cbegin(), end, findReply);
751 758 if (it != end) {
752 759 var = it->first;
753 760 }
754 761 else {
755 762 qCCritical(LOG_VariableController())
756 763 << tr("Impossible to find the variable with the identifier: ") << vIdentifier;
757 764 }
758 765
759 766 return var;
760 767 }
761 768
762 769 std::shared_ptr<IDataSeries> VariableController::VariableControllerPrivate::retrieveDataSeries(
763 770 const QVector<AcquisitionDataPacket> acqDataPacketVector)
764 771 {
765 772 qCDebug(LOG_VariableController()) << tr("TORM: retrieveDataSeries acqDataPacketVector size")
766 773 << acqDataPacketVector.size();
767 774 std::shared_ptr<IDataSeries> dataSeries;
768 775 if (!acqDataPacketVector.isEmpty()) {
769 776 dataSeries = acqDataPacketVector[0].m_DateSeries;
770 777 for (int i = 1; i < acqDataPacketVector.size(); ++i) {
771 778 dataSeries->merge(acqDataPacketVector[i].m_DateSeries.get());
772 779 }
773 780 }
774 781 qCDebug(LOG_VariableController()) << tr("TORM: retrieveDataSeries acqDataPacketVector size END")
775 782 << acqDataPacketVector.size();
776 783 return dataSeries;
777 784 }
778 785
779 786 void VariableController::VariableControllerPrivate::registerProvider(
780 787 std::shared_ptr<IDataProvider> provider)
781 788 {
782 789 if (m_ProviderSet.find(provider) == m_ProviderSet.end()) {
783 790 qCDebug(LOG_VariableController()) << tr("Registering of a new provider")
784 791 << provider->objectName();
785 792 m_ProviderSet.insert(provider);
786 793 connect(provider.get(), &IDataProvider::dataProvided, m_VariableAcquisitionWorker.get(),
787 794 &VariableAcquisitionWorker::onVariableDataAcquired);
788 795 connect(provider.get(), &IDataProvider::dataProvidedProgress,
789 796 m_VariableAcquisitionWorker.get(),
790 797 &VariableAcquisitionWorker::onVariableRetrieveDataInProgress);
791 798 connect(provider.get(), &IDataProvider::dataProvidedFailed,
792 799 m_VariableAcquisitionWorker.get(),
793 800 &VariableAcquisitionWorker::onVariableAcquisitionFailed);
794 801 }
795 802 else {
796 803 qCDebug(LOG_VariableController()) << tr("Cannot register provider, it already exists ");
797 804 }
798 805 }
799 806
800 807 QUuid VariableController::VariableControllerPrivate::acceptVariableRequest(
801 808 QUuid varId, std::shared_ptr<IDataSeries> dataSeries)
802 809 {
803 810 auto itVarHandler = m_VarIdToVarRequestHandler.find(varId);
804 811 if (itVarHandler == m_VarIdToVarRequestHandler.cend()) {
805 812 return QUuid();
806 813 }
807 814
808 815 auto varHandler = itVarHandler->second.get();
809 816 if (varHandler->m_State == VariableRequestHandlerState::OFF) {
810 817 qCCritical(LOG_VariableController())
811 818 << tr("acceptVariableRequest impossible on a variable with OFF state");
812 819 }
813 820
814 821 varHandler->m_RunningVarRequest.m_DataSeries = dataSeries;
815 822 varHandler->m_CanUpdate = true;
816 823
817 824 // Element traité, on a déjà toutes les données necessaires
818 825 auto varGroupId = varHandler->m_RunningVarRequest.m_VariableGroupId;
819 826 qCDebug(LOG_VariableController()) << "Variable::acceptVariableRequest" << varGroupId
820 827 << m_VarGroupIdToVarIds.size();
821 828
822 829 return varHandler->m_RunningVarRequest.m_VariableGroupId;
823 830 }
824 831
825 832 void VariableController::VariableControllerPrivate::updateVariables(QUuid varRequestId)
826 833 {
827 834 qCDebug(LOG_VariableController()) << "VariableControllerPrivate::updateVariables"
828 835 << QThread::currentThread()->objectName() << varRequestId;
829 836
830 837 auto varGroupIdToVarIdsIt = m_VarGroupIdToVarIds.find(varRequestId);
831 838 if (varGroupIdToVarIdsIt == m_VarGroupIdToVarIds.end()) {
832 839 qCWarning(LOG_VariableController())
833 840 << tr("Impossible to updateVariables of unknown variables") << varRequestId;
834 841 return;
835 842 }
836 843
837 844 auto &varIds = varGroupIdToVarIdsIt->second;
838 845 auto varIdsEnd = varIds.end();
839 846 bool processVariableUpdate = true;
840 847 qCDebug(LOG_VariableController()) << "VariableControllerPrivate::updateVariables"
841 848 << varRequestId << varIds.size();
842 849 for (auto varIdsIt = varIds.begin(); (varIdsIt != varIdsEnd) && processVariableUpdate;
843 850 ++varIdsIt) {
844 851 auto itVarHandler = m_VarIdToVarRequestHandler.find(*varIdsIt);
845 852 if (itVarHandler != m_VarIdToVarRequestHandler.cend()) {
846 853 processVariableUpdate &= itVarHandler->second->m_CanUpdate;
847 854 }
848 855 }
849 856
850 857 if (processVariableUpdate) {
851 858 qCDebug(LOG_VariableController()) << "Final update OK for the var request" << varIds.size();
852 859 for (auto varIdsIt = varIds.begin(); varIdsIt != varIdsEnd; ++varIdsIt) {
853 860 auto itVarHandler = m_VarIdToVarRequestHandler.find(*varIdsIt);
854 861 if (itVarHandler != m_VarIdToVarRequestHandler.cend()) {
855 862 if (auto var = findVariable(*varIdsIt)) {
856 863 auto &varRequest = itVarHandler->second->m_RunningVarRequest;
857 864 var->setRange(varRequest.m_RangeRequested);
858 865 var->setCacheRange(varRequest.m_CacheRangeRequested);
859 866 qCDebug(LOG_VariableController()) << tr("1: onDataProvided")
860 867 << varRequest.m_RangeRequested
861 868 << varRequest.m_CacheRangeRequested;
862 869 qCDebug(LOG_VariableController()) << tr("2: onDataProvided var points before")
863 870 << var->nbPoints()
864 871 << varRequest.m_DataSeries->nbPoints();
865 872 var->mergeDataSeries(varRequest.m_DataSeries);
866 873 qCDebug(LOG_VariableController()) << tr("3: onDataProvided var points after")
867 874 << var->nbPoints();
868 875
869 876 emit var->updated();
870 877 qCDebug(LOG_VariableController()) << tr("Update OK");
871 878 }
872 879 else {
873 880 qCCritical(LOG_VariableController())
874 881 << tr("Impossible to update data to a null variable");
875 882 }
876 883 }
877 884 }
878 885 updateVariableRequest(varRequestId);
879 886
880 887 // cleaning varRequestId
881 888 qCDebug(LOG_VariableController()) << tr("m_VarGroupIdToVarIds erase") << varRequestId;
882 889 m_VarGroupIdToVarIds.erase(varRequestId);
883 890 }
884 891 }
885 892
886 893
887 894 void VariableController::VariableControllerPrivate::updateVariableRequest(QUuid varRequestId)
888 895 {
889 896 auto varGroupIdToVarIdsIt = m_VarGroupIdToVarIds.find(varRequestId);
890 897 if (varGroupIdToVarIdsIt == m_VarGroupIdToVarIds.end()) {
891 898 qCCritical(LOG_VariableController()) << QObject::tr(
892 899 "Impossible to updateVariableRequest since varGroupdId isn't here anymore");
893 900
894 901 return;
895 902 }
896 903
897 904 auto &varIds = varGroupIdToVarIdsIt->second;
898 905 auto varIdsEnd = varIds.end();
899 906 for (auto varIdsIt = varIds.begin(); (varIdsIt != varIdsEnd); ++varIdsIt) {
900 907 auto itVarHandler = m_VarIdToVarRequestHandler.find(*varIdsIt);
901 908 if (itVarHandler != m_VarIdToVarRequestHandler.cend()) {
902 909
903 910 auto varHandler = itVarHandler->second.get();
904 911 varHandler->m_CanUpdate = false;
905 912
906 913
907 914 switch (varHandler->m_State) {
908 915 case VariableRequestHandlerState::OFF: {
909 916 qCCritical(LOG_VariableController())
910 917 << QObject::tr("Impossible to update a variable with handler in OFF state");
911 918 } break;
912 919 case VariableRequestHandlerState::RUNNING: {
913 920 varHandler->m_State = VariableRequestHandlerState::OFF;
914 921 varHandler->m_RunningVarRequest = VariableRequest{};
915 922 break;
916 923 }
917 924 case VariableRequestHandlerState::PENDING: {
918 925 varHandler->m_State = VariableRequestHandlerState::RUNNING;
919 926 varHandler->m_RunningVarRequest = varHandler->m_PendingVarRequest;
920 927 varHandler->m_PendingVarRequest = VariableRequest{};
921 928 auto var = findVariable(itVarHandler->first);
922 929 executeVarRequest(var, varHandler->m_RunningVarRequest);
923 930 break;
924 931 }
925 932 default:
926 933 qCCritical(LOG_VariableController())
927 934 << QObject::tr("Unknown VariableRequestHandlerState");
928 935 }
929 936 }
930 937 }
931 938 }
932 939
933 940
934 941 void VariableController::VariableControllerPrivate::cancelVariableRequest(QUuid varRequestId)
935 942 {
936 943 qCDebug(LOG_VariableController()) << tr("cancelVariableRequest") << varRequestId;
937 944
938 945 auto varGroupIdToVarIdsIt = m_VarGroupIdToVarIds.find(varRequestId);
939 946 if (varGroupIdToVarIdsIt == m_VarGroupIdToVarIds.end()) {
940 947 qCCritical(LOG_VariableController())
941 948 << tr("Impossible to cancelVariableRequest for unknown varGroupdId") << varRequestId;
942 949 return;
943 950 }
944 951
945 952 auto &varIds = varGroupIdToVarIdsIt->second;
946 953 auto varIdsEnd = varIds.end();
947 954 for (auto varIdsIt = varIds.begin(); (varIdsIt != varIdsEnd); ++varIdsIt) {
948 955 auto itVarHandler = m_VarIdToVarRequestHandler.find(*varIdsIt);
949 956 if (itVarHandler != m_VarIdToVarRequestHandler.cend()) {
950 957
951 958 auto varHandler = itVarHandler->second.get();
952 959 varHandler->m_VarId = QUuid{};
953 960 switch (varHandler->m_State) {
954 961 case VariableRequestHandlerState::OFF: {
955 962 qCWarning(LOG_VariableController())
956 963 << QObject::tr("Impossible to cancel a variable with no running request");
957 964 break;
958 965 }
959 966 case VariableRequestHandlerState::RUNNING: {
960 967
961 968 if (varHandler->m_RunningVarRequest.m_VariableGroupId == varRequestId) {
962 969 auto var = findVariable(itVarHandler->first);
963 970 auto varProvider = m_VariableToProviderMap.at(var);
964 971 if (varProvider != nullptr) {
965 972 m_VariableAcquisitionWorker->abortProgressRequested(
966 973 itVarHandler->first);
967 974 }
968 975 m_VariableModel->setDataProgress(var, 0.0);
969 976 varHandler->m_CanUpdate = false;
970 977 varHandler->m_State = VariableRequestHandlerState::OFF;
971 978 varHandler->m_RunningVarRequest = VariableRequest{};
972 979 }
973 980 else {
974 981 // TODO: log Impossible to cancel the running variable request beacause its
975 982 // varRequestId isn't not the canceled one
976 983 }
977 984 break;
978 985 }
979 986 case VariableRequestHandlerState::PENDING: {
980 987 if (varHandler->m_RunningVarRequest.m_VariableGroupId == varRequestId) {
981 988 auto var = findVariable(itVarHandler->first);
982 989 auto varProvider = m_VariableToProviderMap.at(var);
983 990 if (varProvider != nullptr) {
984 991 m_VariableAcquisitionWorker->abortProgressRequested(
985 992 itVarHandler->first);
986 993 }
987 994 m_VariableModel->setDataProgress(var, 0.0);
988 995 varHandler->m_CanUpdate = false;
989 996 varHandler->m_State = VariableRequestHandlerState::RUNNING;
990 997 varHandler->m_RunningVarRequest = varHandler->m_PendingVarRequest;
991 998 varHandler->m_PendingVarRequest = VariableRequest{};
992 999 executeVarRequest(var, varHandler->m_RunningVarRequest);
993 1000 }
994 1001 else if (varHandler->m_PendingVarRequest.m_VariableGroupId == varRequestId) {
995 1002 varHandler->m_State = VariableRequestHandlerState::RUNNING;
996 1003 varHandler->m_PendingVarRequest = VariableRequest{};
997 1004 }
998 1005 else {
999 1006 // TODO: log Impossible to cancel the variable request beacause its
1000 1007 // varRequestId isn't not the canceled one
1001 1008 }
1002 1009 break;
1003 1010 }
1004 1011 default:
1005 1012 qCCritical(LOG_VariableController())
1006 1013 << QObject::tr("Unknown VariableRequestHandlerState");
1007 1014 }
1008 1015 }
1009 1016 }
1010 1017 qCDebug(LOG_VariableController()) << tr("cancelVariableRequest: erase") << varRequestId;
1011 1018 m_VarGroupIdToVarIds.erase(varRequestId);
1012 1019 }
1013 1020
1014 1021 void VariableController::VariableControllerPrivate::executeVarRequest(std::shared_ptr<Variable> var,
1015 1022 VariableRequest &varRequest)
1016 1023 {
1017 1024 qCDebug(LOG_VariableController()) << tr("TORM: executeVarRequest");
1018 1025
1019 1026 auto varId = m_VariableToIdentifierMap.at(var);
1020 1027
1021 1028 auto varCacheRange = var->cacheRange();
1022 1029 auto varCacheRangeRequested = varRequest.m_CacheRangeRequested;
1023 1030 auto notInCacheRangeList
1024 1031 = Variable::provideNotInCacheRangeList(varCacheRange, varCacheRangeRequested);
1025 1032 auto inCacheRangeList
1026 1033 = Variable::provideInCacheRangeList(varCacheRange, varCacheRangeRequested);
1027 1034
1028 1035 if (!notInCacheRangeList.empty()) {
1029 1036
1030 1037 auto varProvider = m_VariableToProviderMap.at(var);
1031 1038 if (varProvider != nullptr) {
1032 1039 qCDebug(LOG_VariableController()) << "executeVarRequest " << varRequest.m_RangeRequested
1033 1040 << varRequest.m_CacheRangeRequested;
1034 1041 m_VariableAcquisitionWorker->pushVariableRequest(
1035 1042 varRequest.m_VariableGroupId, varId, varRequest.m_RangeRequested,
1036 1043 varRequest.m_CacheRangeRequested,
1037 1044 DataProviderParameters{std::move(notInCacheRangeList), var->metadata()},
1038 1045 varProvider);
1039 1046 }
1040 1047 else {
1041 1048 qCCritical(LOG_VariableController())
1042 1049 << "Impossible to provide data with a null provider";
1043 1050 }
1044 1051
1045 1052 if (!inCacheRangeList.empty()) {
1046 1053 emit q->updateVarDisplaying(var, inCacheRangeList.first());
1047 1054 }
1048 1055 }
1049 1056 else {
1050 1057 acceptVariableRequest(varId,
1051 1058 var->dataSeries()->subDataSeries(varRequest.m_CacheRangeRequested));
1052 1059 }
1053 1060 }
@@ -1,383 +1,394
1 1 #include <Variable/Variable.h>
2 2 #include <Variable/VariableController.h>
3 3 #include <Variable/VariableModel.h>
4 4
5 5 #include <Common/DateUtils.h>
6 6 #include <Common/MimeTypesDef.h>
7 7 #include <Common/StringUtils.h>
8 8
9 9 #include <Data/IDataSeries.h>
10 10
11 11 #include <DataSource/DataSourceController.h>
12 12 #include <Time/TimeController.h>
13 13
14 14 #include <QMimeData>
15 15 #include <QSize>
16 #include <QTimer>
16 17 #include <unordered_map>
17 18
18 19 Q_LOGGING_CATEGORY(LOG_VariableModel, "VariableModel")
19 20
20 21 namespace {
21 22
22 23 // Column indexes
23 24 const auto NAME_COLUMN = 0;
24 25 const auto TSTART_COLUMN = 1;
25 26 const auto TEND_COLUMN = 2;
26 27 const auto NBPOINTS_COLUMN = 3;
27 28 const auto UNIT_COLUMN = 4;
28 29 const auto MISSION_COLUMN = 5;
29 30 const auto PLUGIN_COLUMN = 6;
30 31 const auto NB_COLUMNS = 7;
31 32
32 33 // Column properties
33 34 const auto DEFAULT_HEIGHT = 25;
34 35 const auto DEFAULT_WIDTH = 100;
35 36
36 37 struct ColumnProperties {
37 38 ColumnProperties(const QString &name = {}, int width = DEFAULT_WIDTH,
38 39 int height = DEFAULT_HEIGHT)
39 40 : m_Name{name}, m_Width{width}, m_Height{height}
40 41 {
41 42 }
42 43
43 44 QString m_Name;
44 45 int m_Width;
45 46 int m_Height;
46 47 };
47 48
48 49 const auto COLUMN_PROPERTIES = QHash<int, ColumnProperties>{
49 50 {NAME_COLUMN, {QObject::tr("Name")}}, {TSTART_COLUMN, {QObject::tr("tStart"), 180}},
50 51 {TEND_COLUMN, {QObject::tr("tEnd"), 180}}, {NBPOINTS_COLUMN, {QObject::tr("Nb points")}},
51 52 {UNIT_COLUMN, {QObject::tr("Unit")}}, {MISSION_COLUMN, {QObject::tr("Mission")}},
52 53 {PLUGIN_COLUMN, {QObject::tr("Plugin")}}};
53 54
54 55 /// Format for datetimes
55 56 const auto DATETIME_FORMAT = QStringLiteral("dd/MM/yyyy \nhh:mm:ss:zzz");
56 57
57 58 QString uniqueName(const QString &defaultName,
58 59 const std::vector<std::shared_ptr<Variable> > &variables)
59 60 {
60 61 auto forbiddenNames = std::vector<QString>(variables.size());
61 62 std::transform(variables.cbegin(), variables.cend(), forbiddenNames.begin(),
62 63 [](const auto &variable) { return variable->name(); });
63 64 auto uniqueName = StringUtils::uniqueName(defaultName, forbiddenNames);
64 65 Q_ASSERT(!uniqueName.isEmpty());
65 66
66 67 return uniqueName;
67 68 }
68 69
69 70 } // namespace
70 71
71 72 struct VariableModel::VariableModelPrivate {
72 73 /// Variables created in SciQlop
73 74 std::vector<std::shared_ptr<Variable> > m_Variables;
74 75 std::unordered_map<std::shared_ptr<Variable>, double> m_VariableToProgress;
75 76 VariableController *m_VariableController;
76 77
77 78 /// Return the row index of the variable. -1 if it's not found
78 79 int indexOfVariable(Variable *variable) const noexcept;
79 80 };
80 81
81 82 VariableModel::VariableModel(VariableController *parent)
82 83 : QAbstractTableModel{parent}, impl{spimpl::make_unique_impl<VariableModelPrivate>()}
83 84 {
84 85 impl->m_VariableController = parent;
85 86 }
86 87
87 88 void VariableModel::addVariable(std::shared_ptr<Variable> variable) noexcept
88 89 {
89 90 auto insertIndex = rowCount();
90 91 beginInsertRows({}, insertIndex, insertIndex);
91 92
92 93 // Generates unique name for the variable
93 94 variable->setName(uniqueName(variable->name(), impl->m_Variables));
94 95
95 96 impl->m_Variables.push_back(variable);
96 97 connect(variable.get(), &Variable::updated, this, &VariableModel::onVariableUpdated);
97 98
98 99 endInsertRows();
99 100 }
100 101
101 102 bool VariableModel::containsVariable(std::shared_ptr<Variable> variable) const noexcept
102 103 {
103 104 auto end = impl->m_Variables.cend();
104 105 return std::find(impl->m_Variables.cbegin(), end, variable) != end;
105 106 }
106 107
107 108 std::shared_ptr<Variable> VariableModel::createVariable(const QString &name,
108 109 const QVariantHash &metadata) noexcept
109 110 {
110 111 auto variable = std::make_shared<Variable>(name, metadata);
111 112 addVariable(variable);
112 113
113 114 return variable;
114 115 }
115 116
116 117 void VariableModel::deleteVariable(std::shared_ptr<Variable> variable) noexcept
117 118 {
118 119 if (!variable) {
119 120 qCCritical(LOG_Variable()) << "Can't delete a null variable from the model";
120 121 return;
121 122 }
122 123
123 124 // Finds variable in the model
124 125 auto begin = impl->m_Variables.cbegin();
125 126 auto end = impl->m_Variables.cend();
126 127 auto it = std::find(begin, end, variable);
127 128 if (it != end) {
128 129 auto removeIndex = std::distance(begin, it);
129 130
130 131 // Deletes variable
131 132 beginRemoveRows({}, removeIndex, removeIndex);
132 133 impl->m_Variables.erase(it);
133 134 endRemoveRows();
134 135 }
135 136 else {
136 137 qCritical(LOG_VariableModel())
137 138 << tr("Can't delete variable %1 from the model: the variable is not in the model")
138 139 .arg(variable->name());
139 140 }
140 141
141 142 // Removes variable from progress map
142 143 impl->m_VariableToProgress.erase(variable);
143 144 }
144 145
145 146
146 147 std::shared_ptr<Variable> VariableModel::variable(int index) const
147 148 {
148 149 return (index >= 0 && index < impl->m_Variables.size()) ? impl->m_Variables[index] : nullptr;
149 150 }
150 151
151 152 std::vector<std::shared_ptr<Variable> > VariableModel::variables() const
152 153 {
153 154 return impl->m_Variables;
154 155 }
155 156
156 157 void VariableModel::setDataProgress(std::shared_ptr<Variable> variable, double progress)
157 158 {
158 159 if (progress > 0.0) {
159 160 impl->m_VariableToProgress[variable] = progress;
160 161 }
161 162 else {
162 163 impl->m_VariableToProgress.erase(variable);
163 164 }
164 165 auto modelIndex = createIndex(impl->indexOfVariable(variable.get()), NAME_COLUMN);
165 166
166 167 emit dataChanged(modelIndex, modelIndex);
167 168 }
168 169
169 170 int VariableModel::columnCount(const QModelIndex &parent) const
170 171 {
171 172 Q_UNUSED(parent);
172 173
173 174 return NB_COLUMNS;
174 175 }
175 176
176 177 int VariableModel::rowCount(const QModelIndex &parent) const
177 178 {
178 179 Q_UNUSED(parent);
179 180
180 181 return impl->m_Variables.size();
181 182 }
182 183
183 184 QVariant VariableModel::data(const QModelIndex &index, int role) const
184 185 {
185 186 if (!index.isValid()) {
186 187 return QVariant{};
187 188 }
188 189
189 190 if (index.row() < 0 || index.row() >= rowCount()) {
190 191 return QVariant{};
191 192 }
192 193
193 194 if (role == Qt::DisplayRole) {
194 195 if (auto variable = impl->m_Variables.at(index.row()).get()) {
195 196 switch (index.column()) {
196 197 case NAME_COLUMN:
197 198 return variable->name();
198 199 case TSTART_COLUMN: {
199 200 auto range = variable->realRange();
200 201 return range != INVALID_RANGE
201 202 ? DateUtils::dateTime(range.m_TStart).toString(DATETIME_FORMAT)
202 203 : QVariant{};
203 204 }
204 205 case TEND_COLUMN: {
205 206 auto range = variable->realRange();
206 207 return range != INVALID_RANGE
207 208 ? DateUtils::dateTime(range.m_TEnd).toString(DATETIME_FORMAT)
208 209 : QVariant{};
209 210 }
210 211 case NBPOINTS_COLUMN:
211 212 return variable->nbPoints();
212 213 case UNIT_COLUMN:
213 214 return variable->metadata().value(QStringLiteral("units"));
214 215 case MISSION_COLUMN:
215 216 return variable->metadata().value(QStringLiteral("mission"));
216 217 case PLUGIN_COLUMN:
217 218 return variable->metadata().value(QStringLiteral("plugin"));
218 219 default:
219 220 // No action
220 221 break;
221 222 }
222 223
223 224 qWarning(LOG_VariableModel())
224 225 << tr("Can't get data (unknown column %1)").arg(index.column());
225 226 }
226 227 else {
227 228 qWarning(LOG_VariableModel()) << tr("Can't get data (no variable)");
228 229 }
229 230 }
230 231 else if (role == VariableRoles::ProgressRole) {
231 232 if (auto variable = impl->m_Variables.at(index.row())) {
232 233
233 234 auto it = impl->m_VariableToProgress.find(variable);
234 235 if (it != impl->m_VariableToProgress.cend()) {
235 236 return it->second;
236 237 }
237 238 }
238 239 }
239 240
240 241 return QVariant{};
241 242 }
242 243
243 244 QVariant VariableModel::headerData(int section, Qt::Orientation orientation, int role) const
244 245 {
245 246 if (role != Qt::DisplayRole && role != Qt::SizeHintRole) {
246 247 return QVariant{};
247 248 }
248 249
249 250 if (orientation == Qt::Horizontal) {
250 251 auto propertiesIt = COLUMN_PROPERTIES.find(section);
251 252 if (propertiesIt != COLUMN_PROPERTIES.cend()) {
252 253 // Role is either DisplayRole or SizeHintRole
253 254 return (role == Qt::DisplayRole)
254 255 ? QVariant{propertiesIt->m_Name}
255 256 : QVariant{QSize{propertiesIt->m_Width, propertiesIt->m_Height}};
256 257 }
257 258 else {
258 259 qWarning(LOG_VariableModel())
259 260 << tr("Can't get header data (unknown column %1)").arg(section);
260 261 }
261 262 }
262 263
263 264 return QVariant{};
264 265 }
265 266
266 267 Qt::ItemFlags VariableModel::flags(const QModelIndex &index) const
267 268 {
268 269 return QAbstractTableModel::flags(index) | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled;
269 270 }
270 271
271 272 Qt::DropActions VariableModel::supportedDropActions() const
272 273 {
273 274 return Qt::CopyAction | Qt::MoveAction;
274 275 }
275 276
276 277 Qt::DropActions VariableModel::supportedDragActions() const
277 278 {
278 279 return Qt::CopyAction | Qt::MoveAction;
279 280 }
280 281
281 282 QStringList VariableModel::mimeTypes() const
282 283 {
283 284 return {MIME_TYPE_VARIABLE_LIST, MIME_TYPE_TIME_RANGE};
284 285 }
285 286
286 287 QMimeData *VariableModel::mimeData(const QModelIndexList &indexes) const
287 288 {
288 289 auto mimeData = new QMimeData;
289 290
290 291 QList<std::shared_ptr<Variable> > variableList;
291 292
292 293
293 294 SqpRange firstTimeRange;
294 295 for (const auto &index : indexes) {
295 296 if (index.column() == 0) { // only the first column
296 297 auto variable = impl->m_Variables.at(index.row());
297 298 if (variable.get() && index.isValid()) {
298 299
299 300 if (variableList.isEmpty()) {
300 301 // Gets the range of the first variable
301 302 firstTimeRange = std::move(variable->range());
302 303 }
303 304
304 305 variableList << variable;
305 306 }
306 307 }
307 308 }
308 309
309 310 auto variablesEncodedData = impl->m_VariableController->mimeDataForVariables(variableList);
310 311 mimeData->setData(MIME_TYPE_VARIABLE_LIST, variablesEncodedData);
311 312
312 313 if (variableList.count() == 1) {
313 314 // No time range MIME data if multiple variables are dragged
314 315 auto timeEncodedData = TimeController::mimeDataForTimeRange(firstTimeRange);
315 316 mimeData->setData(MIME_TYPE_TIME_RANGE, timeEncodedData);
316 317 }
317 318
318 319 return mimeData;
319 320 }
320 321
321 322 bool VariableModel::canDropMimeData(const QMimeData *data, Qt::DropAction action, int row,
322 323 int column, const QModelIndex &parent) const
323 324 {
324 325 // drop of a product
325 return data->hasFormat(MIME_TYPE_PRODUCT_LIST);
326 return data->hasFormat(MIME_TYPE_PRODUCT_LIST)
327 || (data->hasFormat(MIME_TYPE_TIME_RANGE) && parent.isValid()
328 && !data->hasFormat(MIME_TYPE_VARIABLE_LIST));
326 329 }
327 330
328 331 bool VariableModel::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column,
329 332 const QModelIndex &parent)
330 333 {
331 334 auto dropDone = false;
332 335
333 336 if (data->hasFormat(MIME_TYPE_PRODUCT_LIST)) {
334 337
335 338 auto productList
336 339 = DataSourceController::productsDataForMimeData(data->data(MIME_TYPE_PRODUCT_LIST));
337 340
338 341 for (auto metaData : productList) {
339 342 emit requestVariable(metaData.toHash());
340 343 }
341 344
342 345 dropDone = true;
343 346 }
347 else if (data->hasFormat(MIME_TYPE_TIME_RANGE) && parent.isValid()) {
348 auto variable = this->variable(parent.row());
349 auto range = TimeController::timeRangeForMimeData(data->data(MIME_TYPE_TIME_RANGE));
350
351 emit requestVariableRangeUpdate(variable, range);
352
353 dropDone = true;
354 }
344 355
345 356 return dropDone;
346 357 }
347 358
348 359 void VariableModel::abortProgress(const QModelIndex &index)
349 360 {
350 361 if (auto variable = impl->m_Variables.at(index.row())) {
351 362 emit abortProgessRequested(variable);
352 363 }
353 364 }
354 365
355 366 void VariableModel::onVariableUpdated() noexcept
356 367 {
357 368 // Finds variable that has been updated in the model
358 369 if (auto updatedVariable = dynamic_cast<Variable *>(sender())) {
359 370 auto updatedVariableIndex = impl->indexOfVariable(updatedVariable);
360 371
361 372 if (updatedVariableIndex > -1) {
362 373 emit dataChanged(createIndex(updatedVariableIndex, 0),
363 374 createIndex(updatedVariableIndex, columnCount() - 1));
364 375 }
365 376 }
366 377 }
367 378
368 379 int VariableModel::VariableModelPrivate::indexOfVariable(Variable *variable) const noexcept
369 380 {
370 381 auto begin = std::cbegin(m_Variables);
371 382 auto end = std::cend(m_Variables);
372 383 auto it
373 384 = std::find_if(begin, end, [variable](const auto &var) { return var.get() == variable; });
374 385
375 386 if (it != end) {
376 387 // Gets the index of the variable in the model: we assume here that views have the same
377 388 // order as the model
378 389 return std::distance(begin, it);
379 390 }
380 391 else {
381 392 return -1;
382 393 }
383 394 }
@@ -1,18 +1,18
1 1 #ifndef SCIQLOP_MACSCROLLBARSTYLE_H
2 2 #define SCIQLOP_MACSCROLLBARSTYLE_H
3 3
4 4 #include <QProxyStyle>
5 5
6 6 /**
7 7 * @brief Special style to always display the scrollbars on MAC.
8 8 */
9 9 class MacScrollBarStyle : public QProxyStyle {
10 10
11 11 public:
12 12 int styleHint(StyleHint hint, const QStyleOption *option, const QWidget *widget,
13 QStyleHintReturn *returnData) const;
13 QStyleHintReturn *returnData) const override;
14 14
15 15 void selfInstallOn(QWidget *widget, bool installOnSubWidgets);
16 16 };
17 17
18 18 #endif // SCIQLOP_MACSCROLLBARSTYLE_H
@@ -1,57 +1,57
1 1 #ifndef SCIQLOP_VISUALIZATIONDRAGDROPCONTAINER_H
2 2 #define SCIQLOP_VISUALIZATIONDRAGDROPCONTAINER_H
3 3
4 4 #include <Common/spimpl.h>
5 5 #include <QFrame>
6 6 #include <QLoggingCategory>
7 7 #include <QMimeData>
8 8 #include <QVBoxLayout>
9 9
10 10 #include <functional>
11 11
12 12 #include <DragAndDrop/DragDropHelper.h>
13 13
14 14 Q_DECLARE_LOGGING_CATEGORY(LOG_VisualizationDragDropContainer)
15 15
16 16 class VisualizationDragWidget;
17 17
18 18 class VisualizationDragDropContainer : public QFrame {
19 19 Q_OBJECT
20 20
21 21 signals:
22 22 void dropOccuredInContainer(int dropIndex, const QMimeData *mimeData);
23 23 void dropOccuredOnWidget(VisualizationDragWidget *dragWidget, const QMimeData *mimeData);
24 24
25 25 public:
26 enum class DropBehavior { Inserted, Merged, InsertedAndMerged };
26 enum class DropBehavior { Inserted, Merged, InsertedAndMerged, Forbidden };
27 27 using AcceptMimeDataFunction = std::function<bool(const QMimeData *mimeData)>;
28 28
29 29 VisualizationDragDropContainer(QWidget *parent = nullptr);
30 30
31 31 void addDragWidget(VisualizationDragWidget *dragWidget);
32 32 void insertDragWidget(int index, VisualizationDragWidget *dragWidget);
33 33
34 void addAcceptedMimeType(const QString &mimeType, DropBehavior behavior);
34 void setMimeType(const QString &mimeType, DropBehavior behavior);
35 35
36 36 int countDragWidget() const;
37 37
38 38 void setAcceptMimeDataFunction(AcceptMimeDataFunction fun);
39 39
40 40 void setPlaceHolderType(DragDropHelper::PlaceHolderType type,
41 41 const QString &placeHolderText = QString());
42 42
43 43 protected:
44 44 void dragEnterEvent(QDragEnterEvent *event);
45 45 void dragLeaveEvent(QDragLeaveEvent *event);
46 46 void dragMoveEvent(QDragMoveEvent *event);
47 47 void dropEvent(QDropEvent *event);
48 48
49 49 private:
50 50 class VisualizationDragDropContainerPrivate;
51 51 spimpl::unique_impl_ptr<VisualizationDragDropContainerPrivate> impl;
52 52
53 53 private slots:
54 54 void startDrag(VisualizationDragWidget *dragWidget, const QPoint &dragPosition);
55 55 };
56 56
57 57 #endif // SCIQLOP_VISUALIZATIONDRAGDROPCONTAINER_H
@@ -1,16 +1,17
1 1 <RCC>
2 2 <qresource prefix="/">
3 3 <file>icones/dataSourceComponent.png</file>
4 4 <file>icones/dataSourceNode.png</file>
5 5 <file>icones/dataSourceProduct.png</file>
6 6 <file>icones/dataSourceRoot.png</file>
7 7 <file>icones/delete.png</file>
8 8 <file>icones/down.png</file>
9 9 <file>icones/openInspector.png</file>
10 10 <file>icones/next.png</file>
11 11 <file>icones/plot.png</file>
12 12 <file>icones/previous.png</file>
13 13 <file>icones/unplot.png</file>
14 14 <file>icones/up.png</file>
15 <file>icones/time.png</file>
15 16 </qresource>
16 17 </RCC>
@@ -1,156 +1,154
1 1 #include "TimeWidget/TimeWidget.h"
2 2 #include "ui_TimeWidget.h"
3 3
4 4 #include <Common/DateUtils.h>
5 5 #include <Common/MimeTypesDef.h>
6 6
7 7 #include <DragAndDrop/DragDropHelper.h>
8 8 #include <SqpApplication.h>
9 9 #include <Time/TimeController.h>
10 10
11 11 #include <QDrag>
12 12 #include <QDragEnterEvent>
13 13 #include <QDropEvent>
14 14 #include <QMimeData>
15 15
16 16
17 17 struct TimeWidget::TimeWidgetPrivate {
18 18
19 19 explicit TimeWidgetPrivate() {}
20 20
21 21 QPoint m_DragStartPosition;
22 22 };
23 23
24 24 TimeWidget::TimeWidget(QWidget *parent)
25 25 : QWidget{parent},
26 26 ui{new Ui::TimeWidget},
27 27 impl{spimpl::make_unique_impl<TimeWidgetPrivate>()}
28 28 {
29 29 ui->setupUi(this);
30 30
31 31 ui->applyToolButton->setIcon(sqpApp->style()->standardIcon(QStyle::SP_DialogApplyButton));
32 32
33 33 // Connection
34 34 connect(ui->startDateTimeEdit, &QDateTimeEdit::dateTimeChanged, this,
35 35 &TimeWidget::onTimeUpdateRequested);
36 36
37 37 connect(ui->endDateTimeEdit, &QDateTimeEdit::dateTimeChanged, this,
38 38 &TimeWidget::onTimeUpdateRequested);
39 39
40 40
41 41 connect(ui->applyToolButton, &QToolButton::clicked, &sqpApp->timeController(),
42 42 &TimeController::onTimeNotify);
43 43
44 44 // Initialisation
45 45 auto endDateTime = QDateTime::currentDateTimeUtc();
46 46 auto startDateTime = endDateTime.addSecs(-3600); // one hour before
47 47
48 48 ui->startDateTimeEdit->setDateTime(startDateTime);
49 49 ui->endDateTimeEdit->setDateTime(endDateTime);
50 50
51 51 auto dateTime = SqpRange{DateUtils::secondsSinceEpoch(startDateTime),
52 52 DateUtils::secondsSinceEpoch(endDateTime)};
53 53
54 54 sqpApp->timeController().onTimeToUpdate(dateTime);
55 55 }
56 56
57 57
58 58 TimeWidget::~TimeWidget()
59 59 {
60 60 delete ui;
61 61 }
62 62
63 63 void TimeWidget::setTimeRange(SqpRange time)
64 64 {
65 65 auto startDateTime = DateUtils::dateTime(time.m_TStart);
66 66 auto endDateTime = DateUtils::dateTime(time.m_TEnd);
67 67
68 68 ui->startDateTimeEdit->setDateTime(startDateTime);
69 69 ui->endDateTimeEdit->setDateTime(endDateTime);
70 70 }
71 71
72 72 SqpRange TimeWidget::timeRange() const
73 73 {
74 74 return SqpRange{DateUtils::secondsSinceEpoch(ui->startDateTimeEdit->dateTime()),
75 75 DateUtils::secondsSinceEpoch(ui->endDateTimeEdit->dateTime())};
76 76 }
77 77
78 78 void TimeWidget::onTimeUpdateRequested()
79 79 {
80 80 auto dateTime = timeRange();
81 81 emit timeUpdated(std::move(dateTime));
82 82 }
83 83
84 84 void TimeWidget::dragEnterEvent(QDragEnterEvent *event)
85 85 {
86 86 if (event->mimeData()->hasFormat(MIME_TYPE_TIME_RANGE)) {
87 87 event->acceptProposedAction();
88 88 setStyleSheet("QDateTimeEdit{background-color: #BBD5EE; border:2px solid #2A7FD4}");
89 89 }
90 90 else {
91 91 event->ignore();
92 92 }
93 93 }
94 94
95 95 void TimeWidget::dragLeaveEvent(QDragLeaveEvent *event)
96 96 {
97 setStyleSheet(QString());
97 setStyleSheet(QString{});
98 98 }
99 99
100 100 void TimeWidget::dropEvent(QDropEvent *event)
101 101 {
102 102 if (event->mimeData()->hasFormat(MIME_TYPE_TIME_RANGE)) {
103 103 auto mimeData = event->mimeData()->data(MIME_TYPE_TIME_RANGE);
104 104 auto timeRange = TimeController::timeRangeForMimeData(mimeData);
105 105
106 106 setTimeRange(timeRange);
107 107 }
108 108 else {
109 109 event->ignore();
110 110 }
111 111
112 setStyleSheet(QString());
112 setStyleSheet(QString{});
113 113 }
114 114
115 115
116 116 void TimeWidget::mousePressEvent(QMouseEvent *event)
117 117 {
118 118 if (event->button() == Qt::LeftButton) {
119 119 impl->m_DragStartPosition = event->pos();
120 120 }
121 121
122 122 QWidget::mousePressEvent(event);
123 123 }
124 124
125 125 void TimeWidget::mouseMoveEvent(QMouseEvent *event)
126 126 {
127 127 if (!(event->buttons() & Qt::LeftButton)) {
128 128 return;
129 129 }
130 130
131 131 if ((event->pos() - impl->m_DragStartPosition).manhattanLength()
132 132 < QApplication::startDragDistance()) {
133 133 return;
134 134 }
135 135
136 136 // Note: The management of the drag object is done by Qt
137 137 auto drag = new QDrag{this};
138 138
139 139 auto mimeData = new QMimeData;
140 140 auto timeData = TimeController::mimeDataForTimeRange(timeRange());
141 141 mimeData->setData(MIME_TYPE_TIME_RANGE, timeData);
142 142
143 143 drag->setMimeData(mimeData);
144 144
145 auto pixmap = QPixmap(size());
146 render(&pixmap);
147 drag->setPixmap(pixmap);
148 drag->setHotSpot(impl->m_DragStartPosition);
145 auto pixmap = QPixmap{":/icones/time.png"};
146 drag->setPixmap(pixmap.scaledToWidth(22));
149 147
150 148 sqpApp->dragDropHelper().resetDragAndDrop();
151 149
152 150 // Note: The exec() is blocking on windows but not on linux and macOS
153 151 drag->exec(Qt::MoveAction | Qt::CopyAction);
154 152
155 153 QWidget::mouseMoveEvent(event);
156 154 }
@@ -1,453 +1,472
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 auto DRAGGED_MINIATURE_WIDTH = 200; // in pixels
18
17 19 struct VisualizationDragDropContainer::VisualizationDragDropContainerPrivate {
18 20
19 21 QVBoxLayout *m_Layout;
20 22 QHash<QString, VisualizationDragDropContainer::DropBehavior> m_AcceptedMimeTypes;
21 23 QString m_PlaceHolderText;
22 24 DragDropHelper::PlaceHolderType m_PlaceHolderType = DragDropHelper::PlaceHolderType::Graph;
23 25
24 26 VisualizationDragDropContainer::AcceptMimeDataFunction m_AcceptMimeDataFun
25 27 = [](auto mimeData) { return true; };
26 28
27 29 int m_MinContainerHeight = 0;
28 30
29 31 explicit VisualizationDragDropContainerPrivate(QWidget *widget)
30 32 {
31 33 m_Layout = new QVBoxLayout(widget);
32 34 m_Layout->setContentsMargins(0, 0, 0, 0);
33 35 }
34 36
35 37 bool acceptMimeData(const QMimeData *data) const
36 38 {
37 for (const auto &type : m_AcceptedMimeTypes.keys()) {
38 if (data->hasFormat(type) && m_AcceptMimeDataFun(data)) {
39 return true;
39 auto accepted = false;
40 for (auto it = m_AcceptedMimeTypes.constBegin(); it != m_AcceptedMimeTypes.constEnd();
41 ++it) {
42 const auto &type = it.key();
43 const auto &behavior = it.value();
44
45 if (data->hasFormat(type)) {
46 if (behavior != DropBehavior::Forbidden) {
47 accepted = true;
48 }
49 else {
50 accepted = false;
51 break;
52 }
40 53 }
41 54 }
42 55
43 return false;
56 if (accepted) {
57 accepted = m_AcceptMimeDataFun(data);
58 }
59
60 return accepted;
44 61 }
45 62
46 63 bool allowMergeForMimeData(const QMimeData *data) const
47 64 {
48 bool result = false;
65 auto result = false;
49 66 for (auto it = m_AcceptedMimeTypes.constBegin(); it != m_AcceptedMimeTypes.constEnd();
50 67 ++it) {
51 68
52 69 if (data->hasFormat(it.key())
53 70 && (it.value() == VisualizationDragDropContainer::DropBehavior::Merged
54 71 || it.value()
55 72 == VisualizationDragDropContainer::DropBehavior::InsertedAndMerged)) {
56 73 result = true;
57 74 }
58 75 else if (data->hasFormat(it.key())
59 76 && it.value() == VisualizationDragDropContainer::DropBehavior::Inserted) {
60 77 // Merge is forbidden if the mime data contain an acceptable type which cannot be
61 78 // merged
62 79 result = false;
63 80 break;
64 81 }
65 82 }
66 83
67 84 return result;
68 85 }
69 86
70 87 bool allowInsertForMimeData(const QMimeData *data) const
71 88 {
72 89 for (auto it = m_AcceptedMimeTypes.constBegin(); it != m_AcceptedMimeTypes.constEnd();
73 90 ++it) {
74 91 if (data->hasFormat(it.key())
75 92 && (it.value() == VisualizationDragDropContainer::DropBehavior::Inserted
76 93 || it.value()
77 94 == VisualizationDragDropContainer::DropBehavior::InsertedAndMerged)) {
78 95 return true;
79 96 }
80 97 }
81 98
82 99 return false;
83 100 }
84 101
85 102 bool hasPlaceHolder() const
86 103 {
87 104 return sqpApp->dragDropHelper().placeHolder().parentWidget() == m_Layout->parentWidget();
88 105 }
89 106
90 107 VisualizationDragWidget *getChildDragWidgetAt(const QWidget *parent, const QPoint &pos) const
91 108 {
92 109 VisualizationDragWidget *dragWidget = nullptr;
93 110
94 111 for (auto child : parent->children()) {
95 112 auto widget = qobject_cast<VisualizationDragWidget *>(child);
96 113 if (widget && widget->isVisible()) {
97 114 if (widget->frameGeometry().contains(pos)) {
98 115 dragWidget = widget;
99 116 break;
100 117 }
101 118 }
102 119 }
103 120
104 121 return dragWidget;
105 122 }
106 123
107 124 bool cursorIsInContainer(QWidget *container) const
108 125 {
109 return container->isAncestorOf(sqpApp->widgetAt(QCursor::pos()));
126 auto widgetUnderMouse = sqpApp->widgetAt(QCursor::pos());
127 return container->isAncestorOf(widgetUnderMouse) && widgetUnderMouse != container
128 && sqpApp->dragDropHelper().placeHolder().isAncestorOf(widgetUnderMouse);
110 129 }
111 130
112 131 int countDragWidget(const QWidget *parent, bool onlyVisible = false) const
113 132 {
114 133 auto nbGraph = 0;
115 134 for (auto child : parent->children()) {
116 135 if (qobject_cast<VisualizationDragWidget *>(child)) {
117 136 if (!onlyVisible || qobject_cast<VisualizationDragWidget *>(child)->isVisible()) {
118 137 nbGraph += 1;
119 138 }
120 139 }
121 140 }
122 141
123 142 return nbGraph;
124 143 }
125 144
126 145 void findPlaceHolderPosition(const QPoint &pos, bool canInsert, bool canMerge,
127 146 const VisualizationDragDropContainer *container);
128 147 };
129 148
130 149 VisualizationDragDropContainer::VisualizationDragDropContainer(QWidget *parent)
131 150 : QFrame{parent},
132 151 impl{spimpl::make_unique_impl<VisualizationDragDropContainerPrivate>(this)}
133 152 {
134 153 setAcceptDrops(true);
135 154 }
136 155
137 156 void VisualizationDragDropContainer::addDragWidget(VisualizationDragWidget *dragWidget)
138 157 {
139 158 impl->m_Layout->addWidget(dragWidget);
140 159 disconnect(dragWidget, &VisualizationDragWidget::dragDetected, nullptr, nullptr);
141 160 connect(dragWidget, &VisualizationDragWidget::dragDetected, this,
142 161 &VisualizationDragDropContainer::startDrag);
143 162 }
144 163
145 164 void VisualizationDragDropContainer::insertDragWidget(int index,
146 165 VisualizationDragWidget *dragWidget)
147 166 {
148 167 impl->m_Layout->insertWidget(index, dragWidget);
149 168 disconnect(dragWidget, &VisualizationDragWidget::dragDetected, nullptr, nullptr);
150 169 connect(dragWidget, &VisualizationDragWidget::dragDetected, this,
151 170 &VisualizationDragDropContainer::startDrag);
152 171 }
153 172
154 void VisualizationDragDropContainer::addAcceptedMimeType(
173 void VisualizationDragDropContainer::setMimeType(
155 174 const QString &mimeType, VisualizationDragDropContainer::DropBehavior behavior)
156 175 {
157 176 impl->m_AcceptedMimeTypes[mimeType] = behavior;
158 177 }
159 178
160 179 int VisualizationDragDropContainer::countDragWidget() const
161 180 {
162 181 return impl->countDragWidget(this);
163 182 }
164 183
165 184 void VisualizationDragDropContainer::setAcceptMimeDataFunction(
166 185 VisualizationDragDropContainer::AcceptMimeDataFunction fun)
167 186 {
168 187 impl->m_AcceptMimeDataFun = fun;
169 188 }
170 189
171 190 void VisualizationDragDropContainer::setPlaceHolderType(DragDropHelper::PlaceHolderType type,
172 191 const QString &placeHolderText)
173 192 {
174 193 impl->m_PlaceHolderType = type;
175 194 impl->m_PlaceHolderText = placeHolderText;
176 195 }
177 196
178 197 void VisualizationDragDropContainer::startDrag(VisualizationDragWidget *dragWidget,
179 198 const QPoint &dragPosition)
180 199 {
181 200 auto &helper = sqpApp->dragDropHelper();
182 201 helper.resetDragAndDrop();
183 202
184 203 // Note: The management of the drag object is done by Qt
185 204 auto drag = new QDrag{dragWidget};
186 drag->setHotSpot(dragPosition);
187 205
188 206 auto mimeData = dragWidget->mimeData();
189 207 drag->setMimeData(mimeData);
190 208
191 209 auto pixmap = QPixmap(dragWidget->size());
192 210 dragWidget->render(&pixmap);
193 drag->setPixmap(pixmap);
211 drag->setPixmap(pixmap.scaled(DRAGGED_MINIATURE_WIDTH, DRAGGED_MINIATURE_WIDTH,
212 Qt::KeepAspectRatio, Qt::SmoothTransformation));
194 213
195 214 auto image = pixmap.toImage();
196 215 mimeData->setImageData(image);
197 216 mimeData->setUrls({helper.imageTemporaryUrl(image)});
198 217
199 218 if (impl->m_Layout->indexOf(dragWidget) >= 0) {
200 219 helper.setCurrentDragWidget(dragWidget);
201 220
202 221 if (impl->cursorIsInContainer(this)) {
203 222 auto dragWidgetIndex = impl->m_Layout->indexOf(dragWidget);
204 223 helper.insertPlaceHolder(impl->m_Layout, dragWidgetIndex, impl->m_PlaceHolderType,
205 224 impl->m_PlaceHolderText);
206 225 dragWidget->setVisible(false);
207 226 }
208 227 else {
209 228 // The drag starts directly outside the drop zone
210 229 // do not add the placeHolder
211 230 }
212 231
213 232 drag->exec(Qt::MoveAction | Qt::CopyAction, Qt::MoveAction);
214 233
215 234 helper.doCloseWidgets();
216 235 }
217 236 else {
218 237 qCWarning(LOG_VisualizationDragDropContainer())
219 238 << tr("VisualizationDragDropContainer::startDrag, drag aborted, the specified "
220 239 "VisualizationDragWidget is not found in this container.");
221 240 }
222 241 }
223 242
224 243 void VisualizationDragDropContainer::dragEnterEvent(QDragEnterEvent *event)
225 244 {
226 245 if (impl->acceptMimeData(event->mimeData())) {
227 246 event->acceptProposedAction();
228 247
229 248 auto &helper = sqpApp->dragDropHelper();
230 249
231 250 if (!impl->hasPlaceHolder()) {
232 251 auto dragWidget = helper.getCurrentDragWidget();
233 252
234 253 if (dragWidget) {
235 254 // If the drag&drop is internal to the visualization, entering the container hide
236 255 // the dragWidget which was made visible by the dragLeaveEvent
237 256 auto parentWidget
238 257 = qobject_cast<VisualizationDragDropContainer *>(dragWidget->parentWidget());
239 258 if (parentWidget) {
240 259 dragWidget->setVisible(false);
241 260 }
242 261 }
243 262
244 263 auto canMerge = impl->allowMergeForMimeData(event->mimeData());
245 264 auto canInsert = impl->allowInsertForMimeData(event->mimeData());
246 265 impl->findPlaceHolderPosition(event->pos(), canInsert, canMerge, this);
247 266 }
248 267 else {
249 268 // do nothing
250 269 }
251 270 }
252 271 else {
253 272 event->ignore();
254 273 }
255 274
256 275 QWidget::dragEnterEvent(event);
257 276 }
258 277
259 278 void VisualizationDragDropContainer::dragLeaveEvent(QDragLeaveEvent *event)
260 279 {
261 280 Q_UNUSED(event);
262 281
263 282 auto &helper = sqpApp->dragDropHelper();
264 283
265 284 if (!impl->cursorIsInContainer(this)) {
266 285 helper.removePlaceHolder();
267 286 helper.setHightlightedDragWidget(nullptr);
268 287 impl->m_MinContainerHeight = 0;
269 288
270 289 auto dragWidget = helper.getCurrentDragWidget();
271 290 if (dragWidget) {
272 291 // dragWidget has a value only if the drag is started from the visualization
273 292 // In that case, shows the drag widget at its original place
274 293 // So the drag widget doesn't stay hidden if the drop occurs outside the visualization
275 294 // drop zone (It is not possible to catch a drop event outside of the application)
276 295
277 296 if (dragWidget) {
278 297 dragWidget->setVisible(true);
279 298 }
280 299 }
281 300 }
282 301 else {
283 302 // Leave event probably received for a child widget.
284 303 // Do nothing.
285 304 // Note: The DragLeave event, doesn't have any mean to determine who sent it.
286 305 }
287 306
288 307 QWidget::dragLeaveEvent(event);
289 308 }
290 309
291 310 void VisualizationDragDropContainer::dragMoveEvent(QDragMoveEvent *event)
292 311 {
293 312 if (impl->acceptMimeData(event->mimeData())) {
294 313 auto canMerge = impl->allowMergeForMimeData(event->mimeData());
295 314 auto canInsert = impl->allowInsertForMimeData(event->mimeData());
296 315 impl->findPlaceHolderPosition(event->pos(), canInsert, canMerge, this);
297 316 }
298 317 else {
299 318 event->ignore();
300 319 }
301 320
302 321 QWidget::dragMoveEvent(event);
303 322 }
304 323
305 324 void VisualizationDragDropContainer::dropEvent(QDropEvent *event)
306 325 {
307 326 auto &helper = sqpApp->dragDropHelper();
308 327
309 328 if (impl->acceptMimeData(event->mimeData())) {
310 329 auto dragWidget = helper.getCurrentDragWidget();
311 330 if (impl->hasPlaceHolder()) {
312 331 // drop where the placeHolder is located
313 332
314 333 auto canInsert = impl->allowInsertForMimeData(event->mimeData());
315 334 if (canInsert) {
316 335 auto droppedIndex = impl->m_Layout->indexOf(&helper.placeHolder());
317 336
318 337 if (dragWidget) {
319 338 auto dragWidgetIndex = impl->m_Layout->indexOf(dragWidget);
320 339 if (dragWidgetIndex >= 0 && dragWidgetIndex < droppedIndex) {
321 340 // Correction of the index if the drop occurs in the same container
322 341 // and if the drag is started from the visualization (in that case, the
323 342 // dragWidget is hidden)
324 343 droppedIndex -= 1;
325 344 }
326 345
327 346 dragWidget->setVisible(true);
328 347 }
329 348
330 349 event->acceptProposedAction();
331 350
332 351 helper.removePlaceHolder();
333 352
334 353 emit dropOccuredInContainer(droppedIndex, event->mimeData());
335 354 }
336 355 else {
337 356 qCWarning(LOG_VisualizationDragDropContainer()) << tr(
338 357 "VisualizationDragDropContainer::dropEvent, dropping on the placeHolder, but "
339 358 "the insertion is forbidden.");
340 359 Q_ASSERT(false);
341 360 }
342 361 }
343 362 else if (helper.getHightlightedDragWidget()) {
344 363 // drop on the highlighted widget
345 364
346 365 auto canMerge = impl->allowMergeForMimeData(event->mimeData());
347 366 if (canMerge) {
348 367 event->acceptProposedAction();
349 368 emit dropOccuredOnWidget(helper.getHightlightedDragWidget(), event->mimeData());
350 369 }
351 370 else {
352 371 qCWarning(LOG_VisualizationDragDropContainer())
353 372 << tr("VisualizationDragDropContainer::dropEvent, dropping on a widget, but "
354 373 "the merge is forbidden.");
355 374 Q_ASSERT(false);
356 375 }
357 376 }
358 377 }
359 378 else {
360 379 event->ignore();
361 380 }
362 381
363 382 sqpApp->dragDropHelper().setHightlightedDragWidget(nullptr);
364 383 impl->m_MinContainerHeight = 0;
365 384
366 385 QWidget::dropEvent(event);
367 386 }
368 387
369 388
370 389 void VisualizationDragDropContainer::VisualizationDragDropContainerPrivate::findPlaceHolderPosition(
371 390 const QPoint &pos, bool canInsert, bool canMerge,
372 391 const VisualizationDragDropContainer *container)
373 392 {
374 393 auto &helper = sqpApp->dragDropHelper();
375 394
376 395 auto absPos = container->mapToGlobal(pos);
377 auto isOnPlaceHolder = sqpApp->widgetAt(absPos) == &(helper.placeHolder());
396 auto isOnPlaceHolder = helper.placeHolder().isAncestorOf(sqpApp->widgetAt(absPos));
378 397
379 398 if (countDragWidget(container, true) == 0) {
380 399 // Drop on an empty container, just add the placeHolder at the top
381 400 helper.insertPlaceHolder(m_Layout, 0, m_PlaceHolderType, m_PlaceHolderText);
382 401 }
383 402 else if (!isOnPlaceHolder) {
384 403 auto nbDragWidget = countDragWidget(container);
385 404 if (nbDragWidget > 0) {
386 405
387 406 if (m_MinContainerHeight == 0) {
388 407 m_MinContainerHeight = container->size().height();
389 408 }
390 409
391 410 m_MinContainerHeight = qMin(m_MinContainerHeight, container->size().height());
392 411 auto graphHeight = qMax(m_MinContainerHeight / nbDragWidget, GRAPH_MINIMUM_HEIGHT);
393 412
394 413 auto posY = pos.y();
395 414 auto dropIndex = floor(posY / graphHeight);
396 415 auto zoneSize = qMin(graphHeight / 4.0, 75.0);
397 416
398 417
399 418 auto isOnTop = posY < dropIndex * graphHeight + zoneSize;
400 419 auto isOnBottom = posY > (dropIndex + 1) * graphHeight - zoneSize;
401 420
402 421 auto placeHolderIndex = m_Layout->indexOf(&(helper.placeHolder()));
403 422
404 423 auto dragWidgetHovered = getChildDragWidgetAt(container, pos);
405 424
406 425 if (canInsert && (isOnTop || isOnBottom || !canMerge)) {
407 426 if (isOnBottom) {
408 427 dropIndex += 1;
409 428 }
410 429
411 430 if (helper.getCurrentDragWidget()) {
412 431 auto dragWidgetIndex = m_Layout->indexOf(helper.getCurrentDragWidget());
413 432 if (dragWidgetIndex >= 0 && dragWidgetIndex <= dropIndex) {
414 433 // Correction of the index if the drop occurs in the same container
415 434 // and if the drag is started from the visualization (in that case, the
416 435 // dragWidget is hidden)
417 436 dropIndex += 1;
418 437 }
419 438 }
420 439
421 440 if (dropIndex != placeHolderIndex) {
422 441 helper.insertPlaceHolder(m_Layout, dropIndex, m_PlaceHolderType,
423 442 m_PlaceHolderText);
424 443 }
425 444
426 445 helper.setHightlightedDragWidget(nullptr);
427 446 }
428 447 else if (canMerge && dragWidgetHovered) {
429 448 // drop on the middle -> merge
430 449 if (hasPlaceHolder()) {
431 450 helper.removePlaceHolder();
432 451 }
433 452
434 453 helper.setHightlightedDragWidget(dragWidgetHovered);
435 454 }
436 455 else {
437 456 qCWarning(LOG_VisualizationDragDropContainer())
438 457 << tr("VisualizationDragDropContainer::findPlaceHolderPosition, no valid drop "
439 458 "action.");
440 459 }
441 460 }
442 461 else {
443 462 qCWarning(LOG_VisualizationDragDropContainer())
444 463 << tr("VisualizationDragDropContainer::findPlaceHolderPosition, no widget "
445 464 "found in the "
446 465 "container");
447 466 }
448 467 }
449 468 else {
450 469 // the mouse is hover the placeHolder
451 470 // Do nothing
452 471 }
453 472 }
@@ -1,319 +1,320
1 1 #include "Visualization/VisualizationTabWidget.h"
2 2 #include "Visualization/IVisualizationWidgetVisitor.h"
3 3 #include "ui_VisualizationTabWidget.h"
4 4
5 5 #include "Visualization/VisualizationGraphWidget.h"
6 6 #include "Visualization/VisualizationZoneWidget.h"
7 7
8 8 #include "Visualization/MacScrollBarStyle.h"
9 9
10 10 #include "Variable/VariableController.h"
11 11
12 12 #include "Common/MimeTypesDef.h"
13 13
14 14 #include "DragAndDrop/DragDropHelper.h"
15 15 #include "SqpApplication.h"
16 16
17 17 Q_LOGGING_CATEGORY(LOG_VisualizationTabWidget, "VisualizationTabWidget")
18 18
19 19 namespace {
20 20
21 21 /// Generates a default name for a new zone, according to the number of zones already displayed in
22 22 /// the tab
23 23 QString defaultZoneName(const QLayout &layout)
24 24 {
25 25 auto count = 0;
26 26 for (auto i = 0; i < layout.count(); ++i) {
27 27 if (dynamic_cast<VisualizationZoneWidget *>(layout.itemAt(i)->widget())) {
28 28 count++;
29 29 }
30 30 }
31 31
32 32 return QObject::tr("Zone %1").arg(count + 1);
33 33 }
34 34
35 35 /**
36 36 * Applies a function to all zones of the tab represented by its layout
37 37 * @param layout the layout that contains zones
38 38 * @param fun the function to apply to each zone
39 39 */
40 40 template <typename Fun>
41 41 void processZones(QLayout &layout, Fun fun)
42 42 {
43 43 for (auto i = 0; i < layout.count(); ++i) {
44 44 if (auto item = layout.itemAt(i)) {
45 45 if (auto visualizationZoneWidget
46 46 = dynamic_cast<VisualizationZoneWidget *>(item->widget())) {
47 47 fun(*visualizationZoneWidget);
48 48 }
49 49 }
50 50 }
51 51 }
52 52
53 53 } // namespace
54 54
55 55 struct VisualizationTabWidget::VisualizationTabWidgetPrivate {
56 56 explicit VisualizationTabWidgetPrivate(const QString &name) : m_Name{name} {}
57 57
58 58 QString m_Name;
59 59
60 60 #ifdef Q_OS_MAC
61 61 std::unique_ptr<MacScrollBarStyle> m_MacScrollBarStyle = std::make_unique<MacScrollBarStyle>();
62 62 #endif
63 63
64 64 void dropGraph(int index, VisualizationTabWidget *tabWidget);
65 65 void dropZone(int index, VisualizationTabWidget *tabWidget);
66 66 void dropVariables(const QList<std::shared_ptr<Variable> > &variables, int index,
67 67 VisualizationTabWidget *tabWidget);
68 68 };
69 69
70 70 VisualizationTabWidget::VisualizationTabWidget(const QString &name, QWidget *parent)
71 71 : QWidget{parent},
72 72 ui{new Ui::VisualizationTabWidget},
73 73 impl{spimpl::make_unique_impl<VisualizationTabWidgetPrivate>(name)}
74 74 {
75 75 ui->setupUi(this);
76 76
77 77 #ifdef Q_OS_MAC
78 78 impl->m_MacScrollBarStyle->selfInstallOn(ui->scrollArea, true);
79 79 #endif
80 80
81 81 ui->dragDropContainer->setPlaceHolderType(DragDropHelper::PlaceHolderType::Zone, "Zone");
82 ui->dragDropContainer->layout()->setContentsMargins(0, 0, 0, 5);
83 ui->dragDropContainer->addAcceptedMimeType(
84 MIME_TYPE_GRAPH, VisualizationDragDropContainer::DropBehavior::Inserted);
85 ui->dragDropContainer->addAcceptedMimeType(
86 MIME_TYPE_ZONE, VisualizationDragDropContainer::DropBehavior::Inserted);
87 ui->dragDropContainer->addAcceptedMimeType(
88 MIME_TYPE_VARIABLE_LIST, VisualizationDragDropContainer::DropBehavior::Inserted);
82 ui->dragDropContainer->layout()->setContentsMargins(0, 0, 0, 12);
83 ui->dragDropContainer->layout()->setSpacing(0);
84 ui->dragDropContainer->setMimeType(MIME_TYPE_GRAPH,
85 VisualizationDragDropContainer::DropBehavior::Inserted);
86 ui->dragDropContainer->setMimeType(MIME_TYPE_ZONE,
87 VisualizationDragDropContainer::DropBehavior::Inserted);
88 ui->dragDropContainer->setMimeType(MIME_TYPE_VARIABLE_LIST,
89 VisualizationDragDropContainer::DropBehavior::Inserted);
89 90
90 91 ui->dragDropContainer->setAcceptMimeDataFunction([this](auto mimeData) {
91 92 return sqpApp->dragDropHelper().checkMimeDataForVisualization(mimeData,
92 93 ui->dragDropContainer);
93 94 });
94 95
95 96 connect(ui->dragDropContainer, &VisualizationDragDropContainer::dropOccuredInContainer, this,
96 97 &VisualizationTabWidget::dropMimeData);
97 98
98 99 sqpApp->dragDropHelper().addDragDropScrollArea(ui->scrollArea);
99 100
100 101 // Widget is deleted when closed
101 102 setAttribute(Qt::WA_DeleteOnClose);
102 103 }
103 104
104 105 VisualizationTabWidget::~VisualizationTabWidget()
105 106 {
106 107 sqpApp->dragDropHelper().removeDragDropScrollArea(ui->scrollArea);
107 108 delete ui;
108 109 }
109 110
110 111 void VisualizationTabWidget::addZone(VisualizationZoneWidget *zoneWidget)
111 112 {
112 113 ui->dragDropContainer->addDragWidget(zoneWidget);
113 114 }
114 115
115 116 void VisualizationTabWidget::insertZone(int index, VisualizationZoneWidget *zoneWidget)
116 117 {
117 118 ui->dragDropContainer->insertDragWidget(index, zoneWidget);
118 119 }
119 120
120 121 VisualizationZoneWidget *VisualizationTabWidget::createZone(std::shared_ptr<Variable> variable)
121 122 {
122 123 return createZone({variable}, -1);
123 124 }
124 125
125 126 VisualizationZoneWidget *
126 127 VisualizationTabWidget::createZone(const QList<std::shared_ptr<Variable> > &variables, int index)
127 128 {
128 129 auto zoneWidget = createEmptyZone(index);
129 130
130 131 // Creates a new graph into the zone
131 132 zoneWidget->createGraph(variables, index);
132 133
133 134 return zoneWidget;
134 135 }
135 136
136 137 VisualizationZoneWidget *VisualizationTabWidget::createEmptyZone(int index)
137 138 {
138 139 auto zoneWidget
139 140 = new VisualizationZoneWidget{defaultZoneName(*ui->dragDropContainer->layout()), this};
140 141 this->insertZone(index, zoneWidget);
141 142
142 143 return zoneWidget;
143 144 }
144 145
145 146 void VisualizationTabWidget::accept(IVisualizationWidgetVisitor *visitor)
146 147 {
147 148 if (visitor) {
148 149 visitor->visitEnter(this);
149 150
150 151 // Apply visitor to zone children: widgets different from zones are not visited (no action)
151 152 processZones(tabLayout(), [visitor](VisualizationZoneWidget &zoneWidget) {
152 153 zoneWidget.accept(visitor);
153 154 });
154 155
155 156 visitor->visitLeave(this);
156 157 }
157 158 else {
158 159 qCCritical(LOG_VisualizationTabWidget()) << tr("Can't visit widget : the visitor is null");
159 160 }
160 161 }
161 162
162 163 bool VisualizationTabWidget::canDrop(const Variable &variable) const
163 164 {
164 165 // A tab can always accomodate a variable
165 166 Q_UNUSED(variable);
166 167 return true;
167 168 }
168 169
169 170 bool VisualizationTabWidget::contains(const Variable &variable) const
170 171 {
171 172 Q_UNUSED(variable);
172 173 return false;
173 174 }
174 175
175 176 QString VisualizationTabWidget::name() const
176 177 {
177 178 return impl->m_Name;
178 179 }
179 180
180 181 void VisualizationTabWidget::closeEvent(QCloseEvent *event)
181 182 {
182 183 // Closes zones in the tab
183 184 processZones(tabLayout(), [](VisualizationZoneWidget &zoneWidget) { zoneWidget.close(); });
184 185
185 186 QWidget::closeEvent(event);
186 187 }
187 188
188 189 QLayout &VisualizationTabWidget::tabLayout() const noexcept
189 190 {
190 191 return *ui->dragDropContainer->layout();
191 192 }
192 193
193 194 void VisualizationTabWidget::dropMimeData(int index, const QMimeData *mimeData)
194 195 {
195 196 if (mimeData->hasFormat(MIME_TYPE_GRAPH)) {
196 197 impl->dropGraph(index, this);
197 198 }
198 199 else if (mimeData->hasFormat(MIME_TYPE_ZONE)) {
199 200 impl->dropZone(index, this);
200 201 }
201 202 else if (mimeData->hasFormat(MIME_TYPE_VARIABLE_LIST)) {
202 203 auto variables = sqpApp->variableController().variablesForMimeData(
203 204 mimeData->data(MIME_TYPE_VARIABLE_LIST));
204 205 impl->dropVariables(variables, index, this);
205 206 }
206 207 else {
207 208 qCWarning(LOG_VisualizationZoneWidget())
208 209 << tr("VisualizationTabWidget::dropMimeData, unknown MIME data received.");
209 210 }
210 211 }
211 212
212 213 void VisualizationTabWidget::VisualizationTabWidgetPrivate::dropGraph(
213 214 int index, VisualizationTabWidget *tabWidget)
214 215 {
215 216 auto &helper = sqpApp->dragDropHelper();
216 217
217 218 auto graphWidget = qobject_cast<VisualizationGraphWidget *>(helper.getCurrentDragWidget());
218 219 if (!graphWidget) {
219 220 qCWarning(LOG_VisualizationZoneWidget())
220 221 << tr("VisualizationTabWidget::dropGraph, drop aborted, the dropped graph is not "
221 222 "found or invalid.");
222 223 Q_ASSERT(false);
223 224 return;
224 225 }
225 226
226 227 auto parentDragDropContainer
227 228 = qobject_cast<VisualizationDragDropContainer *>(graphWidget->parentWidget());
228 229 if (!parentDragDropContainer) {
229 230 qCWarning(LOG_VisualizationZoneWidget())
230 231 << tr("VisualizationTabWidget::dropGraph, drop aborted, the parent container of "
231 232 "the dropped graph is not found.");
232 233 Q_ASSERT(false);
233 234 return;
234 235 }
235 236
236 237 auto nbGraph = parentDragDropContainer->countDragWidget();
237 238
238 239 const auto &variables = graphWidget->variables();
239 240
240 241 if (!variables.isEmpty()) {
241 242 // Abort the requests for the variables (if any)
242 243 // Commented, because it's not sure if it's needed or not
243 244 // for (const auto& var : variables)
244 245 //{
245 246 // sqpApp->variableController().onAbortProgressRequested(var);
246 247 //}
247 248
248 249 if (nbGraph == 1) {
249 250 // This is the only graph in the previous zone, close the zone
250 251 helper.delayedCloseWidget(graphWidget->parentZoneWidget());
251 252 }
252 253 else {
253 254 // Close the graph
254 255 helper.delayedCloseWidget(graphWidget);
255 256 }
256 257
257 258 tabWidget->createZone(variables, index);
258 259 }
259 260 else {
260 261 // The graph is empty, create an empty zone and move the graph inside
261 262
262 263 auto parentZoneWidget = graphWidget->parentZoneWidget();
263 264
264 265 parentDragDropContainer->layout()->removeWidget(graphWidget);
265 266
266 267 auto zoneWidget = tabWidget->createEmptyZone(index);
267 268 zoneWidget->addGraph(graphWidget);
268 269
269 270 // Close the old zone if it was the only graph inside
270 271 if (nbGraph == 1) {
271 272 helper.delayedCloseWidget(parentZoneWidget);
272 273 }
273 274 }
274 275 }
275 276
276 277 void VisualizationTabWidget::VisualizationTabWidgetPrivate::dropZone(
277 278 int index, VisualizationTabWidget *tabWidget)
278 279 {
279 280 auto &helper = sqpApp->dragDropHelper();
280 281
281 282 auto zoneWidget = qobject_cast<VisualizationZoneWidget *>(helper.getCurrentDragWidget());
282 283 if (!zoneWidget) {
283 284 qCWarning(LOG_VisualizationZoneWidget())
284 285 << tr("VisualizationTabWidget::dropZone, drop aborted, the dropped zone is not "
285 286 "found or invalid.");
286 287 Q_ASSERT(false);
287 288 return;
288 289 }
289 290
290 291 auto parentDragDropContainer
291 292 = qobject_cast<VisualizationDragDropContainer *>(zoneWidget->parentWidget());
292 293 if (!parentDragDropContainer) {
293 294 qCWarning(LOG_VisualizationZoneWidget())
294 295 << tr("VisualizationTabWidget::dropZone, drop aborted, the parent container of "
295 296 "the dropped zone is not found.");
296 297 Q_ASSERT(false);
297 298 return;
298 299 }
299 300
300 301 // Simple move of the zone, no variable operation associated
301 302 parentDragDropContainer->layout()->removeWidget(zoneWidget);
302 303 tabWidget->ui->dragDropContainer->insertDragWidget(index, zoneWidget);
303 304 }
304 305
305 306 void VisualizationTabWidget::VisualizationTabWidgetPrivate::dropVariables(
306 307 const QList<std::shared_ptr<Variable> > &variables, int index,
307 308 VisualizationTabWidget *tabWidget)
308 309 {
309 310 // Note: the AcceptMimeDataFunction (set on the drop container) ensure there is a single and
310 311 // compatible variable here
311 312 if (variables.count() > 1) {
312 313 qCWarning(LOG_VisualizationZoneWidget())
313 314 << tr("VisualizationTabWidget::dropVariables, dropping multiple variables, operation "
314 315 "aborted.");
315 316 return;
316 317 }
317 318
318 319 tabWidget->createZone(variables, index);
319 320 }
@@ -1,500 +1,507
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 ui->dragDropContainer->addAcceptedMimeType(
106 MIME_TYPE_GRAPH, VisualizationDragDropContainer::DropBehavior::Inserted);
107 ui->dragDropContainer->addAcceptedMimeType(
105 ui->dragDropContainer->setMimeType(MIME_TYPE_GRAPH,
106 VisualizationDragDropContainer::DropBehavior::Inserted);
107 ui->dragDropContainer->setMimeType(
108 108 MIME_TYPE_VARIABLE_LIST, VisualizationDragDropContainer::DropBehavior::InsertedAndMerged);
109 ui->dragDropContainer->addAcceptedMimeType(
110 MIME_TYPE_TIME_RANGE, VisualizationDragDropContainer::DropBehavior::Merged);
109 ui->dragDropContainer->setMimeType(MIME_TYPE_TIME_RANGE,
110 VisualizationDragDropContainer::DropBehavior::Merged);
111 ui->dragDropContainer->setMimeType(MIME_TYPE_ZONE,
112 VisualizationDragDropContainer::DropBehavior::Forbidden);
111 113 ui->dragDropContainer->setAcceptMimeDataFunction([this](auto mimeData) {
112 114 return sqpApp->dragDropHelper().checkMimeDataForVisualization(mimeData,
113 115 ui->dragDropContainer);
114 116 });
115 117
116 118 connect(ui->dragDropContainer, &VisualizationDragDropContainer::dropOccuredInContainer, this,
117 119 &VisualizationZoneWidget::dropMimeData);
118 120 connect(ui->dragDropContainer, &VisualizationDragDropContainer::dropOccuredOnWidget, this,
119 121 &VisualizationZoneWidget::dropMimeDataOnGraph);
120 122
121 123 // 'Close' options : widget is deleted when closed
122 124 setAttribute(Qt::WA_DeleteOnClose);
123 125 connect(ui->closeButton, &QToolButton::clicked, this, &VisualizationZoneWidget::close);
124 126 ui->closeButton->setIcon(sqpApp->style()->standardIcon(QStyle::SP_TitleBarCloseButton));
125 127
126 128 // Synchronisation id
127 129 QMetaObject::invokeMethod(&sqpApp->variableController(), "onAddSynchronizationGroupId",
128 130 Qt::QueuedConnection, Q_ARG(QUuid, impl->m_SynchronisationGroupId));
129 131 }
130 132
131 133 VisualizationZoneWidget::~VisualizationZoneWidget()
132 134 {
133 135 delete ui;
134 136 }
135 137
136 138 void VisualizationZoneWidget::addGraph(VisualizationGraphWidget *graphWidget)
137 139 {
138 140 // Synchronize new graph with others in the zone
139 141 impl->m_Synchronizer->addGraph(*graphWidget);
140 142
141 143 ui->dragDropContainer->addDragWidget(graphWidget);
142 144 }
143 145
144 146 void VisualizationZoneWidget::insertGraph(int index, VisualizationGraphWidget *graphWidget)
145 147 {
146 148 // Synchronize new graph with others in the zone
147 149 impl->m_Synchronizer->addGraph(*graphWidget);
148 150
149 151 ui->dragDropContainer->insertDragWidget(index, graphWidget);
150 152 }
151 153
152 154 VisualizationGraphWidget *VisualizationZoneWidget::createGraph(std::shared_ptr<Variable> variable)
153 155 {
154 156 return createGraph(variable, -1);
155 157 }
156 158
157 159 VisualizationGraphWidget *VisualizationZoneWidget::createGraph(std::shared_ptr<Variable> variable,
158 160 int index)
159 161 {
160 162 auto graphWidget
161 163 = new VisualizationGraphWidget{defaultGraphName(*ui->dragDropContainer->layout()), this};
162 164
163 165
164 166 // Set graph properties
165 167 graphWidget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::MinimumExpanding);
166 168 graphWidget->setMinimumHeight(GRAPH_MINIMUM_HEIGHT);
167 169
168 170
169 171 // Lambda to synchronize zone widget
170 172 auto synchronizeZoneWidget = [this, graphWidget](const SqpRange &graphRange,
171 173 const SqpRange &oldGraphRange) {
172 174
173 175 auto zoomType = VariableController::getZoomType(graphRange, oldGraphRange);
174 176 auto frameLayout = ui->dragDropContainer->layout();
175 177 for (auto i = 0; i < frameLayout->count(); ++i) {
176 178 auto graphChild
177 179 = dynamic_cast<VisualizationGraphWidget *>(frameLayout->itemAt(i)->widget());
178 180 if (graphChild && (graphChild != graphWidget)) {
179 181
180 182 auto graphChildRange = graphChild->graphRange();
181 183 switch (zoomType) {
182 184 case AcquisitionZoomType::ZoomIn: {
183 185 auto deltaLeft = graphRange.m_TStart - oldGraphRange.m_TStart;
184 186 auto deltaRight = oldGraphRange.m_TEnd - graphRange.m_TEnd;
185 187 graphChildRange.m_TStart += deltaLeft;
186 188 graphChildRange.m_TEnd -= deltaRight;
187 189 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: ZoomIn");
188 190 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: deltaLeft")
189 191 << deltaLeft;
190 192 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: deltaRight")
191 193 << deltaRight;
192 194 qCDebug(LOG_VisualizationZoneWidget())
193 195 << tr("TORM: dt") << graphRange.m_TEnd - graphRange.m_TStart;
194 196
195 197 break;
196 198 }
197 199
198 200 case AcquisitionZoomType::ZoomOut: {
199 201 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: ZoomOut");
200 202 auto deltaLeft = oldGraphRange.m_TStart - graphRange.m_TStart;
201 203 auto deltaRight = graphRange.m_TEnd - oldGraphRange.m_TEnd;
202 204 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: deltaLeft")
203 205 << deltaLeft;
204 206 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: deltaRight")
205 207 << deltaRight;
206 208 qCDebug(LOG_VisualizationZoneWidget())
207 209 << tr("TORM: dt") << graphRange.m_TEnd - graphRange.m_TStart;
208 210 graphChildRange.m_TStart -= deltaLeft;
209 211 graphChildRange.m_TEnd += deltaRight;
210 212 break;
211 213 }
212 214 case AcquisitionZoomType::PanRight: {
213 215 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: PanRight");
214 216 auto deltaLeft = graphRange.m_TStart - oldGraphRange.m_TStart;
215 217 auto deltaRight = graphRange.m_TEnd - oldGraphRange.m_TEnd;
216 218 graphChildRange.m_TStart += deltaLeft;
217 219 graphChildRange.m_TEnd += deltaRight;
218 220 qCDebug(LOG_VisualizationZoneWidget())
219 221 << tr("TORM: dt") << graphRange.m_TEnd - graphRange.m_TStart;
220 222 break;
221 223 }
222 224 case AcquisitionZoomType::PanLeft: {
223 225 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: PanLeft");
224 226 auto deltaLeft = oldGraphRange.m_TStart - graphRange.m_TStart;
225 227 auto deltaRight = oldGraphRange.m_TEnd - graphRange.m_TEnd;
226 228 graphChildRange.m_TStart -= deltaLeft;
227 229 graphChildRange.m_TEnd -= deltaRight;
228 230 break;
229 231 }
230 232 case AcquisitionZoomType::Unknown: {
231 233 qCDebug(LOG_VisualizationZoneWidget())
232 234 << tr("Impossible to synchronize: zoom type unknown");
233 235 break;
234 236 }
235 237 default:
236 238 qCCritical(LOG_VisualizationZoneWidget())
237 239 << tr("Impossible to synchronize: zoom type not take into account");
238 240 // No action
239 241 break;
240 242 }
241 243 graphChild->enableAcquisition(false);
242 244 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: Range before: ")
243 245 << graphChild->graphRange();
244 246 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: Range after : ")
245 247 << graphChildRange;
246 248 qCDebug(LOG_VisualizationZoneWidget())
247 249 << tr("TORM: child dt") << graphChildRange.m_TEnd - graphChildRange.m_TStart;
248 250 graphChild->setGraphRange(graphChildRange);
249 251 graphChild->enableAcquisition(true);
250 252 }
251 253 }
252 254 };
253 255
254 256 // connection for synchronization
255 257 connect(graphWidget, &VisualizationGraphWidget::synchronize, synchronizeZoneWidget);
256 258 connect(graphWidget, &VisualizationGraphWidget::variableAdded, this,
257 259 &VisualizationZoneWidget::onVariableAdded);
258 260 connect(graphWidget, &VisualizationGraphWidget::variableAboutToBeRemoved, this,
259 261 &VisualizationZoneWidget::onVariableAboutToBeRemoved);
260 262
261 263 auto range = SqpRange{};
262 264 if (auto firstGraph = impl->firstGraph(this)) {
263 265 // Case of a new graph in a existant zone
264 266 range = firstGraph->graphRange();
265 267 }
266 268 else {
267 269 // Case of a new graph as the first of the zone
268 270 range = variable->range();
269 271 }
270 272
271 273 this->insertGraph(index, graphWidget);
272 274
273 275 graphWidget->addVariable(variable, range);
274 276 graphWidget->setYRange(variable);
275 277
276 278 return graphWidget;
277 279 }
278 280
279 281 VisualizationGraphWidget *
280 282 VisualizationZoneWidget::createGraph(const QList<std::shared_ptr<Variable> > variables, int index)
281 283 {
282 284 if (variables.isEmpty()) {
283 285 return nullptr;
284 286 }
285 287
286 288 auto graphWidget = createGraph(variables.first(), index);
287 289 for (auto variableIt = variables.cbegin() + 1; variableIt != variables.cend(); ++variableIt) {
288 290 graphWidget->addVariable(*variableIt, graphWidget->graphRange());
289 291 }
290 292
291 293 return graphWidget;
292 294 }
293 295
294 296 void VisualizationZoneWidget::accept(IVisualizationWidgetVisitor *visitor)
295 297 {
296 298 if (visitor) {
297 299 visitor->visitEnter(this);
298 300
299 301 // Apply visitor to graph children: widgets different from graphs are not visited (no
300 302 // action)
301 303 processGraphs(
302 304 *ui->dragDropContainer->layout(),
303 305 [visitor](VisualizationGraphWidget &graphWidget) { graphWidget.accept(visitor); });
304 306
305 307 visitor->visitLeave(this);
306 308 }
307 309 else {
308 310 qCCritical(LOG_VisualizationZoneWidget()) << tr("Can't visit widget : the visitor is null");
309 311 }
310 312 }
311 313
312 314 bool VisualizationZoneWidget::canDrop(const Variable &variable) const
313 315 {
314 316 // A tab can always accomodate a variable
315 317 Q_UNUSED(variable);
316 318 return true;
317 319 }
318 320
319 321 bool VisualizationZoneWidget::contains(const Variable &variable) const
320 322 {
321 323 Q_UNUSED(variable);
322 324 return false;
323 325 }
324 326
325 327 QString VisualizationZoneWidget::name() const
326 328 {
327 329 return ui->zoneNameLabel->text();
328 330 }
329 331
330 332 QMimeData *VisualizationZoneWidget::mimeData() const
331 333 {
332 334 auto mimeData = new QMimeData;
333 335 mimeData->setData(MIME_TYPE_ZONE, QByteArray{});
334 336
337 if (auto firstGraph = impl->firstGraph(this)) {
338 auto timeRangeData = TimeController::mimeDataForTimeRange(firstGraph->graphRange());
339 mimeData->setData(MIME_TYPE_TIME_RANGE, timeRangeData);
340 }
341
335 342 return mimeData;
336 343 }
337 344
338 345 bool VisualizationZoneWidget::isDragAllowed() const
339 346 {
340 347 return true;
341 348 }
342 349
343 350 void VisualizationZoneWidget::closeEvent(QCloseEvent *event)
344 351 {
345 352 // Closes graphs in the zone
346 353 processGraphs(*ui->dragDropContainer->layout(),
347 354 [](VisualizationGraphWidget &graphWidget) { graphWidget.close(); });
348 355
349 356 // Delete synchronization group from variable controller
350 357 QMetaObject::invokeMethod(&sqpApp->variableController(), "onRemoveSynchronizationGroupId",
351 358 Qt::QueuedConnection, Q_ARG(QUuid, impl->m_SynchronisationGroupId));
352 359
353 360 QWidget::closeEvent(event);
354 361 }
355 362
356 363 void VisualizationZoneWidget::onVariableAdded(std::shared_ptr<Variable> variable)
357 364 {
358 365 QMetaObject::invokeMethod(&sqpApp->variableController(), "onAddSynchronized",
359 366 Qt::QueuedConnection, Q_ARG(std::shared_ptr<Variable>, variable),
360 367 Q_ARG(QUuid, impl->m_SynchronisationGroupId));
361 368 }
362 369
363 370 void VisualizationZoneWidget::onVariableAboutToBeRemoved(std::shared_ptr<Variable> variable)
364 371 {
365 372 QMetaObject::invokeMethod(&sqpApp->variableController(), "desynchronize", Qt::QueuedConnection,
366 373 Q_ARG(std::shared_ptr<Variable>, variable),
367 374 Q_ARG(QUuid, impl->m_SynchronisationGroupId));
368 375 }
369 376
370 377 void VisualizationZoneWidget::dropMimeData(int index, const QMimeData *mimeData)
371 378 {
372 379 if (mimeData->hasFormat(MIME_TYPE_GRAPH)) {
373 380 impl->dropGraph(index, this);
374 381 }
375 382 else if (mimeData->hasFormat(MIME_TYPE_VARIABLE_LIST)) {
376 383 auto variables = sqpApp->variableController().variablesForMimeData(
377 384 mimeData->data(MIME_TYPE_VARIABLE_LIST));
378 385 impl->dropVariables(variables, index, this);
379 386 }
380 387 else {
381 388 qCWarning(LOG_VisualizationZoneWidget())
382 389 << tr("VisualizationZoneWidget::dropMimeData, unknown MIME data received.");
383 390 }
384 391 }
385 392
386 393 void VisualizationZoneWidget::dropMimeDataOnGraph(VisualizationDragWidget *dragWidget,
387 394 const QMimeData *mimeData)
388 395 {
389 396 auto graphWidget = qobject_cast<VisualizationGraphWidget *>(dragWidget);
390 397 if (!graphWidget) {
391 398 qCWarning(LOG_VisualizationZoneWidget())
392 399 << tr("VisualizationZoneWidget::dropMimeDataOnGraph, dropping in an unknown widget, "
393 400 "drop aborted");
394 401 Q_ASSERT(false);
395 402 return;
396 403 }
397 404
398 405 if (mimeData->hasFormat(MIME_TYPE_VARIABLE_LIST)) {
399 406 auto variables = sqpApp->variableController().variablesForMimeData(
400 407 mimeData->data(MIME_TYPE_VARIABLE_LIST));
401 408 for (const auto &var : variables) {
402 409 graphWidget->addVariable(var, graphWidget->graphRange());
403 410 }
404 411 }
405 412 else if (mimeData->hasFormat(MIME_TYPE_TIME_RANGE)) {
406 413 auto range = TimeController::timeRangeForMimeData(mimeData->data(MIME_TYPE_TIME_RANGE));
407 414 graphWidget->setGraphRange(range);
408 415 }
409 416 else {
410 417 qCWarning(LOG_VisualizationZoneWidget())
411 418 << tr("VisualizationZoneWidget::dropMimeDataOnGraph, unknown MIME data received.");
412 419 }
413 420 }
414 421
415 422 void VisualizationZoneWidget::VisualizationZoneWidgetPrivate::dropGraph(
416 423 int index, VisualizationZoneWidget *zoneWidget)
417 424 {
418 425 auto &helper = sqpApp->dragDropHelper();
419 426
420 427 auto graphWidget = qobject_cast<VisualizationGraphWidget *>(helper.getCurrentDragWidget());
421 428 if (!graphWidget) {
422 429 qCWarning(LOG_VisualizationZoneWidget())
423 430 << tr("VisualizationZoneWidget::dropGraph, drop aborted, the dropped graph is not "
424 431 "found or invalid.");
425 432 Q_ASSERT(false);
426 433 return;
427 434 }
428 435
429 436 auto parentDragDropContainer
430 437 = qobject_cast<VisualizationDragDropContainer *>(graphWidget->parentWidget());
431 438 if (!parentDragDropContainer) {
432 439 qCWarning(LOG_VisualizationZoneWidget())
433 440 << tr("VisualizationZoneWidget::dropGraph, drop aborted, the parent container of "
434 441 "the dropped graph is not found.");
435 442 Q_ASSERT(false);
436 443 return;
437 444 }
438 445
439 446 const auto &variables = graphWidget->variables();
440 447
441 448 if (parentDragDropContainer != zoneWidget->ui->dragDropContainer && !variables.isEmpty()) {
442 449 // The drop didn't occur in the same zone
443 450
444 451 // Abort the requests for the variables (if any)
445 452 // Commented, because it's not sure if it's needed or not
446 453 // for (const auto& var : variables)
447 454 //{
448 455 // sqpApp->variableController().onAbortProgressRequested(var);
449 456 //}
450 457
451 458 auto previousParentZoneWidget = graphWidget->parentZoneWidget();
452 459 auto nbGraph = parentDragDropContainer->countDragWidget();
453 460 if (nbGraph == 1) {
454 461 // This is the only graph in the previous zone, close the zone
455 462 helper.delayedCloseWidget(previousParentZoneWidget);
456 463 }
457 464 else {
458 465 // Close the graph
459 466 helper.delayedCloseWidget(graphWidget);
460 467 }
461 468
462 469 // Creates the new graph in the zone
463 470 zoneWidget->createGraph(variables, index);
464 471 }
465 472 else {
466 473 // The drop occurred in the same zone or the graph is empty
467 474 // Simple move of the graph, no variable operation associated
468 475 parentDragDropContainer->layout()->removeWidget(graphWidget);
469 476
470 477 if (variables.isEmpty() && parentDragDropContainer != zoneWidget->ui->dragDropContainer) {
471 478 // The graph is empty and dropped in a different zone.
472 479 // Take the range of the first graph in the zone (if existing).
473 480 auto layout = zoneWidget->ui->dragDropContainer->layout();
474 481 if (layout->count() > 0) {
475 482 if (auto visualizationGraphWidget
476 483 = qobject_cast<VisualizationGraphWidget *>(layout->itemAt(0)->widget())) {
477 484 graphWidget->setGraphRange(visualizationGraphWidget->graphRange());
478 485 }
479 486 }
480 487 }
481 488
482 489 zoneWidget->ui->dragDropContainer->insertDragWidget(index, graphWidget);
483 490 }
484 491 }
485 492
486 493 void VisualizationZoneWidget::VisualizationZoneWidgetPrivate::dropVariables(
487 494 const QList<std::shared_ptr<Variable> > &variables, int index,
488 495 VisualizationZoneWidget *zoneWidget)
489 496 {
490 497 // Note: the AcceptMimeDataFunction (set on the drop container) ensure there is a single and
491 498 // compatible variable here
492 499 if (variables.count() > 1) {
493 500 qCWarning(LOG_VisualizationZoneWidget())
494 501 << tr("VisualizationZoneWidget::dropVariables, dropping multiple variables, operation "
495 502 "aborted.");
496 503 return;
497 504 }
498 505
499 506 zoneWidget->createGraph(variables, index);
500 507 }
@@ -1,125 +1,125
1 1 <?xml version="1.0" encoding="UTF-8"?>
2 2 <ui version="4.0">
3 3 <class>VisualizationZoneWidget</class>
4 4 <widget class="QWidget" name="VisualizationZoneWidget">
5 5 <property name="geometry">
6 6 <rect>
7 7 <x>0</x>
8 8 <y>0</y>
9 9 <width>400</width>
10 10 <height>300</height>
11 11 </rect>
12 12 </property>
13 13 <property name="windowTitle">
14 14 <string>Form</string>
15 15 </property>
16 16 <layout class="QVBoxLayout" name="verticalLayout_2">
17 17 <property name="spacing">
18 18 <number>0</number>
19 19 </property>
20 20 <property name="leftMargin">
21 21 <number>0</number>
22 22 </property>
23 23 <property name="topMargin">
24 24 <number>0</number>
25 25 </property>
26 26 <property name="rightMargin">
27 27 <number>0</number>
28 28 </property>
29 29 <property name="bottomMargin">
30 <number>0</number>
30 <number>6</number>
31 31 </property>
32 32 <item>
33 33 <widget class="QWidget" name="infobar" native="true">
34 34 <property name="sizePolicy">
35 35 <sizepolicy hsizetype="Preferred" vsizetype="Minimum">
36 36 <horstretch>0</horstretch>
37 37 <verstretch>0</verstretch>
38 38 </sizepolicy>
39 39 </property>
40 40 <layout class="QHBoxLayout" name="horizontalLayout">
41 41 <property name="spacing">
42 42 <number>0</number>
43 43 </property>
44 44 <property name="leftMargin">
45 45 <number>0</number>
46 46 </property>
47 47 <property name="topMargin">
48 48 <number>0</number>
49 49 </property>
50 50 <property name="rightMargin">
51 51 <number>0</number>
52 52 </property>
53 53 <property name="bottomMargin">
54 54 <number>0</number>
55 55 </property>
56 56 <item>
57 57 <widget class="QLabel" name="zoneNameLabel">
58 58 <property name="styleSheet">
59 59 <string notr="true">color: rgb(127, 127, 127);
60 60 </string>
61 61 </property>
62 62 <property name="text">
63 63 <string>TextLabel</string>
64 64 </property>
65 65 </widget>
66 66 </item>
67 67 <item>
68 68 <widget class="QToolButton" name="closeButton">
69 69 <property name="styleSheet">
70 70 <string notr="true">background-color: transparent;</string>
71 71 </property>
72 72 <property name="text">
73 73 <string>Close</string>
74 74 </property>
75 75 </widget>
76 76 </item>
77 77 </layout>
78 78 </widget>
79 79 </item>
80 80 <item>
81 81 <widget class="VisualizationDragDropContainer" name="dragDropContainer">
82 82 <property name="sizePolicy">
83 83 <sizepolicy hsizetype="Preferred" vsizetype="Expanding">
84 84 <horstretch>0</horstretch>
85 85 <verstretch>0</verstretch>
86 86 </sizepolicy>
87 87 </property>
88 88 <property name="frameShape">
89 89 <enum>QFrame::Box</enum>
90 90 </property>
91 91 <property name="frameShadow">
92 92 <enum>QFrame::Raised</enum>
93 93 </property>
94 94 <property name="lineWidth">
95 95 <number>1</number>
96 96 </property>
97 97 <layout class="QVBoxLayout" name="verticalLayout">
98 98 <property name="leftMargin">
99 99 <number>0</number>
100 100 </property>
101 101 <property name="topMargin">
102 102 <number>0</number>
103 103 </property>
104 104 <property name="rightMargin">
105 105 <number>0</number>
106 106 </property>
107 107 <property name="bottomMargin">
108 108 <number>0</number>
109 109 </property>
110 110 </layout>
111 111 </widget>
112 112 </item>
113 113 </layout>
114 114 </widget>
115 115 <customwidgets>
116 116 <customwidget>
117 117 <class>VisualizationDragDropContainer</class>
118 118 <extends>QFrame</extends>
119 119 <header>Visualization/VisualizationDragDropContainer.h</header>
120 120 <container>1</container>
121 121 </customwidget>
122 122 </customwidgets>
123 123 <resources/>
124 124 <connections/>
125 125 </ui>
General Comments 0
You need to be logged in to leave comments. Login now