##// END OF EJS Templates
Merge branch 'feature/CatalogueCatalogue' into develop
mperrinel -
r1305:2a3a8fe0212d merge
parent child
Show More
@@ -1,84 +1,86
1 1 #ifndef SCIQLOP_CATALOGUECONTROLLER_H
2 2 #define SCIQLOP_CATALOGUECONTROLLER_H
3 3
4 4 #include "CoreGlobal.h"
5 5
6 6 #include <Data/SqpRange.h>
7 7
8 8 #include <QLoggingCategory>
9 9 #include <QObject>
10 10 #include <QUuid>
11 11
12 12 #include <Common/spimpl.h>
13 13
14 14 #include <memory>
15 15
16 16 class DBCatalogue;
17 17 class DBEvent;
18 18 class DBEventProduct;
19 19
20 20 Q_DECLARE_LOGGING_CATEGORY(LOG_CatalogueController)
21 21
22 22 class DataSourceItem;
23 23 class Variable;
24 24
25 25 /**
26 26 * @brief The CatalogueController class aims to handle catalogues and event using the CatalogueAPI
27 27 * library.
28 28 */
29 29 class SCIQLOP_CORE_EXPORT CatalogueController : public QObject {
30 30 Q_OBJECT
31 31 public:
32 32 explicit CatalogueController(QObject *parent = 0);
33 33 virtual ~CatalogueController();
34 34
35 35 // DB
36 36 QStringList getRepositories() const;
37 37 void addDB(const QString &dbPath);
38 38 void saveDB(const QString &destinationPath, const QString &repository);
39 39
40 40 // Event
41 41 /// retrieveEvents with empty repository retrieve them from the default repository
42 42 std::list<std::shared_ptr<DBEvent> > retrieveEvents(const QString &repository) const;
43 43 std::list<std::shared_ptr<DBEvent> > retrieveAllEvents() const;
44 44
45 45 void addEvent(std::shared_ptr<DBEvent> event);
46 46 void updateEvent(std::shared_ptr<DBEvent> event);
47 47 void updateEventProduct(std::shared_ptr<DBEventProduct> eventProduct);
48 48 void removeEvent(std::shared_ptr<DBEvent> event);
49 49 // void trashEvent(std::shared_ptr<DBEvent> event);
50 50 // void restore(std::shared_ptr<DBEvent> event);
51 51 void saveEvent(std::shared_ptr<DBEvent> event);
52 52 void discardEvent(std::shared_ptr<DBEvent> event, bool &removed);
53 53 bool eventHasChanges(std::shared_ptr<DBEvent> event) const;
54 54
55 55 // Catalogue
56 56 std::list<std::shared_ptr<DBEvent> >
57 57 retrieveEventsFromCatalogue(std::shared_ptr<DBCatalogue> catalogue) const;
58 // bool createCatalogue(const QString &name, QVector<QUuid> eventList);
58
59 59 /// retrieveEvents with empty repository retrieve them from the default repository
60 60 std::list<std::shared_ptr<DBCatalogue> > retrieveCatalogues(const QString &repository
61 61 = QString()) const;
62 void addCatalogue(std::shared_ptr<DBCatalogue> catalogue);
62 63 void updateCatalogue(std::shared_ptr<DBCatalogue> catalogue);
63 64 void removeCatalogue(std::shared_ptr<DBCatalogue> catalogue);
64 65 void saveCatalogue(std::shared_ptr<DBCatalogue> catalogue);
66 void discardCatalogue(std::shared_ptr<DBCatalogue> catalogue, bool &removed);
65 67
66 68 void saveAll();
67 69 bool hasChanges() const;
68 70
69 71 /// Returns the MIME data associated to a list of variables
70 72 QByteArray mimeDataForEvents(const QVector<std::shared_ptr<DBEvent> > &events) const;
71 73
72 74 /// Returns the list of variables contained in a MIME data
73 75 QVector<std::shared_ptr<DBEvent> > eventsForMimeData(const QByteArray &mimeData) const;
74 76
75 77 public slots:
76 78 /// Manage init/end of the controller
77 79 void initialize();
78 80
79 81 private:
80 82 class CatalogueControllerPrivate;
81 83 spimpl::unique_impl_ptr<CatalogueControllerPrivate> impl;
82 84 };
83 85
84 86 #endif // SCIQLOP_CATALOGUECONTROLLER_H
@@ -1,462 +1,526
1 1 #include <Catalogue/CatalogueController.h>
2 2
3 3 #include <Variable/Variable.h>
4 4
5 5 #include <CatalogueDao.h>
6 6
7 7 #include <ComparaisonPredicate.h>
8 8 #include <CompoundPredicate.h>
9 9 #include <DBCatalogue.h>
10 10 #include <DBEvent.h>
11 11 #include <DBEventProduct.h>
12 12 #include <DBTag.h>
13 13 #include <IRequestPredicate.h>
14 14
15 15 #include <QDataStream>
16 16 #include <QMutex>
17 17 #include <QThread>
18 18
19 19 #include <QDir>
20 20 #include <QStandardPaths>
21 21
22 22 Q_LOGGING_CATEGORY(LOG_CatalogueController, "CatalogueController")
23 23
24 24 namespace {
25 25
26 26 static QString REPOSITORY_WORK_SUFFIX = QString{"_work"};
27 27 static QString REPOSITORY_TRASH_SUFFIX = QString{"_trash"};
28 28 }
29 29
30 /**
31 * Possible types of an repository
32 */
33 enum class DBType { SYNC, WORK, TRASH };
30 34 class CatalogueController::CatalogueControllerPrivate {
31 35
32 36 public:
33 37 explicit CatalogueControllerPrivate(CatalogueController *parent) : m_Q{parent} {}
34 38
35 39 CatalogueDao m_CatalogueDao;
36 40
37 41 QStringList m_RepositoryList;
38 42 CatalogueController *m_Q;
39 43
40 QSet<QString> m_EventKeysWithChanges;
44 QSet<QString> m_KeysWithChanges;
41 45
42 46 QString eventUniqueKey(const std::shared_ptr<DBEvent> &event) const;
47 QString catalogueUniqueKey(const std::shared_ptr<DBCatalogue> &catalogue) const;
43 48
44 49 void copyDBtoDB(const QString &dbFrom, const QString &dbTo);
45 50 QString toWorkRepository(QString repository);
46 51 QString toSyncRepository(QString repository);
47 52 void savAllDB();
48 53
49 54 void saveEvent(std::shared_ptr<DBEvent> event, bool persist = true);
50 55 void saveCatalogue(std::shared_ptr<DBCatalogue> catalogue, bool persist = true);
56
57 std::shared_ptr<IRequestPredicate> createFinder(const QUuid &uniqId, const QString &repository,
58 DBType type);
51 59 };
52 60
53 61 CatalogueController::CatalogueController(QObject *parent)
54 62 : impl{spimpl::make_unique_impl<CatalogueControllerPrivate>(this)}
55 63 {
56 64 qCDebug(LOG_CatalogueController()) << tr("CatalogueController construction")
57 65 << QThread::currentThread();
58 66 }
59 67
60 68 CatalogueController::~CatalogueController()
61 69 {
62 70 qCDebug(LOG_CatalogueController()) << tr("CatalogueController destruction")
63 71 << QThread::currentThread();
64 72 }
65 73
66 74 QStringList CatalogueController::getRepositories() const
67 75 {
68 76 return impl->m_RepositoryList;
69 77 }
70 78
71 79 void CatalogueController::addDB(const QString &dbPath)
72 80 {
73 81 QDir dbDir(dbPath);
74 82 if (dbDir.exists()) {
75 83 auto dirName = dbDir.dirName();
76 84
77 85 if (std::find(impl->m_RepositoryList.cbegin(), impl->m_RepositoryList.cend(), dirName)
78 86 != impl->m_RepositoryList.cend()) {
79 87 qCCritical(LOG_CatalogueController())
80 88 << tr("Impossible to addDB that is already loaded");
81 89 }
82 90
83 91 if (!impl->m_CatalogueDao.addDB(dbPath, dirName)) {
84 92 qCCritical(LOG_CatalogueController())
85 93 << tr("Impossible to addDB %1 from %2 ").arg(dirName, dbPath);
86 94 }
87 95 else {
88 96 impl->m_RepositoryList << dirName;
89 97 impl->copyDBtoDB(dirName, impl->toWorkRepository(dirName));
90 98 }
91 99 }
92 100 else {
93 101 qCCritical(LOG_CatalogueController()) << tr("Impossible to addDB that not exists: ")
94 102 << dbPath;
95 103 }
96 104 }
97 105
98 106 void CatalogueController::saveDB(const QString &destinationPath, const QString &repository)
99 107 {
100 108 if (!impl->m_CatalogueDao.saveDB(destinationPath, repository)) {
101 109 qCCritical(LOG_CatalogueController())
102 110 << tr("Impossible to saveDB %1 from %2 ").arg(repository, destinationPath);
103 111 }
104 112 }
105 113
106 114 std::list<std::shared_ptr<DBEvent> >
107 115 CatalogueController::retrieveEvents(const QString &repository) const
108 116 {
109 117 QString dbDireName = repository.isEmpty() ? REPOSITORY_DEFAULT : repository;
110 118
111 119 auto eventsShared = std::list<std::shared_ptr<DBEvent> >{};
112 120 auto events = impl->m_CatalogueDao.getEvents(impl->toWorkRepository(dbDireName));
113 121 for (auto event : events) {
114 122 eventsShared.push_back(std::make_shared<DBEvent>(event));
115 123 }
116 124 return eventsShared;
117 125 }
118 126
119 127 std::list<std::shared_ptr<DBEvent> > CatalogueController::retrieveAllEvents() const
120 128 {
121 129 auto eventsShared = std::list<std::shared_ptr<DBEvent> >{};
122 130 for (auto repository : impl->m_RepositoryList) {
123 131 eventsShared.splice(eventsShared.end(), retrieveEvents(repository));
124 132 }
125 133
126 134 return eventsShared;
127 135 }
128 136
129 137 std::list<std::shared_ptr<DBEvent> >
130 138 CatalogueController::retrieveEventsFromCatalogue(std::shared_ptr<DBCatalogue> catalogue) const
131 139 {
132 140 auto eventsShared = std::list<std::shared_ptr<DBEvent> >{};
133 141 auto events = impl->m_CatalogueDao.getCatalogueEvents(*catalogue);
134 142 for (auto event : events) {
135 143 eventsShared.push_back(std::make_shared<DBEvent>(event));
136 144 }
137 145 return eventsShared;
138 146 }
139 147
140 148 void CatalogueController::updateEvent(std::shared_ptr<DBEvent> event)
141 149 {
142 150 event->setRepository(impl->toWorkRepository(event->getRepository()));
143 151
144 152 auto uniqueId = impl->eventUniqueKey(event);
145 impl->m_EventKeysWithChanges.insert(uniqueId);
153 impl->m_KeysWithChanges.insert(uniqueId);
146 154
147 155 impl->m_CatalogueDao.updateEvent(*event);
148 156 }
149 157
150 158 void CatalogueController::updateEventProduct(std::shared_ptr<DBEventProduct> eventProduct)
151 159 {
152 160 impl->m_CatalogueDao.updateEventProduct(*eventProduct);
153 161 }
154 162
155 163 void CatalogueController::removeEvent(std::shared_ptr<DBEvent> event)
156 164 {
157 165 // Remove it from both repository and repository_work
158 166 event->setRepository(impl->toWorkRepository(event->getRepository()));
159 167 impl->m_CatalogueDao.removeEvent(*event);
160 168 event->setRepository(impl->toSyncRepository(event->getRepository()));
161 169 impl->m_CatalogueDao.removeEvent(*event);
162 170 impl->savAllDB();
163 171 }
164 172
165 173 void CatalogueController::addEvent(std::shared_ptr<DBEvent> event)
166 174 {
167 175 event->setRepository(impl->toWorkRepository(event->getRepository()));
168 176
169 177 auto eventTemp = *event;
170 178 impl->m_CatalogueDao.addEvent(eventTemp);
171 179
172 180 // Call update is necessary at the creation of add Event if it has some tags or some event
173 181 // products
174 182 if (!event->getEventProducts().empty() || !event->getTags().empty()) {
175 183
176 184 auto eventProductsTemp = eventTemp.getEventProducts();
177 185 auto eventProductTempUpdated = std::list<DBEventProduct>{};
178 186 for (auto eventProductTemp : eventProductsTemp) {
179 187 eventProductTemp.setEvent(eventTemp);
180 188 eventProductTempUpdated.push_back(eventProductTemp);
181 189 }
182 190 eventTemp.setEventProducts(eventProductTempUpdated);
183 191
184 192 impl->m_CatalogueDao.updateEvent(eventTemp);
185 193 }
186 194
187 // update event parameter
188 auto uniqIdPredicate = std::make_shared<ComparaisonPredicate>(
189 QString{"uniqId"}, event->getUniqId(), ComparaisonOperation::EQUALEQUAL);
190
191 auto workRepositoryPredicate = std::make_shared<ComparaisonPredicate>(
192 QString{"repository"}, impl->toWorkRepository(event->getRepository()),
193 ComparaisonOperation::EQUALEQUAL);
194
195 auto workPred = std::make_shared<CompoundPredicate>(CompoundOperation::AND);
196 workPred->AddRequestPredicate(uniqIdPredicate);
197 workPred->AddRequestPredicate(workRepositoryPredicate);
195 auto workPred = impl->createFinder(event->getUniqId(), event->getRepository(), DBType::WORK);
198 196
199 197 auto workEvent = impl->m_CatalogueDao.getEvent(workPred);
200 198 *event = workEvent;
201 199
200
202 201 auto uniqueId = impl->eventUniqueKey(event);
203 impl->m_EventKeysWithChanges.insert(uniqueId);
202 impl->m_KeysWithChanges.insert(uniqueId);
204 203 }
205 204
206 205 void CatalogueController::saveEvent(std::shared_ptr<DBEvent> event)
207 206 {
208 207 impl->saveEvent(event, true);
209 impl->m_EventKeysWithChanges.remove(impl->eventUniqueKey(event));
208 impl->m_KeysWithChanges.remove(impl->eventUniqueKey(event));
210 209 }
211 210
212 211 void CatalogueController::discardEvent(std::shared_ptr<DBEvent> event, bool &removed)
213 212 {
214 auto uniqIdPredicate = std::make_shared<ComparaisonPredicate>(
215 QString{"uniqId"}, event->getUniqId(), ComparaisonOperation::EQUALEQUAL);
216
217 auto syncRepositoryPredicate = std::make_shared<ComparaisonPredicate>(
218 QString{"repository"}, impl->toSyncRepository(event->getRepository()),
219 ComparaisonOperation::EQUALEQUAL);
220
221 auto syncPred = std::make_shared<CompoundPredicate>(CompoundOperation::AND);
222 syncPred->AddRequestPredicate(uniqIdPredicate);
223 syncPred->AddRequestPredicate(syncRepositoryPredicate);
224
225
226 auto workRepositoryPredicate = std::make_shared<ComparaisonPredicate>(
227 QString{"repository"}, impl->toWorkRepository(event->getRepository()),
228 ComparaisonOperation::EQUALEQUAL);
229
230 auto workPred = std::make_shared<CompoundPredicate>(CompoundOperation::AND);
231 workPred->AddRequestPredicate(uniqIdPredicate);
232 workPred->AddRequestPredicate(workRepositoryPredicate);
233
213 auto syncPred = impl->createFinder(event->getUniqId(), event->getRepository(), DBType::SYNC);
214 auto workPred = impl->createFinder(event->getUniqId(), event->getRepository(), DBType::WORK);
234 215
235 216 auto syncEvent = impl->m_CatalogueDao.getEvent(syncPred);
236 217 if (!syncEvent.getUniqId().isNull()) {
237 218 removed = false;
238 219 impl->m_CatalogueDao.copyEvent(syncEvent, impl->toWorkRepository(event->getRepository()),
239 220 true);
240 221
241 222 auto workEvent = impl->m_CatalogueDao.getEvent(workPred);
242 223 *event = workEvent;
243 impl->m_EventKeysWithChanges.remove(impl->eventUniqueKey(event));
224 impl->m_KeysWithChanges.remove(impl->eventUniqueKey(event));
244 225 }
245 226 else {
246 227 removed = true;
247 228 // Since the element wasn't in sync repository. Discard it means remove it
248 229 event->setRepository(impl->toWorkRepository(event->getRepository()));
249 230 impl->m_CatalogueDao.removeEvent(*event);
250 231 }
251 232 }
252 233
253 234 bool CatalogueController::eventHasChanges(std::shared_ptr<DBEvent> event) const
254 235 {
255 return impl->m_EventKeysWithChanges.contains(impl->eventUniqueKey(event));
236 return impl->m_KeysWithChanges.contains(impl->eventUniqueKey(event));
256 237 }
257 238
258 239 std::list<std::shared_ptr<DBCatalogue> >
259 240 CatalogueController::retrieveCatalogues(const QString &repository) const
260 241 {
261 242 QString dbDireName = repository.isEmpty() ? REPOSITORY_DEFAULT : repository;
262 243
263 244 auto cataloguesShared = std::list<std::shared_ptr<DBCatalogue> >{};
264 245 auto catalogues = impl->m_CatalogueDao.getCatalogues(impl->toWorkRepository(dbDireName));
265 246 for (auto catalogue : catalogues) {
266 247 cataloguesShared.push_back(std::make_shared<DBCatalogue>(catalogue));
267 248 }
268 249 return cataloguesShared;
269 250 }
270 251
252 void CatalogueController::addCatalogue(std::shared_ptr<DBCatalogue> catalogue)
253 {
254 catalogue->setRepository(impl->toWorkRepository(catalogue->getRepository()));
255
256 auto catalogueTemp = *catalogue;
257 impl->m_CatalogueDao.addCatalogue(catalogueTemp);
258
259 auto workPred
260 = impl->createFinder(catalogue->getUniqId(), catalogue->getRepository(), DBType::WORK);
261
262 auto workCatalogue = impl->m_CatalogueDao.getCatalogue(workPred);
263 *catalogue = workCatalogue;
264
265 auto uniqueId = impl->catalogueUniqueKey(catalogue);
266 impl->m_KeysWithChanges.insert(uniqueId);
267 }
268
271 269 void CatalogueController::updateCatalogue(std::shared_ptr<DBCatalogue> catalogue)
272 270 {
273 271 catalogue->setRepository(impl->toWorkRepository(catalogue->getRepository()));
274 272
273 auto uniqueId = impl->catalogueUniqueKey(catalogue);
274 impl->m_KeysWithChanges.insert(uniqueId);
275
275 276 impl->m_CatalogueDao.updateCatalogue(*catalogue);
276 277 }
277 278
278 279 void CatalogueController::removeCatalogue(std::shared_ptr<DBCatalogue> catalogue)
279 280 {
280 281 // Remove it from both repository and repository_work
281 282 catalogue->setRepository(impl->toWorkRepository(catalogue->getRepository()));
282 283 impl->m_CatalogueDao.removeCatalogue(*catalogue);
283 284 catalogue->setRepository(impl->toSyncRepository(catalogue->getRepository()));
284 285 impl->m_CatalogueDao.removeCatalogue(*catalogue);
286 impl->savAllDB();
285 287 }
286 288
287 289 void CatalogueController::saveCatalogue(std::shared_ptr<DBCatalogue> catalogue)
288 290 {
289 291 impl->saveCatalogue(catalogue, true);
292 impl->m_KeysWithChanges.remove(impl->catalogueUniqueKey(catalogue));
293 }
294
295 void CatalogueController::discardCatalogue(std::shared_ptr<DBCatalogue> catalogue, bool &removed)
296 {
297 auto syncPred
298 = impl->createFinder(catalogue->getUniqId(), catalogue->getRepository(), DBType::SYNC);
299 auto workPred
300 = impl->createFinder(catalogue->getUniqId(), catalogue->getRepository(), DBType::WORK);
301
302 auto syncCatalogue = impl->m_CatalogueDao.getCatalogue(syncPred);
303 if (!syncCatalogue.getUniqId().isNull()) {
304 removed = false;
305 impl->m_CatalogueDao.copyCatalogue(
306 syncCatalogue, impl->toWorkRepository(catalogue->getRepository()), true);
307
308 auto workCatalogue = impl->m_CatalogueDao.getCatalogue(workPred);
309 *catalogue = workCatalogue;
310 impl->m_KeysWithChanges.remove(impl->catalogueUniqueKey(catalogue));
311 }
312 else {
313 removed = true;
314 // Since the element wasn't in sync repository. Discard it means remove it
315 catalogue->setRepository(impl->toWorkRepository(catalogue->getRepository()));
316 impl->m_CatalogueDao.removeCatalogue(*catalogue);
317 }
290 318 }
291 319
292 320 void CatalogueController::saveAll()
293 321 {
294 322 for (auto repository : impl->m_RepositoryList) {
295 323 // Save Event
296 324 auto events = this->retrieveEvents(repository);
297 325 for (auto event : events) {
298 326 impl->saveEvent(event, false);
299 327 }
300 328
301 329 // Save Catalogue
302 330 auto catalogues = this->retrieveCatalogues(repository);
303 331 for (auto catalogue : catalogues) {
304 332 impl->saveCatalogue(catalogue, false);
305 333 }
306 334 }
307 335
308 336 impl->savAllDB();
309 impl->m_EventKeysWithChanges.clear();
337 impl->m_KeysWithChanges.clear();
310 338 }
311 339
312 340 bool CatalogueController::hasChanges() const
313 341 {
314 return !impl->m_EventKeysWithChanges.isEmpty(); // TODO: catalogues
342 return !impl->m_KeysWithChanges.isEmpty();
315 343 }
316 344
317 345 QByteArray
318 346 CatalogueController::mimeDataForEvents(const QVector<std::shared_ptr<DBEvent> > &events) const
319 347 {
320 348 auto encodedData = QByteArray{};
321 349
322 350 QMap<QString, QVariantList> idsPerRepository;
323 351 for (auto event : events) {
324 352 idsPerRepository[event->getRepository()] << event->getUniqId();
325 353 }
326 354
327 355 QDataStream stream{&encodedData, QIODevice::WriteOnly};
328 356 stream << idsPerRepository;
329 357
330 358 return encodedData;
331 359 }
332 360
333 361 QVector<std::shared_ptr<DBEvent> >
334 362 CatalogueController::eventsForMimeData(const QByteArray &mimeData) const
335 363 {
336 364 auto events = QVector<std::shared_ptr<DBEvent> >{};
337 365 QDataStream stream{mimeData};
338 366
339 367 QMap<QString, QVariantList> idsPerRepository;
340 368 stream >> idsPerRepository;
341 369
342 370 for (auto it = idsPerRepository.cbegin(); it != idsPerRepository.cend(); ++it) {
343 371 auto repository = it.key();
344 372 auto allRepositoryEvent = retrieveEvents(repository);
345 373 for (auto uuid : it.value()) {
346 374 for (auto repositoryEvent : allRepositoryEvent) {
347 375 if (uuid.toUuid() == repositoryEvent->getUniqId()) {
348 376 events << repositoryEvent;
349 377 }
350 378 }
351 379 }
352 380 }
353 381
354 382 return events;
355 383 }
356 384
357 385 void CatalogueController::initialize()
358 386 {
359 387 qCDebug(LOG_CatalogueController()) << tr("CatalogueController init")
360 388 << QThread::currentThread();
361 389
362 390 impl->m_CatalogueDao.initialize();
363 391 auto defaultRepositoryLocation
364 392 = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
365 393
366 394 QDir defaultRepositoryLocationDir;
367 395 if (defaultRepositoryLocationDir.mkpath(defaultRepositoryLocation)) {
368 396 defaultRepositoryLocationDir.cd(defaultRepositoryLocation);
369 397 auto defaultRepository = defaultRepositoryLocationDir.absoluteFilePath(REPOSITORY_DEFAULT);
370 398
371 399 qCInfo(LOG_CatalogueController()) << tr("Persistant data loading from: ")
372 400 << defaultRepository;
373 401
374 402 QDir dbDir(defaultRepository);
375 403 impl->m_RepositoryList << REPOSITORY_DEFAULT;
376 404 if (dbDir.exists()) {
377 405 auto dirName = dbDir.dirName();
378 406
379 407 if (impl->m_CatalogueDao.addDB(defaultRepository, dirName)) {
380 408 impl->copyDBtoDB(dirName, impl->toWorkRepository(dirName));
381 409 }
382 410 }
383 411 else {
384 412 qCInfo(LOG_CatalogueController()) << tr("Initialisation of Default repository detected")
385 413 << defaultRepository;
386 414 }
387 415 }
388 416 else {
389 417 qCWarning(LOG_CatalogueController())
390 418 << tr("Cannot load the persistent default repository from ")
391 419 << defaultRepositoryLocation;
392 420 }
393 421
394 422 qCDebug(LOG_CatalogueController()) << tr("CatalogueController init END");
395 423 }
396 424
397 425 QString CatalogueController::CatalogueControllerPrivate::eventUniqueKey(
398 426 const std::shared_ptr<DBEvent> &event) const
399 427 {
400 428 return event->getUniqId().toString().append(event->getRepository());
401 429 }
402 430
431 QString CatalogueController::CatalogueControllerPrivate::catalogueUniqueKey(
432 const std::shared_ptr<DBCatalogue> &catalogue) const
433 {
434 return catalogue->getUniqId().toString().append(catalogue->getRepository());
435 }
436
403 437 void CatalogueController::CatalogueControllerPrivate::copyDBtoDB(const QString &dbFrom,
404 438 const QString &dbTo)
405 439 {
406 440 // auto cataloguesShared = std::list<std::shared_ptr<DBCatalogue> >{};
407 441 auto catalogues = m_CatalogueDao.getCatalogues(dbFrom);
408 442 auto events = m_CatalogueDao.getEvents(dbFrom);
409 443 for (auto catalogue : catalogues) {
410 444 m_CatalogueDao.copyCatalogue(catalogue, dbTo, true);
411 445 }
412 446
413 447 for (auto event : events) {
414 448 m_CatalogueDao.copyEvent(event, dbTo, true);
415 449 }
416 450 }
417 451
418 452 QString CatalogueController::CatalogueControllerPrivate::toWorkRepository(QString repository)
419 453 {
420 454 auto syncRepository = toSyncRepository(repository);
421 455
422 456 return QString("%1%2").arg(syncRepository, REPOSITORY_WORK_SUFFIX);
423 457 }
424 458
425 459 QString CatalogueController::CatalogueControllerPrivate::toSyncRepository(QString repository)
426 460 {
427 461 auto syncRepository = repository;
428 462 if (repository.endsWith(REPOSITORY_WORK_SUFFIX)) {
429 463 syncRepository.remove(REPOSITORY_WORK_SUFFIX);
430 464 }
431 465 else if (repository.endsWith(REPOSITORY_TRASH_SUFFIX)) {
432 466 syncRepository.remove(REPOSITORY_TRASH_SUFFIX);
433 467 }
434 468 return syncRepository;
435 469 }
436 470
437 471 void CatalogueController::CatalogueControllerPrivate::savAllDB()
438 472 {
439 473 for (auto repository : m_RepositoryList) {
440 474 auto defaultRepositoryLocation
441 475 = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
442 476 m_CatalogueDao.saveDB(defaultRepositoryLocation, repository);
443 477 }
444 478 }
445 479
446 480 void CatalogueController::CatalogueControllerPrivate::saveEvent(std::shared_ptr<DBEvent> event,
447 481 bool persist)
448 482 {
449 483 m_CatalogueDao.copyEvent(*event, toSyncRepository(event->getRepository()), true);
450 484 if (persist) {
451 485 savAllDB();
452 486 }
453 487 }
454 488
455 489 void CatalogueController::CatalogueControllerPrivate::saveCatalogue(
456 490 std::shared_ptr<DBCatalogue> catalogue, bool persist)
457 491 {
458 492 m_CatalogueDao.copyCatalogue(*catalogue, toSyncRepository(catalogue->getRepository()), true);
459 493 if (persist) {
460 494 savAllDB();
461 495 }
462 496 }
497
498 std::shared_ptr<IRequestPredicate> CatalogueController::CatalogueControllerPrivate::createFinder(
499 const QUuid &uniqId, const QString &repository, DBType type)
500 {
501 // update catalogue parameter
502 auto uniqIdPredicate = std::make_shared<ComparaisonPredicate>(QString{"uniqId"}, uniqId,
503 ComparaisonOperation::EQUALEQUAL);
504
505 auto repositoryType = repository;
506 switch (type) {
507 case DBType::SYNC:
508 repositoryType = toSyncRepository(repositoryType);
509 break;
510 case DBType::WORK:
511 repositoryType = toWorkRepository(repositoryType);
512 break;
513 case DBType::TRASH:
514 default:
515 break;
516 }
517
518 auto repositoryPredicate = std::make_shared<ComparaisonPredicate>(
519 QString{"repository"}, repositoryType, ComparaisonOperation::EQUALEQUAL);
520
521 auto finderPred = std::make_shared<CompoundPredicate>(CompoundOperation::AND);
522 finderPred->AddRequestPredicate(uniqIdPredicate);
523 finderPred->AddRequestPredicate(repositoryPredicate);
524
525 return finderPred;
526 }
@@ -1,46 +1,51
1 1 #ifndef SCIQLOP_CATALOGUESIDEBARWIDGET_H
2 2 #define SCIQLOP_CATALOGUESIDEBARWIDGET_H
3 3
4 4 #include <Common/spimpl.h>
5 5 #include <QLoggingCategory>
6 6 #include <QTreeWidgetItem>
7 7 #include <QWidget>
8 8
9 class CatalogueAbstractTreeItem;
9 10 class DBCatalogue;
10 11
11 12 namespace Ui {
12 13 class CatalogueSideBarWidget;
13 14 }
14 15
15 16 Q_DECLARE_LOGGING_CATEGORY(LOG_CatalogueSideBarWidget)
16 17
17 18 class CatalogueSideBarWidget : public QWidget {
18 19 Q_OBJECT
19 20
20 21 signals:
21 22 void catalogueSelected(const QVector<std::shared_ptr<DBCatalogue> > &catalogues);
22 23 void databaseSelected(const QStringList &databases);
23 24 void allEventsSelected();
24 25 void trashSelected();
25 26 void selectionCleared();
26 27
27 28 public:
28 29 explicit CatalogueSideBarWidget(QWidget *parent = 0);
29 30 virtual ~CatalogueSideBarWidget();
30 31
31 void addCatalogue(const std::shared_ptr<DBCatalogue> &catalogue, const QString &repository);
32 CatalogueAbstractTreeItem *addCatalogue(const std::shared_ptr<DBCatalogue> &catalogue,
33 const QString &repository);
32 34 void setCatalogueChanges(const std::shared_ptr<DBCatalogue> &catalogue, bool hasChanges);
33 35
34 36 QVector<std::shared_ptr<DBCatalogue> > getCatalogues(const QString &repository) const;
35 37
38 private slots:
39 void emitSelection();
40
36 41 private:
37 42 Ui::CatalogueSideBarWidget *ui;
38 43
39 44 class CatalogueSideBarWidgetPrivate;
40 45 spimpl::unique_impl_ptr<CatalogueSideBarWidgetPrivate> impl;
41 46
42 47 private slots:
43 48 void onContextMenuRequested(const QPoint &pos);
44 49 };
45 50
46 51 #endif // SCIQLOP_CATALOGUESIDEBARWIDGET_H
@@ -1,35 +1,36
1 1 #ifndef SCIQLOP_CATALOGUEABSTRACTTREEITEM_H
2 2 #define SCIQLOP_CATALOGUEABSTRACTTREEITEM_H
3 3
4 4 #include <Common/spimpl.h>
5 5 #include <QVariant>
6 6 #include <QVector>
7 7
8 8 class QMimeData;
9 9
10 10 class CatalogueAbstractTreeItem {
11 11 public:
12 12 constexpr static const int DEFAULT_TYPE = -1;
13 13
14 14 CatalogueAbstractTreeItem(int type = DEFAULT_TYPE);
15 15 virtual ~CatalogueAbstractTreeItem();
16 16
17 17 void addChild(CatalogueAbstractTreeItem *child);
18 void removeChild(CatalogueAbstractTreeItem *child);
18 19 QVector<CatalogueAbstractTreeItem *> children() const;
19 20 CatalogueAbstractTreeItem *parent() const;
20 21
21 22 int type() const;
22 23 QString text(int column = 0) const;
23 24
24 25 virtual QVariant data(int column, int role) const;
25 26 virtual Qt::ItemFlags flags(int column) const;
26 27 virtual bool setData(int column, int role, const QVariant &value);
27 28 virtual bool canDropMimeData(const QMimeData *data, Qt::DropAction action);
28 29 virtual bool dropMimeData(const QMimeData *data, Qt::DropAction action);
29 30
30 31 private:
31 32 class CatalogueAbstractTreeItemPrivate;
32 33 spimpl::unique_impl_ptr<CatalogueAbstractTreeItemPrivate> impl;
33 34 };
34 35
35 36 #endif // SCIQLOP_CATALOGUEABSTRACTTREEITEM_H
@@ -1,56 +1,59
1 1 #ifndef SCIQLOP_CATALOGUETREEMODEL_H
2 2 #define SCIQLOP_CATALOGUETREEMODEL_H
3 3
4 4 #include <Common/spimpl.h>
5 5 #include <QAbstractItemModel>
6 6
7 7 class CatalogueAbstractTreeItem;
8 8
9 9 /**
10 10 * @brief Model to display catalogue items based on QTreeWidgetItem
11 11 * @warning Do not use the method QTreeWidgetItem::treeWidget for an item added to this model or the
12 12 * application will crash
13 13 */
14 14 class CatalogueTreeModel : public QAbstractItemModel {
15 15 Q_OBJECT
16 16
17 17 signals:
18 18 void itemRenamed(const QModelIndex &index);
19 19 void itemDropped(const QModelIndex &parentIndex);
20 20
21 21 public:
22 22 CatalogueTreeModel(QObject *parent = nullptr);
23 23
24 24 enum class Column { Name, Validation, Count };
25 25
26 26 QModelIndex addTopLevelItem(CatalogueAbstractTreeItem *item);
27 27 QVector<CatalogueAbstractTreeItem *> topLevelItems() const;
28 28
29 29 void addChildItem(CatalogueAbstractTreeItem *child, const QModelIndex &parentIndex);
30 void removeChildItem(CatalogueAbstractTreeItem *child, const QModelIndex &parentIndex);
31 /// Refresh the data for the specified index
32 void refresh(const QModelIndex &index);
30 33
31 34 CatalogueAbstractTreeItem *item(const QModelIndex &index) const;
32 35 QModelIndex indexOf(CatalogueAbstractTreeItem *item, int column = 0) const;
33 36
34 37 // model
35 38 QModelIndex index(int row, int column,
36 39 const QModelIndex &parent = QModelIndex()) const override;
37 40 QModelIndex parent(const QModelIndex &index) const override;
38 41 int rowCount(const QModelIndex &parent) const override;
39 42 int columnCount(const QModelIndex &parent) const override;
40 43 Qt::ItemFlags flags(const QModelIndex &index) const override;
41 44 QVariant data(const QModelIndex &index, int role) const override;
42 45 bool setData(const QModelIndex &index, const QVariant &value, int role) override;
43 46
44 47 bool canDropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column,
45 48 const QModelIndex &parent) const override;
46 49 bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column,
47 50 const QModelIndex &parent) override;
48 51 Qt::DropActions supportedDropActions() const;
49 52 QStringList mimeTypes() const;
50 53
51 54 private:
52 55 class CatalogueTreeModelPrivate;
53 56 spimpl::unique_impl_ptr<CatalogueTreeModelPrivate> impl;
54 57 };
55 58
56 59 #endif // CATALOGUETREEMODEL_H
@@ -1,147 +1,147
1 1 #include "Catalogue/CatalogueActionManager.h"
2 2
3 3 #include <Actions/ActionsGuiController.h>
4 4 #include <Catalogue/CatalogueController.h>
5 5 #include <DataSource/DataSourceItem.h>
6 6 #include <SqpApplication.h>
7 7 #include <Variable/Variable.h>
8 8 #include <Visualization/VisualizationGraphWidget.h>
9 9 #include <Visualization/VisualizationSelectionZoneItem.h>
10 10
11 11 #include <Catalogue/CatalogueEventsWidget.h>
12 12 #include <Catalogue/CatalogueExplorer.h>
13 13 #include <Catalogue/CatalogueSideBarWidget.h>
14 14 #include <Catalogue/CreateEventDialog.h>
15 15
16 16 #include <CatalogueDao.h>
17 17 #include <DBCatalogue.h>
18 18 #include <DBEvent.h>
19 19 #include <DBEventProduct.h>
20 20
21 21 #include <QBoxLayout>
22 22 #include <QComboBox>
23 23 #include <QDialog>
24 24 #include <QDialogButtonBox>
25 25 #include <QLineEdit>
26 26 #include <memory>
27 27
28 28 struct CatalogueActionManager::CatalogueActionManagerPrivate {
29 29
30 30 CatalogueExplorer *m_CatalogueExplorer = nullptr;
31 31
32 32 CatalogueActionManagerPrivate(CatalogueExplorer *catalogueExplorer)
33 33 : m_CatalogueExplorer(catalogueExplorer)
34 34 {
35 35 }
36 36
37 37 void createEventFromZones(const QString &eventName,
38 38 const QVector<VisualizationSelectionZoneItem *> &zones,
39 39 const std::shared_ptr<DBCatalogue> &catalogue = nullptr)
40 40 {
41 41 auto event = std::make_shared<DBEvent>();
42 42 event->setName(eventName);
43 43
44 44 std::list<DBEventProduct> productList;
45 45 for (auto zone : zones) {
46 46 auto graph = zone->parentGraphWidget();
47 47 for (auto var : graph->variables()) {
48 48 auto eventProduct = std::make_shared<DBEventProduct>();
49 49 eventProduct->setEvent(*event);
50 50
51 51 auto productId
52 52 = var->metadata().value(DataSourceItem::ID_DATA_KEY, "UnknownID").toString();
53 53
54 54 auto zoneRange = zone->range();
55 55 eventProduct->setTStart(zoneRange.m_TStart);
56 56 eventProduct->setTEnd(zoneRange.m_TEnd);
57 57
58 58 eventProduct->setProductId(productId);
59 59
60 60 productList.push_back(*eventProduct);
61 61
62 62 m_CatalogueExplorer->addSelectionZoneItem(event, productId, zone);
63 63 }
64 64 }
65 65
66 66 event->setEventProducts(productList);
67 67
68 68 sqpApp->catalogueController().addEvent(event);
69 69
70 70
71 71 if (catalogue) {
72 // TODO
73 // catalogue->addEvent(event);
72 catalogue->addEvent(event->getUniqId());
73 sqpApp->catalogueController().updateCatalogue(catalogue);
74 74 m_CatalogueExplorer->sideBarWidget().setCatalogueChanges(catalogue, true);
75 75 if (m_CatalogueExplorer->eventsWidget().displayedCatalogues().contains(catalogue)) {
76 76 m_CatalogueExplorer->eventsWidget().addEvent(event);
77 77 m_CatalogueExplorer->eventsWidget().setEventChanges(event, true);
78 78 }
79 79 }
80 80 else if (m_CatalogueExplorer->eventsWidget().isAllEventsDisplayed()) {
81 81 m_CatalogueExplorer->eventsWidget().addEvent(event);
82 82 m_CatalogueExplorer->eventsWidget().setEventChanges(event, true);
83 83 }
84 84 }
85 85 };
86 86
87 87 CatalogueActionManager::CatalogueActionManager(CatalogueExplorer *catalogueExplorer)
88 88 : impl{spimpl::make_unique_impl<CatalogueActionManagerPrivate>(catalogueExplorer)}
89 89 {
90 90 }
91 91
92 92 void CatalogueActionManager::installSelectionZoneActions()
93 93 {
94 94 auto &actionController = sqpApp->actionsGuiController();
95 95
96 96 auto createEventEnableFuntion = [](auto zones) {
97 97
98 98 // Checks that all variables in the zones doesn't refer to the same product
99 99 QSet<QString> usedDatasource;
100 100 for (auto zone : zones) {
101 101 auto graph = zone->parentGraphWidget();
102 102 auto variables = graph->variables();
103 103
104 104 for (auto var : variables) {
105 105 auto datasourceId = var->metadata().value(DataSourceItem::ID_DATA_KEY).toString();
106 106 if (!usedDatasource.contains(datasourceId)) {
107 107 usedDatasource.insert(datasourceId);
108 108 }
109 109 else {
110 110 return false;
111 111 }
112 112 }
113 113 }
114 114
115 115 return true;
116 116 };
117 117
118 118 auto createEventAction = actionController.addSectionZoneAction(
119 119 {QObject::tr("Catalogues")}, QObject::tr("New Event..."), [this](auto zones) {
120 120 CreateEventDialog dialog(
121 121 impl->m_CatalogueExplorer->sideBarWidget().getCatalogues(REPOSITORY_DEFAULT));
122 122 dialog.hideCatalogueChoice();
123 123 if (dialog.exec() == QDialog::Accepted) {
124 124 impl->createEventFromZones(dialog.eventName(), zones);
125 125 }
126 126 });
127 127 createEventAction->setEnableFunction(createEventEnableFuntion);
128 128
129 129 auto createEventInCatalogueAction = actionController.addSectionZoneAction(
130 130 {QObject::tr("Catalogues")}, QObject::tr("New Event in Catalogue..."), [this](auto zones) {
131 131 CreateEventDialog dialog(
132 132 impl->m_CatalogueExplorer->sideBarWidget().getCatalogues(REPOSITORY_DEFAULT));
133 133 if (dialog.exec() == QDialog::Accepted) {
134 134 auto selectedCatalogue = dialog.selectedCatalogue();
135 135 if (!selectedCatalogue) {
136 136 selectedCatalogue = std::make_shared<DBCatalogue>();
137 137 selectedCatalogue->setName(dialog.catalogueName());
138 // sqpApp->catalogueController().addCatalogue(selectedCatalogue); TODO
138 sqpApp->catalogueController().addCatalogue(selectedCatalogue);
139 139 impl->m_CatalogueExplorer->sideBarWidget().addCatalogue(selectedCatalogue,
140 140 REPOSITORY_DEFAULT);
141 141 }
142 142
143 143 impl->createEventFromZones(dialog.eventName(), zones, selectedCatalogue);
144 144 }
145 145 });
146 146 createEventInCatalogueAction->setEnableFunction(createEventEnableFuntion);
147 147 }
@@ -1,337 +1,414
1 1 #include "Catalogue/CatalogueSideBarWidget.h"
2 2 #include "ui_CatalogueSideBarWidget.h"
3 3 #include <SqpApplication.h>
4 4
5 5 #include <Catalogue/CatalogueController.h>
6 6 #include <Catalogue/CatalogueExplorerHelper.h>
7 7 #include <Catalogue/CatalogueTreeItems/CatalogueTextTreeItem.h>
8 8 #include <Catalogue/CatalogueTreeItems/CatalogueTreeItem.h>
9 9 #include <Catalogue/CatalogueTreeModel.h>
10 10 #include <CatalogueDao.h>
11 11 #include <ComparaisonPredicate.h>
12 12 #include <DBCatalogue.h>
13 13
14 14 #include <QMenu>
15 #include <QMessageBox>
15 16
16 17 Q_LOGGING_CATEGORY(LOG_CatalogueSideBarWidget, "CatalogueSideBarWidget")
17 18
18 19
19 20 constexpr auto ALL_EVENT_ITEM_TYPE = CatalogueAbstractTreeItem::DEFAULT_TYPE + 1;
20 21 constexpr auto TRASH_ITEM_TYPE = CatalogueAbstractTreeItem::DEFAULT_TYPE + 2;
21 22 constexpr auto CATALOGUE_ITEM_TYPE = CatalogueAbstractTreeItem::DEFAULT_TYPE + 3;
22 23 constexpr auto DATABASE_ITEM_TYPE = CatalogueAbstractTreeItem::DEFAULT_TYPE + 4;
23 24
24 25
25 26 struct CatalogueSideBarWidget::CatalogueSideBarWidgetPrivate {
26 27
27 28 CatalogueTreeModel *m_TreeModel = nullptr;
28 29
29 30 void configureTreeWidget(QTreeView *treeView);
30 31 QModelIndex addDatabaseItem(const QString &name);
31 32 CatalogueAbstractTreeItem *getDatabaseItem(const QString &name);
32 void addCatalogueItem(const std::shared_ptr<DBCatalogue> &catalogue,
33 CatalogueAbstractTreeItem *addCatalogueItem(const std::shared_ptr<DBCatalogue> &catalogue,
33 34 const QModelIndex &databaseIndex);
34 35
35 36 CatalogueTreeItem *getCatalogueItem(const std::shared_ptr<DBCatalogue> &catalogue) const;
36 void setHasChanges(bool value, const QModelIndex &index, QTreeView *treeView);
37 void setHasChanges(bool value, const QModelIndex &index, CatalogueSideBarWidget *sideBarWidget);
37 38 bool hasChanges(const QModelIndex &index, QTreeView *treeView);
38 39
39 40 int selectionType(QTreeView *treeView) const
40 41 {
41 42 auto selectedItems = treeView->selectionModel()->selectedRows();
42 43 if (selectedItems.isEmpty()) {
43 44 return CatalogueAbstractTreeItem::DEFAULT_TYPE;
44 45 }
45 46 else {
46 47 auto firstIndex = selectedItems.first();
47 48 auto firstItem = m_TreeModel->item(firstIndex);
48 49 if (!firstItem) {
49 50 Q_ASSERT(false);
50 51 return CatalogueAbstractTreeItem::DEFAULT_TYPE;
51 52 }
52 53 auto selectionType = firstItem->type();
53 54
54 55 for (auto itemIndex : selectedItems) {
55 56 auto item = m_TreeModel->item(itemIndex);
56 57 if (!item || item->type() != selectionType) {
57 58 // Incoherent multi selection
58 59 selectionType = CatalogueAbstractTreeItem::DEFAULT_TYPE;
59 60 break;
60 61 }
61 62 }
62 63
63 64 return selectionType;
64 65 }
65 66 }
66 67
67 68 QVector<std::shared_ptr<DBCatalogue> > selectedCatalogues(QTreeView *treeView) const
68 69 {
69 70 QVector<std::shared_ptr<DBCatalogue> > catalogues;
70 71 auto selectedItems = treeView->selectionModel()->selectedRows();
71 72 for (auto itemIndex : selectedItems) {
72 73 auto item = m_TreeModel->item(itemIndex);
73 74 if (item && item->type() == CATALOGUE_ITEM_TYPE) {
74 75 catalogues.append(static_cast<CatalogueTreeItem *>(item)->catalogue());
75 76 }
76 77 }
77 78
78 79 return catalogues;
79 80 }
80 81
81 82 QStringList selectedRepositories(QTreeView *treeView) const
82 83 {
83 84 QStringList repositories;
84 85 auto selectedItems = treeView->selectionModel()->selectedRows();
85 86 for (auto itemIndex : selectedItems) {
86 87 auto item = m_TreeModel->item(itemIndex);
87 88 if (item && item->type() == DATABASE_ITEM_TYPE) {
88 89 repositories.append(item->text());
89 90 }
90 91 }
91 92
92 93 return repositories;
93 94 }
94 95 };
95 96
96 97 CatalogueSideBarWidget::CatalogueSideBarWidget(QWidget *parent)
97 98 : QWidget(parent),
98 99 ui(new Ui::CatalogueSideBarWidget),
99 100 impl{spimpl::make_unique_impl<CatalogueSideBarWidgetPrivate>()}
100 101 {
101 102 ui->setupUi(this);
102 103
103 104 impl->m_TreeModel = new CatalogueTreeModel(this);
104 105 ui->treeView->setModel(impl->m_TreeModel);
105 106
106 107 impl->configureTreeWidget(ui->treeView);
107 108
108 109 ui->treeView->header()->setStretchLastSection(false);
109 110 ui->treeView->header()->setSectionResizeMode(QHeaderView::ResizeToContents);
110 111 ui->treeView->header()->setSectionResizeMode(0, QHeaderView::Stretch);
111 112
112 auto emitSelection = [this]() {
113 connect(ui->treeView, &QTreeView::clicked, this, &CatalogueSideBarWidget::emitSelection);
114 connect(ui->treeView->selectionModel(), &QItemSelectionModel::currentChanged, this,
115 &CatalogueSideBarWidget::emitSelection);
113 116
114 auto selectionType = impl->selectionType(ui->treeView);
115 117
116 switch (selectionType) {
117 case CATALOGUE_ITEM_TYPE:
118 emit this->catalogueSelected(impl->selectedCatalogues(ui->treeView));
119 break;
120 case DATABASE_ITEM_TYPE:
121 emit this->databaseSelected(impl->selectedRepositories(ui->treeView));
122 break;
123 case ALL_EVENT_ITEM_TYPE:
124 emit this->allEventsSelected();
125 break;
126 case TRASH_ITEM_TYPE:
127 emit this->trashSelected();
128 break;
129 default:
130 emit this->selectionCleared();
131 break;
118 connect(ui->btnAdd, &QToolButton::clicked, [this]() {
119 auto catalogue = std::make_shared<DBCatalogue>();
120 catalogue->setName(QString("Cat"));
121 sqpApp->catalogueController().addCatalogue(catalogue);
122 auto item = this->addCatalogue(catalogue, REPOSITORY_DEFAULT);
123 this->setCatalogueChanges(catalogue, true);
124 ui->treeView->edit(impl->m_TreeModel->indexOf(item));
125
126 });
127
128
129 connect(impl->m_TreeModel, &CatalogueTreeModel::itemDropped, [this](auto index) {
130 auto item = impl->m_TreeModel->item(index);
131 if (item && item->type() == CATALOGUE_ITEM_TYPE) {
132 auto catalogue = static_cast<CatalogueTreeItem *>(item)->catalogue();
133 this->setCatalogueChanges(catalogue, true);
132 134 }
133 };
135 });
136 connect(ui->btnRemove, &QToolButton::clicked, [this]() {
137 QVector<QPair<std::shared_ptr<DBCatalogue>, CatalogueAbstractTreeItem *> >
138 cataloguesToItems;
139 auto selectedIndexes = ui->treeView->selectionModel()->selectedRows();
140
141 for (auto index : selectedIndexes) {
142 auto item = impl->m_TreeModel->item(index);
143 if (item && item->type() == CATALOGUE_ITEM_TYPE) {
144 auto catalogue = static_cast<CatalogueTreeItem *>(item)->catalogue();
145 cataloguesToItems << qMakePair(catalogue, item);
146 }
147 }
148
149 if (!cataloguesToItems.isEmpty()) {
150
151 if (QMessageBox::warning(this, tr("Remove Catalogue(s)"),
152 tr("The selected catalogues(s) will be completly removed "
153 "from the repository!\nAre you sure you want to continue?"),
154 QMessageBox::Yes | QMessageBox::No, QMessageBox::No)
155 == QMessageBox::Yes) {
134 156
135 connect(ui->treeView, &QTreeView::clicked, emitSelection);
136 connect(ui->treeView->selectionModel(), &QItemSelectionModel::currentChanged, emitSelection);
137 connect(impl->m_TreeModel, &CatalogueTreeModel::itemRenamed, [emitSelection, this](auto index) {
157 for (auto catalogueToItem : cataloguesToItems) {
158 sqpApp->catalogueController().removeCatalogue(catalogueToItem.first);
159 impl->m_TreeModel->removeChildItem(
160 catalogueToItem.second,
161 impl->m_TreeModel->indexOf(catalogueToItem.second->parent()));
162 }
163 }
164 }
165 });
166
167 connect(impl->m_TreeModel, &CatalogueTreeModel::itemRenamed, [this](auto index) {
138 168 auto selectedIndexes = ui->treeView->selectionModel()->selectedRows();
139 169 if (selectedIndexes.contains(index)) {
140 emitSelection();
170 this->emitSelection();
141 171 }
142
143 auto item = impl->m_TreeModel->item(index);
144 impl->setHasChanges(true, index, ui->treeView);
172 impl->setHasChanges(true, index, this);
145 173 });
146 174
147 175 ui->treeView->setContextMenuPolicy(Qt::CustomContextMenu);
148 176 connect(ui->treeView, &QTreeView::customContextMenuRequested, this,
149 177 &CatalogueSideBarWidget::onContextMenuRequested);
150 178 }
151 179
152 180 CatalogueSideBarWidget::~CatalogueSideBarWidget()
153 181 {
154 182 delete ui;
155 183 }
156 184
157 void CatalogueSideBarWidget::addCatalogue(const std::shared_ptr<DBCatalogue> &catalogue,
185 CatalogueAbstractTreeItem *
186 CatalogueSideBarWidget::addCatalogue(const std::shared_ptr<DBCatalogue> &catalogue,
158 187 const QString &repository)
159 188 {
160 189 auto repositoryItem = impl->getDatabaseItem(repository);
161 impl->addCatalogueItem(catalogue, impl->m_TreeModel->indexOf(repositoryItem));
190 return impl->addCatalogueItem(catalogue, impl->m_TreeModel->indexOf(repositoryItem));
162 191 }
163 192
164 193 void CatalogueSideBarWidget::setCatalogueChanges(const std::shared_ptr<DBCatalogue> &catalogue,
165 194 bool hasChanges)
166 195 {
167 196 if (auto catalogueItem = impl->getCatalogueItem(catalogue)) {
168 197 auto index = impl->m_TreeModel->indexOf(catalogueItem);
169 impl->setHasChanges(hasChanges, index, ui->treeView);
198 impl->setHasChanges(hasChanges, index, this);
170 199 // catalogueItem->refresh();
171 200 }
172 201 }
173 202
174 203 QVector<std::shared_ptr<DBCatalogue> >
175 204 CatalogueSideBarWidget::getCatalogues(const QString &repository) const
176 205 {
177 206 QVector<std::shared_ptr<DBCatalogue> > result;
178 207 auto repositoryItem = impl->getDatabaseItem(repository);
179 208 for (auto child : repositoryItem->children()) {
180 209 if (child->type() == CATALOGUE_ITEM_TYPE) {
181 210 auto catalogueItem = static_cast<CatalogueTreeItem *>(child);
182 211 result << catalogueItem->catalogue();
183 212 }
184 213 else {
185 214 qCWarning(LOG_CatalogueSideBarWidget()) << "getCatalogues: invalid structure";
186 215 }
187 216 }
188 217
189 218 return result;
190 219 }
191 220
221 void CatalogueSideBarWidget::emitSelection()
222 {
223 auto selectionType = impl->selectionType(ui->treeView);
224
225 switch (selectionType) {
226 case CATALOGUE_ITEM_TYPE:
227 emit this->catalogueSelected(impl->selectedCatalogues(ui->treeView));
228 break;
229 case DATABASE_ITEM_TYPE:
230 emit this->databaseSelected(impl->selectedRepositories(ui->treeView));
231 break;
232 case ALL_EVENT_ITEM_TYPE:
233 emit this->allEventsSelected();
234 break;
235 case TRASH_ITEM_TYPE:
236 emit this->trashSelected();
237 break;
238 default:
239 emit this->selectionCleared();
240 break;
241 }
242 }
243
192 244 void CatalogueSideBarWidget::onContextMenuRequested(const QPoint &pos)
193 245 {
194 246 QMenu menu{this};
195 247
196 248 auto currentIndex = ui->treeView->currentIndex();
197 249 auto currentItem = impl->m_TreeModel->item(currentIndex);
198 250 if (!currentItem) {
199 251 return;
200 252 }
201 253
202 254 switch (currentItem->type()) {
203 255 case CATALOGUE_ITEM_TYPE:
204 256 menu.addAction("Rename", [this, currentIndex]() { ui->treeView->edit(currentIndex); });
205 257 break;
206 258 case DATABASE_ITEM_TYPE:
207 259 break;
208 260 case ALL_EVENT_ITEM_TYPE:
209 261 break;
210 262 case TRASH_ITEM_TYPE:
211 263 menu.addAction("Empty Trash", []() {
212 264 // TODO
213 265 });
214 266 break;
215 267 default:
216 268 break;
217 269 }
218 270
219 271 if (!menu.isEmpty()) {
220 272 menu.exec(ui->treeView->mapToGlobal(pos));
221 273 }
222 274 }
223 275
224 276 void CatalogueSideBarWidget::CatalogueSideBarWidgetPrivate::configureTreeWidget(QTreeView *treeView)
225 277 {
226 278 auto allEventsItem = new CatalogueTextTreeItem{QIcon{":/icones/allEvents.png"}, "All Events",
227 279 ALL_EVENT_ITEM_TYPE};
228 280 auto allEventIndex = m_TreeModel->addTopLevelItem(allEventsItem);
229 281 treeView->setCurrentIndex(allEventIndex);
230 282
231 283 auto trashItem
232 284 = new CatalogueTextTreeItem{QIcon{":/icones/trash.png"}, "Trash", TRASH_ITEM_TYPE};
233 285 m_TreeModel->addTopLevelItem(trashItem);
234 286
235 287 auto separator = new QFrame{treeView};
236 288 separator->setFrameShape(QFrame::HLine);
237 289 auto separatorItem
238 290 = new CatalogueTextTreeItem{QIcon{}, QString{}, CatalogueAbstractTreeItem::DEFAULT_TYPE};
239 291 separatorItem->setEnabled(false);
240 292 auto separatorIndex = m_TreeModel->addTopLevelItem(separatorItem);
241 293 treeView->setIndexWidget(separatorIndex, separator);
242 294
243 295 auto repositories = sqpApp->catalogueController().getRepositories();
244 296 for (auto dbname : repositories) {
245 297 auto dbIndex = addDatabaseItem(dbname);
246 298 auto catalogues = sqpApp->catalogueController().retrieveCatalogues(dbname);
247 299 for (auto catalogue : catalogues) {
248 300 addCatalogueItem(catalogue, dbIndex);
249 301 }
250 302 }
251 303
252 304 treeView->expandAll();
253 305 }
254 306
255 307 QModelIndex
256 308 CatalogueSideBarWidget::CatalogueSideBarWidgetPrivate::addDatabaseItem(const QString &name)
257 309 {
258 310 auto databaseItem
259 311 = new CatalogueTextTreeItem{QIcon{":/icones/database.png"}, {name}, DATABASE_ITEM_TYPE};
260 312 auto databaseIndex = m_TreeModel->addTopLevelItem(databaseItem);
261 313
262 314 return databaseIndex;
263 315 }
264 316
265 317 CatalogueAbstractTreeItem *
266 318 CatalogueSideBarWidget::CatalogueSideBarWidgetPrivate::getDatabaseItem(const QString &name)
267 319 {
268 320 for (auto item : m_TreeModel->topLevelItems()) {
269 321 if (item->type() == DATABASE_ITEM_TYPE && item->text() == name) {
270 322 return item;
271 323 }
272 324 }
273 325
274 326 return nullptr;
275 327 }
276 328
277 void CatalogueSideBarWidget::CatalogueSideBarWidgetPrivate::addCatalogueItem(
329 CatalogueAbstractTreeItem *CatalogueSideBarWidget::CatalogueSideBarWidgetPrivate::addCatalogueItem(
278 330 const std::shared_ptr<DBCatalogue> &catalogue, const QModelIndex &databaseIndex)
279 331 {
280 332 auto catalogueItem
281 333 = new CatalogueTreeItem{catalogue, QIcon{":/icones/catalogue.png"}, CATALOGUE_ITEM_TYPE};
282 334 m_TreeModel->addChildItem(catalogueItem, databaseIndex);
335
336 return catalogueItem;
283 337 }
284 338
285 339 CatalogueTreeItem *CatalogueSideBarWidget::CatalogueSideBarWidgetPrivate::getCatalogueItem(
286 340 const std::shared_ptr<DBCatalogue> &catalogue) const
287 341 {
288 342 for (auto item : m_TreeModel->topLevelItems()) {
289 343 if (item->type() == DATABASE_ITEM_TYPE) {
290 344 for (auto childItem : item->children()) {
291 345 if (childItem->type() == CATALOGUE_ITEM_TYPE) {
292 346 auto catalogueItem = static_cast<CatalogueTreeItem *>(childItem);
293 347 if (catalogueItem->catalogue() == catalogue) {
294 348 return catalogueItem;
295 349 }
296 350 }
297 351 else {
298 352 qCWarning(LOG_CatalogueSideBarWidget()) << "getCatalogueItem: Invalid tree "
299 353 "structure. A database item should "
300 354 "only contain catalogues.";
301 355 Q_ASSERT(false);
302 356 }
303 357 }
304 358 }
305 359 }
306 360
307 361 return nullptr;
308 362 }
309 363
310 void CatalogueSideBarWidget::CatalogueSideBarWidgetPrivate::setHasChanges(bool value,
311 const QModelIndex &index,
312 QTreeView *treeView)
364 void CatalogueSideBarWidget::CatalogueSideBarWidgetPrivate::setHasChanges(
365 bool value, const QModelIndex &index, CatalogueSideBarWidget *sideBarWidget)
313 366 {
367 std::shared_ptr<DBCatalogue> catalogue = nullptr;
368 auto item = m_TreeModel->item(index);
369 if (item && item->type() == CATALOGUE_ITEM_TYPE) {
370 catalogue = static_cast<CatalogueTreeItem *>(item)->catalogue();
371 }
372
314 373 auto validationIndex = index.sibling(index.row(), (int)CatalogueTreeModel::Column::Validation);
315 374 if (value) {
316 if (!hasChanges(validationIndex, treeView)) {
375 if (!hasChanges(validationIndex, sideBarWidget->ui->treeView)) {
317 376 auto widget = CatalogueExplorerHelper::buildValidationWidget(
318 treeView, [this, validationIndex,
319 treeView]() { setHasChanges(false, validationIndex, treeView); },
320 [this, validationIndex, treeView]() {
321 setHasChanges(false, validationIndex, treeView);
377 sideBarWidget->ui->treeView,
378 [this, validationIndex, sideBarWidget, catalogue]() {
379 if (catalogue) {
380 sqpApp->catalogueController().saveCatalogue(catalogue);
381 }
382 setHasChanges(false, validationIndex, sideBarWidget);
383 },
384 [this, validationIndex, sideBarWidget, catalogue, item]() {
385 if (catalogue) {
386 bool removed;
387 sqpApp->catalogueController().discardCatalogue(catalogue, removed);
388
389 if (removed) {
390 m_TreeModel->removeChildItem(item,
391 m_TreeModel->indexOf(item->parent()));
392 }
393 else {
394 m_TreeModel->refresh(m_TreeModel->indexOf(item));
395 setHasChanges(false, validationIndex, sideBarWidget);
396 }
397 sideBarWidget->emitSelection();
398 }
322 399 });
323 treeView->setIndexWidget(validationIndex, widget);
400 sideBarWidget->ui->treeView->setIndexWidget(validationIndex, widget);
324 401 }
325 402 }
326 403 else {
327 404 // Note: the widget is destroyed
328 treeView->setIndexWidget(validationIndex, nullptr);
405 sideBarWidget->ui->treeView->setIndexWidget(validationIndex, nullptr);
329 406 }
330 407 }
331 408
332 409 bool CatalogueSideBarWidget::CatalogueSideBarWidgetPrivate::hasChanges(const QModelIndex &index,
333 410 QTreeView *treeView)
334 411 {
335 412 auto validationIndex = index.sibling(index.row(), (int)CatalogueTreeModel::Column::Validation);
336 413 return treeView->indexWidget(validationIndex) != nullptr;
337 414 }
@@ -1,81 +1,87
1 1 #include "Catalogue/CatalogueTreeItems/CatalogueAbstractTreeItem.h"
2 2
3 3 struct CatalogueAbstractTreeItem::CatalogueAbstractTreeItemPrivate {
4 4 int m_Type;
5 5 QVector<CatalogueAbstractTreeItem *> m_Children;
6 6 CatalogueAbstractTreeItem *m_Parent = nullptr;
7 7
8 8 CatalogueAbstractTreeItemPrivate(int type) : m_Type(type) {}
9 9 };
10 10
11 11 CatalogueAbstractTreeItem::CatalogueAbstractTreeItem(int type)
12 12 : impl{spimpl::make_unique_impl<CatalogueAbstractTreeItemPrivate>(type)}
13 13 {
14 14 }
15 15
16 16 CatalogueAbstractTreeItem::~CatalogueAbstractTreeItem()
17 17 {
18 18 qDeleteAll(impl->m_Children);
19 19 }
20 20
21 21 void CatalogueAbstractTreeItem::addChild(CatalogueAbstractTreeItem *child)
22 22 {
23 23 impl->m_Children << child;
24 24 child->impl->m_Parent = this;
25 25 }
26 26
27 void CatalogueAbstractTreeItem::removeChild(CatalogueAbstractTreeItem *child)
28 {
29 impl->m_Children.removeAll(child);
30 delete child;
31 }
32
27 33 QVector<CatalogueAbstractTreeItem *> CatalogueAbstractTreeItem::children() const
28 34 {
29 35 return impl->m_Children;
30 36 }
31 37
32 38 CatalogueAbstractTreeItem *CatalogueAbstractTreeItem::parent() const
33 39 {
34 40 return impl->m_Parent;
35 41 }
36 42
37 43 int CatalogueAbstractTreeItem::type() const
38 44 {
39 45 return impl->m_Type;
40 46 }
41 47
42 48 QString CatalogueAbstractTreeItem::text(int column) const
43 49 {
44 50 return data(0, Qt::DisplayRole).toString();
45 51 }
46 52
47 53 QVariant CatalogueAbstractTreeItem::data(int column, int role) const
48 54 {
49 55 Q_UNUSED(column);
50 56 Q_UNUSED(role);
51 57 return QVariant();
52 58 }
53 59
54 60 Qt::ItemFlags CatalogueAbstractTreeItem::flags(int column) const
55 61 {
56 62 Q_UNUSED(column);
57 63 return Qt::NoItemFlags;
58 64 }
59 65
60 66 bool CatalogueAbstractTreeItem::setData(int column, int role, const QVariant &value)
61 67 {
62 68 Q_UNUSED(column);
63 69 Q_UNUSED(role);
64 70 Q_UNUSED(value);
65 71
66 72 return false;
67 73 }
68 74
69 75 bool CatalogueAbstractTreeItem::canDropMimeData(const QMimeData *data, Qt::DropAction action)
70 76 {
71 77 Q_UNUSED(data);
72 78 Q_UNUSED(action);
73 79 return false;
74 80 }
75 81
76 82 bool CatalogueAbstractTreeItem::dropMimeData(const QMimeData *data, Qt::DropAction action)
77 83 {
78 84 Q_UNUSED(data);
79 85 Q_UNUSED(action);
80 86 return false;
81 87 }
@@ -1,95 +1,105
1 1 #include "Catalogue/CatalogueTreeItems/CatalogueTreeItem.h"
2 2 #include <Catalogue/CatalogueExplorerHelper.h>
3 3
4 4 #include <Catalogue/CatalogueController.h>
5 5 #include <Common/MimeTypesDef.h>
6 6 #include <QIcon>
7 7 #include <QMimeData>
8 8 #include <SqpApplication.h>
9 9
10 10 #include <memory>
11 11
12 12 #include <DBCatalogue.h>
13 13
14 14 struct CatalogueTreeItem::CatalogueTreeItemPrivate {
15 15
16 16 std::shared_ptr<DBCatalogue> m_Catalogue;
17 17 QIcon m_Icon;
18 18
19 19 CatalogueTreeItemPrivate(std::shared_ptr<DBCatalogue> catalogue, const QIcon &icon)
20 20 : m_Catalogue(catalogue), m_Icon(icon)
21 21 {
22 22 }
23 23 };
24 24
25 25
26 26 CatalogueTreeItem::CatalogueTreeItem(std::shared_ptr<DBCatalogue> catalogue, const QIcon &icon,
27 27 int type)
28 28 : CatalogueAbstractTreeItem(type),
29 29 impl{spimpl::make_unique_impl<CatalogueTreeItemPrivate>(catalogue, icon)}
30 30 {
31 31 }
32 32
33 33 QVariant CatalogueTreeItem::data(int column, int role) const
34 34 {
35 35 if (column == 0) {
36 36 switch (role) {
37 37 case Qt::EditRole: // fallthrough
38 38 case Qt::DisplayRole:
39 39 return impl->m_Catalogue->getName();
40 40 case Qt::DecorationRole:
41 41 return impl->m_Icon;
42 42 default:
43 43 break;
44 44 }
45 45 }
46 46
47 47 return QVariant();
48 48 }
49 49
50 50 bool CatalogueTreeItem::setData(int column, int role, const QVariant &value)
51 51 {
52 52 bool result = false;
53 53
54 54 if (role == Qt::EditRole && column == 0) {
55 55 auto newName = value.toString();
56 56 if (newName != impl->m_Catalogue->getName()) {
57 57 impl->m_Catalogue->setName(newName);
58 58 sqpApp->catalogueController().updateCatalogue(impl->m_Catalogue);
59 59 result = true;
60 60 }
61 61 }
62 62
63 63 return result;
64 64 }
65 65
66 66 Qt::ItemFlags CatalogueTreeItem::flags(int column) const
67 67 {
68 68 if (column == 0) {
69 69 return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable
70 70 | Qt::ItemIsDropEnabled;
71 71 }
72 72 else {
73 73 return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
74 74 }
75 75 }
76 76
77 77 bool CatalogueTreeItem::canDropMimeData(const QMimeData *data, Qt::DropAction action)
78 78 {
79 return data->hasFormat(MIME_TYPE_EVENT_LIST);
79 auto events = sqpApp->catalogueController().eventsForMimeData(data->data(MIME_TYPE_EVENT_LIST));
80 auto canDrop = data->hasFormat(MIME_TYPE_EVENT_LIST);
81
82 for (auto event : events) {
83 canDrop &= (event->getRepository() == impl->m_Catalogue->getRepository());
84 }
85 return canDrop;
80 86 }
81 87
82 88 bool CatalogueTreeItem::dropMimeData(const QMimeData *data, Qt::DropAction action)
83 89 {
84 90 Q_ASSERT(canDropMimeData(data, action));
85
86 auto events = sqpApp->catalogueController().eventsForMimeData(data->data(MIME_TYPE_EVENT_LIST));
87 // impl->m_Catalogue->addEvents(events); TODO: move events in the new catalogue
88 91 // Warning: Check that the events aren't already in the catalogue
89 92 // Also check for the repository !!!
93
94 auto events = sqpApp->catalogueController().eventsForMimeData(data->data(MIME_TYPE_EVENT_LIST));
95
96 for (auto event : events) {
97 impl->m_Catalogue->addEvent(event->getUniqId());
98 sqpApp->catalogueController().updateCatalogue(impl->m_Catalogue);
99 }
90 100 }
91 101
92 102 std::shared_ptr<DBCatalogue> CatalogueTreeItem::catalogue() const
93 103 {
94 104 return impl->m_Catalogue;
95 105 }
@@ -1,201 +1,218
1 1 #include "Catalogue/CatalogueTreeModel.h"
2 2 #include <Catalogue/CatalogueTreeItems/CatalogueAbstractTreeItem.h>
3 3
4 4 #include <QMimeData>
5 5 #include <memory>
6 6
7 7 #include <Common/MimeTypesDef.h>
8 8
9 9 struct CatalogueTreeModel::CatalogueTreeModelPrivate {
10 10 std::unique_ptr<CatalogueAbstractTreeItem> m_RootItem = nullptr;
11 11
12 12 CatalogueTreeModelPrivate() : m_RootItem{std::make_unique<CatalogueAbstractTreeItem>()} {}
13 13 };
14 14
15 15 CatalogueTreeModel::CatalogueTreeModel(QObject *parent)
16 16 : QAbstractItemModel(parent), impl{spimpl::make_unique_impl<CatalogueTreeModelPrivate>()}
17 17 {
18 18 }
19 19
20 20 QModelIndex CatalogueTreeModel::addTopLevelItem(CatalogueAbstractTreeItem *item)
21 21 {
22 22 auto nbTopLevelItems = impl->m_RootItem->children().count();
23 23 beginInsertRows(QModelIndex(), nbTopLevelItems, nbTopLevelItems);
24 24 impl->m_RootItem->addChild(item);
25 25 endInsertRows();
26 26
27 27 emit dataChanged(QModelIndex(), QModelIndex());
28 28
29 29 return index(nbTopLevelItems, 0);
30 30 }
31 31
32 32 QVector<CatalogueAbstractTreeItem *> CatalogueTreeModel::topLevelItems() const
33 33 {
34 34 return impl->m_RootItem->children();
35 35 }
36 36
37 37 void CatalogueTreeModel::addChildItem(CatalogueAbstractTreeItem *child,
38 38 const QModelIndex &parentIndex)
39 39 {
40 40 auto parentItem = item(parentIndex);
41 41 int c = parentItem->children().count();
42 42 beginInsertRows(parentIndex, c, c);
43 43 parentItem->addChild(child);
44 44 endInsertRows();
45 45
46 46 emit dataChanged(parentIndex, parentIndex);
47 47 }
48 48
49 void CatalogueTreeModel::removeChildItem(CatalogueAbstractTreeItem *child,
50 const QModelIndex &parentIndex)
51 {
52 auto parentItem = item(parentIndex);
53 int i = parentItem->children().indexOf(child);
54 beginRemoveRows(parentIndex, i, i);
55 parentItem->removeChild(child);
56 endRemoveRows();
57
58 emit dataChanged(parentIndex, parentIndex);
59 }
60
61 void CatalogueTreeModel::refresh(const QModelIndex &index)
62 {
63 emit dataChanged(index, index);
64 }
65
49 66 CatalogueAbstractTreeItem *CatalogueTreeModel::item(const QModelIndex &index) const
50 67 {
51 68 return static_cast<CatalogueAbstractTreeItem *>(index.internalPointer());
52 69 }
53 70
54 71 QModelIndex CatalogueTreeModel::indexOf(CatalogueAbstractTreeItem *item, int column) const
55 72 {
56 73 auto parentItem = item->parent();
57 74 if (!parentItem) {
58 75 return QModelIndex();
59 76 }
60 77
61 78 auto row = parentItem->children().indexOf(item);
62 79 return createIndex(row, column, item);
63 80 }
64 81
65 82 QModelIndex CatalogueTreeModel::index(int row, int column, const QModelIndex &parent) const
66 83 {
67 84 if (column > 0) {
68 85 int a = 0;
69 86 }
70 87
71 88 if (!hasIndex(row, column, parent)) {
72 89 return QModelIndex();
73 90 }
74 91
75 92 CatalogueAbstractTreeItem *parentItem = nullptr;
76 93
77 94 if (!parent.isValid()) {
78 95 parentItem = impl->m_RootItem.get();
79 96 }
80 97 else {
81 98 parentItem = item(parent);
82 99 }
83 100
84 101 auto childItem = parentItem->children().value(row);
85 102 if (childItem) {
86 103 return createIndex(row, column, childItem);
87 104 }
88 105
89 106 return QModelIndex();
90 107 }
91 108
92 109
93 110 QModelIndex CatalogueTreeModel::parent(const QModelIndex &index) const
94 111 {
95 112 if (!index.isValid()) {
96 113 return QModelIndex();
97 114 }
98 115
99 116 auto childItem = item(index);
100 117 auto parentItem = childItem->parent();
101 118
102 119 if (parentItem == nullptr || parentItem->parent() == nullptr) {
103 120 return QModelIndex();
104 121 }
105 122
106 123 auto row = parentItem->parent()->children().indexOf(parentItem);
107 124 return createIndex(row, 0, parentItem);
108 125 }
109 126
110 127 int CatalogueTreeModel::rowCount(const QModelIndex &parent) const
111 128 {
112 129 CatalogueAbstractTreeItem *parentItem = nullptr;
113 130
114 131 if (!parent.isValid()) {
115 132 parentItem = impl->m_RootItem.get();
116 133 }
117 134 else {
118 135 parentItem = item(parent);
119 136 }
120 137
121 138 return parentItem->children().count();
122 139 }
123 140
124 141 int CatalogueTreeModel::columnCount(const QModelIndex &parent) const
125 142 {
126 143 return (int)Column::Count;
127 144 }
128 145
129 146 Qt::ItemFlags CatalogueTreeModel::flags(const QModelIndex &index) const
130 147 {
131 148 auto treeItem = item(index);
132 149 if (treeItem) {
133 150 return treeItem->flags(index.column());
134 151 }
135 152
136 153 return Qt::NoItemFlags;
137 154 }
138 155
139 156 QVariant CatalogueTreeModel::data(const QModelIndex &index, int role) const
140 157 {
141 158 auto treeItem = item(index);
142 159 if (treeItem) {
143 160 return treeItem->data(index.column(), role);
144 161 }
145 162
146 163 return QModelIndex();
147 164 }
148 165
149 166 bool CatalogueTreeModel::setData(const QModelIndex &index, const QVariant &value, int role)
150 167 {
151 168 auto treeItem = item(index);
152 169 if (treeItem) {
153 170 auto result = treeItem->setData(index.column(), role, value);
154 171
155 172 if (result && index.column() == (int)Column::Name) {
156 173 emit itemRenamed(index);
157 174 }
158 175
159 176 return result;
160 177 }
161 178
162 179 return false;
163 180 }
164 181 bool CatalogueTreeModel::canDropMimeData(const QMimeData *data, Qt::DropAction action, int row,
165 182 int column, const QModelIndex &parent) const
166 183 {
167 184 auto draggedIndex = parent;
168 185 auto draggedItem = item(draggedIndex);
169 186 if (draggedItem) {
170 187 return draggedItem->canDropMimeData(data, action);
171 188 }
172 189
173 190 return false;
174 191 }
175 192
176 193 bool CatalogueTreeModel::dropMimeData(const QMimeData *data, Qt::DropAction action, int row,
177 194 int column, const QModelIndex &parent)
178 195 {
179 196 bool result = false;
180 197
181 198 auto draggedIndex = parent;
182 199 auto draggedItem = item(draggedIndex);
183 200 if (draggedItem) {
184 201 result = draggedItem->dropMimeData(data, action);
185 202 if (result) {
186 203 emit itemDropped(draggedIndex);
187 204 }
188 205 }
189 206
190 207 return result;
191 208 }
192 209
193 210 Qt::DropActions CatalogueTreeModel::supportedDropActions() const
194 211 {
195 212 return Qt::CopyAction | Qt::MoveAction;
196 213 }
197 214
198 215 QStringList CatalogueTreeModel::mimeTypes() const
199 216 {
200 217 return {MIME_TYPE_EVENT_LIST};
201 218 }
@@ -1,1055 +1,1054
1 1 #include "Visualization/VisualizationGraphWidget.h"
2 2 #include "Visualization/IVisualizationWidgetVisitor.h"
3 3 #include "Visualization/VisualizationCursorItem.h"
4 4 #include "Visualization/VisualizationDefs.h"
5 5 #include "Visualization/VisualizationGraphHelper.h"
6 6 #include "Visualization/VisualizationGraphRenderingDelegate.h"
7 7 #include "Visualization/VisualizationMultiZoneSelectionDialog.h"
8 8 #include "Visualization/VisualizationSelectionZoneItem.h"
9 9 #include "Visualization/VisualizationSelectionZoneManager.h"
10 10 #include "Visualization/VisualizationWidget.h"
11 11 #include "Visualization/VisualizationZoneWidget.h"
12 12 #include "ui_VisualizationGraphWidget.h"
13 13
14 14 #include <Actions/ActionsGuiController.h>
15 15 #include <Common/MimeTypesDef.h>
16 16 #include <Data/ArrayData.h>
17 17 #include <Data/IDataSeries.h>
18 18 #include <Data/SpectrogramSeries.h>
19 19 #include <DragAndDrop/DragDropGuiController.h>
20 20 #include <Settings/SqpSettingsDefs.h>
21 21 #include <SqpApplication.h>
22 22 #include <Time/TimeController.h>
23 23 #include <Variable/Variable.h>
24 24 #include <Variable/VariableController.h>
25 25
26 26 #include <unordered_map>
27 27
28 28 Q_LOGGING_CATEGORY(LOG_VisualizationGraphWidget, "VisualizationGraphWidget")
29 29
30 30 namespace {
31 31
32 32 /// Key pressed to enable drag&drop in all modes
33 33 const auto DRAG_DROP_MODIFIER = Qt::AltModifier;
34 34
35 35 /// Key pressed to enable zoom on horizontal axis
36 36 const auto HORIZONTAL_ZOOM_MODIFIER = Qt::ControlModifier;
37 37
38 38 /// Key pressed to enable zoom on vertical axis
39 39 const auto VERTICAL_ZOOM_MODIFIER = Qt::ShiftModifier;
40 40
41 41 /// Speed of a step of a wheel event for a pan, in percentage of the axis range
42 42 const auto PAN_SPEED = 5;
43 43
44 44 /// Key pressed to enable a calibration pan
45 45 const auto VERTICAL_PAN_MODIFIER = Qt::AltModifier;
46 46
47 47 /// Key pressed to enable multi selection of selection zones
48 48 const auto MULTI_ZONE_SELECTION_MODIFIER = Qt::ControlModifier;
49 49
50 50 /// Minimum size for the zoom box, in percentage of the axis range
51 51 const auto ZOOM_BOX_MIN_SIZE = 0.8;
52 52
53 53 /// Format of the dates appearing in the label of a cursor
54 54 const auto CURSOR_LABELS_DATETIME_FORMAT = QStringLiteral("yyyy/MM/dd\nhh:mm:ss:zzz");
55 55
56 56 } // namespace
57 57
58 58 struct VisualizationGraphWidget::VisualizationGraphWidgetPrivate {
59 59
60 60 explicit VisualizationGraphWidgetPrivate(const QString &name)
61 61 : m_Name{name},
62 62 m_Flags{GraphFlag::EnableAll},
63 63 m_IsCalibration{false},
64 64 m_RenderingDelegate{nullptr}
65 65 {
66 66 }
67 67
68 68 void updateData(PlottablesMap &plottables, std::shared_ptr<Variable> variable,
69 69 const SqpRange &range)
70 70 {
71 71 VisualizationGraphHelper::updateData(plottables, variable, range);
72 72
73 73 // Prevents that data has changed to update rendering
74 74 m_RenderingDelegate->onPlotUpdated();
75 75 }
76 76
77 77 QString m_Name;
78 78 // 1 variable -> n qcpplot
79 79 std::map<std::shared_ptr<Variable>, PlottablesMap> m_VariableToPlotMultiMap;
80 80 GraphFlags m_Flags;
81 81 bool m_IsCalibration;
82 82 /// Delegate used to attach rendering features to the plot
83 83 std::unique_ptr<VisualizationGraphRenderingDelegate> m_RenderingDelegate;
84 84
85 85 QCPItemRect *m_DrawingZoomRect = nullptr;
86 86 QStack<QPair<QCPRange, QCPRange> > m_ZoomStack;
87 87
88 88 std::unique_ptr<VisualizationCursorItem> m_HorizontalCursor = nullptr;
89 89 std::unique_ptr<VisualizationCursorItem> m_VerticalCursor = nullptr;
90 90
91 91 VisualizationSelectionZoneItem *m_DrawingZone = nullptr;
92 92 VisualizationSelectionZoneItem *m_HoveredZone = nullptr;
93 93 QVector<VisualizationSelectionZoneItem *> m_SelectionZones;
94 94
95 95 bool m_HasMovedMouse = false; // Indicates if the mouse moved in a releaseMouse even
96 96
97 97 bool m_VariableAutoRangeOnInit = true;
98 98
99 99 void startDrawingRect(const QPoint &pos, QCustomPlot &plot)
100 100 {
101 101 removeDrawingRect(plot);
102 102
103 103 auto axisPos = posToAxisPos(pos, plot);
104 104
105 105 m_DrawingZoomRect = new QCPItemRect{&plot};
106 106 QPen p;
107 107 p.setWidth(2);
108 108 m_DrawingZoomRect->setPen(p);
109 109
110 110 m_DrawingZoomRect->topLeft->setCoords(axisPos);
111 111 m_DrawingZoomRect->bottomRight->setCoords(axisPos);
112 112 }
113 113
114 114 void removeDrawingRect(QCustomPlot &plot)
115 115 {
116 116 if (m_DrawingZoomRect) {
117 117 plot.removeItem(m_DrawingZoomRect); // the item is deleted by QCustomPlot
118 118 m_DrawingZoomRect = nullptr;
119 119 plot.replot(QCustomPlot::rpQueuedReplot);
120 120 }
121 121 }
122 122
123 123 void startDrawingZone(const QPoint &pos, VisualizationGraphWidget *graph)
124 124 {
125 125 endDrawingZone(graph);
126 126
127 127 auto axisPos = posToAxisPos(pos, graph->plot());
128 128
129 129 m_DrawingZone = new VisualizationSelectionZoneItem{&graph->plot()};
130 130 m_DrawingZone->setRange(axisPos.x(), axisPos.x());
131 131 m_DrawingZone->setEditionEnabled(false);
132 132 }
133 133
134 134 void endDrawingZone(VisualizationGraphWidget *graph)
135 135 {
136 136 if (m_DrawingZone) {
137 137 auto drawingZoneRange = m_DrawingZone->range();
138 138 if (qAbs(drawingZoneRange.m_TEnd - drawingZoneRange.m_TStart) > 0) {
139 139 m_DrawingZone->setEditionEnabled(true);
140 140 addSelectionZone(m_DrawingZone);
141 141 }
142 142 else {
143 143 graph->plot().removeItem(m_DrawingZone); // the item is deleted by QCustomPlot
144 144 }
145 145
146 146 graph->plot().replot(QCustomPlot::rpQueuedReplot);
147 147 m_DrawingZone = nullptr;
148 148 }
149 149 }
150 150
151 151 void setSelectionZonesEditionEnabled(bool value)
152 152 {
153 153 for (auto s : m_SelectionZones) {
154 154 s->setEditionEnabled(value);
155 155 }
156 156 }
157 157
158 158 void addSelectionZone(VisualizationSelectionZoneItem *zone) { m_SelectionZones << zone; }
159 159
160 160 VisualizationSelectionZoneItem *selectionZoneAt(const QPoint &pos,
161 161 const QCustomPlot &plot) const
162 162 {
163 163 VisualizationSelectionZoneItem *selectionZoneItemUnderCursor = nullptr;
164 164 auto minDistanceToZone = -1;
165 165 for (auto zone : m_SelectionZones) {
166 166 auto distanceToZone = zone->selectTest(pos, false);
167 167 if ((minDistanceToZone < 0 || distanceToZone <= minDistanceToZone)
168 168 && distanceToZone >= 0 && distanceToZone < plot.selectionTolerance()) {
169 169 selectionZoneItemUnderCursor = zone;
170 170 }
171 171 }
172 172
173 173 return selectionZoneItemUnderCursor;
174 174 }
175 175
176 176 QVector<VisualizationSelectionZoneItem *> selectionZonesAt(const QPoint &pos,
177 177 const QCustomPlot &plot) const
178 178 {
179 179 QVector<VisualizationSelectionZoneItem *> zones;
180 180 for (auto zone : m_SelectionZones) {
181 181 auto distanceToZone = zone->selectTest(pos, false);
182 182 if (distanceToZone >= 0 && distanceToZone < plot.selectionTolerance()) {
183 183 zones << zone;
184 184 }
185 185 }
186 186
187 187 return zones;
188 188 }
189 189
190 190 void moveSelectionZoneOnTop(VisualizationSelectionZoneItem *zone, QCustomPlot &plot)
191 191 {
192 192 if (!m_SelectionZones.isEmpty() && m_SelectionZones.last() != zone) {
193 193 zone->moveToTop();
194 194 m_SelectionZones.removeAll(zone);
195 195 m_SelectionZones.append(zone);
196 196 }
197 197 }
198 198
199 199 QPointF posToAxisPos(const QPoint &pos, QCustomPlot &plot) const
200 200 {
201 201 auto axisX = plot.axisRect()->axis(QCPAxis::atBottom);
202 202 auto axisY = plot.axisRect()->axis(QCPAxis::atLeft);
203 203 return QPointF{axisX->pixelToCoord(pos.x()), axisY->pixelToCoord(pos.y())};
204 204 }
205 205
206 206 bool pointIsInAxisRect(const QPointF &axisPoint, QCustomPlot &plot) const
207 207 {
208 208 auto axisX = plot.axisRect()->axis(QCPAxis::atBottom);
209 209 auto axisY = plot.axisRect()->axis(QCPAxis::atLeft);
210 210 return axisX->range().contains(axisPoint.x()) && axisY->range().contains(axisPoint.y());
211 211 }
212 212 };
213 213
214 214 VisualizationGraphWidget::VisualizationGraphWidget(const QString &name, QWidget *parent)
215 215 : VisualizationDragWidget{parent},
216 216 ui{new Ui::VisualizationGraphWidget},
217 217 impl{spimpl::make_unique_impl<VisualizationGraphWidgetPrivate>(name)}
218 218 {
219 219 ui->setupUi(this);
220 220
221 221 // 'Close' options : widget is deleted when closed
222 222 setAttribute(Qt::WA_DeleteOnClose);
223 223
224 224 // Set qcpplot properties :
225 225 // - zoom is enabled
226 226 // - Mouse wheel on qcpplot is intercepted to determine the zoom orientation
227 227 ui->widget->setInteractions(QCP::iRangeZoom);
228 228 ui->widget->axisRect()->setRangeDrag(Qt::Horizontal | Qt::Vertical);
229 229
230 230 // The delegate must be initialized after the ui as it uses the plot
231 231 impl->m_RenderingDelegate = std::make_unique<VisualizationGraphRenderingDelegate>(*this);
232 232
233 233 // Init the cursors
234 234 impl->m_HorizontalCursor = std::make_unique<VisualizationCursorItem>(&plot());
235 235 impl->m_HorizontalCursor->setOrientation(Qt::Horizontal);
236 236 impl->m_VerticalCursor = std::make_unique<VisualizationCursorItem>(&plot());
237 237 impl->m_VerticalCursor->setOrientation(Qt::Vertical);
238 238
239 239 connect(ui->widget, &QCustomPlot::mousePress, this, &VisualizationGraphWidget::onMousePress);
240 240 connect(ui->widget, &QCustomPlot::mouseRelease, this,
241 241 &VisualizationGraphWidget::onMouseRelease);
242 242 connect(ui->widget, &QCustomPlot::mouseMove, this, &VisualizationGraphWidget::onMouseMove);
243 243 connect(ui->widget, &QCustomPlot::mouseWheel, this, &VisualizationGraphWidget::onMouseWheel);
244 244 connect(ui->widget, &QCustomPlot::mouseDoubleClick, this,
245 245 &VisualizationGraphWidget::onMouseDoubleClick);
246 246 connect(ui->widget->xAxis, static_cast<void (QCPAxis::*)(const QCPRange &, const QCPRange &)>(
247 247 &QCPAxis::rangeChanged),
248 248 this, &VisualizationGraphWidget::onRangeChanged, Qt::DirectConnection);
249 249
250 250 // Activates menu when right clicking on the graph
251 251 ui->widget->setContextMenuPolicy(Qt::CustomContextMenu);
252 252 connect(ui->widget, &QCustomPlot::customContextMenuRequested, this,
253 253 &VisualizationGraphWidget::onGraphMenuRequested);
254 254
255 255 connect(this, &VisualizationGraphWidget::requestDataLoading, &sqpApp->variableController(),
256 256 &VariableController::onRequestDataLoading);
257 257
258 258 connect(&sqpApp->variableController(), &VariableController::updateVarDisplaying, this,
259 259 &VisualizationGraphWidget::onUpdateVarDisplaying);
260 260
261 261 #ifdef Q_OS_MAC
262 262 plot().setPlottingHint(QCP::phFastPolylines, true);
263 263 #endif
264 264 }
265 265
266 266
267 267 VisualizationGraphWidget::~VisualizationGraphWidget()
268 268 {
269 269 delete ui;
270 270 }
271 271
272 272 VisualizationZoneWidget *VisualizationGraphWidget::parentZoneWidget() const noexcept
273 273 {
274 274 auto parent = parentWidget();
275 275 while (parent != nullptr && !qobject_cast<VisualizationZoneWidget *>(parent)) {
276 276 parent = parent->parentWidget();
277 277 }
278 278
279 279 return qobject_cast<VisualizationZoneWidget *>(parent);
280 280 }
281 281
282 282 VisualizationWidget *VisualizationGraphWidget::parentVisualizationWidget() const
283 283 {
284 284 auto parent = parentWidget();
285 285 while (parent != nullptr && !qobject_cast<VisualizationWidget *>(parent)) {
286 286 parent = parent->parentWidget();
287 287 }
288 288
289 289 return qobject_cast<VisualizationWidget *>(parent);
290 290 }
291 291
292 292 void VisualizationGraphWidget::setFlags(GraphFlags flags)
293 293 {
294 294 impl->m_Flags = std::move(flags);
295 295 }
296 296
297 297 void VisualizationGraphWidget::addVariable(std::shared_ptr<Variable> variable, SqpRange range)
298 298 {
299 299 /// Lambda used to set graph's units and range according to the variable passed in parameter
300 300 auto loadRange = [this](std::shared_ptr<Variable> variable, const SqpRange &range) {
301 301 impl->m_RenderingDelegate->setAxesUnits(*variable);
302 302
303 303 this->setFlags(GraphFlag::DisableAll);
304 304 setGraphRange(range);
305 305 this->setFlags(GraphFlag::EnableAll);
306
307 306 emit requestDataLoading({variable}, range, false);
308 307 };
309 308
310 309 connect(variable.get(), SIGNAL(updated()), this, SLOT(onDataCacheVariableUpdated()));
311 310
312 311 // Calls update of graph's range and units when the data of the variable have been initialized.
313 312 // Note: we use QueuedConnection here as the update event must be called in the UI thread
314 313 connect(variable.get(), &Variable::dataInitialized, this,
315 314 [ varW = std::weak_ptr<Variable>{variable}, range, loadRange, this ]() {
316 315 if (auto var = varW.lock()) {
317 316 // If the variable is the first added in the graph, we load its range
318 317 auto firstVariableInGraph = range == INVALID_RANGE;
319 318 auto loadedRange = graphRange();
320 319 if (impl->m_VariableAutoRangeOnInit) {
321 320 loadedRange = firstVariableInGraph ? var->range() : range;
322 321 }
323 322 loadRange(var, loadedRange);
324 323 setYRange(var);
325 324 }
326 325 },
327 326 Qt::QueuedConnection);
328 327
329 328 // Uses delegate to create the qcpplot components according to the variable
330 329 auto createdPlottables = VisualizationGraphHelper::create(variable, *ui->widget);
331 330
332 331 // Sets graph properties
333 332 impl->m_RenderingDelegate->setGraphProperties(*variable, createdPlottables);
334 333
335 334 impl->m_VariableToPlotMultiMap.insert({variable, std::move(createdPlottables)});
336 335
337 336 // If the variable already has its data loaded, load its units and its range in the graph
338 337 if (variable->dataSeries() != nullptr) {
339 338 loadRange(variable, range);
340 339 }
341 340
342 341 emit variableAdded(variable);
343 342 }
344 343
345 344 void VisualizationGraphWidget::removeVariable(std::shared_ptr<Variable> variable) noexcept
346 345 {
347 346 // Each component associated to the variable :
348 347 // - is removed from qcpplot (which deletes it)
349 348 // - is no longer referenced in the map
350 349 auto variableIt = impl->m_VariableToPlotMultiMap.find(variable);
351 350 if (variableIt != impl->m_VariableToPlotMultiMap.cend()) {
352 351 emit variableAboutToBeRemoved(variable);
353 352
354 353 auto &plottablesMap = variableIt->second;
355 354
356 355 for (auto plottableIt = plottablesMap.cbegin(), plottableEnd = plottablesMap.cend();
357 356 plottableIt != plottableEnd;) {
358 357 ui->widget->removePlottable(plottableIt->second);
359 358 plottableIt = plottablesMap.erase(plottableIt);
360 359 }
361 360
362 361 impl->m_VariableToPlotMultiMap.erase(variableIt);
363 362 }
364 363
365 364 // Updates graph
366 365 ui->widget->replot();
367 366 }
368 367
369 368 QList<std::shared_ptr<Variable> > VisualizationGraphWidget::variables() const
370 369 {
371 370 auto variables = QList<std::shared_ptr<Variable> >{};
372 371 for (auto it = std::cbegin(impl->m_VariableToPlotMultiMap);
373 372 it != std::cend(impl->m_VariableToPlotMultiMap); ++it) {
374 373 variables << it->first;
375 374 }
376 375
377 376 return variables;
378 377 }
379 378
380 379 void VisualizationGraphWidget::setYRange(std::shared_ptr<Variable> variable)
381 380 {
382 381 if (!variable) {
383 382 qCCritical(LOG_VisualizationGraphWidget()) << "Can't set y-axis range: variable is null";
384 383 return;
385 384 }
386 385
387 386 VisualizationGraphHelper::setYAxisRange(variable, *ui->widget);
388 387 }
389 388
390 389 SqpRange VisualizationGraphWidget::graphRange() const noexcept
391 390 {
392 391 auto graphRange = ui->widget->xAxis->range();
393 392 return SqpRange{graphRange.lower, graphRange.upper};
394 393 }
395 394
396 395 void VisualizationGraphWidget::setGraphRange(const SqpRange &range, bool calibration)
397 396 {
398 397 qCDebug(LOG_VisualizationGraphWidget()) << tr("VisualizationGraphWidget::setGraphRange START");
399 398
400 399 if (calibration) {
401 400 impl->m_IsCalibration = true;
402 401 }
403 402
404 403 ui->widget->xAxis->setRange(range.m_TStart, range.m_TEnd);
405 404 ui->widget->replot();
406 405
407 406 if (calibration) {
408 407 impl->m_IsCalibration = false;
409 408 }
410 409
411 410 qCDebug(LOG_VisualizationGraphWidget()) << tr("VisualizationGraphWidget::setGraphRange END");
412 411 }
413 412
414 413 void VisualizationGraphWidget::setAutoRangeOnVariableInitialization(bool value)
415 414 {
416 415 impl->m_VariableAutoRangeOnInit = value;
417 416 }
418 417
419 418 QVector<SqpRange> VisualizationGraphWidget::selectionZoneRanges() const
420 419 {
421 420 QVector<SqpRange> ranges;
422 421 for (auto zone : impl->m_SelectionZones) {
423 422 ranges << zone->range();
424 423 }
425 424
426 425 return ranges;
427 426 }
428 427
429 428 void VisualizationGraphWidget::addSelectionZones(const QVector<SqpRange> &ranges)
430 429 {
431 430 for (const auto &range : ranges) {
432 431 // note: ownership is transfered to QCustomPlot
433 432 auto zone = new VisualizationSelectionZoneItem(&plot());
434 433 zone->setRange(range.m_TStart, range.m_TEnd);
435 434 impl->addSelectionZone(zone);
436 435 }
437 436
438 437 plot().replot(QCustomPlot::rpQueuedReplot);
439 438 }
440 439
441 440 VisualizationSelectionZoneItem *VisualizationGraphWidget::addSelectionZone(const QString &name,
442 441 const SqpRange &range)
443 442 {
444 443 // note: ownership is transfered to QCustomPlot
445 444 auto zone = new VisualizationSelectionZoneItem(&plot());
446 445 zone->setName(name);
447 446 zone->setRange(range.m_TStart, range.m_TEnd);
448 447 impl->addSelectionZone(zone);
449 448
450 449 plot().replot(QCustomPlot::rpQueuedReplot);
451 450
452 451 return zone;
453 452 }
454 453
455 454 void VisualizationGraphWidget::removeSelectionZone(VisualizationSelectionZoneItem *selectionZone)
456 455 {
457 456 parentVisualizationWidget()->selectionZoneManager().setSelected(selectionZone, false);
458 457
459 458 if (impl->m_HoveredZone == selectionZone) {
460 459 impl->m_HoveredZone = nullptr;
461 460 setCursor(Qt::ArrowCursor);
462 461 }
463 462
464 463 impl->m_SelectionZones.removeAll(selectionZone);
465 464 plot().removeItem(selectionZone);
466 465 plot().replot(QCustomPlot::rpQueuedReplot);
467 466 }
468 467
469 468 void VisualizationGraphWidget::undoZoom()
470 469 {
471 470 auto zoom = impl->m_ZoomStack.pop();
472 471 auto axisX = plot().axisRect()->axis(QCPAxis::atBottom);
473 472 auto axisY = plot().axisRect()->axis(QCPAxis::atLeft);
474 473
475 474 axisX->setRange(zoom.first);
476 475 axisY->setRange(zoom.second);
477 476
478 477 plot().replot(QCustomPlot::rpQueuedReplot);
479 478 }
480 479
481 480 void VisualizationGraphWidget::accept(IVisualizationWidgetVisitor *visitor)
482 481 {
483 482 if (visitor) {
484 483 visitor->visit(this);
485 484 }
486 485 else {
487 486 qCCritical(LOG_VisualizationGraphWidget())
488 487 << tr("Can't visit widget : the visitor is null");
489 488 }
490 489 }
491 490
492 491 bool VisualizationGraphWidget::canDrop(const Variable &variable) const
493 492 {
494 493 auto isSpectrogram = [](const auto &variable) {
495 494 return std::dynamic_pointer_cast<SpectrogramSeries>(variable.dataSeries()) != nullptr;
496 495 };
497 496
498 497 // - A spectrogram series can't be dropped on graph with existing plottables
499 498 // - No data series can be dropped on graph with existing spectrogram series
500 499 return isSpectrogram(variable)
501 500 ? impl->m_VariableToPlotMultiMap.empty()
502 501 : std::none_of(
503 502 impl->m_VariableToPlotMultiMap.cbegin(), impl->m_VariableToPlotMultiMap.cend(),
504 503 [isSpectrogram](const auto &entry) { return isSpectrogram(*entry.first); });
505 504 }
506 505
507 506 bool VisualizationGraphWidget::contains(const Variable &variable) const
508 507 {
509 508 // Finds the variable among the keys of the map
510 509 auto variablePtr = &variable;
511 510 auto findVariable
512 511 = [variablePtr](const auto &entry) { return variablePtr == entry.first.get(); };
513 512
514 513 auto end = impl->m_VariableToPlotMultiMap.cend();
515 514 auto it = std::find_if(impl->m_VariableToPlotMultiMap.cbegin(), end, findVariable);
516 515 return it != end;
517 516 }
518 517
519 518 QString VisualizationGraphWidget::name() const
520 519 {
521 520 return impl->m_Name;
522 521 }
523 522
524 523 QMimeData *VisualizationGraphWidget::mimeData(const QPoint &position) const
525 524 {
526 525 auto mimeData = new QMimeData;
527 526
528 527 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(position, plot());
529 528 if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones
530 529 && selectionZoneItemUnderCursor) {
531 530 mimeData->setData(MIME_TYPE_TIME_RANGE, TimeController::mimeDataForTimeRange(
532 531 selectionZoneItemUnderCursor->range()));
533 532 mimeData->setData(MIME_TYPE_SELECTION_ZONE, TimeController::mimeDataForTimeRange(
534 533 selectionZoneItemUnderCursor->range()));
535 534 }
536 535 else {
537 536 mimeData->setData(MIME_TYPE_GRAPH, QByteArray{});
538 537
539 538 auto timeRangeData = TimeController::mimeDataForTimeRange(graphRange());
540 539 mimeData->setData(MIME_TYPE_TIME_RANGE, timeRangeData);
541 540 }
542 541
543 542 return mimeData;
544 543 }
545 544
546 545 QPixmap VisualizationGraphWidget::customDragPixmap(const QPoint &dragPosition)
547 546 {
548 547 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(dragPosition, plot());
549 548 if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones
550 549 && selectionZoneItemUnderCursor) {
551 550
552 551 auto zoneTopLeft = selectionZoneItemUnderCursor->topLeft->pixelPosition();
553 552 auto zoneBottomRight = selectionZoneItemUnderCursor->bottomRight->pixelPosition();
554 553
555 554 auto zoneSize = QSizeF{qAbs(zoneBottomRight.x() - zoneTopLeft.x()),
556 555 qAbs(zoneBottomRight.y() - zoneTopLeft.y())}
557 556 .toSize();
558 557
559 558 auto pixmap = QPixmap(zoneSize);
560 559 render(&pixmap, QPoint(), QRegion{QRect{zoneTopLeft.toPoint(), zoneSize}});
561 560
562 561 return pixmap;
563 562 }
564 563
565 564 return QPixmap();
566 565 }
567 566
568 567 bool VisualizationGraphWidget::isDragAllowed() const
569 568 {
570 569 return true;
571 570 }
572 571
573 572 void VisualizationGraphWidget::highlightForMerge(bool highlighted)
574 573 {
575 574 if (highlighted) {
576 575 plot().setBackground(QBrush(QColor("#BBD5EE")));
577 576 }
578 577 else {
579 578 plot().setBackground(QBrush(Qt::white));
580 579 }
581 580
582 581 plot().update();
583 582 }
584 583
585 584 void VisualizationGraphWidget::addVerticalCursor(double time)
586 585 {
587 586 impl->m_VerticalCursor->setPosition(time);
588 587 impl->m_VerticalCursor->setVisible(true);
589 588
590 589 auto text
591 590 = DateUtils::dateTime(time).toString(CURSOR_LABELS_DATETIME_FORMAT).replace(' ', '\n');
592 591 impl->m_VerticalCursor->setLabelText(text);
593 592 }
594 593
595 594 void VisualizationGraphWidget::addVerticalCursorAtViewportPosition(double position)
596 595 {
597 596 impl->m_VerticalCursor->setAbsolutePosition(position);
598 597 impl->m_VerticalCursor->setVisible(true);
599 598
600 599 auto axis = plot().axisRect()->axis(QCPAxis::atBottom);
601 600 auto text
602 601 = DateUtils::dateTime(axis->pixelToCoord(position)).toString(CURSOR_LABELS_DATETIME_FORMAT);
603 602 impl->m_VerticalCursor->setLabelText(text);
604 603 }
605 604
606 605 void VisualizationGraphWidget::removeVerticalCursor()
607 606 {
608 607 impl->m_VerticalCursor->setVisible(false);
609 608 plot().replot(QCustomPlot::rpQueuedReplot);
610 609 }
611 610
612 611 void VisualizationGraphWidget::addHorizontalCursor(double value)
613 612 {
614 613 impl->m_HorizontalCursor->setPosition(value);
615 614 impl->m_HorizontalCursor->setVisible(true);
616 615 impl->m_HorizontalCursor->setLabelText(QString::number(value));
617 616 }
618 617
619 618 void VisualizationGraphWidget::addHorizontalCursorAtViewportPosition(double position)
620 619 {
621 620 impl->m_HorizontalCursor->setAbsolutePosition(position);
622 621 impl->m_HorizontalCursor->setVisible(true);
623 622
624 623 auto axis = plot().axisRect()->axis(QCPAxis::atLeft);
625 624 impl->m_HorizontalCursor->setLabelText(QString::number(axis->pixelToCoord(position)));
626 625 }
627 626
628 627 void VisualizationGraphWidget::removeHorizontalCursor()
629 628 {
630 629 impl->m_HorizontalCursor->setVisible(false);
631 630 plot().replot(QCustomPlot::rpQueuedReplot);
632 631 }
633 632
634 633 void VisualizationGraphWidget::closeEvent(QCloseEvent *event)
635 634 {
636 635 Q_UNUSED(event);
637 636
638 637 // Prevents that all variables will be removed from graph when it will be closed
639 638 for (auto &variableEntry : impl->m_VariableToPlotMultiMap) {
640 639 emit variableAboutToBeRemoved(variableEntry.first);
641 640 }
642 641 }
643 642
644 643 void VisualizationGraphWidget::enterEvent(QEvent *event)
645 644 {
646 645 Q_UNUSED(event);
647 646 impl->m_RenderingDelegate->showGraphOverlay(true);
648 647 }
649 648
650 649 void VisualizationGraphWidget::leaveEvent(QEvent *event)
651 650 {
652 651 Q_UNUSED(event);
653 652 impl->m_RenderingDelegate->showGraphOverlay(false);
654 653
655 654 if (auto parentZone = parentZoneWidget()) {
656 655 parentZone->notifyMouseLeaveGraph(this);
657 656 }
658 657 else {
659 658 qCWarning(LOG_VisualizationGraphWidget()) << "leaveEvent: No parent zone widget";
660 659 }
661 660
662 661 if (impl->m_HoveredZone) {
663 662 impl->m_HoveredZone->setHovered(false);
664 663 impl->m_HoveredZone = nullptr;
665 664 }
666 665 }
667 666
668 667 QCustomPlot &VisualizationGraphWidget::plot() const noexcept
669 668 {
670 669 return *ui->widget;
671 670 }
672 671
673 672 void VisualizationGraphWidget::onGraphMenuRequested(const QPoint &pos) noexcept
674 673 {
675 674 QMenu graphMenu{};
676 675
677 676 // Iterates on variables (unique keys)
678 677 for (auto it = impl->m_VariableToPlotMultiMap.cbegin(),
679 678 end = impl->m_VariableToPlotMultiMap.cend();
680 679 it != end; it = impl->m_VariableToPlotMultiMap.upper_bound(it->first)) {
681 680 // 'Remove variable' action
682 681 graphMenu.addAction(tr("Remove variable %1").arg(it->first->name()),
683 682 [ this, var = it->first ]() { removeVariable(var); });
684 683 }
685 684
686 685 if (!impl->m_ZoomStack.isEmpty()) {
687 686 if (!graphMenu.isEmpty()) {
688 687 graphMenu.addSeparator();
689 688 }
690 689
691 690 graphMenu.addAction(tr("Undo Zoom"), [this]() { undoZoom(); });
692 691 }
693 692
694 693 // Selection Zone Actions
695 694 auto selectionZoneItem = impl->selectionZoneAt(pos, plot());
696 695 if (selectionZoneItem) {
697 696 auto selectedItems = parentVisualizationWidget()->selectionZoneManager().selectedItems();
698 697 selectedItems.removeAll(selectionZoneItem);
699 698 selectedItems.prepend(selectionZoneItem); // Put the current selection zone first
700 699
701 700 auto zoneActions = sqpApp->actionsGuiController().selectionZoneActions();
702 701 if (!zoneActions.isEmpty() && !graphMenu.isEmpty()) {
703 702 graphMenu.addSeparator();
704 703 }
705 704
706 705 QHash<QString, QMenu *> subMenus;
707 706 QHash<QString, bool> subMenusEnabled;
708 707
709 708 for (auto zoneAction : zoneActions) {
710 709
711 710 auto isEnabled = zoneAction->isEnabled(selectedItems);
712 711
713 712 auto menu = &graphMenu;
714 713 for (auto subMenuName : zoneAction->subMenuList()) {
715 714 if (!subMenus.contains(subMenuName)) {
716 715 menu = menu->addMenu(subMenuName);
717 716 subMenus[subMenuName] = menu;
718 717 subMenusEnabled[subMenuName] = isEnabled;
719 718 }
720 719 else {
721 720 menu = subMenus.value(subMenuName);
722 721 if (isEnabled) {
723 722 // The sub menu is enabled if at least one of its actions is enabled
724 723 subMenusEnabled[subMenuName] = true;
725 724 }
726 725 }
727 726 }
728 727
729 728 auto action = menu->addAction(zoneAction->name());
730 729 action->setEnabled(isEnabled);
731 730 action->setShortcut(zoneAction->displayedShortcut());
732 731 QObject::connect(action, &QAction::triggered,
733 732 [zoneAction, selectedItems]() { zoneAction->execute(selectedItems); });
734 733 }
735 734
736 735 for (auto it = subMenus.cbegin(); it != subMenus.cend(); ++it) {
737 736 it.value()->setEnabled(subMenusEnabled[it.key()]);
738 737 }
739 738 }
740 739
741 740 if (!graphMenu.isEmpty()) {
742 741 graphMenu.exec(QCursor::pos());
743 742 }
744 743 }
745 744
746 745 void VisualizationGraphWidget::onRangeChanged(const QCPRange &t1, const QCPRange &t2)
747 746 {
748 747 qCDebug(LOG_VisualizationGraphWidget()) << tr("TORM: VisualizationGraphWidget::onRangeChanged")
749 748 << QThread::currentThread()->objectName() << "DoAcqui"
750 749 << impl->m_Flags.testFlag(GraphFlag::EnableAcquisition);
751 750
752 751 auto graphRange = SqpRange{t1.lower, t1.upper};
753 752 auto oldGraphRange = SqpRange{t2.lower, t2.upper};
754 753
755 754 if (impl->m_Flags.testFlag(GraphFlag::EnableAcquisition)) {
756 755 QVector<std::shared_ptr<Variable> > variableUnderGraphVector;
757 756
758 757 for (auto it = impl->m_VariableToPlotMultiMap.begin(),
759 758 end = impl->m_VariableToPlotMultiMap.end();
760 759 it != end; it = impl->m_VariableToPlotMultiMap.upper_bound(it->first)) {
761 760 variableUnderGraphVector.push_back(it->first);
762 761 }
763 762 emit requestDataLoading(std::move(variableUnderGraphVector), graphRange,
764 763 !impl->m_IsCalibration);
765 764 }
766 765
767 766 if (impl->m_Flags.testFlag(GraphFlag::EnableSynchronization) && !impl->m_IsCalibration) {
768 767 qCDebug(LOG_VisualizationGraphWidget())
769 768 << tr("TORM: VisualizationGraphWidget::Synchronize notify !!")
770 769 << QThread::currentThread()->objectName() << graphRange << oldGraphRange;
771 770 emit synchronize(graphRange, oldGraphRange);
772 771 }
773 772
774 773 auto pos = mapFromGlobal(QCursor::pos());
775 774 auto axisPos = impl->posToAxisPos(pos, plot());
776 775 if (auto parentZone = parentZoneWidget()) {
777 776 if (impl->pointIsInAxisRect(axisPos, plot())) {
778 777 parentZone->notifyMouseMoveInGraph(pos, axisPos, this);
779 778 }
780 779 else {
781 780 parentZone->notifyMouseLeaveGraph(this);
782 781 }
783 782 }
784 783 else {
785 784 qCWarning(LOG_VisualizationGraphWidget()) << "onMouseMove: No parent zone widget";
786 785 }
787 786
788 787 // Quits calibration
789 788 impl->m_IsCalibration = false;
790 789 }
791 790
792 791 void VisualizationGraphWidget::onMouseDoubleClick(QMouseEvent *event) noexcept
793 792 {
794 793 impl->m_RenderingDelegate->onMouseDoubleClick(event);
795 794 }
796 795
797 796 void VisualizationGraphWidget::onMouseMove(QMouseEvent *event) noexcept
798 797 {
799 798 // Handles plot rendering when mouse is moving
800 799 impl->m_RenderingDelegate->onMouseMove(event);
801 800
802 801 auto axisPos = impl->posToAxisPos(event->pos(), plot());
803 802
804 803 // Zoom box and zone drawing
805 804 if (impl->m_DrawingZoomRect) {
806 805 impl->m_DrawingZoomRect->bottomRight->setCoords(axisPos);
807 806 }
808 807 else if (impl->m_DrawingZone) {
809 808 impl->m_DrawingZone->setEnd(axisPos.x());
810 809 }
811 810
812 811 // Cursor
813 812 if (auto parentZone = parentZoneWidget()) {
814 813 if (impl->pointIsInAxisRect(axisPos, plot())) {
815 814 parentZone->notifyMouseMoveInGraph(event->pos(), axisPos, this);
816 815 }
817 816 else {
818 817 parentZone->notifyMouseLeaveGraph(this);
819 818 }
820 819 }
821 820 else {
822 821 qCWarning(LOG_VisualizationGraphWidget()) << "onMouseMove: No parent zone widget";
823 822 }
824 823
825 824 // Search for the selection zone under the mouse
826 825 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(event->pos(), plot());
827 826 if (selectionZoneItemUnderCursor && !impl->m_DrawingZone
828 827 && sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones) {
829 828
830 829 // Sets the appropriate cursor shape
831 830 auto cursorShape = selectionZoneItemUnderCursor->curshorShapeForPosition(event->pos());
832 831 setCursor(cursorShape);
833 832
834 833 // Manages the hovered zone
835 834 if (selectionZoneItemUnderCursor != impl->m_HoveredZone) {
836 835 if (impl->m_HoveredZone) {
837 836 impl->m_HoveredZone->setHovered(false);
838 837 }
839 838 selectionZoneItemUnderCursor->setHovered(true);
840 839 impl->m_HoveredZone = selectionZoneItemUnderCursor;
841 840 plot().replot(QCustomPlot::rpQueuedReplot);
842 841 }
843 842 }
844 843 else {
845 844 // There is no zone under the mouse or the interaction mode is not "selection zones"
846 845 if (impl->m_HoveredZone) {
847 846 impl->m_HoveredZone->setHovered(false);
848 847 impl->m_HoveredZone = nullptr;
849 848 }
850 849
851 850 setCursor(Qt::ArrowCursor);
852 851 }
853 852
854 853 impl->m_HasMovedMouse = true;
855 854 VisualizationDragWidget::mouseMoveEvent(event);
856 855 }
857 856
858 857 void VisualizationGraphWidget::onMouseWheel(QWheelEvent *event) noexcept
859 858 {
860 859 auto value = event->angleDelta().x() + event->angleDelta().y();
861 860 if (value != 0) {
862 861
863 862 auto direction = value > 0 ? 1.0 : -1.0;
864 863 auto isZoomX = event->modifiers().testFlag(HORIZONTAL_ZOOM_MODIFIER);
865 864 auto isZoomY = event->modifiers().testFlag(VERTICAL_ZOOM_MODIFIER);
866 865 impl->m_IsCalibration = event->modifiers().testFlag(VERTICAL_PAN_MODIFIER);
867 866
868 867 auto zoomOrientations = QFlags<Qt::Orientation>{};
869 868 zoomOrientations.setFlag(Qt::Horizontal, isZoomX);
870 869 zoomOrientations.setFlag(Qt::Vertical, isZoomY);
871 870
872 871 ui->widget->axisRect()->setRangeZoom(zoomOrientations);
873 872
874 873 if (!isZoomX && !isZoomY) {
875 874 auto axis = plot().axisRect()->axis(QCPAxis::atBottom);
876 875 auto diff = direction * (axis->range().size() * (PAN_SPEED / 100.0));
877 876
878 877 axis->setRange(axis->range() + diff);
879 878
880 879 if (plot().noAntialiasingOnDrag()) {
881 880 plot().setNotAntialiasedElements(QCP::aeAll);
882 881 }
883 882
884 883 plot().replot(QCustomPlot::rpQueuedReplot);
885 884 }
886 885 }
887 886 }
888 887
889 888 void VisualizationGraphWidget::onMousePress(QMouseEvent *event) noexcept
890 889 {
891 890 auto isDragDropClick = event->modifiers().testFlag(DRAG_DROP_MODIFIER);
892 891 auto isSelectionZoneMode
893 892 = sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones;
894 893 auto isLeftClick = event->buttons().testFlag(Qt::LeftButton);
895 894
896 895 if (!isDragDropClick && isLeftClick) {
897 896 if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::ZoomBox) {
898 897 // Starts a zoom box
899 898 impl->startDrawingRect(event->pos(), plot());
900 899 }
901 900 else if (isSelectionZoneMode && impl->m_DrawingZone == nullptr) {
902 901 // Starts a new selection zone
903 902 auto zoneAtPos = impl->selectionZoneAt(event->pos(), plot());
904 903 if (!zoneAtPos) {
905 904 impl->startDrawingZone(event->pos(), this);
906 905 }
907 906 }
908 907 }
909 908
910 909 // Allows mouse panning only in default mode
911 910 plot().setInteraction(QCP::iRangeDrag, sqpApp->plotsInteractionMode()
912 911 == SqpApplication::PlotsInteractionMode::None
913 912 && !isDragDropClick);
914 913
915 914 // Allows zone edition only in selection zone mode without drag&drop
916 915 impl->setSelectionZonesEditionEnabled(isSelectionZoneMode && !isDragDropClick);
917 916
918 917 // Selection / Deselection
919 918 if (isSelectionZoneMode) {
920 919 auto isMultiSelectionClick = event->modifiers().testFlag(MULTI_ZONE_SELECTION_MODIFIER);
921 920 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(event->pos(), plot());
922 921
923 922
924 923 if (selectionZoneItemUnderCursor && !selectionZoneItemUnderCursor->selected()
925 924 && !isMultiSelectionClick) {
926 925 parentVisualizationWidget()->selectionZoneManager().select(
927 926 {selectionZoneItemUnderCursor});
928 927 }
929 928 else if (!selectionZoneItemUnderCursor && !isMultiSelectionClick && isLeftClick) {
930 929 parentVisualizationWidget()->selectionZoneManager().clearSelection();
931 930 }
932 931 else {
933 932 // No selection change
934 933 }
935 934
936 935 if (selectionZoneItemUnderCursor && isLeftClick) {
937 936 selectionZoneItemUnderCursor->setAssociatedEditedZones(
938 937 parentVisualizationWidget()->selectionZoneManager().selectedItems());
939 938 }
940 939 }
941 940
942 941
943 942 impl->m_HasMovedMouse = false;
944 943 VisualizationDragWidget::mousePressEvent(event);
945 944 }
946 945
947 946 void VisualizationGraphWidget::onMouseRelease(QMouseEvent *event) noexcept
948 947 {
949 948 if (impl->m_DrawingZoomRect) {
950 949
951 950 auto axisX = plot().axisRect()->axis(QCPAxis::atBottom);
952 951 auto axisY = plot().axisRect()->axis(QCPAxis::atLeft);
953 952
954 953 auto newAxisXRange = QCPRange{impl->m_DrawingZoomRect->topLeft->coords().x(),
955 954 impl->m_DrawingZoomRect->bottomRight->coords().x()};
956 955
957 956 auto newAxisYRange = QCPRange{impl->m_DrawingZoomRect->topLeft->coords().y(),
958 957 impl->m_DrawingZoomRect->bottomRight->coords().y()};
959 958
960 959 impl->removeDrawingRect(plot());
961 960
962 961 if (newAxisXRange.size() > axisX->range().size() * (ZOOM_BOX_MIN_SIZE / 100.0)
963 962 && newAxisYRange.size() > axisY->range().size() * (ZOOM_BOX_MIN_SIZE / 100.0)) {
964 963 impl->m_ZoomStack.push(qMakePair(axisX->range(), axisY->range()));
965 964 axisX->setRange(newAxisXRange);
966 965 axisY->setRange(newAxisYRange);
967 966
968 967 plot().replot(QCustomPlot::rpQueuedReplot);
969 968 }
970 969 }
971 970
972 971 impl->endDrawingZone(this);
973 972
974 973 // Selection / Deselection
975 974 auto isSelectionZoneMode
976 975 = sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones;
977 976 if (isSelectionZoneMode) {
978 977 auto isMultiSelectionClick = event->modifiers().testFlag(MULTI_ZONE_SELECTION_MODIFIER);
979 978 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(event->pos(), plot());
980 979 if (selectionZoneItemUnderCursor && event->button() == Qt::LeftButton
981 980 && !impl->m_HasMovedMouse) {
982 981
983 982 auto zonesUnderCursor = impl->selectionZonesAt(event->pos(), plot());
984 983 if (zonesUnderCursor.count() > 1) {
985 984 // There are multiple zones under the mouse.
986 985 // Performs the selection with a selection dialog.
987 986 VisualizationMultiZoneSelectionDialog dialog{this};
988 987 dialog.setZones(zonesUnderCursor);
989 988 dialog.move(mapToGlobal(event->pos() - QPoint(dialog.width() / 2, 20)));
990 989 dialog.activateWindow();
991 990 dialog.raise();
992 991 if (dialog.exec() == QDialog::Accepted) {
993 992 auto selection = dialog.selectedZones();
994 993
995 994 if (!isMultiSelectionClick) {
996 995 parentVisualizationWidget()->selectionZoneManager().clearSelection();
997 996 }
998 997
999 998 for (auto it = selection.cbegin(); it != selection.cend(); ++it) {
1000 999 auto zone = it.key();
1001 1000 auto isSelected = it.value();
1002 1001 parentVisualizationWidget()->selectionZoneManager().setSelected(zone,
1003 1002 isSelected);
1004 1003
1005 1004 if (isSelected) {
1006 1005 // Puts the zone on top of the stack so it can be moved or resized
1007 1006 impl->moveSelectionZoneOnTop(zone, plot());
1008 1007 }
1009 1008 }
1010 1009 }
1011 1010 }
1012 1011 else {
1013 1012 if (!isMultiSelectionClick) {
1014 1013 parentVisualizationWidget()->selectionZoneManager().select(
1015 1014 {selectionZoneItemUnderCursor});
1016 1015 impl->moveSelectionZoneOnTop(selectionZoneItemUnderCursor, plot());
1017 1016 }
1018 1017 else {
1019 1018 parentVisualizationWidget()->selectionZoneManager().setSelected(
1020 1019 selectionZoneItemUnderCursor, !selectionZoneItemUnderCursor->selected()
1021 1020 || event->button() == Qt::RightButton);
1022 1021 }
1023 1022 }
1024 1023 }
1025 1024 else {
1026 1025 // No selection change
1027 1026 }
1028 1027 }
1029 1028 }
1030 1029
1031 1030 void VisualizationGraphWidget::onDataCacheVariableUpdated()
1032 1031 {
1033 1032 auto graphRange = ui->widget->xAxis->range();
1034 1033 auto dateTime = SqpRange{graphRange.lower, graphRange.upper};
1035 1034
1036 1035 for (auto &variableEntry : impl->m_VariableToPlotMultiMap) {
1037 1036 auto variable = variableEntry.first;
1038 1037 qCDebug(LOG_VisualizationGraphWidget())
1039 1038 << "TORM: VisualizationGraphWidget::onDataCacheVariableUpdated S" << variable->range();
1040 1039 qCDebug(LOG_VisualizationGraphWidget())
1041 1040 << "TORM: VisualizationGraphWidget::onDataCacheVariableUpdated E" << dateTime;
1042 1041 if (dateTime.contains(variable->range()) || dateTime.intersect(variable->range())) {
1043 1042 impl->updateData(variableEntry.second, variable, variable->range());
1044 1043 }
1045 1044 }
1046 1045 }
1047 1046
1048 1047 void VisualizationGraphWidget::onUpdateVarDisplaying(std::shared_ptr<Variable> variable,
1049 1048 const SqpRange &range)
1050 1049 {
1051 1050 auto it = impl->m_VariableToPlotMultiMap.find(variable);
1052 1051 if (it != impl->m_VariableToPlotMultiMap.end()) {
1053 1052 impl->updateData(it->second, variable, range);
1054 1053 }
1055 1054 }
@@ -1,106 +1,105
1 1 <?xml version="1.0" encoding="UTF-8"?>
2 2 <ui version="4.0">
3 3 <class>CatalogueSideBarWidget</class>
4 4 <widget class="QWidget" name="CatalogueSideBarWidget">
5 5 <property name="geometry">
6 6 <rect>
7 7 <x>0</x>
8 8 <y>0</y>
9 9 <width>330</width>
10 10 <height>523</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">
17 17 <property name="leftMargin">
18 18 <number>0</number>
19 19 </property>
20 20 <property name="topMargin">
21 21 <number>0</number>
22 22 </property>
23 23 <property name="rightMargin">
24 24 <number>0</number>
25 25 </property>
26 26 <property name="bottomMargin">
27 27 <number>0</number>
28 28 </property>
29 29 <item>
30 30 <layout class="QHBoxLayout" name="horizontalLayout">
31 31 <item>
32 32 <widget class="QToolButton" name="btnAdd">
33 33 <property name="enabled">
34 <bool>false</bool>
34 <bool>true</bool>
35 35 </property>
36 36 <property name="text">
37 37 <string>+</string>
38 38 </property>
39 39 <property name="icon">
40 40 <iconset resource="../../resources/sqpguiresources.qrc">
41 41 <normaloff>:/icones/add.png</normaloff>:/icones/add.png</iconset>
42 42 </property>
43 43 <property name="autoRaise">
44 44 <bool>true</bool>
45 45 </property>
46 46 </widget>
47 47 </item>
48 48 <item>
49 49 <widget class="QToolButton" name="btnRemove">
50 50 <property name="enabled">
51 <bool>false</bool>
51 <bool>true</bool>
52 52 </property>
53 53 <property name="text">
54 54 <string> - </string>
55 55 </property>
56 56 <property name="icon">
57 57 <iconset resource="../../resources/sqpguiresources.qrc">
58 58 <normaloff>:/icones/remove.png</normaloff>:/icones/remove.png</iconset>
59 59 </property>
60 60 <property name="autoRaise">
61 61 <bool>true</bool>
62 62 </property>
63 63 </widget>
64 64 </item>
65 65 <item>
66 66 <spacer name="horizontalSpacer">
67 67 <property name="orientation">
68 68 <enum>Qt::Horizontal</enum>
69 69 </property>
70 70 <property name="sizeHint" stdset="0">
71 71 <size>
72 72 <width>40</width>
73 73 <height>20</height>
74 74 </size>
75 75 </property>
76 76 </spacer>
77 77 </item>
78 78 </layout>
79 79 </item>
80 80 <item>
81 81 <widget class="QTreeView" name="treeView">
82 82 <property name="acceptDrops">
83 83 <bool>true</bool>
84 84 </property>
85 85 <property name="dragDropMode">
86 86 <enum>QAbstractItemView::DragDrop</enum>
87 87 </property>
88 88 <property name="selectionMode">
89 89 <enum>QAbstractItemView::ExtendedSelection</enum>
90 90 </property>
91 91 <attribute name="headerVisible">
92 92 <bool>false</bool>
93 93 </attribute>
94 94 <attribute name="headerStretchLastSection">
95 95 <bool>false</bool>
96 96 </attribute>
97 97 </widget>
98 98 </item>
99 99 </layout>
100 100 </widget>
101 101 <resources>
102 102 <include location="../../resources/sqpguiresources.qrc"/>
103 <include location="../../resources/sqpguiresources.qrc"/>
104 103 </resources>
105 104 <connections/>
106 105 </ui>
General Comments 0
You need to be logged in to leave comments. Login now