##// END OF EJS Templates
Merge branch 'feature/FixCatalogue' into develop
perrinel -
r1373:989011859ba2 merge
parent child
Show More
@@ -1,19 +1,23
1 1 #ifndef SCIQLOP_DATEUTILS_H
2 2 #define SCIQLOP_DATEUTILS_H
3 3
4 4 #include "CoreGlobal.h"
5 5
6 6 #include <QDateTime>
7 7
8 /// Format for datetimes
9 const auto DATETIME_FORMAT = QStringLiteral("dd/MM/yyyy \nhh:mm:ss:zzz");
10 const auto DATETIME_FORMAT_ONE_LINE = QStringLiteral("dd/MM/yyyy hh:mm:ss:zzz");
11
8 12 /**
9 13 * Utility class with methods for dates
10 14 */
11 15 struct SCIQLOP_CORE_EXPORT DateUtils {
12 16 /// Converts seconds (since epoch) to datetime. By default, the datetime is in UTC
13 17 static QDateTime dateTime(double secs, Qt::TimeSpec timeSpec = Qt::UTC) noexcept;
14 18
15 19 /// Converts datetime to seconds since epoch
16 20 static double secondsSinceEpoch(const QDateTime &dateTime) noexcept;
17 21 };
18 22
19 23 #endif // SCIQLOP_DATEUTILS_H
@@ -1,566 +1,574
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 30 /**
31 31 * Possible types of an repository
32 32 */
33 33 enum class DBType { SYNC, WORK, TRASH };
34 34 class CatalogueController::CatalogueControllerPrivate {
35 35
36 36 public:
37 37 explicit CatalogueControllerPrivate(CatalogueController *parent) : m_Q{parent} {}
38 38
39 39 CatalogueDao m_CatalogueDao;
40 40
41 41 QStringList m_RepositoryList;
42 42 CatalogueController *m_Q;
43 43
44 44 QSet<QString> m_KeysWithChanges;
45 45
46 46 QString eventUniqueKey(const std::shared_ptr<DBEvent> &event) const;
47 47 QString catalogueUniqueKey(const std::shared_ptr<DBCatalogue> &catalogue) const;
48 48
49 49 void copyDBtoDB(const QString &dbFrom, const QString &dbTo);
50 50 QString toWorkRepository(QString repository);
51 51 QString toSyncRepository(QString repository);
52 52 void savAllDB();
53 53
54 54 void saveEvent(std::shared_ptr<DBEvent> event, bool persist = true);
55 55 void saveCatalogue(std::shared_ptr<DBCatalogue> catalogue, bool persist = true);
56 56
57 57 std::shared_ptr<IRequestPredicate> createFinder(const QUuid &uniqId, const QString &repository,
58 58 DBType type);
59 59 };
60 60
61 61 CatalogueController::CatalogueController(QObject *parent)
62 62 : impl{spimpl::make_unique_impl<CatalogueControllerPrivate>(this)}
63 63 {
64 64 qCDebug(LOG_CatalogueController()) << tr("CatalogueController construction")
65 65 << QThread::currentThread();
66 66 }
67 67
68 68 CatalogueController::~CatalogueController()
69 69 {
70 70 qCDebug(LOG_CatalogueController()) << tr("CatalogueController destruction")
71 71 << QThread::currentThread();
72 72 }
73 73
74 74 QStringList CatalogueController::getRepositories() const
75 75 {
76 76 return impl->m_RepositoryList;
77 77 }
78 78
79 79 void CatalogueController::addDB(const QString &dbPath)
80 80 {
81 81 QDir dbDir(dbPath);
82 82 if (dbDir.exists()) {
83 83 auto dirName = dbDir.dirName();
84 84
85 85 if (std::find(impl->m_RepositoryList.cbegin(), impl->m_RepositoryList.cend(), dirName)
86 86 != impl->m_RepositoryList.cend()) {
87 87 qCCritical(LOG_CatalogueController())
88 88 << tr("Impossible to addDB that is already loaded");
89 89 }
90 90
91 91 if (!impl->m_CatalogueDao.addDB(dbPath, dirName)) {
92 92 qCCritical(LOG_CatalogueController())
93 93 << tr("Impossible to addDB %1 from %2 ").arg(dirName, dbPath);
94 94 }
95 95 else {
96 96 impl->m_RepositoryList << dirName;
97 97 impl->copyDBtoDB(dirName, impl->toWorkRepository(dirName));
98 98 }
99 99 }
100 100 else {
101 101 qCCritical(LOG_CatalogueController()) << tr("Impossible to addDB that not exists: ")
102 102 << dbPath;
103 103 }
104 104 }
105 105
106 106 void CatalogueController::saveDB(const QString &destinationPath, const QString &repository)
107 107 {
108 108 if (!impl->m_CatalogueDao.saveDB(destinationPath, repository)) {
109 109 qCCritical(LOG_CatalogueController())
110 110 << tr("Impossible to saveDB %1 from %2 ").arg(repository, destinationPath);
111 111 }
112 112 }
113 113
114 114 std::list<std::shared_ptr<DBEvent> >
115 115 CatalogueController::retrieveEvents(const QString &repository) const
116 116 {
117 117 QString dbDireName = repository.isEmpty() ? REPOSITORY_DEFAULT : repository;
118 118
119 119 auto eventsShared = std::list<std::shared_ptr<DBEvent> >{};
120 120 auto events = impl->m_CatalogueDao.getEvents(impl->toWorkRepository(dbDireName));
121 121 for (auto event : events) {
122 122 eventsShared.push_back(std::make_shared<DBEvent>(event));
123 123 }
124 124 return eventsShared;
125 125 }
126 126
127 127 std::list<std::shared_ptr<DBEvent> > CatalogueController::retrieveAllEvents() const
128 128 {
129 129 auto eventsShared = std::list<std::shared_ptr<DBEvent> >{};
130 130 for (auto repository : impl->m_RepositoryList) {
131 131 eventsShared.splice(eventsShared.end(), retrieveEvents(repository));
132 132 }
133 133
134 134 return eventsShared;
135 135 }
136 136
137 137 std::list<std::shared_ptr<DBEvent> >
138 138 CatalogueController::retrieveEventsFromCatalogue(std::shared_ptr<DBCatalogue> catalogue) const
139 139 {
140 140 auto eventsShared = std::list<std::shared_ptr<DBEvent> >{};
141 141 auto events = impl->m_CatalogueDao.getCatalogueEvents(*catalogue);
142 142 for (auto event : events) {
143 143 eventsShared.push_back(std::make_shared<DBEvent>(event));
144 144 }
145 145 return eventsShared;
146 146 }
147 147
148 148 void CatalogueController::updateEvent(std::shared_ptr<DBEvent> event)
149 149 {
150 150 event->setRepository(impl->toWorkRepository(event->getRepository()));
151 151
152 152 auto uniqueId = impl->eventUniqueKey(event);
153 153 impl->m_KeysWithChanges.insert(uniqueId);
154 154
155 155 impl->m_CatalogueDao.updateEvent(*event);
156 156 }
157 157
158 158 void CatalogueController::updateEventProduct(std::shared_ptr<DBEventProduct> eventProduct)
159 159 {
160 160 impl->m_CatalogueDao.updateEventProduct(*eventProduct);
161 161 }
162 162
163 163 void CatalogueController::removeEvent(std::shared_ptr<DBEvent> event)
164 164 {
165 165 // Remove it from both repository and repository_work
166 166 event->setRepository(impl->toWorkRepository(event->getRepository()));
167 167 impl->m_CatalogueDao.removeEvent(*event);
168 168 event->setRepository(impl->toSyncRepository(event->getRepository()));
169 169 impl->m_CatalogueDao.removeEvent(*event);
170 170 impl->savAllDB();
171 171 }
172 172
173 173 void CatalogueController::addEvent(std::shared_ptr<DBEvent> event)
174 174 {
175 175 event->setRepository(impl->toWorkRepository(event->getRepository()));
176 176
177 177 auto eventTemp = *event;
178 178 impl->m_CatalogueDao.addEvent(eventTemp);
179 179
180 180 // Call update is necessary at the creation of add Event if it has some tags or some event
181 181 // products
182 182 if (!event->getEventProducts().empty() || !event->getTags().empty()) {
183 183
184 184 auto eventProductsTemp = eventTemp.getEventProducts();
185 185 auto eventProductTempUpdated = std::list<DBEventProduct>{};
186 186 for (auto eventProductTemp : eventProductsTemp) {
187 187 eventProductTemp.setEvent(eventTemp);
188 188 eventProductTempUpdated.push_back(eventProductTemp);
189 189 }
190 190 eventTemp.setEventProducts(eventProductTempUpdated);
191 191
192 192 impl->m_CatalogueDao.updateEvent(eventTemp);
193 193 }
194 194
195 195 auto workPred = impl->createFinder(event->getUniqId(), event->getRepository(), DBType::WORK);
196 196
197 197 auto workEvent = impl->m_CatalogueDao.getEvent(workPred);
198 198 *event = workEvent;
199 199
200 200
201 201 auto uniqueId = impl->eventUniqueKey(event);
202 202 impl->m_KeysWithChanges.insert(uniqueId);
203 203 }
204 204
205 205 void CatalogueController::saveEvent(std::shared_ptr<DBEvent> event)
206 206 {
207 207 impl->saveEvent(event, true);
208 208 impl->m_KeysWithChanges.remove(impl->eventUniqueKey(event));
209 209 }
210 210
211 211 void CatalogueController::discardEvent(std::shared_ptr<DBEvent> event, bool &removed)
212 212 {
213 213 auto syncPred = impl->createFinder(event->getUniqId(), event->getRepository(), DBType::SYNC);
214 214 auto workPred = impl->createFinder(event->getUniqId(), event->getRepository(), DBType::WORK);
215 215
216 216 auto syncEvent = impl->m_CatalogueDao.getEvent(syncPred);
217 217 if (!syncEvent.getUniqId().isNull()) {
218 218 removed = false;
219 219 impl->m_CatalogueDao.copyEvent(syncEvent, impl->toWorkRepository(event->getRepository()),
220 220 true);
221 221
222 222 auto workEvent = impl->m_CatalogueDao.getEvent(workPred);
223 223 *event = workEvent;
224 224 impl->m_KeysWithChanges.remove(impl->eventUniqueKey(event));
225 225 }
226 226 else {
227 227 removed = true;
228 228 // Since the element wasn't in sync repository. Discard it means remove it
229 229 event->setRepository(impl->toWorkRepository(event->getRepository()));
230 230 impl->m_CatalogueDao.removeEvent(*event);
231 231 }
232 232 }
233 233
234 234 bool CatalogueController::eventHasChanges(std::shared_ptr<DBEvent> event) const
235 235 {
236 236 return impl->m_KeysWithChanges.contains(impl->eventUniqueKey(event));
237 237 }
238 238
239 239 std::list<std::shared_ptr<DBCatalogue> >
240 240 CatalogueController::retrieveCatalogues(const QString &repository) const
241 241 {
242 242 QString dbDireName = repository.isEmpty() ? REPOSITORY_DEFAULT : repository;
243 243
244 244 auto cataloguesShared = std::list<std::shared_ptr<DBCatalogue> >{};
245 245 auto catalogues = impl->m_CatalogueDao.getCatalogues(impl->toWorkRepository(dbDireName));
246 246 for (auto catalogue : catalogues) {
247 247 cataloguesShared.push_back(std::make_shared<DBCatalogue>(catalogue));
248 248 }
249 249 return cataloguesShared;
250 250 }
251 251
252 252 void CatalogueController::addCatalogue(std::shared_ptr<DBCatalogue> catalogue)
253 253 {
254 254 catalogue->setRepository(impl->toWorkRepository(catalogue->getRepository()));
255 255
256 256 auto catalogueTemp = *catalogue;
257 257 impl->m_CatalogueDao.addCatalogue(catalogueTemp);
258 258
259 259 auto workPred
260 260 = impl->createFinder(catalogue->getUniqId(), catalogue->getRepository(), DBType::WORK);
261 261
262 262 auto workCatalogue = impl->m_CatalogueDao.getCatalogue(workPred);
263 263 *catalogue = workCatalogue;
264 264
265 265 auto uniqueId = impl->catalogueUniqueKey(catalogue);
266 266 impl->m_KeysWithChanges.insert(uniqueId);
267 267 }
268 268
269 269 void CatalogueController::updateCatalogue(std::shared_ptr<DBCatalogue> catalogue)
270 270 {
271 271 catalogue->setRepository(impl->toWorkRepository(catalogue->getRepository()));
272 272
273 273 auto uniqueId = impl->catalogueUniqueKey(catalogue);
274 274 impl->m_KeysWithChanges.insert(uniqueId);
275 275
276 276 impl->m_CatalogueDao.updateCatalogue(*catalogue);
277 277 }
278 278
279 279 void CatalogueController::removeCatalogue(std::shared_ptr<DBCatalogue> catalogue)
280 280 {
281 281 // Remove it from both repository and repository_work
282 282 catalogue->setRepository(impl->toWorkRepository(catalogue->getRepository()));
283 283 impl->m_CatalogueDao.removeCatalogue(*catalogue);
284 284 catalogue->setRepository(impl->toSyncRepository(catalogue->getRepository()));
285 285 impl->m_CatalogueDao.removeCatalogue(*catalogue);
286 286 impl->savAllDB();
287 287 }
288 288
289 289 void CatalogueController::saveCatalogue(std::shared_ptr<DBCatalogue> catalogue)
290 290 {
291 291 impl->saveCatalogue(catalogue, true);
292 292 impl->m_KeysWithChanges.remove(impl->catalogueUniqueKey(catalogue));
293
294 // remove key of events of the catalogue
295 if (catalogue->getType() == CatalogueType::STATIC) {
296 auto events = this->retrieveEventsFromCatalogue(catalogue);
297 for (auto event : events) {
298 impl->m_KeysWithChanges.remove(impl->eventUniqueKey(event));
299 }
300 }
293 301 }
294 302
295 303 void CatalogueController::discardCatalogue(std::shared_ptr<DBCatalogue> catalogue, bool &removed)
296 304 {
297 305 auto syncPred
298 306 = impl->createFinder(catalogue->getUniqId(), catalogue->getRepository(), DBType::SYNC);
299 307 auto workPred
300 308 = impl->createFinder(catalogue->getUniqId(), catalogue->getRepository(), DBType::WORK);
301 309
302 310 auto syncCatalogue = impl->m_CatalogueDao.getCatalogue(syncPred);
303 311 if (!syncCatalogue.getUniqId().isNull()) {
304 312 removed = false;
305 313 impl->m_CatalogueDao.copyCatalogue(
306 314 syncCatalogue, impl->toWorkRepository(catalogue->getRepository()), true);
307 315
308 316 auto workCatalogue = impl->m_CatalogueDao.getCatalogue(workPred);
309 317 *catalogue = workCatalogue;
310 318 impl->m_KeysWithChanges.remove(impl->catalogueUniqueKey(catalogue));
311 319 }
312 320 else {
313 321 removed = true;
314 322 // Since the element wasn't in sync repository. Discard it means remove it
315 323 catalogue->setRepository(impl->toWorkRepository(catalogue->getRepository()));
316 324 impl->m_CatalogueDao.removeCatalogue(*catalogue);
317 325 }
318 326 }
319 327
320 328 void CatalogueController::saveAll()
321 329 {
322 330 for (auto repository : impl->m_RepositoryList) {
323 331 // Save Event
324 332 auto events = this->retrieveEvents(repository);
325 333 for (auto event : events) {
326 334 impl->saveEvent(event, false);
327 335 }
328 336
329 337 // Save Catalogue
330 338 auto catalogues = this->retrieveCatalogues(repository);
331 339 for (auto catalogue : catalogues) {
332 340 impl->saveCatalogue(catalogue, false);
333 341 }
334 342 }
335 343
336 344 impl->savAllDB();
337 345 impl->m_KeysWithChanges.clear();
338 346 }
339 347
340 348 bool CatalogueController::hasChanges() const
341 349 {
342 350 return !impl->m_KeysWithChanges.isEmpty();
343 351 }
344 352
345 353 QByteArray
346 354 CatalogueController::mimeDataForEvents(const QVector<std::shared_ptr<DBEvent> > &events) const
347 355 {
348 356 auto encodedData = QByteArray{};
349 357
350 358 QMap<QString, QVariantList> idsPerRepository;
351 359 for (auto event : events) {
352 360 idsPerRepository[event->getRepository()] << event->getUniqId();
353 361 }
354 362
355 363 QDataStream stream{&encodedData, QIODevice::WriteOnly};
356 364 stream << idsPerRepository;
357 365
358 366 return encodedData;
359 367 }
360 368
361 369 QVector<std::shared_ptr<DBEvent> >
362 370 CatalogueController::eventsForMimeData(const QByteArray &mimeData) const
363 371 {
364 372 auto events = QVector<std::shared_ptr<DBEvent> >{};
365 373 QDataStream stream{mimeData};
366 374
367 375 QMap<QString, QVariantList> idsPerRepository;
368 376 stream >> idsPerRepository;
369 377
370 378 for (auto it = idsPerRepository.cbegin(); it != idsPerRepository.cend(); ++it) {
371 379 auto repository = it.key();
372 380 auto allRepositoryEvent = retrieveEvents(repository);
373 381 for (auto uuid : it.value()) {
374 382 for (auto repositoryEvent : allRepositoryEvent) {
375 383 if (uuid.toUuid() == repositoryEvent->getUniqId()) {
376 384 events << repositoryEvent;
377 385 }
378 386 }
379 387 }
380 388 }
381 389
382 390 return events;
383 391 }
384 392
385 393 QByteArray CatalogueController::mimeDataForCatalogues(
386 394 const QVector<std::shared_ptr<DBCatalogue> > &catalogues) const
387 395 {
388 396 auto encodedData = QByteArray{};
389 397
390 398 QMap<QString, QVariantList> idsPerRepository;
391 399 for (auto catalogue : catalogues) {
392 400 idsPerRepository[catalogue->getRepository()] << catalogue->getUniqId();
393 401 }
394 402
395 403 QDataStream stream{&encodedData, QIODevice::WriteOnly};
396 404 stream << idsPerRepository;
397 405
398 406 return encodedData;
399 407 }
400 408
401 409 QVector<std::shared_ptr<DBCatalogue> >
402 410 CatalogueController::cataloguesForMimeData(const QByteArray &mimeData) const
403 411 {
404 412 auto catalogues = QVector<std::shared_ptr<DBCatalogue> >{};
405 413 QDataStream stream{mimeData};
406 414
407 415 QMap<QString, QVariantList> idsPerRepository;
408 416 stream >> idsPerRepository;
409 417
410 418 for (auto it = idsPerRepository.cbegin(); it != idsPerRepository.cend(); ++it) {
411 419 auto repository = it.key();
412 420 auto allRepositoryCatalogues = retrieveCatalogues(repository);
413 421 for (auto uuid : it.value()) {
414 422 for (auto repositoryCatalogues : allRepositoryCatalogues) {
415 423 if (uuid.toUuid() == repositoryCatalogues->getUniqId()) {
416 424 catalogues << repositoryCatalogues;
417 425 }
418 426 }
419 427 }
420 428 }
421 429
422 430 return catalogues;
423 431 }
424 432
425 433 void CatalogueController::initialize()
426 434 {
427 435 qCDebug(LOG_CatalogueController()) << tr("CatalogueController init")
428 436 << QThread::currentThread();
429 437
430 438 impl->m_CatalogueDao.initialize();
431 439 auto defaultRepositoryLocation
432 440 = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
433 441
434 442 QDir defaultRepositoryLocationDir;
435 443 if (defaultRepositoryLocationDir.mkpath(defaultRepositoryLocation)) {
436 444 defaultRepositoryLocationDir.cd(defaultRepositoryLocation);
437 445 auto defaultRepository = defaultRepositoryLocationDir.absoluteFilePath(REPOSITORY_DEFAULT);
438 446
439 447 qCInfo(LOG_CatalogueController()) << tr("Persistant data loading from: ")
440 448 << defaultRepository;
441 449
442 450 QDir dbDir(defaultRepository);
443 451 impl->m_RepositoryList << REPOSITORY_DEFAULT;
444 452 if (dbDir.exists()) {
445 453 auto dirName = dbDir.dirName();
446 454
447 455 if (impl->m_CatalogueDao.addDB(defaultRepository, dirName)) {
448 456 impl->copyDBtoDB(dirName, impl->toWorkRepository(dirName));
449 457 }
450 458 }
451 459 else {
452 460 qCInfo(LOG_CatalogueController()) << tr("Initialisation of Default repository detected")
453 461 << defaultRepository;
454 462 }
455 463 }
456 464 else {
457 465 qCWarning(LOG_CatalogueController())
458 466 << tr("Cannot load the persistent default repository from ")
459 467 << defaultRepositoryLocation;
460 468 }
461 469
462 470 qCDebug(LOG_CatalogueController()) << tr("CatalogueController init END");
463 471 }
464 472
465 473 QString CatalogueController::CatalogueControllerPrivate::eventUniqueKey(
466 474 const std::shared_ptr<DBEvent> &event) const
467 475 {
468 476 return event->getUniqId().toString().append(event->getRepository());
469 477 }
470 478
471 479 QString CatalogueController::CatalogueControllerPrivate::catalogueUniqueKey(
472 480 const std::shared_ptr<DBCatalogue> &catalogue) const
473 481 {
474 482 return catalogue->getUniqId().toString().append(catalogue->getRepository());
475 483 }
476 484
477 485 void CatalogueController::CatalogueControllerPrivate::copyDBtoDB(const QString &dbFrom,
478 486 const QString &dbTo)
479 487 {
480 488 // auto cataloguesShared = std::list<std::shared_ptr<DBCatalogue> >{};
481 489 auto catalogues = m_CatalogueDao.getCatalogues(dbFrom);
482 490 auto events = m_CatalogueDao.getEvents(dbFrom);
483 491 for (auto catalogue : catalogues) {
484 492 m_CatalogueDao.copyCatalogue(catalogue, dbTo, true);
485 493 }
486 494
487 495 for (auto event : events) {
488 496 m_CatalogueDao.copyEvent(event, dbTo, true);
489 497 }
490 498 }
491 499
492 500 QString CatalogueController::CatalogueControllerPrivate::toWorkRepository(QString repository)
493 501 {
494 502 auto syncRepository = toSyncRepository(repository);
495 503
496 504 return QString("%1%2").arg(syncRepository, REPOSITORY_WORK_SUFFIX);
497 505 }
498 506
499 507 QString CatalogueController::CatalogueControllerPrivate::toSyncRepository(QString repository)
500 508 {
501 509 auto syncRepository = repository;
502 510 if (repository.endsWith(REPOSITORY_WORK_SUFFIX)) {
503 511 syncRepository.remove(REPOSITORY_WORK_SUFFIX);
504 512 }
505 513 else if (repository.endsWith(REPOSITORY_TRASH_SUFFIX)) {
506 514 syncRepository.remove(REPOSITORY_TRASH_SUFFIX);
507 515 }
508 516 return syncRepository;
509 517 }
510 518
511 519 void CatalogueController::CatalogueControllerPrivate::savAllDB()
512 520 {
513 521 for (auto repository : m_RepositoryList) {
514 522 auto defaultRepositoryLocation
515 523 = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
516 524 m_CatalogueDao.saveDB(defaultRepositoryLocation, repository);
517 525 }
518 526 }
519 527
520 528 void CatalogueController::CatalogueControllerPrivate::saveEvent(std::shared_ptr<DBEvent> event,
521 529 bool persist)
522 530 {
523 531 m_CatalogueDao.copyEvent(*event, toSyncRepository(event->getRepository()), true);
524 532 if (persist) {
525 533 savAllDB();
526 534 }
527 535 }
528 536
529 537 void CatalogueController::CatalogueControllerPrivate::saveCatalogue(
530 538 std::shared_ptr<DBCatalogue> catalogue, bool persist)
531 539 {
532 540 m_CatalogueDao.copyCatalogue(*catalogue, toSyncRepository(catalogue->getRepository()), true);
533 541 if (persist) {
534 542 savAllDB();
535 543 }
536 544 }
537 545
538 546 std::shared_ptr<IRequestPredicate> CatalogueController::CatalogueControllerPrivate::createFinder(
539 547 const QUuid &uniqId, const QString &repository, DBType type)
540 548 {
541 549 // update catalogue parameter
542 550 auto uniqIdPredicate = std::make_shared<ComparaisonPredicate>(QString{"uniqId"}, uniqId,
543 551 ComparaisonOperation::EQUALEQUAL);
544 552
545 553 auto repositoryType = repository;
546 554 switch (type) {
547 555 case DBType::SYNC:
548 556 repositoryType = toSyncRepository(repositoryType);
549 557 break;
550 558 case DBType::WORK:
551 559 repositoryType = toWorkRepository(repositoryType);
552 560 break;
553 561 case DBType::TRASH:
554 562 default:
555 563 break;
556 564 }
557 565
558 566 auto repositoryPredicate = std::make_shared<ComparaisonPredicate>(
559 567 QString{"repository"}, repositoryType, ComparaisonOperation::EQUALEQUAL);
560 568
561 569 auto finderPred = std::make_shared<CompoundPredicate>(CompoundOperation::AND);
562 570 finderPred->AddRequestPredicate(uniqIdPredicate);
563 571 finderPred->AddRequestPredicate(repositoryPredicate);
564 572
565 573 return finderPred;
566 574 }
@@ -1,396 +1,393
1 1 #include <Variable/Variable.h>
2 2 #include <Variable/VariableController.h>
3 3 #include <Variable/VariableModel.h>
4 4
5 5 #include <Common/DateUtils.h>
6 6 #include <Common/MimeTypesDef.h>
7 7 #include <Common/StringUtils.h>
8 8
9 9 #include <Data/IDataSeries.h>
10 10
11 11 #include <DataSource/DataSourceController.h>
12 12 #include <Time/TimeController.h>
13 13
14 14 #include <QMimeData>
15 15 #include <QSize>
16 16 #include <QTimer>
17 17 #include <unordered_map>
18 18
19 19 Q_LOGGING_CATEGORY(LOG_VariableModel, "VariableModel")
20 20
21 21 namespace {
22 22
23 23 // Column indexes
24 24 const auto NAME_COLUMN = 0;
25 25 const auto TSTART_COLUMN = 1;
26 26 const auto TEND_COLUMN = 2;
27 27 const auto NBPOINTS_COLUMN = 3;
28 28 const auto UNIT_COLUMN = 4;
29 29 const auto MISSION_COLUMN = 5;
30 30 const auto PLUGIN_COLUMN = 6;
31 31 const auto NB_COLUMNS = 7;
32 32
33 33 // Column properties
34 34 const auto DEFAULT_HEIGHT = 25;
35 35 const auto DEFAULT_WIDTH = 100;
36 36
37 37 struct ColumnProperties {
38 38 ColumnProperties(const QString &name = {}, int width = DEFAULT_WIDTH,
39 39 int height = DEFAULT_HEIGHT)
40 40 : m_Name{name}, m_Width{width}, m_Height{height}
41 41 {
42 42 }
43 43
44 44 QString m_Name;
45 45 int m_Width;
46 46 int m_Height;
47 47 };
48 48
49 49 const auto COLUMN_PROPERTIES = QHash<int, ColumnProperties>{
50 50 {NAME_COLUMN, {QObject::tr("Name")}}, {TSTART_COLUMN, {QObject::tr("tStart"), 180}},
51 51 {TEND_COLUMN, {QObject::tr("tEnd"), 180}}, {NBPOINTS_COLUMN, {QObject::tr("Nb points")}},
52 52 {UNIT_COLUMN, {QObject::tr("Unit")}}, {MISSION_COLUMN, {QObject::tr("Mission")}},
53 53 {PLUGIN_COLUMN, {QObject::tr("Plugin")}}};
54 54
55 /// Format for datetimes
56 const auto DATETIME_FORMAT = QStringLiteral("dd/MM/yyyy \nhh:mm:ss:zzz");
57
58 55 QString uniqueName(const QString &defaultName,
59 56 const std::vector<std::shared_ptr<Variable> > &variables)
60 57 {
61 58 auto forbiddenNames = std::vector<QString>(variables.size());
62 59 std::transform(variables.cbegin(), variables.cend(), forbiddenNames.begin(),
63 60 [](const auto &variable) { return variable->name(); });
64 61 auto uniqueName = StringUtils::uniqueName(defaultName, forbiddenNames);
65 62 Q_ASSERT(!uniqueName.isEmpty());
66 63
67 64 return uniqueName;
68 65 }
69 66
70 67 } // namespace
71 68
72 69 struct VariableModel::VariableModelPrivate {
73 70 /// Variables created in SciQlop
74 71 std::vector<std::shared_ptr<Variable> > m_Variables;
75 72 std::unordered_map<std::shared_ptr<Variable>, double> m_VariableToProgress;
76 73 VariableController *m_VariableController;
77 74
78 75 /// Return the row index of the variable. -1 if it's not found
79 76 int indexOfVariable(Variable *variable) const noexcept;
80 77 };
81 78
82 79 VariableModel::VariableModel(VariableController *parent)
83 80 : QAbstractTableModel{parent}, impl{spimpl::make_unique_impl<VariableModelPrivate>()}
84 81 {
85 82 impl->m_VariableController = parent;
86 83 }
87 84
88 85 void VariableModel::addVariable(std::shared_ptr<Variable> variable) noexcept
89 86 {
90 87 auto insertIndex = rowCount();
91 88 beginInsertRows({}, insertIndex, insertIndex);
92 89
93 90 // Generates unique name for the variable
94 91 variable->setName(uniqueName(variable->name(), impl->m_Variables));
95 92
96 93 impl->m_Variables.push_back(variable);
97 94 connect(variable.get(), &Variable::updated, this, &VariableModel::onVariableUpdated);
98 95
99 96 endInsertRows();
100 97 }
101 98
102 99 bool VariableModel::containsVariable(std::shared_ptr<Variable> variable) const noexcept
103 100 {
104 101 auto end = impl->m_Variables.cend();
105 102 return std::find(impl->m_Variables.cbegin(), end, variable) != end;
106 103 }
107 104
108 105 std::shared_ptr<Variable> VariableModel::createVariable(const QString &name,
109 106 const QVariantHash &metadata) noexcept
110 107 {
111 108 auto variable = std::make_shared<Variable>(name, metadata);
112 109 addVariable(variable);
113 110
114 111 return variable;
115 112 }
116 113
117 114 void VariableModel::deleteVariable(std::shared_ptr<Variable> variable) noexcept
118 115 {
119 116 if (!variable) {
120 117 qCCritical(LOG_Variable()) << "Can't delete a null variable from the model";
121 118 return;
122 119 }
123 120
124 121 // Finds variable in the model
125 122 auto begin = impl->m_Variables.cbegin();
126 123 auto end = impl->m_Variables.cend();
127 124 auto it = std::find(begin, end, variable);
128 125 if (it != end) {
129 126 auto removeIndex = std::distance(begin, it);
130 127
131 128 // Deletes variable
132 129 beginRemoveRows({}, removeIndex, removeIndex);
133 130 impl->m_Variables.erase(it);
134 131 endRemoveRows();
135 132 }
136 133 else {
137 134 qCritical(LOG_VariableModel())
138 135 << tr("Can't delete variable %1 from the model: the variable is not in the model")
139 136 .arg(variable->name());
140 137 }
141 138
142 139 // Removes variable from progress map
143 140 impl->m_VariableToProgress.erase(variable);
144 141 }
145 142
146 143
147 144 std::shared_ptr<Variable> VariableModel::variable(int index) const
148 145 {
149 146 return (index >= 0u && static_cast<size_t>(index) < impl->m_Variables.size())
150 147 ? impl->m_Variables[index]
151 148 : nullptr;
152 149 }
153 150
154 151 std::vector<std::shared_ptr<Variable> > VariableModel::variables() const
155 152 {
156 153 return impl->m_Variables;
157 154 }
158 155
159 156 void VariableModel::setDataProgress(std::shared_ptr<Variable> variable, double progress)
160 157 {
161 158 if (progress > 0.0) {
162 159 impl->m_VariableToProgress[variable] = progress;
163 160 }
164 161 else {
165 162 impl->m_VariableToProgress.erase(variable);
166 163 }
167 164 auto modelIndex = createIndex(impl->indexOfVariable(variable.get()), NAME_COLUMN);
168 165
169 166 emit dataChanged(modelIndex, modelIndex);
170 167 }
171 168
172 169 int VariableModel::columnCount(const QModelIndex &parent) const
173 170 {
174 171 Q_UNUSED(parent);
175 172
176 173 return NB_COLUMNS;
177 174 }
178 175
179 176 int VariableModel::rowCount(const QModelIndex &parent) const
180 177 {
181 178 Q_UNUSED(parent);
182 179
183 180 return impl->m_Variables.size();
184 181 }
185 182
186 183 QVariant VariableModel::data(const QModelIndex &index, int role) const
187 184 {
188 185 if (!index.isValid()) {
189 186 return QVariant{};
190 187 }
191 188
192 189 if (index.row() < 0 || index.row() >= rowCount()) {
193 190 return QVariant{};
194 191 }
195 192
196 193 if (role == Qt::DisplayRole) {
197 194 if (auto variable = impl->m_Variables.at(index.row()).get()) {
198 195 switch (index.column()) {
199 196 case NAME_COLUMN:
200 197 return variable->name();
201 198 case TSTART_COLUMN: {
202 199 auto range = variable->realRange();
203 200 return range != INVALID_RANGE
204 201 ? DateUtils::dateTime(range.m_TStart).toString(DATETIME_FORMAT)
205 202 : QVariant{};
206 203 }
207 204 case TEND_COLUMN: {
208 205 auto range = variable->realRange();
209 206 return range != INVALID_RANGE
210 207 ? DateUtils::dateTime(range.m_TEnd).toString(DATETIME_FORMAT)
211 208 : QVariant{};
212 209 }
213 210 case NBPOINTS_COLUMN:
214 211 return variable->nbPoints();
215 212 case UNIT_COLUMN:
216 213 return variable->metadata().value(QStringLiteral("units"));
217 214 case MISSION_COLUMN:
218 215 return variable->metadata().value(QStringLiteral("mission"));
219 216 case PLUGIN_COLUMN:
220 217 return variable->metadata().value(QStringLiteral("plugin"));
221 218 default:
222 219 // No action
223 220 break;
224 221 }
225 222
226 223 qWarning(LOG_VariableModel())
227 224 << tr("Can't get data (unknown column %1)").arg(index.column());
228 225 }
229 226 else {
230 227 qWarning(LOG_VariableModel()) << tr("Can't get data (no variable)");
231 228 }
232 229 }
233 230 else if (role == VariableRoles::ProgressRole) {
234 231 if (auto variable = impl->m_Variables.at(index.row())) {
235 232
236 233 auto it = impl->m_VariableToProgress.find(variable);
237 234 if (it != impl->m_VariableToProgress.cend()) {
238 235 return it->second;
239 236 }
240 237 }
241 238 }
242 239
243 240 return QVariant{};
244 241 }
245 242
246 243 QVariant VariableModel::headerData(int section, Qt::Orientation orientation, int role) const
247 244 {
248 245 if (role != Qt::DisplayRole && role != Qt::SizeHintRole) {
249 246 return QVariant{};
250 247 }
251 248
252 249 if (orientation == Qt::Horizontal) {
253 250 auto propertiesIt = COLUMN_PROPERTIES.find(section);
254 251 if (propertiesIt != COLUMN_PROPERTIES.cend()) {
255 252 // Role is either DisplayRole or SizeHintRole
256 253 return (role == Qt::DisplayRole)
257 254 ? QVariant{propertiesIt->m_Name}
258 255 : QVariant{QSize{propertiesIt->m_Width, propertiesIt->m_Height}};
259 256 }
260 257 else {
261 258 qWarning(LOG_VariableModel())
262 259 << tr("Can't get header data (unknown column %1)").arg(section);
263 260 }
264 261 }
265 262
266 263 return QVariant{};
267 264 }
268 265
269 266 Qt::ItemFlags VariableModel::flags(const QModelIndex &index) const
270 267 {
271 268 return QAbstractTableModel::flags(index) | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled;
272 269 }
273 270
274 271 Qt::DropActions VariableModel::supportedDropActions() const
275 272 {
276 273 return Qt::CopyAction | Qt::MoveAction;
277 274 }
278 275
279 276 Qt::DropActions VariableModel::supportedDragActions() const
280 277 {
281 278 return Qt::CopyAction | Qt::MoveAction;
282 279 }
283 280
284 281 QStringList VariableModel::mimeTypes() const
285 282 {
286 283 return {MIME_TYPE_VARIABLE_LIST, MIME_TYPE_TIME_RANGE};
287 284 }
288 285
289 286 QMimeData *VariableModel::mimeData(const QModelIndexList &indexes) const
290 287 {
291 288 auto mimeData = new QMimeData;
292 289
293 290 QList<std::shared_ptr<Variable> > variableList;
294 291
295 292
296 293 SqpRange firstTimeRange;
297 294 for (const auto &index : indexes) {
298 295 if (index.column() == 0) { // only the first column
299 296 auto variable = impl->m_Variables.at(index.row());
300 297 if (variable.get() && index.isValid()) {
301 298
302 299 if (variableList.isEmpty()) {
303 300 // Gets the range of the first variable
304 301 firstTimeRange = std::move(variable->range());
305 302 }
306 303
307 304 variableList << variable;
308 305 }
309 306 }
310 307 }
311 308
312 309 auto variablesEncodedData = impl->m_VariableController->mimeDataForVariables(variableList);
313 310 mimeData->setData(MIME_TYPE_VARIABLE_LIST, variablesEncodedData);
314 311
315 312 if (variableList.count() == 1) {
316 313 // No time range MIME data if multiple variables are dragged
317 314 auto timeEncodedData = TimeController::mimeDataForTimeRange(firstTimeRange);
318 315 mimeData->setData(MIME_TYPE_TIME_RANGE, timeEncodedData);
319 316 }
320 317
321 318 return mimeData;
322 319 }
323 320
324 321 bool VariableModel::canDropMimeData(const QMimeData *data, Qt::DropAction action, int row,
325 322 int column, const QModelIndex &parent) const
326 323 {
327 324 // drop of a product
328 325 return data->hasFormat(MIME_TYPE_PRODUCT_LIST)
329 326 || (data->hasFormat(MIME_TYPE_TIME_RANGE) && parent.isValid()
330 327 && !data->hasFormat(MIME_TYPE_VARIABLE_LIST));
331 328 }
332 329
333 330 bool VariableModel::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column,
334 331 const QModelIndex &parent)
335 332 {
336 333 auto dropDone = false;
337 334
338 335 if (data->hasFormat(MIME_TYPE_PRODUCT_LIST)) {
339 336
340 337 auto productList
341 338 = DataSourceController::productsDataForMimeData(data->data(MIME_TYPE_PRODUCT_LIST));
342 339
343 340 for (auto metaData : productList) {
344 341 emit requestVariable(metaData.toHash());
345 342 }
346 343
347 344 dropDone = true;
348 345 }
349 346 else if (data->hasFormat(MIME_TYPE_TIME_RANGE) && parent.isValid()) {
350 347 auto variable = this->variable(parent.row());
351 348 auto range = TimeController::timeRangeForMimeData(data->data(MIME_TYPE_TIME_RANGE));
352 349
353 350 emit requestVariableRangeUpdate(variable, range);
354 351
355 352 dropDone = true;
356 353 }
357 354
358 355 return dropDone;
359 356 }
360 357
361 358 void VariableModel::abortProgress(const QModelIndex &index)
362 359 {
363 360 if (auto variable = impl->m_Variables.at(index.row())) {
364 361 emit abortProgessRequested(variable);
365 362 }
366 363 }
367 364
368 365 void VariableModel::onVariableUpdated() noexcept
369 366 {
370 367 // Finds variable that has been updated in the model
371 368 if (auto updatedVariable = dynamic_cast<Variable *>(sender())) {
372 369 auto updatedVariableIndex = impl->indexOfVariable(updatedVariable);
373 370
374 371 if (updatedVariableIndex > -1) {
375 372 emit dataChanged(createIndex(updatedVariableIndex, 0),
376 373 createIndex(updatedVariableIndex, columnCount() - 1));
377 374 }
378 375 }
379 376 }
380 377
381 378 int VariableModel::VariableModelPrivate::indexOfVariable(Variable *variable) const noexcept
382 379 {
383 380 auto begin = std::cbegin(m_Variables);
384 381 auto end = std::cend(m_Variables);
385 382 auto it
386 383 = std::find_if(begin, end, [variable](const auto &var) { return var.get() == variable; });
387 384
388 385 if (it != end) {
389 386 // Gets the index of the variable in the model: we assume here that views have the same
390 387 // order as the model
391 388 return std::distance(begin, it);
392 389 }
393 390 else {
394 391 return -1;
395 392 }
396 393 }
@@ -1,64 +1,72
1 1 #ifndef SCIQLOP_CATALOGUEEVENTSWIDGET_H
2 2 #define SCIQLOP_CATALOGUEEVENTSWIDGET_H
3 3
4 4 #include <Common/spimpl.h>
5 5 #include <QLoggingCategory>
6 6 #include <QWidget>
7 7
8 8 class DBCatalogue;
9 9 class DBEvent;
10 10 class DBEventProduct;
11 11 class VisualizationWidget;
12 12 class VisualizationSelectionZoneItem;
13 13
14 14 namespace Ui {
15 15 class CatalogueEventsWidget;
16 16 }
17 17
18 18 Q_DECLARE_LOGGING_CATEGORY(LOG_CatalogueEventsWidget)
19 19
20 20 class CatalogueEventsWidget : public QWidget {
21 21 Q_OBJECT
22 22
23 23 signals:
24 24 void eventsSelected(const QVector<std::shared_ptr<DBEvent> > &event);
25 25 void eventsRemoved(const QVector<std::shared_ptr<DBEvent> > &event);
26 26 void eventProductsSelected(
27 27 const QVector<QPair<std::shared_ptr<DBEvent>, std::shared_ptr<DBEventProduct> > >
28 28 &eventproducts);
29 29 void selectionCleared();
30 30 void selectionZoneAdded(const std::shared_ptr<DBEvent> &event, const QString &productId,
31 31 VisualizationSelectionZoneItem *selectionZone);
32 32
33 void eventCataloguesModified(const QVector<std::shared_ptr<DBCatalogue> > &catalogues);
34
33 35 public:
34 36 explicit CatalogueEventsWidget(QWidget *parent = 0);
35 37 virtual ~CatalogueEventsWidget();
36 38
37 39 void setVisualizationWidget(VisualizationWidget *visualization);
38 40
39 41 void addEvent(const std::shared_ptr<DBEvent> &event);
40 42 void setEventChanges(const std::shared_ptr<DBEvent> &event, bool hasChanges);
43 void setEventsChanges(const std::shared_ptr<DBEvent> &event, bool hasChanges);
41 44
42 45 QVector<std::shared_ptr<DBCatalogue> > displayedCatalogues() const;
43 46 bool isAllEventsDisplayed() const;
44 47 bool isEventDisplayed(const std::shared_ptr<DBEvent> &event) const;
45 48
46 49 void refreshEvent(const std::shared_ptr<DBEvent> &event);
47 50
48 51 public slots:
49 52 void populateWithCatalogues(const QVector<std::shared_ptr<DBCatalogue> > &catalogues);
50 53 void populateWithAllEvents();
51 54 void clear();
52 55 void refresh();
53 56
57 // QWidget interface
58 protected:
59 void keyPressEvent(QKeyEvent *event);
60
61
54 62 private:
55 63 Ui::CatalogueEventsWidget *ui;
56 64
57 65 class CatalogueEventsWidgetPrivate;
58 66 spimpl::unique_impl_ptr<CatalogueEventsWidgetPrivate> impl;
59 67
60 68 private slots:
61 69 void emitSelection();
62 70 };
63 71
64 72 #endif // SCIQLOP_CATALOGUEEVENTSWIDGET_H
@@ -1,51 +1,56
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 9 class CatalogueAbstractTreeItem;
10 10 class DBCatalogue;
11 11
12 12 namespace Ui {
13 13 class CatalogueSideBarWidget;
14 14 }
15 15
16 16 Q_DECLARE_LOGGING_CATEGORY(LOG_CatalogueSideBarWidget)
17 17
18 18 class CatalogueSideBarWidget : public QWidget {
19 19 Q_OBJECT
20 20
21 21 signals:
22 22 void catalogueSelected(const QVector<std::shared_ptr<DBCatalogue> > &catalogues);
23 23 void databaseSelected(const QStringList &databases);
24 24 void allEventsSelected();
25 25 void trashSelected();
26 26 void selectionCleared();
27 void catalogueSaved(const std::shared_ptr<DBCatalogue> &catalogue);
27 28
28 29 public:
29 30 explicit CatalogueSideBarWidget(QWidget *parent = 0);
30 31 virtual ~CatalogueSideBarWidget();
31 32
32 33 CatalogueAbstractTreeItem *addCatalogue(const std::shared_ptr<DBCatalogue> &catalogue,
33 34 const QString &repository);
34 35 void setCatalogueChanges(const std::shared_ptr<DBCatalogue> &catalogue, bool hasChanges);
35 36
36 37 QVector<std::shared_ptr<DBCatalogue> > getCatalogues(const QString &repository) const;
37 38
39 // QWidget interface
40 protected:
41 void keyPressEvent(QKeyEvent *event);
42
38 43 private slots:
39 44 void emitSelection();
40 45
41 46 private:
42 47 Ui::CatalogueSideBarWidget *ui;
43 48
44 49 class CatalogueSideBarWidgetPrivate;
45 50 spimpl::unique_impl_ptr<CatalogueSideBarWidgetPrivate> impl;
46 51
47 52 private slots:
48 53 void onContextMenuRequested(const QPoint &pos);
49 54 };
50 55
51 56 #endif // SCIQLOP_CATALOGUESIDEBARWIDGET_H
1 NO CONTENT: modified file chmod 100644 => 100755, binary diff hidden
@@ -1,472 +1,484
1 1 #include "Catalogue/CatalogueEventsModel.h"
2 2
3 3 #include <Catalogue/CatalogueController.h>
4 4 #include <Common/DateUtils.h>
5 5 #include <Common/MimeTypesDef.h>
6 6 #include <DBEvent.h>
7 7 #include <DBEventProduct.h>
8 8 #include <DBTag.h>
9 9 #include <Data/SqpRange.h>
10 10 #include <SqpApplication.h>
11 11 #include <Time/TimeController.h>
12 12
13 13 #include <list>
14 14 #include <unordered_map>
15 15
16 16 #include <QHash>
17 17 #include <QMimeData>
18 18
19 19 Q_LOGGING_CATEGORY(LOG_CatalogueEventsModel, "CatalogueEventsModel")
20 20
21 21 const auto EVENT_ITEM_TYPE = 1;
22 22 const auto EVENT_PRODUCT_ITEM_TYPE = 2;
23 23
24 24 struct CatalogueEventsModel::CatalogueEventsModelPrivate {
25 25 QVector<std::shared_ptr<DBEvent> > m_Events;
26 26 std::unordered_map<DBEvent *, QVector<std::shared_ptr<DBEventProduct> > > m_EventProducts;
27 27 QVector<std::shared_ptr<DBCatalogue> > m_SourceCatalogue;
28 28
29 29 QStringList columnNames()
30 30 {
31 31 return QStringList{tr("Event"), tr("TStart"), tr("TEnd"),
32 32 tr("Tags"), tr("Product"), tr("")};
33 33 }
34 34
35 35 QVariant sortData(int col, const std::shared_ptr<DBEvent> &event) const
36 36 {
37 37 if (col == (int)CatalogueEventsModel::Column::Validation) {
38 38 auto hasChanges = sqpApp->catalogueController().eventHasChanges(event);
39 39 return hasChanges ? true : QVariant();
40 40 }
41 41
42 42 return eventData(col, event);
43 43 }
44 44
45 45 QVariant eventData(int col, const std::shared_ptr<DBEvent> &event) const
46 46 {
47 47 switch (static_cast<Column>(col)) {
48 48 case CatalogueEventsModel::Column::Name:
49 49 return event->getName();
50 50 case CatalogueEventsModel::Column::TStart:
51 return nbEventProducts(event) > 0 ? DateUtils::dateTime(event->getTStart())
52 : QVariant{};
51 return nbEventProducts(event) > 0
52 ? DateUtils::dateTime(event->getTStart())
53 .toString(DATETIME_FORMAT_ONE_LINE)
54 : QVariant{};
53 55 case CatalogueEventsModel::Column::TEnd:
54 return nbEventProducts(event) > 0 ? DateUtils::dateTime(event->getTEnd())
55 : QVariant{};
56 case CatalogueEventsModel::Column::Product:
57 return QString::number(nbEventProducts(event)) + " product(s)";
56 return nbEventProducts(event) > 0
57 ? DateUtils::dateTime(event->getTEnd())
58 .toString(DATETIME_FORMAT_ONE_LINE)
59 : QVariant{};
60 case CatalogueEventsModel::Column::Product: {
61 auto eventProducts = event->getEventProducts();
62 QStringList eventProductList;
63 for (auto evtProduct : eventProducts) {
64 eventProductList << evtProduct.getProductId();
65 }
66 return eventProductList.join(";");
67 }
58 68 case CatalogueEventsModel::Column::Tags: {
59 69 QString tagList;
60 70 auto tags = event->getTags();
61 71 for (auto tag : tags) {
62 72 tagList += tag.getName();
63 73 tagList += ' ';
64 74 }
65 75
66 76 return tagList;
67 77 }
68 78 case CatalogueEventsModel::Column::Validation:
69 79 return QVariant();
70 80 default:
71 81 break;
72 82 }
73 83
74 84 Q_ASSERT(false);
75 85 return QStringLiteral("Unknown Data");
76 86 }
77 87
78 88 void parseEventProduct(const std::shared_ptr<DBEvent> &event)
79 89 {
80 90 for (auto product : event->getEventProducts()) {
81 91 m_EventProducts[event.get()].append(std::make_shared<DBEventProduct>(product));
82 92 }
83 93 }
84 94
85 95 int nbEventProducts(const std::shared_ptr<DBEvent> &event) const
86 96 {
87 97 auto eventProductsIt = m_EventProducts.find(event.get());
88 98 if (eventProductsIt != m_EventProducts.cend()) {
89 99 return m_EventProducts.at(event.get()).count();
90 100 }
91 101 else {
92 102 return 0;
93 103 }
94 104 }
95 105
96 106 QVariant eventProductData(int col, const std::shared_ptr<DBEventProduct> &eventProduct) const
97 107 {
98 108 switch (static_cast<Column>(col)) {
99 109 case CatalogueEventsModel::Column::Name:
100 110 return eventProduct->getProductId();
101 111 case CatalogueEventsModel::Column::TStart:
102 return DateUtils::dateTime(eventProduct->getTStart());
112 return DateUtils::dateTime(eventProduct->getTStart())
113 .toString(DATETIME_FORMAT_ONE_LINE);
103 114 case CatalogueEventsModel::Column::TEnd:
104 return DateUtils::dateTime(eventProduct->getTEnd());
115 return DateUtils::dateTime(eventProduct->getTEnd())
116 .toString(DATETIME_FORMAT_ONE_LINE);
105 117 case CatalogueEventsModel::Column::Product:
106 118 return eventProduct->getProductId();
107 119 case CatalogueEventsModel::Column::Tags:
108 120 return QString();
109 121 case CatalogueEventsModel::Column::Validation:
110 122 return QVariant();
111 123 default:
112 124 break;
113 125 }
114 126
115 127 Q_ASSERT(false);
116 128 return QStringLiteral("Unknown Data");
117 129 }
118 130
119 131 void refreshChildrenOfIndex(CatalogueEventsModel *model, const QModelIndex &index) const
120 132 {
121 133 auto childCount = model->rowCount(index);
122 134 auto colCount = model->columnCount();
123 135 emit model->dataChanged(model->index(0, 0, index),
124 136 model->index(childCount, colCount, index));
125 137 }
126 138 };
127 139
128 140 CatalogueEventsModel::CatalogueEventsModel(QObject *parent)
129 141 : QAbstractItemModel(parent), impl{spimpl::make_unique_impl<CatalogueEventsModelPrivate>()}
130 142 {
131 143 }
132 144
133 145 void CatalogueEventsModel::setSourceCatalogues(
134 146 const QVector<std::shared_ptr<DBCatalogue> > &catalogues)
135 147 {
136 148 impl->m_SourceCatalogue = catalogues;
137 149 }
138 150
139 151 void CatalogueEventsModel::setEvents(const QVector<std::shared_ptr<DBEvent> > &events)
140 152 {
141 153 beginResetModel();
142 154
143 155 impl->m_Events = events;
144 156 impl->m_EventProducts.clear();
145 157 for (auto event : events) {
146 158 impl->parseEventProduct(event);
147 159 }
148 160
149 161 endResetModel();
150 162 }
151 163
152 164 std::shared_ptr<DBEvent> CatalogueEventsModel::getEvent(const QModelIndex &index) const
153 165 {
154 166 if (itemTypeOf(index) == CatalogueEventsModel::ItemType::Event) {
155 167 return impl->m_Events.value(index.row());
156 168 }
157 169 else {
158 170 return nullptr;
159 171 }
160 172 }
161 173
162 174 std::shared_ptr<DBEvent> CatalogueEventsModel::getParentEvent(const QModelIndex &index) const
163 175 {
164 176 if (itemTypeOf(index) == CatalogueEventsModel::ItemType::EventProduct) {
165 177 return getEvent(index.parent());
166 178 }
167 179 else {
168 180 return nullptr;
169 181 }
170 182 }
171 183
172 184 std::shared_ptr<DBEventProduct>
173 185 CatalogueEventsModel::getEventProduct(const QModelIndex &index) const
174 186 {
175 187 if (itemTypeOf(index) == CatalogueEventsModel::ItemType::EventProduct) {
176 188 auto event = static_cast<DBEvent *>(index.internalPointer());
177 189 return impl->m_EventProducts.at(event).value(index.row());
178 190 }
179 191 else {
180 192 return nullptr;
181 193 }
182 194 }
183 195
184 196 void CatalogueEventsModel::addEvent(const std::shared_ptr<DBEvent> &event)
185 197 {
186 198 beginInsertRows(QModelIndex(), impl->m_Events.count(), impl->m_Events.count());
187 199 impl->m_Events.append(event);
188 200 impl->parseEventProduct(event);
189 201 endInsertRows();
190 202
191 203 // Also refreshes its children event products
192 204 auto eventIndex = index(impl->m_Events.count(), 0);
193 205 impl->refreshChildrenOfIndex(this, eventIndex);
194 206 }
195 207
196 208 void CatalogueEventsModel::removeEvent(const std::shared_ptr<DBEvent> &event)
197 209 {
198 210 auto index = impl->m_Events.indexOf(event);
199 211 if (index >= 0) {
200 212 beginRemoveRows(QModelIndex(), index, index);
201 213 impl->m_Events.removeAt(index);
202 214 impl->m_EventProducts.erase(event.get());
203 215 endRemoveRows();
204 216 }
205 217 }
206 218
207 219 QVector<std::shared_ptr<DBEvent> > CatalogueEventsModel::events() const
208 220 {
209 221 return impl->m_Events;
210 222 }
211 223
212 224 void CatalogueEventsModel::refreshEvent(const std::shared_ptr<DBEvent> &event,
213 225 bool refreshEventProducts)
214 226 {
215 227 auto eventIndex = indexOf(event);
216 228 if (eventIndex.isValid()) {
217 229
218 230 if (refreshEventProducts) {
219 231 // Reparse the associated event products
220 232
221 233 auto nbEventProducts = impl->nbEventProducts(event);
222 234 auto newNbOfEventProducts = event->getEventProducts().size();
223 235 if (newNbOfEventProducts < nbEventProducts) {
224 236 beginRemoveRows(eventIndex, newNbOfEventProducts, nbEventProducts - 1);
225 237 impl->m_EventProducts.erase(event.get());
226 238 impl->parseEventProduct(event);
227 239 endRemoveRows();
228 240 }
229 241 else if (newNbOfEventProducts > nbEventProducts) {
230 242 beginInsertRows(eventIndex, nbEventProducts, newNbOfEventProducts - 1);
231 243 impl->m_EventProducts.erase(event.get());
232 244 impl->parseEventProduct(event);
233 245 endInsertRows();
234 246 }
235 247 else { // newNbOfEventProducts == nbEventProducts
236 248 impl->m_EventProducts.erase(event.get());
237 249 impl->parseEventProduct(event);
238 250 }
239 251 }
240 252
241 253 // Refreshes the event line
242 254 auto colCount = columnCount();
243 255 emit dataChanged(eventIndex, index(eventIndex.row(), colCount));
244 256
245 257 // Also refreshes its children event products
246 258 impl->refreshChildrenOfIndex(this, eventIndex);
247 259 }
248 260 else {
249 261 qCWarning(LOG_CatalogueEventsModel()) << "refreshEvent: event not found.";
250 262 }
251 263 }
252 264
253 265 QModelIndex CatalogueEventsModel::indexOf(const std::shared_ptr<DBEvent> &event) const
254 266 {
255 267 auto row = impl->m_Events.indexOf(event);
256 268 if (row >= 0) {
257 269 return index(row, 0);
258 270 }
259 271
260 272 return QModelIndex();
261 273 }
262 274
263 275 QModelIndex CatalogueEventsModel::index(int row, int column, const QModelIndex &parent) const
264 276 {
265 277 if (!hasIndex(row, column, parent)) {
266 278 return QModelIndex();
267 279 }
268 280
269 281 switch (itemTypeOf(parent)) {
270 282 case CatalogueEventsModel::ItemType::Root:
271 283 return createIndex(row, column);
272 284 case CatalogueEventsModel::ItemType::Event: {
273 285 auto event = getEvent(parent);
274 286 return createIndex(row, column, event.get());
275 287 }
276 288 case CatalogueEventsModel::ItemType::EventProduct:
277 289 break;
278 290 default:
279 291 break;
280 292 }
281 293
282 294 return QModelIndex();
283 295 }
284 296
285 297 QModelIndex CatalogueEventsModel::parent(const QModelIndex &index) const
286 298 {
287 299 switch (itemTypeOf(index)) {
288 300 case CatalogueEventsModel::ItemType::EventProduct: {
289 301 auto parentEvent = static_cast<DBEvent *>(index.internalPointer());
290 302 auto it
291 303 = std::find_if(impl->m_Events.cbegin(), impl->m_Events.cend(),
292 304 [parentEvent](auto event) { return event.get() == parentEvent; });
293 305
294 306 if (it != impl->m_Events.cend()) {
295 307 return createIndex(it - impl->m_Events.cbegin(), 0);
296 308 }
297 309 else {
298 310 return QModelIndex();
299 311 }
300 312 }
301 313 case CatalogueEventsModel::ItemType::Root:
302 314 break;
303 315 case CatalogueEventsModel::ItemType::Event:
304 316 break;
305 317 default:
306 318 break;
307 319 }
308 320
309 321 return QModelIndex();
310 322 }
311 323
312 324 int CatalogueEventsModel::rowCount(const QModelIndex &parent) const
313 325 {
314 326 if (parent.column() > 0) {
315 327 return 0;
316 328 }
317 329
318 330 switch (itemTypeOf(parent)) {
319 331 case CatalogueEventsModel::ItemType::Root:
320 332 return impl->m_Events.count();
321 333 case CatalogueEventsModel::ItemType::Event: {
322 334 auto event = getEvent(parent);
323 335 return impl->m_EventProducts[event.get()].count();
324 336 }
325 337 case CatalogueEventsModel::ItemType::EventProduct:
326 338 break;
327 339 default:
328 340 break;
329 341 }
330 342
331 343 return 0;
332 344 }
333 345
334 346 int CatalogueEventsModel::columnCount(const QModelIndex &parent) const
335 347 {
336 348 return static_cast<int>(CatalogueEventsModel::Column::NbColumn);
337 349 }
338 350
339 351 Qt::ItemFlags CatalogueEventsModel::flags(const QModelIndex &index) const
340 352 {
341 353 return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled;
342 354 }
343 355
344 356 QVariant CatalogueEventsModel::data(const QModelIndex &index, int role) const
345 357 {
346 358 if (index.isValid()) {
347 359
348 360 auto type = itemTypeOf(index);
349 361 if (type == CatalogueEventsModel::ItemType::Event) {
350 362 auto event = getEvent(index);
351 363 switch (role) {
352 364 case Qt::DisplayRole:
353 365 return impl->eventData(index.column(), event);
354 366 break;
355 367 }
356 368 }
357 369 else if (type == CatalogueEventsModel::ItemType::EventProduct) {
358 370 auto product = getEventProduct(index);
359 371 switch (role) {
360 372 case Qt::DisplayRole:
361 373 return impl->eventProductData(index.column(), product);
362 374 break;
363 375 }
364 376 }
365 377 }
366 378
367 379 return QVariant{};
368 380 }
369 381
370 382 QVariant CatalogueEventsModel::headerData(int section, Qt::Orientation orientation, int role) const
371 383 {
372 384 if (orientation == Qt::Horizontal && role == Qt::DisplayRole) {
373 385 return impl->columnNames().value(section);
374 386 }
375 387
376 388 return QVariant();
377 389 }
378 390
379 391 void CatalogueEventsModel::sort(int column, Qt::SortOrder order)
380 392 {
381 393 beginResetModel();
382 394 std::sort(impl->m_Events.begin(), impl->m_Events.end(),
383 395 [this, column, order](auto e1, auto e2) {
384 396 auto data1 = impl->sortData(column, e1);
385 397 auto data2 = impl->sortData(column, e2);
386 398
387 399 auto result = data1.toString() < data2.toString();
388 400
389 401 return order == Qt::AscendingOrder ? result : !result;
390 402 });
391 403
392 404 endResetModel();
393 405 emit modelSorted();
394 406 }
395 407
396 408 Qt::DropActions CatalogueEventsModel::supportedDragActions() const
397 409 {
398 410 return Qt::CopyAction | Qt::MoveAction;
399 411 }
400 412
401 413 QStringList CatalogueEventsModel::mimeTypes() const
402 414 {
403 415 return {MIME_TYPE_EVENT_LIST, MIME_TYPE_SOURCE_CATALOGUE_LIST, MIME_TYPE_TIME_RANGE};
404 416 }
405 417
406 418 QMimeData *CatalogueEventsModel::mimeData(const QModelIndexList &indexes) const
407 419 {
408 420 auto mimeData = new QMimeData;
409 421
410 422 bool isFirst = true;
411 423
412 424 QVector<std::shared_ptr<DBEvent> > eventList;
413 425 QVector<std::shared_ptr<DBEventProduct> > eventProductList;
414 426
415 427 SqpRange firstTimeRange;
416 428 for (const auto &index : indexes) {
417 429 if (index.column() == 0) { // only the first column
418 430
419 431 auto type = itemTypeOf(index);
420 432 if (type == ItemType::Event) {
421 433 auto event = getEvent(index);
422 434 eventList << event;
423 435
424 436 if (isFirst) {
425 437 isFirst = false;
426 438 firstTimeRange.m_TStart = event->getTStart();
427 439 firstTimeRange.m_TEnd = event->getTEnd();
428 440 }
429 441 }
430 442 else if (type == ItemType::EventProduct) {
431 443 auto product = getEventProduct(index);
432 444 eventProductList << product;
433 445
434 446 if (isFirst) {
435 447 isFirst = false;
436 448 firstTimeRange.m_TStart = product->getTStart();
437 449 firstTimeRange.m_TEnd = product->getTEnd();
438 450 }
439 451 }
440 452 }
441 453 }
442 454
443 455 if (!eventList.isEmpty() && eventProductList.isEmpty()) {
444 456 auto eventsEncodedData = sqpApp->catalogueController().mimeDataForEvents(eventList);
445 457 mimeData->setData(MIME_TYPE_EVENT_LIST, eventsEncodedData);
446 458
447 459 auto sourceCataloguesEncodedData
448 460 = sqpApp->catalogueController().mimeDataForCatalogues(impl->m_SourceCatalogue);
449 461 mimeData->setData(MIME_TYPE_SOURCE_CATALOGUE_LIST, sourceCataloguesEncodedData);
450 462 }
451 463
452 464 if (eventList.count() + eventProductList.count() == 1) {
453 465 // No time range MIME data if multiple events are dragged
454 466 auto timeEncodedData = TimeController::mimeDataForTimeRange(firstTimeRange);
455 467 mimeData->setData(MIME_TYPE_TIME_RANGE, timeEncodedData);
456 468 }
457 469
458 470 return mimeData;
459 471 }
460 472
461 473 CatalogueEventsModel::ItemType CatalogueEventsModel::itemTypeOf(const QModelIndex &index) const
462 474 {
463 475 if (!index.isValid()) {
464 476 return ItemType::Root;
465 477 }
466 478 else if (index.internalPointer() == nullptr) {
467 479 return ItemType::Event;
468 480 }
469 481 else {
470 482 return ItemType::EventProduct;
471 483 }
472 484 }
@@ -1,594 +1,629
1 1 #include "Catalogue/CatalogueEventsWidget.h"
2 2 #include "ui_CatalogueEventsWidget.h"
3 3
4 4 #include <Catalogue/CatalogueController.h>
5 5 #include <Catalogue/CatalogueEventsModel.h>
6 6 #include <Catalogue/CatalogueExplorerHelper.h>
7 7 #include <CatalogueDao.h>
8 8 #include <DBCatalogue.h>
9 9 #include <DBEventProduct.h>
10 10 #include <DataSource/DataSourceController.h>
11 11 #include <DataSource/DataSourceItem.h>
12 12 #include <SqpApplication.h>
13 13 #include <Variable/Variable.h>
14 14 #include <Variable/VariableController.h>
15 15 #include <Visualization/VisualizationGraphWidget.h>
16 16 #include <Visualization/VisualizationTabWidget.h>
17 17 #include <Visualization/VisualizationWidget.h>
18 18 #include <Visualization/VisualizationZoneWidget.h>
19 19
20 20 #include <QDialog>
21 21 #include <QDialogButtonBox>
22 #include <QKeyEvent>
22 23 #include <QListWidget>
23 24 #include <QMessageBox>
24 25
25 26 Q_LOGGING_CATEGORY(LOG_CatalogueEventsWidget, "CatalogueEventsWidget")
26 27
27 /// Fixed size of the validation column
28 const auto VALIDATION_COLUMN_SIZE = 35;
29
30 28 /// Percentage added to the range of a event when it is displayed
31 29 const auto EVENT_RANGE_MARGE = 30; // in %
32 30
33 31 struct CatalogueEventsWidget::CatalogueEventsWidgetPrivate {
34 32
35 33 CatalogueEventsModel *m_Model = nullptr;
36 34 QStringList m_ZonesForTimeMode;
37 35 QString m_ZoneForGraphMode;
38 36 QVector<std::shared_ptr<DBCatalogue> > m_DisplayedCatalogues;
39 37 bool m_AllEventDisplayed = false;
40 38 QVector<VisualizationGraphWidget *> m_CustomGraphs;
41 39
42 40 VisualizationWidget *m_VisualizationWidget = nullptr;
43 41
44 42 void setEvents(const QVector<std::shared_ptr<DBEvent> > &events, CatalogueEventsWidget *widget)
45 43 {
46 44 widget->ui->treeView->setSortingEnabled(false);
47 45 m_Model->setSourceCatalogues(m_DisplayedCatalogues);
48 46 m_Model->setEvents(events);
49 47 widget->ui->treeView->setSortingEnabled(true);
50 48
51 49 for (auto event : events) {
52 50 if (sqpApp->catalogueController().eventHasChanges(event)) {
53 51 auto index = m_Model->indexOf(event);
54 52 widget->setEventChanges(event, true);
55 53 }
56 54 }
57 55 }
58 56
59 57 void addEvent(const std::shared_ptr<DBEvent> &event, QTreeView *treeView)
60 58 {
61 59 treeView->setSortingEnabled(false);
62 60 m_Model->addEvent(event);
63 61 treeView->setSortingEnabled(true);
64 62 }
65 63
66 64 void removeEvent(const std::shared_ptr<DBEvent> &event, QTreeView *treeView)
67 65 {
68 66 treeView->setSortingEnabled(false);
69 67 m_Model->removeEvent(event);
70 68 treeView->setSortingEnabled(true);
71 69 }
72 70
73 71 QStringList getAvailableVisualizationZoneList() const
74 72 {
75 73 if (m_VisualizationWidget) {
76 74 if (auto tab = m_VisualizationWidget->currentTabWidget()) {
77 75 return tab->availableZoneWidgets();
78 76 }
79 77 }
80 78
81 79 return QStringList{};
82 80 }
83 81
84 82 QStringList selectZone(QWidget *parent, const QStringList &selectedZones,
85 83 bool allowMultiSelection, const QPoint &location)
86 84 {
87 85 auto availableZones = getAvailableVisualizationZoneList();
88 86 if (availableZones.isEmpty()) {
89 87 return QStringList{};
90 88 }
91 89
92 90 QDialog d(parent, Qt::Tool);
93 91 d.setWindowTitle("Choose a zone");
94 92 auto layout = new QVBoxLayout{&d};
95 93 layout->setContentsMargins(0, 0, 0, 0);
96 94 auto listWidget = new QListWidget{&d};
97 95 layout->addWidget(listWidget);
98 96
99 97 QSet<QListWidgetItem *> checkedItems;
100 98 for (auto zone : availableZones) {
101 99 auto item = new QListWidgetItem{zone};
102 100 item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsUserCheckable);
103 101 if (selectedZones.contains(zone)) {
104 102 item->setCheckState(Qt::Checked);
105 103 checkedItems << item;
106 104 }
107 105 else {
108 106 item->setCheckState(Qt::Unchecked);
109 107 }
110 108
111 109 listWidget->addItem(item);
112 110 }
113 111
114 112 auto buttonBox = new QDialogButtonBox{QDialogButtonBox::Ok, &d};
115 113 layout->addWidget(buttonBox);
116 114
117 115 QObject::connect(buttonBox, &QDialogButtonBox::accepted, &d, &QDialog::accept);
118 116 QObject::connect(buttonBox, &QDialogButtonBox::rejected, &d, &QDialog::reject);
119 117
120 118 QObject::connect(listWidget, &QListWidget::itemChanged,
121 119 [&checkedItems, allowMultiSelection, listWidget](auto item) {
122 120 if (item->checkState() == Qt::Checked) {
123 121 if (!allowMultiSelection) {
124 122 for (auto checkedItem : checkedItems) {
125 123 listWidget->blockSignals(true);
126 124 checkedItem->setCheckState(Qt::Unchecked);
127 125 listWidget->blockSignals(false);
128 126 }
129 127
130 128 checkedItems.clear();
131 129 }
132 130 checkedItems << item;
133 131 }
134 132 else {
135 133 checkedItems.remove(item);
136 134 }
137 135 });
138 136
139 137 QStringList result;
140 138
141 139 d.setMinimumWidth(120);
142 140 d.resize(d.minimumSizeHint());
143 141 d.move(location);
144 142 if (d.exec() == QDialog::Accepted) {
145 143 for (auto item : checkedItems) {
146 144 result += item->text();
147 145 }
148 146 }
149 147 else {
150 148 result = selectedZones;
151 149 }
152 150
153 151 return result;
154 152 }
155 153
156 154 void updateForTimeMode(QTreeView *treeView)
157 155 {
158 156 auto selectedRows = treeView->selectionModel()->selectedRows();
159 157
160 158 if (selectedRows.count() == 1) {
161 159 auto event = m_Model->getEvent(selectedRows.first());
162 160 if (event) {
163 161 if (m_VisualizationWidget) {
164 162 if (auto tab = m_VisualizationWidget->currentTabWidget()) {
165 163
166 164 for (auto zoneName : m_ZonesForTimeMode) {
167 165 if (auto zone = tab->getZoneWithName(zoneName)) {
168 166 SqpRange eventRange;
169 167 eventRange.m_TStart = event->getTStart();
170 168 eventRange.m_TEnd = event->getTEnd();
171 169 zone->setZoneRange(eventRange);
172 170 }
173 171 }
174 172 }
175 173 else {
176 174 qCWarning(LOG_CatalogueEventsWidget())
177 175 << "updateTimeZone: no tab found in the visualization";
178 176 }
179 177 }
180 178 else {
181 179 qCWarning(LOG_CatalogueEventsWidget())
182 180 << "updateTimeZone: visualization widget not found";
183 181 }
184 182 }
185 183 }
186 184 else {
187 185 qCWarning(LOG_CatalogueEventsWidget())
188 186 << "updateTimeZone: not compatible with multiple events selected";
189 187 }
190 188 }
191 189
192 190 QVector<SqpRange> getGraphRanges(const std::shared_ptr<DBEvent> &event)
193 191 {
194 192 // Retrieves the range of each product and the maximum size
195 193 QVector<SqpRange> graphRanges;
196 194 double maxDt = 0;
197 195 for (auto eventProduct : event->getEventProducts()) {
198 196 SqpRange eventRange;
199 197 eventRange.m_TStart = eventProduct.getTStart();
200 198 eventRange.m_TEnd = eventProduct.getTEnd();
201 199 graphRanges << eventRange;
202 200
203 201 auto dt = eventRange.m_TEnd - eventRange.m_TStart;
204 202 if (dt > maxDt) {
205 203 maxDt = dt;
206 204 }
207 205 }
208 206
209 207 // Adds the marge
210 208 maxDt *= (100.0 + EVENT_RANGE_MARGE) / 100.0;
211 209
212 210 // Corrects the graph ranges so that they all have the same size
213 211 QVector<SqpRange> correctedGraphRanges;
214 212 for (auto range : graphRanges) {
215 213 auto dt = range.m_TEnd - range.m_TStart;
216 214 auto diff = qAbs((maxDt - dt) / 2.0);
217 215
218 216 SqpRange correctedRange;
219 217 correctedRange.m_TStart = range.m_TStart - diff;
220 218 correctedRange.m_TEnd = range.m_TEnd + diff;
221 219
222 220 correctedGraphRanges << correctedRange;
223 221 }
224 222
225 223 return correctedGraphRanges;
226 224 }
227 225
228 226 void updateForGraphMode(CatalogueEventsWidget *catalogueEventWidget)
229 227 {
230 228 auto selectedRows = catalogueEventWidget->ui->treeView->selectionModel()->selectedRows();
231 229 if (selectedRows.count() != 1) {
232 230 qCWarning(LOG_CatalogueEventsWidget())
233 231 << "updateGraphMode: not compatible with multiple events selected";
234 232 return;
235 233 }
236 234
237 235 if (!m_VisualizationWidget) {
238 236 qCWarning(LOG_CatalogueEventsWidget())
239 237 << "updateGraphMode: visualization widget not found";
240 238 return;
241 239 }
242 240
243 241 auto event = m_Model->getEvent(selectedRows.first());
244 242 if (!event) {
245 243 // A event product is probably selected
246 244 qCInfo(LOG_CatalogueEventsWidget()) << "updateGraphMode: no events are selected";
247 245 return;
248 246 }
249 247
250 248 auto tab = m_VisualizationWidget->currentTabWidget();
251 249 if (!tab) {
252 250 qCWarning(LOG_CatalogueEventsWidget())
253 251 << "updateGraphMode: no tab found in the visualization";
254 252 return;
255 253 }
256 254
257 255 auto zone = tab->getZoneWithName(m_ZoneForGraphMode);
258 256 if (!zone) {
259 257 qCWarning(LOG_CatalogueEventsWidget()) << "updateGraphMode: zone not found";
260 258 return;
261 259 }
262 260
263 261 // Closes the previous graph and delete the asociated variables
264 262 for (auto graph : m_CustomGraphs) {
265 263 graph->close();
266 264 auto variables = graph->variables().toVector();
267 265
268 266 QMetaObject::invokeMethod(&sqpApp->variableController(), "deleteVariables",
269 267 Qt::QueuedConnection,
270 268 Q_ARG(QVector<std::shared_ptr<Variable> >, variables));
271 269 }
272 270 m_CustomGraphs.clear();
273 271
274 272 // Closes the remaining graphs inside the zone
275 273 zone->closeAllGraphs();
276 274
277 275 // Calculates the range of each graph which will be created
278 276 auto graphRange = getGraphRanges(event);
279 277
280 278 // Loops through the event products and create the graph
281 279 auto itRange = graphRange.cbegin();
282 280 for (auto eventProduct : event->getEventProducts()) {
283 281 auto productId = eventProduct.getProductId();
284 282
285 283 auto range = *itRange;
286 284 ++itRange;
287 285
288 286 SqpRange productRange;
289 287 productRange.m_TStart = eventProduct.getTStart();
290 288 productRange.m_TEnd = eventProduct.getTEnd();
291 289
292 290 auto context = new QObject{catalogueEventWidget};
293 291 QObject::connect(
294 292 &sqpApp->variableController(), &VariableController::variableAdded, context,
295 293 [this, catalogueEventWidget, zone, context, event, range, productRange,
296 294 productId](auto variable) {
297 295
298 296 if (variable->metadata().value(DataSourceItem::ID_DATA_KEY).toString()
299 297 == productId) {
300 298 auto graph = zone->createGraph(variable);
301 299 graph->setAutoRangeOnVariableInitialization(false);
302 300
303 301 auto selectionZone
304 302 = graph->addSelectionZone(event->getName(), productRange);
305 303 emit catalogueEventWidget->selectionZoneAdded(event, productId,
306 304 selectionZone);
307 305 m_CustomGraphs << graph;
308 306
309 307 graph->setGraphRange(range, true);
310 308
311 309 // Removes the graph from the graph list if it is closed manually
312 310 QObject::connect(graph, &VisualizationGraphWidget::destroyed,
313 311 [this, graph]() { m_CustomGraphs.removeAll(graph); });
314 312
315 313 delete context; // removes the connection
316 314 }
317 315 },
318 316 Qt::QueuedConnection);
319 317
320 318 QMetaObject::invokeMethod(&sqpApp->dataSourceController(),
321 319 "requestVariableFromProductIdKey", Qt::QueuedConnection,
322 320 Q_ARG(QString, productId));
323 321 }
324 322 }
325 323
326 324 void getSelectedItems(
327 325 QTreeView *treeView, QVector<std::shared_ptr<DBEvent> > &events,
328 326 QVector<QPair<std::shared_ptr<DBEvent>, std::shared_ptr<DBEventProduct> > > &eventProducts)
329 327 {
330 328 for (auto rowIndex : treeView->selectionModel()->selectedRows()) {
331 329 auto itemType = m_Model->itemTypeOf(rowIndex);
332 330 if (itemType == CatalogueEventsModel::ItemType::Event) {
333 331 events << m_Model->getEvent(rowIndex);
334 332 }
335 333 else if (itemType == CatalogueEventsModel::ItemType::EventProduct) {
336 334 eventProducts << qMakePair(m_Model->getParentEvent(rowIndex),
337 335 m_Model->getEventProduct(rowIndex));
338 336 }
339 337 }
340 338 }
341 339 };
342 340
343 341 CatalogueEventsWidget::CatalogueEventsWidget(QWidget *parent)
344 342 : QWidget(parent),
345 343 ui(new Ui::CatalogueEventsWidget),
346 344 impl{spimpl::make_unique_impl<CatalogueEventsWidgetPrivate>()}
347 345 {
348 346 ui->setupUi(this);
349 347
350 348 impl->m_Model = new CatalogueEventsModel{this};
351 349 ui->treeView->setModel(impl->m_Model);
352 350
353 351 ui->treeView->setSortingEnabled(true);
354 352 ui->treeView->setDragDropMode(QAbstractItemView::DragDrop);
355 353 ui->treeView->setDragEnabled(true);
356 354
355
357 356 connect(ui->btnTime, &QToolButton::clicked, [this](auto checked) {
358 357 if (checked) {
359 358 ui->btnChart->setChecked(false);
360 359 impl->m_ZonesForTimeMode
361 360 = impl->selectZone(this, impl->m_ZonesForTimeMode, true,
362 361 this->mapToGlobal(ui->btnTime->frameGeometry().center()));
363 362
364 363 impl->updateForTimeMode(ui->treeView);
365 364 }
366 365 });
367 366
368 367 connect(ui->btnChart, &QToolButton::clicked, [this](auto checked) {
369 368 if (checked) {
370 369 ui->btnTime->setChecked(false);
371 370 impl->m_ZoneForGraphMode
372 371 = impl->selectZone(this, {impl->m_ZoneForGraphMode}, false,
373 372 this->mapToGlobal(ui->btnChart->frameGeometry().center()))
374 373 .value(0);
375 374
376 375 impl->updateForGraphMode(this);
377 376 }
378 377 });
379 378
380 379 connect(ui->btnRemove, &QToolButton::clicked, [this]() {
381 380 QVector<std::shared_ptr<DBEvent> > events;
382 381 QVector<QPair<std::shared_ptr<DBEvent>, std::shared_ptr<DBEventProduct> > > eventProducts;
383 382 impl->getSelectedItems(ui->treeView, events, eventProducts);
384 383
385 384 if (!events.isEmpty() && eventProducts.isEmpty()) {
386 385
387 if (QMessageBox::warning(this, tr("Remove Event(s)"),
388 tr("The selected event(s) will be permanently removed "
389 "from the repository!\nAre you sure you want to continue?"),
390 QMessageBox::Yes | QMessageBox::No, QMessageBox::No)
391 == QMessageBox::Yes) {
386 auto canRemoveEvent
387 = !this->isAllEventsDisplayed()
388 || (QMessageBox::warning(
389 this, tr("Remove Event(s)"),
390 tr("The selected event(s) will be permanently removed "
391 "from the repository!\nAre you sure you want to continue?"),
392 QMessageBox::Yes | QMessageBox::No, QMessageBox::No)
393 == QMessageBox::Yes);
392 394
395 if (canRemoveEvent) {
393 396 for (auto event : events) {
394 sqpApp->catalogueController().removeEvent(event);
395 impl->removeEvent(event, ui->treeView);
397 if (this->isAllEventsDisplayed()) {
398 sqpApp->catalogueController().removeEvent(event);
399 impl->removeEvent(event, ui->treeView);
400 }
401 else {
402 QVector<std::shared_ptr<DBCatalogue> > modifiedCatalogues;
403 for (auto catalogue : this->displayedCatalogues()) {
404 if (catalogue->removeEvent(event->getUniqId())) {
405 sqpApp->catalogueController().updateCatalogue(catalogue);
406 modifiedCatalogues << catalogue;
407 }
408 }
409 if (!modifiedCatalogues.empty()) {
410 emit eventCataloguesModified(modifiedCatalogues);
411 }
412 }
413 impl->m_Model->removeEvent(event);
396 414 }
397 415
416
398 417 emit this->eventsRemoved(events);
399 418 }
400 419 }
401 420 });
402 421
403 422 connect(ui->treeView, &QTreeView::clicked, this, &CatalogueEventsWidget::emitSelection);
404 423 connect(ui->treeView->selectionModel(), &QItemSelectionModel::selectionChanged, this,
405 424 &CatalogueEventsWidget::emitSelection);
406 425
407 426 ui->btnRemove->setEnabled(false); // Disabled by default when nothing is selected
408 427 connect(ui->treeView->selectionModel(), &QItemSelectionModel::selectionChanged, [this]() {
409 428 auto isNotMultiSelection = ui->treeView->selectionModel()->selectedRows().count() <= 1;
410 429 ui->btnChart->setEnabled(isNotMultiSelection);
411 430 ui->btnTime->setEnabled(isNotMultiSelection);
412 431
413 432 if (isNotMultiSelection && ui->btnTime->isChecked()) {
414 433 impl->updateForTimeMode(ui->treeView);
415 434 }
416 435 else if (isNotMultiSelection && ui->btnChart->isChecked()) {
417 436 impl->updateForGraphMode(this);
418 437 }
419 438
420 439 QVector<std::shared_ptr<DBEvent> > events;
421 440 QVector<QPair<std::shared_ptr<DBEvent>, std::shared_ptr<DBEventProduct> > > eventProducts;
422 441 impl->getSelectedItems(ui->treeView, events, eventProducts);
423 442 ui->btnRemove->setEnabled(!events.isEmpty() && eventProducts.isEmpty());
424 443 });
425 444
426 445 ui->treeView->header()->setSectionResizeMode(QHeaderView::ResizeToContents);
427 446 ui->treeView->header()->setSectionResizeMode((int)CatalogueEventsModel::Column::Tags,
428 447 QHeaderView::Stretch);
429 448 ui->treeView->header()->setSectionResizeMode((int)CatalogueEventsModel::Column::Validation,
430 QHeaderView::Fixed);
449 QHeaderView::ResizeToContents);
431 450 ui->treeView->header()->setSectionResizeMode((int)CatalogueEventsModel::Column::Name,
432 451 QHeaderView::Interactive);
433 ui->treeView->header()->resizeSection((int)CatalogueEventsModel::Column::Validation,
434 VALIDATION_COLUMN_SIZE);
452 ui->treeView->header()->setSectionResizeMode((int)CatalogueEventsModel::Column::TStart,
453 QHeaderView::ResizeToContents);
454 ui->treeView->header()->setSectionResizeMode((int)CatalogueEventsModel::Column::TEnd,
455 QHeaderView::ResizeToContents);
435 456 ui->treeView->header()->setSortIndicatorShown(true);
436 457
437 458 connect(impl->m_Model, &CatalogueEventsModel::modelSorted, [this]() {
438 459 auto allEvents = impl->m_Model->events();
439 460 for (auto event : allEvents) {
440 461 setEventChanges(event, sqpApp->catalogueController().eventHasChanges(event));
441 462 }
442 463 });
443 464
444 465 populateWithAllEvents();
445 466 }
446 467
447 468 CatalogueEventsWidget::~CatalogueEventsWidget()
448 469 {
449 470 delete ui;
450 471 }
451 472
452 473 void CatalogueEventsWidget::setVisualizationWidget(VisualizationWidget *visualization)
453 474 {
454 475 impl->m_VisualizationWidget = visualization;
455 476 }
456 477
457 478 void CatalogueEventsWidget::addEvent(const std::shared_ptr<DBEvent> &event)
458 479 {
459 480 impl->addEvent(event, ui->treeView);
460 481 }
461 482
462 483 void CatalogueEventsWidget::setEventChanges(const std::shared_ptr<DBEvent> &event, bool hasChanges)
463 484 {
464 485 impl->m_Model->refreshEvent(event);
465 486
466 487 auto eventIndex = impl->m_Model->indexOf(event);
467 488 auto validationIndex
468 489 = eventIndex.sibling(eventIndex.row(), (int)CatalogueEventsModel::Column::Validation);
469 490
470 491 if (validationIndex.isValid()) {
471 492 if (hasChanges) {
472 493 if (ui->treeView->indexWidget(validationIndex) == nullptr) {
473 494 auto widget = CatalogueExplorerHelper::buildValidationWidget(
474 495 ui->treeView,
475 496 [this, event]() {
476 497 sqpApp->catalogueController().saveEvent(event);
477 498 setEventChanges(event, false);
478 499 },
479 500 [this, event]() {
480 501 bool removed = false;
481 502 sqpApp->catalogueController().discardEvent(event, removed);
482 503 if (removed) {
483 504 impl->m_Model->removeEvent(event);
484 505 }
485 506 else {
486 507 setEventChanges(event, false);
487 508 impl->m_Model->refreshEvent(event, true);
488 509 }
489 510 emitSelection();
490 511 });
491 512 ui->treeView->setIndexWidget(validationIndex, widget);
513 ui->treeView->header()->resizeSection((int)CatalogueEventsModel::Column::Validation,
514 QHeaderView::ResizeToContents);
492 515 }
493 516 }
494 517 else {
495 518 // Note: the widget is destroyed
496 519 ui->treeView->setIndexWidget(validationIndex, nullptr);
497 520 }
498 521 }
499 522 else {
500 523 qCWarning(LOG_CatalogueEventsWidget())
501 524 << "setEventChanges: the event is not displayed in the model.";
502 525 }
503 526 }
504 527
505 528 QVector<std::shared_ptr<DBCatalogue> > CatalogueEventsWidget::displayedCatalogues() const
506 529 {
507 530 return impl->m_DisplayedCatalogues;
508 531 }
509 532
510 533 bool CatalogueEventsWidget::isAllEventsDisplayed() const
511 534 {
512 535 return impl->m_AllEventDisplayed;
513 536 }
514 537
515 538 bool CatalogueEventsWidget::isEventDisplayed(const std::shared_ptr<DBEvent> &event) const
516 539 {
517 540 return impl->m_Model->indexOf(event).isValid();
518 541 }
519 542
520 543 void CatalogueEventsWidget::refreshEvent(const std::shared_ptr<DBEvent> &event)
521 544 {
522 545 impl->m_Model->refreshEvent(event, true);
523 546 }
524 547
525 548 void CatalogueEventsWidget::populateWithCatalogues(
526 549 const QVector<std::shared_ptr<DBCatalogue> > &catalogues)
527 550 {
528 551 impl->m_DisplayedCatalogues = catalogues;
529 552 impl->m_AllEventDisplayed = false;
530 553
531 554 QSet<QUuid> eventIds;
532 555 QVector<std::shared_ptr<DBEvent> > events;
533 556
534 557 for (auto catalogue : catalogues) {
535 558 auto catalogueEvents = sqpApp->catalogueController().retrieveEventsFromCatalogue(catalogue);
536 559 for (auto event : catalogueEvents) {
537 560 if (!eventIds.contains(event->getUniqId())) {
538 561 events << event;
539 562 eventIds.insert(event->getUniqId());
540 563 }
541 564 }
542 565 }
543 566
544 567 impl->setEvents(events, this);
545 568 }
546 569
547 570 void CatalogueEventsWidget::populateWithAllEvents()
548 571 {
549 572 impl->m_DisplayedCatalogues.clear();
550 573 impl->m_AllEventDisplayed = true;
551 574
552 575 auto allEvents = sqpApp->catalogueController().retrieveAllEvents();
553 576
554 577 QVector<std::shared_ptr<DBEvent> > events;
555 578 for (auto event : allEvents) {
556 579 events << event;
557 580 }
558 581
559 582 impl->setEvents(events, this);
560 583 }
561 584
562 585 void CatalogueEventsWidget::clear()
563 586 {
564 587 impl->m_DisplayedCatalogues.clear();
565 588 impl->m_AllEventDisplayed = false;
566 589 impl->setEvents({}, this);
567 590 }
568 591
569 592 void CatalogueEventsWidget::refresh()
570 593 {
571 594 if (isAllEventsDisplayed()) {
572 595 populateWithAllEvents();
573 596 }
574 597 else if (!impl->m_DisplayedCatalogues.isEmpty()) {
575 598 populateWithCatalogues(impl->m_DisplayedCatalogues);
576 599 }
577 600 }
578 601
579 602 void CatalogueEventsWidget::emitSelection()
580 603 {
581 604 QVector<std::shared_ptr<DBEvent> > events;
582 605 QVector<QPair<std::shared_ptr<DBEvent>, std::shared_ptr<DBEventProduct> > > eventProducts;
583 606 impl->getSelectedItems(ui->treeView, events, eventProducts);
584 607
585 608 if (!events.isEmpty() && eventProducts.isEmpty()) {
586 609 emit eventsSelected(events);
587 610 }
588 611 else if (events.isEmpty() && !eventProducts.isEmpty()) {
589 612 emit eventProductsSelected(eventProducts);
590 613 }
591 614 else {
592 615 emit selectionCleared();
593 616 }
594 617 }
618
619
620 void CatalogueEventsWidget::keyPressEvent(QKeyEvent *event)
621 {
622 switch (event->key()) {
623 case Qt::Key_Delete: {
624 ui->btnRemove->click();
625 }
626 default:
627 break;
628 }
629 }
@@ -1,193 +1,203
1 1 #include "Catalogue/CatalogueExplorer.h"
2 2 #include "ui_CatalogueExplorer.h"
3 3
4 4 #include <Catalogue/CatalogueActionManager.h>
5 5 #include <Catalogue/CatalogueController.h>
6 6 #include <SqpApplication.h>
7 7 #include <Visualization/VisualizationGraphWidget.h>
8 8 #include <Visualization/VisualizationSelectionZoneItem.h>
9 9 #include <Visualization/VisualizationWidget.h>
10 10
11 11 #include <DBCatalogue.h>
12 12 #include <DBEvent.h>
13 13 #include <DBEventProduct.h>
14 14
15 15 #include <unordered_map>
16 16
17 17 struct CatalogueExplorer::CatalogueExplorerPrivate {
18 18 CatalogueActionManager m_ActionManager;
19 19 std::unordered_map<std::shared_ptr<DBEvent>, QVector<VisualizationSelectionZoneItem *> >
20 20 m_SelectionZonesPerEvents;
21 21
22 22 QMetaObject::Connection m_Conn;
23 23
24 24 CatalogueExplorerPrivate(CatalogueExplorer *catalogueExplorer)
25 25 : m_ActionManager(catalogueExplorer)
26 26 {
27 27 }
28 28 };
29 29
30 30 CatalogueExplorer::CatalogueExplorer(QWidget *parent)
31 31 : QDialog(parent, Qt::Dialog | Qt::WindowMinMaxButtonsHint | Qt::WindowCloseButtonHint),
32 32 ui(new Ui::CatalogueExplorer),
33 33 impl{spimpl::make_unique_impl<CatalogueExplorerPrivate>(this)}
34 34 {
35 35 ui->setupUi(this);
36 36
37 37 impl->m_ActionManager.installSelectionZoneActions();
38 38
39 39 // Updates events and inspector when something is selected in the catalogue widget
40 40 connect(ui->catalogues, &CatalogueSideBarWidget::catalogueSelected, [this](auto catalogues) {
41 41 if (catalogues.count() == 1) {
42 42 ui->inspector->setCatalogue(catalogues.first());
43 43 }
44 44 else {
45 45 ui->inspector->showPage(CatalogueInspectorWidget::Page::Empty);
46 46 }
47 47
48 48 ui->events->populateWithCatalogues(catalogues);
49 49 });
50 50
51 51 connect(ui->catalogues, &CatalogueSideBarWidget::databaseSelected, [this](auto databases) {
52 52 ui->inspector->showPage(CatalogueInspectorWidget::Page::Empty);
53 53 });
54 54
55 55 connect(ui->catalogues, &CatalogueSideBarWidget::trashSelected, [this]() {
56 56 ui->inspector->showPage(CatalogueInspectorWidget::Page::Empty);
57 57 ui->events->clear();
58 58 });
59 59
60 60 connect(ui->catalogues, &CatalogueSideBarWidget::allEventsSelected, [this]() {
61 61 ui->inspector->showPage(CatalogueInspectorWidget::Page::Empty);
62 62 ui->events->populateWithAllEvents();
63 63 });
64 64
65 65 connect(ui->catalogues, &CatalogueSideBarWidget::databaseSelected, [this](auto databaseList) {
66 66 QVector<std::shared_ptr<DBCatalogue> > catalogueList;
67 67 for (auto database : databaseList) {
68 68 catalogueList.append(ui->catalogues->getCatalogues(database));
69 69 }
70 70 ui->events->populateWithCatalogues(catalogueList);
71 71 ui->inspector->showPage(CatalogueInspectorWidget::Page::Empty);
72 72 });
73 73
74 74 connect(ui->catalogues, &CatalogueSideBarWidget::selectionCleared, [this]() {
75 75 ui->inspector->showPage(CatalogueInspectorWidget::Page::Empty);
76 76 ui->events->clear();
77 77 });
78 78
79 connect(ui->catalogues, &CatalogueSideBarWidget::catalogueSaved, ui->events,
80 &CatalogueEventsWidget::refresh);
81
79 82 // Updates the inspectot when something is selected in the events
80 83 connect(ui->events, &CatalogueEventsWidget::eventsSelected, [this](auto events) {
81 84 if (events.count() == 1) {
82 85 ui->inspector->setEvent(events.first());
83 86 }
84 87 else {
85 88 ui->inspector->showPage(CatalogueInspectorWidget::Page::Empty);
86 89 }
87 90 });
88 91
89 92 connect(ui->events, &CatalogueEventsWidget::eventProductsSelected, [this](auto eventProducts) {
90 93 if (eventProducts.count() == 1) {
91 94 ui->inspector->setEventProduct(eventProducts.first().first,
92 95 eventProducts.first().second);
93 96 }
94 97 else {
95 98 ui->inspector->showPage(CatalogueInspectorWidget::Page::Empty);
96 99 }
97 100 });
98 101
99 102 connect(ui->events, &CatalogueEventsWidget::selectionCleared,
100 103 [this]() { ui->inspector->showPage(CatalogueInspectorWidget::Page::Empty); });
101 104
102 105 // Manage Selection Zones associated to events
103 106 connect(ui->events, &CatalogueEventsWidget::selectionZoneAdded,
104 107 [this](auto event, auto productId, auto zone) {
105 108 this->addSelectionZoneItem(event, productId, zone);
106 109 });
107 110
108 111 connect(ui->events, &CatalogueEventsWidget::eventsRemoved, [this](auto events) {
109 112 for (auto event : events) {
110 113 auto associatedSelectionZonesIt = impl->m_SelectionZonesPerEvents.find(event);
111 114 if (associatedSelectionZonesIt != impl->m_SelectionZonesPerEvents.cend()) {
112 115 for (auto selectionZone : associatedSelectionZonesIt->second) {
113 116 auto parentGraph = selectionZone->parentGraphWidget();
114 117 parentGraph->removeSelectionZone(selectionZone);
115 118 }
116 119
117 120 impl->m_SelectionZonesPerEvents.erase(event);
118 121 }
119 122 }
120 123 });
121 124
122 125 // Updates changes from the inspector
123 126 connect(ui->inspector, &CatalogueInspectorWidget::catalogueUpdated, [this](auto catalogue) {
124 127 sqpApp->catalogueController().updateCatalogue(catalogue);
125 128 ui->catalogues->setCatalogueChanges(catalogue, true);
126 129 });
127 130
128 131 connect(ui->inspector, &CatalogueInspectorWidget::eventUpdated, [this](auto event) {
129 132 sqpApp->catalogueController().updateEvent(event);
130 133 ui->events->setEventChanges(event, true);
131 134 });
132 135
133 136 connect(ui->inspector, &CatalogueInspectorWidget::eventProductUpdated,
134 137 [this](auto event, auto eventProduct) {
135 138 sqpApp->catalogueController().updateEventProduct(eventProduct);
136 139 ui->events->setEventChanges(event, true);
137 140 });
141
142 connect(ui->events, &CatalogueEventsWidget::eventCataloguesModified,
143 [this](const QVector<std::shared_ptr<DBCatalogue> > &catalogues) {
144 for (auto catalogue : catalogues) {
145 ui->catalogues->setCatalogueChanges(catalogue, true);
146 }
147 });
138 148 }
139 149
140 150 CatalogueExplorer::~CatalogueExplorer()
141 151 {
142 152 disconnect(impl->m_Conn);
143 153 delete ui;
144 154 }
145 155
146 156 void CatalogueExplorer::setVisualizationWidget(VisualizationWidget *visualization)
147 157 {
148 158 ui->events->setVisualizationWidget(visualization);
149 159 }
150 160
151 161 CatalogueEventsWidget &CatalogueExplorer::eventsWidget() const
152 162 {
153 163 return *ui->events;
154 164 }
155 165
156 166 CatalogueSideBarWidget &CatalogueExplorer::sideBarWidget() const
157 167 {
158 168 return *ui->catalogues;
159 169 }
160 170
161 171 void CatalogueExplorer::clearSelectionZones()
162 172 {
163 173 impl->m_SelectionZonesPerEvents.clear();
164 174 }
165 175
166 176 void CatalogueExplorer::addSelectionZoneItem(const std::shared_ptr<DBEvent> &event,
167 177 const QString &productId,
168 178 VisualizationSelectionZoneItem *selectionZone)
169 179 {
170 180 impl->m_SelectionZonesPerEvents[event] << selectionZone;
171 181 connect(selectionZone, &VisualizationSelectionZoneItem::rangeEdited,
172 182 [event, productId, this](auto range) {
173 183 auto productList = event->getEventProducts();
174 184 for (auto &product : productList) {
175 185 if (product.getProductId() == productId) {
176 186 product.setTStart(range.m_TStart);
177 187 product.setTEnd(range.m_TEnd);
178 188 }
179 189 }
180 190 event->setEventProducts(productList);
181 191 sqpApp->catalogueController().updateEvent(event);
182 192 ui->events->refreshEvent(event);
183 193 ui->events->setEventChanges(event, true);
184 194 ui->inspector->refresh();
185 195 });
186 196
187 197 impl->m_Conn = connect(selectionZone, &VisualizationSelectionZoneItem::destroyed,
188 198 [event, selectionZone, this]() {
189 199 if (!impl->m_SelectionZonesPerEvents.empty()) {
190 200 impl->m_SelectionZonesPerEvents[event].removeAll(selectionZone);
191 201 }
192 202 });
193 203 }
@@ -1,32 +1,30
1 1 #include "Catalogue/CatalogueExplorerHelper.h"
2 2
3 3 #include <QBoxLayout>
4 4 #include <QToolButton>
5 5
6 const auto VALIDATION_BUTTON_ICON_SIZE = 12;
7 6
8 7 QWidget *CatalogueExplorerHelper::buildValidationWidget(QWidget *parent, std::function<void()> save,
9 8 std::function<void()> discard)
10 9 {
11 10 auto widget = new QWidget{parent};
12 11
13 12 auto layout = new QHBoxLayout{widget};
14 layout->setContentsMargins(0, 0, 0, 0);
15 layout->setSpacing(0);
16 13
17 14 auto btnValid = new QToolButton{widget};
18 15 btnValid->setIcon(QIcon{":/icones/save"});
19 btnValid->setIconSize(QSize{VALIDATION_BUTTON_ICON_SIZE, VALIDATION_BUTTON_ICON_SIZE});
20 16 btnValid->setAutoRaise(true);
21 17 QObject::connect(btnValid, &QToolButton::clicked, save);
22 18 layout->addWidget(btnValid);
23 19
24 20 auto btnDiscard = new QToolButton{widget};
25 21 btnDiscard->setIcon(QIcon{":/icones/discard"});
26 btnDiscard->setIconSize(QSize{VALIDATION_BUTTON_ICON_SIZE, VALIDATION_BUTTON_ICON_SIZE});
27 22 btnDiscard->setAutoRaise(true);
28 23 QObject::connect(btnDiscard, &QToolButton::clicked, discard);
29 24 layout->addWidget(btnDiscard);
30 25
26 layout->setContentsMargins(0, 0, 0, 0);
27 layout->setSpacing(0);
28
31 29 return widget;
32 30 }
@@ -1,227 +1,236
1 1 #include "Catalogue/CatalogueInspectorWidget.h"
2 2 #include "ui_CatalogueInspectorWidget.h"
3 3
4 4 #include <Common/DateUtils.h>
5 5 #include <DBCatalogue.h>
6 6 #include <DBEvent.h>
7 7 #include <DBEventProduct.h>
8 8 #include <DBTag.h>
9 9
10 10 struct CatalogueInspectorWidget::CatalogueInspectorWidgetPrivate {
11 11 std::shared_ptr<DBCatalogue> m_DisplayedCatalogue = nullptr;
12 12 std::shared_ptr<DBEvent> m_DisplayedEvent = nullptr;
13 13 std::shared_ptr<DBEventProduct> m_DisplayedEventProduct = nullptr;
14 14
15 15 void connectCatalogueUpdateSignals(CatalogueInspectorWidget *inspector,
16 16 Ui::CatalogueInspectorWidget *ui);
17 17 void connectEventUpdateSignals(CatalogueInspectorWidget *inspector,
18 18 Ui::CatalogueInspectorWidget *ui);
19 19 };
20 20
21 21 CatalogueInspectorWidget::CatalogueInspectorWidget(QWidget *parent)
22 22 : QWidget(parent),
23 23 ui(new Ui::CatalogueInspectorWidget),
24 24 impl{spimpl::make_unique_impl<CatalogueInspectorWidgetPrivate>()}
25 25 {
26 26 ui->setupUi(this);
27 27 showPage(Page::Empty);
28 28
29 29 impl->connectCatalogueUpdateSignals(this, ui);
30 30 impl->connectEventUpdateSignals(this, ui);
31
32 ui->dateTimeEventTStart->setDisplayFormat(DATETIME_FORMAT);
33 ui->dateTimeEventTEnd->setDisplayFormat(DATETIME_FORMAT);
31 34 }
32 35
33 36 CatalogueInspectorWidget::~CatalogueInspectorWidget()
34 37 {
35 38 delete ui;
36 39 }
37 40
38 41 void CatalogueInspectorWidget::CatalogueInspectorWidgetPrivate::connectCatalogueUpdateSignals(
39 42 CatalogueInspectorWidget *inspector, Ui::CatalogueInspectorWidget *ui)
40 43 {
41 44 connect(ui->leCatalogueName, &QLineEdit::editingFinished, [ui, inspector, this]() {
42 45 if (ui->leCatalogueName->text() != m_DisplayedCatalogue->getName()) {
43 46 m_DisplayedCatalogue->setName(ui->leCatalogueName->text());
44 47 emit inspector->catalogueUpdated(m_DisplayedCatalogue);
45 48 }
46 49 });
47 50
48 51 connect(ui->leCatalogueAuthor, &QLineEdit::editingFinished, [ui, inspector, this]() {
49 52 if (ui->leCatalogueAuthor->text() != m_DisplayedCatalogue->getAuthor()) {
50 53 m_DisplayedCatalogue->setAuthor(ui->leCatalogueAuthor->text());
51 54 emit inspector->catalogueUpdated(m_DisplayedCatalogue);
52 55 }
53 56 });
54 57 }
55 58
56 59 void CatalogueInspectorWidget::CatalogueInspectorWidgetPrivate::connectEventUpdateSignals(
57 60 CatalogueInspectorWidget *inspector, Ui::CatalogueInspectorWidget *ui)
58 61 {
59 62 connect(ui->leEventName, &QLineEdit::editingFinished, [ui, inspector, this]() {
60 63 if (ui->leEventName->text() != m_DisplayedEvent->getName()) {
61 64 m_DisplayedEvent->setName(ui->leEventName->text());
62 65 emit inspector->eventUpdated(m_DisplayedEvent);
63 66 }
64 67 });
65 68
66 69 connect(ui->leEventTags, &QLineEdit::editingFinished, [ui, inspector, this]() {
67 70 auto tags = ui->leEventTags->text().split(QRegExp("\\s+"), QString::SkipEmptyParts);
68 71 std::list<QString> tagNames;
69 72 for (auto tag : tags) {
70 73 tagNames.push_back(tag);
71 74 }
72 75
73 76 if (m_DisplayedEvent->getTagsNames() != tagNames) {
74 77 m_DisplayedEvent->setTagsNames(tagNames);
75 78 emit inspector->eventUpdated(m_DisplayedEvent);
76 79 }
77 80 });
78 81
79 82 connect(ui->leEventProduct, &QLineEdit::editingFinished, [ui, inspector, this]() {
80 83 if (ui->leEventProduct->text() != m_DisplayedEventProduct->getProductId()) {
81 84 auto oldProductId = m_DisplayedEventProduct->getProductId();
82 85 m_DisplayedEventProduct->setProductId(ui->leEventProduct->text());
83 86
84 87 auto eventProducts = m_DisplayedEvent->getEventProducts();
85 88 for (auto &eventProduct : eventProducts) {
86 89 if (eventProduct.getProductId() == oldProductId) {
87 90 eventProduct.setProductId(m_DisplayedEventProduct->getProductId());
88 91 }
89 92 }
90 93 m_DisplayedEvent->setEventProducts(eventProducts);
91 94
92 95 emit inspector->eventUpdated(m_DisplayedEvent);
93 96 }
94 97 });
95 98
96 99 connect(ui->dateTimeEventTStart, &QDateTimeEdit::editingFinished, [ui, inspector, this]() {
97 100 auto time = DateUtils::secondsSinceEpoch(ui->dateTimeEventTStart->dateTime());
98 101 if (time != m_DisplayedEventProduct->getTStart()) {
99 102 m_DisplayedEventProduct->setTStart(time);
100 103
101 104 auto eventProducts = m_DisplayedEvent->getEventProducts();
102 105 for (auto &eventProduct : eventProducts) {
103 106 if (eventProduct.getProductId() == m_DisplayedEventProduct->getProductId()) {
104 107 eventProduct.setTStart(m_DisplayedEventProduct->getTStart());
105 108 }
106 109 }
107 110 m_DisplayedEvent->setEventProducts(eventProducts);
108 111
109 112 emit inspector->eventUpdated(m_DisplayedEvent);
110 113 }
111 114 });
112 115
113 116 connect(ui->dateTimeEventTEnd, &QDateTimeEdit::editingFinished, [ui, inspector, this]() {
114 117 auto time = DateUtils::secondsSinceEpoch(ui->dateTimeEventTEnd->dateTime());
115 118 if (time != m_DisplayedEventProduct->getTEnd()) {
116 119 m_DisplayedEventProduct->setTEnd(time);
117 120
118 121 auto eventProducts = m_DisplayedEvent->getEventProducts();
119 122 for (auto &eventProduct : eventProducts) {
120 123 if (eventProduct.getProductId() == m_DisplayedEventProduct->getProductId()) {
121 124 eventProduct.setTEnd(m_DisplayedEventProduct->getTEnd());
122 125 }
123 126 }
124 127 m_DisplayedEvent->setEventProducts(eventProducts);
125 128
126 129 emit inspector->eventUpdated(m_DisplayedEvent);
127 130 }
128 131 });
129 132 }
130 133
131 134 void CatalogueInspectorWidget::showPage(CatalogueInspectorWidget::Page page)
132 135 {
133 136 ui->stackedWidget->setCurrentIndex(static_cast<int>(page));
134 137 }
135 138
136 139 CatalogueInspectorWidget::Page CatalogueInspectorWidget::currentPage() const
137 140 {
138 141 return static_cast<Page>(ui->stackedWidget->currentIndex());
139 142 }
140 143
141 144 void CatalogueInspectorWidget::setEvent(const std::shared_ptr<DBEvent> &event)
142 145 {
143 146 impl->m_DisplayedEvent = event;
144 147
145 148 blockSignals(true);
146 149
147 150 showPage(Page::EventProperties);
148 151 ui->leEventName->setEnabled(true);
149 152 ui->leEventName->setText(event->getName());
150 153 ui->leEventProduct->setEnabled(false);
151 ui->leEventProduct->setText(
152 QString::number(event->getEventProducts().size()).append(" product(s)"));
154
155 auto eventProducts = event->getEventProducts();
156 QStringList eventProductList;
157 for (auto evtProduct : eventProducts) {
158 eventProductList << evtProduct.getProductId();
159 }
160
161 ui->leEventProduct->setText(eventProductList.join(";"));
153 162
154 163 QString tagList;
155 164 auto tags = event->getTagsNames();
156 165 for (auto tag : tags) {
157 166 tagList += tag;
158 167 tagList += ' ';
159 168 }
160 169
161 170 ui->leEventTags->setEnabled(true);
162 171 ui->leEventTags->setText(tagList);
163 172
164 173 ui->dateTimeEventTStart->setEnabled(false);
165 174 ui->dateTimeEventTEnd->setEnabled(false);
166 175
167 176 ui->dateTimeEventTStart->setDateTime(DateUtils::dateTime(event->getTStart()));
168 177 ui->dateTimeEventTEnd->setDateTime(DateUtils::dateTime(event->getTEnd()));
169 178
170 179 blockSignals(false);
171 180 }
172 181
173 182 void CatalogueInspectorWidget::setEventProduct(const std::shared_ptr<DBEvent> &event,
174 183 const std::shared_ptr<DBEventProduct> &eventProduct)
175 184 {
176 185
177 186 impl->m_DisplayedEvent = event;
178 187 impl->m_DisplayedEventProduct = eventProduct;
179 188
180 189 blockSignals(true);
181 190
182 191 showPage(Page::EventProperties);
183 192 ui->leEventName->setEnabled(false);
184 193 ui->leEventName->setText(event->getName());
185 194 ui->leEventProduct->setEnabled(false);
186 195 ui->leEventProduct->setText(eventProduct->getProductId());
187 196
188 197 ui->leEventTags->setEnabled(false);
189 198 ui->leEventTags->clear();
190 199
191 200 ui->dateTimeEventTStart->setEnabled(true);
192 201 ui->dateTimeEventTEnd->setEnabled(true);
193 202
194 203 ui->dateTimeEventTStart->setDateTime(DateUtils::dateTime(eventProduct->getTStart()));
195 204 ui->dateTimeEventTEnd->setDateTime(DateUtils::dateTime(eventProduct->getTEnd()));
196 205
197 206 blockSignals(false);
198 207 }
199 208
200 209 void CatalogueInspectorWidget::setCatalogue(const std::shared_ptr<DBCatalogue> &catalogue)
201 210 {
202 211 impl->m_DisplayedCatalogue = catalogue;
203 212
204 213 blockSignals(true);
205 214
206 215 showPage(Page::CatalogueProperties);
207 216 ui->leCatalogueName->setText(catalogue->getName());
208 217 ui->leCatalogueAuthor->setText(catalogue->getAuthor());
209 218
210 219 blockSignals(false);
211 220 }
212 221
213 222 void CatalogueInspectorWidget::refresh()
214 223 {
215 224 switch (static_cast<Page>(ui->stackedWidget->currentIndex())) {
216 225 case Page::CatalogueProperties:
217 226 setCatalogue(impl->m_DisplayedCatalogue);
218 227 break;
219 228 case Page::EventProperties: {
220 229 auto isEventShowed = ui->leEventName->isEnabled();
221 230 setEvent(impl->m_DisplayedEvent);
222 231 if (!isEventShowed && impl->m_DisplayedEvent) {
223 232 setEventProduct(impl->m_DisplayedEvent, impl->m_DisplayedEventProduct);
224 233 }
225 234 }
226 235 }
227 236 }
@@ -1,429 +1,447
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 <Common/MimeTypesDef.h>
12 12 #include <ComparaisonPredicate.h>
13 13 #include <DBCatalogue.h>
14 14
15 #include <QKeyEvent>
15 16 #include <QMenu>
16 17 #include <QMessageBox>
17 18 #include <QMimeData>
18 19
19 20 Q_LOGGING_CATEGORY(LOG_CatalogueSideBarWidget, "CatalogueSideBarWidget")
20 21
21 22
22 23 constexpr auto ALL_EVENT_ITEM_TYPE = CatalogueAbstractTreeItem::DEFAULT_TYPE + 1;
23 24 constexpr auto TRASH_ITEM_TYPE = CatalogueAbstractTreeItem::DEFAULT_TYPE + 2;
24 25 constexpr auto CATALOGUE_ITEM_TYPE = CatalogueAbstractTreeItem::DEFAULT_TYPE + 3;
25 26 constexpr auto DATABASE_ITEM_TYPE = CatalogueAbstractTreeItem::DEFAULT_TYPE + 4;
26 27
27 28
28 29 struct CatalogueSideBarWidget::CatalogueSideBarWidgetPrivate {
29 30
30 31 CatalogueTreeModel *m_TreeModel = nullptr;
31 32
32 33 void configureTreeWidget(QTreeView *treeView);
33 34 QModelIndex addDatabaseItem(const QString &name);
34 35 CatalogueAbstractTreeItem *getDatabaseItem(const QString &name);
35 36 CatalogueAbstractTreeItem *addCatalogueItem(const std::shared_ptr<DBCatalogue> &catalogue,
36 37 const QModelIndex &databaseIndex);
37 38
38 39 CatalogueTreeItem *getCatalogueItem(const std::shared_ptr<DBCatalogue> &catalogue) const;
39 40 void setHasChanges(bool value, const QModelIndex &index, CatalogueSideBarWidget *sideBarWidget);
40 41 bool hasChanges(const QModelIndex &index, QTreeView *treeView);
41 42
42 43 int selectionType(QTreeView *treeView) const
43 44 {
44 45 auto selectedItems = treeView->selectionModel()->selectedRows();
45 46 if (selectedItems.isEmpty()) {
46 47 return CatalogueAbstractTreeItem::DEFAULT_TYPE;
47 48 }
48 49 else {
49 50 auto firstIndex = selectedItems.first();
50 51 auto firstItem = m_TreeModel->item(firstIndex);
51 52 if (!firstItem) {
52 53 Q_ASSERT(false);
53 54 return CatalogueAbstractTreeItem::DEFAULT_TYPE;
54 55 }
55 56 auto selectionType = firstItem->type();
56 57
57 58 for (auto itemIndex : selectedItems) {
58 59 auto item = m_TreeModel->item(itemIndex);
59 60 if (!item || item->type() != selectionType) {
60 61 // Incoherent multi selection
61 62 selectionType = CatalogueAbstractTreeItem::DEFAULT_TYPE;
62 63 break;
63 64 }
64 65 }
65 66
66 67 return selectionType;
67 68 }
68 69 }
69 70
70 71 QVector<std::shared_ptr<DBCatalogue> > selectedCatalogues(QTreeView *treeView) const
71 72 {
72 73 QVector<std::shared_ptr<DBCatalogue> > catalogues;
73 74 auto selectedItems = treeView->selectionModel()->selectedRows();
74 75 for (auto itemIndex : selectedItems) {
75 76 auto item = m_TreeModel->item(itemIndex);
76 77 if (item && item->type() == CATALOGUE_ITEM_TYPE) {
77 78 catalogues.append(static_cast<CatalogueTreeItem *>(item)->catalogue());
78 79 }
79 80 }
80 81
81 82 return catalogues;
82 83 }
83 84
84 85 QStringList selectedRepositories(QTreeView *treeView) const
85 86 {
86 87 QStringList repositories;
87 88 auto selectedItems = treeView->selectionModel()->selectedRows();
88 89 for (auto itemIndex : selectedItems) {
89 90 auto item = m_TreeModel->item(itemIndex);
90 91 if (item && item->type() == DATABASE_ITEM_TYPE) {
91 92 repositories.append(item->text());
92 93 }
93 94 }
94 95
95 96 return repositories;
96 97 }
97 98 };
98 99
99 100 CatalogueSideBarWidget::CatalogueSideBarWidget(QWidget *parent)
100 101 : QWidget(parent),
101 102 ui(new Ui::CatalogueSideBarWidget),
102 103 impl{spimpl::make_unique_impl<CatalogueSideBarWidgetPrivate>()}
103 104 {
104 105 ui->setupUi(this);
105 106
106 107 impl->m_TreeModel = new CatalogueTreeModel(this);
107 108 ui->treeView->setModel(impl->m_TreeModel);
108 109
109 110 impl->configureTreeWidget(ui->treeView);
110 111
111 112 ui->treeView->header()->setStretchLastSection(false);
112 113 ui->treeView->header()->setSectionResizeMode(QHeaderView::ResizeToContents);
113 ui->treeView->header()->setSectionResizeMode(0, QHeaderView::Stretch);
114 ui->treeView->header()->setSectionResizeMode((int)CatalogueTreeModel::Column::Name,
115 QHeaderView::Stretch);
114 116
115 117 connect(ui->treeView, &QTreeView::clicked, this, &CatalogueSideBarWidget::emitSelection);
116 118 connect(ui->treeView->selectionModel(), &QItemSelectionModel::currentChanged, this,
117 119 &CatalogueSideBarWidget::emitSelection);
118 120
119 121
120 122 connect(ui->btnAdd, &QToolButton::clicked, [this]() {
121 123 auto catalogue = std::make_shared<DBCatalogue>();
122 124 catalogue->setName(QString("Cat"));
123 125 sqpApp->catalogueController().addCatalogue(catalogue);
124 126 auto item = this->addCatalogue(catalogue, REPOSITORY_DEFAULT);
125 127 this->setCatalogueChanges(catalogue, true);
126 128 ui->treeView->edit(impl->m_TreeModel->indexOf(item));
127 129
128 130 });
129 131
130 132
131 133 connect(impl->m_TreeModel, &CatalogueTreeModel::itemDropped,
132 134 [this](auto index, auto mimeData, auto action) {
133 135 auto item = impl->m_TreeModel->item(index);
134 136 if (item && item->type() == CATALOGUE_ITEM_TYPE) {
135 137 auto catalogue = static_cast<CatalogueTreeItem *>(item)->catalogue();
136 138 this->setCatalogueChanges(catalogue, true);
137 139 }
138 140
139 141 if (action == Qt::MoveAction) {
140 142 /// Display a save button on source catalogues
141 143 auto sourceCatalogues = sqpApp->catalogueController().cataloguesForMimeData(
142 144 mimeData->data(MIME_TYPE_SOURCE_CATALOGUE_LIST));
143 145 for (auto catalogue : sourceCatalogues) {
144 146 if (auto catalogueItem = impl->getCatalogueItem(catalogue)) {
145 147 this->setCatalogueChanges(catalogue, true);
146 148 }
147 149 }
148 150 }
149 151 });
150 152
151 153 connect(ui->btnRemove, &QToolButton::clicked, [this]() {
152 154 QVector<QPair<std::shared_ptr<DBCatalogue>, CatalogueAbstractTreeItem *> >
153 155 cataloguesToItems;
154 156 auto selectedIndexes = ui->treeView->selectionModel()->selectedRows();
155 157
156 158 for (auto index : selectedIndexes) {
157 159 auto item = impl->m_TreeModel->item(index);
158 160 if (item && item->type() == CATALOGUE_ITEM_TYPE) {
159 161 auto catalogue = static_cast<CatalogueTreeItem *>(item)->catalogue();
160 162 cataloguesToItems << qMakePair(catalogue, item);
161 163 }
162 164 }
163 165
164 166 if (!cataloguesToItems.isEmpty()) {
165 167
166 168 if (QMessageBox::warning(this, tr("Remove Catalogue(s)"),
167 169 tr("The selected catalogues(s) will be completly removed "
168 170 "from the repository!\nAre you sure you want to continue?"),
169 171 QMessageBox::Yes | QMessageBox::No, QMessageBox::No)
170 172 == QMessageBox::Yes) {
171 173
172 174 for (auto catalogueToItem : cataloguesToItems) {
173 175 sqpApp->catalogueController().removeCatalogue(catalogueToItem.first);
174 176 impl->m_TreeModel->removeChildItem(
175 177 catalogueToItem.second,
176 178 impl->m_TreeModel->indexOf(catalogueToItem.second->parent()));
177 179 }
180 emitSelection();
178 181 }
179 182 }
180 183 });
181 184
182 185 connect(impl->m_TreeModel, &CatalogueTreeModel::itemRenamed, [this](auto index) {
183 186 auto selectedIndexes = ui->treeView->selectionModel()->selectedRows();
184 187 if (selectedIndexes.contains(index)) {
185 188 this->emitSelection();
186 189 }
187 190 impl->setHasChanges(true, index, this);
188 191 });
189 192
190 193 ui->treeView->setContextMenuPolicy(Qt::CustomContextMenu);
191 194 connect(ui->treeView, &QTreeView::customContextMenuRequested, this,
192 195 &CatalogueSideBarWidget::onContextMenuRequested);
193 196 }
194 197
195 198 CatalogueSideBarWidget::~CatalogueSideBarWidget()
196 199 {
197 200 delete ui;
198 201 }
199 202
200 203 CatalogueAbstractTreeItem *
201 204 CatalogueSideBarWidget::addCatalogue(const std::shared_ptr<DBCatalogue> &catalogue,
202 205 const QString &repository)
203 206 {
204 207 auto repositoryItem = impl->getDatabaseItem(repository);
205 208 return impl->addCatalogueItem(catalogue, impl->m_TreeModel->indexOf(repositoryItem));
206 209 }
207 210
208 211 void CatalogueSideBarWidget::setCatalogueChanges(const std::shared_ptr<DBCatalogue> &catalogue,
209 212 bool hasChanges)
210 213 {
211 214 if (auto catalogueItem = impl->getCatalogueItem(catalogue)) {
212 215 auto index = impl->m_TreeModel->indexOf(catalogueItem);
213 216 impl->setHasChanges(hasChanges, index, this);
214 217 // catalogueItem->refresh();
215 218 }
216 219 }
217 220
218 221 QVector<std::shared_ptr<DBCatalogue> >
219 222 CatalogueSideBarWidget::getCatalogues(const QString &repository) const
220 223 {
221 224 QVector<std::shared_ptr<DBCatalogue> > result;
222 225 auto repositoryItem = impl->getDatabaseItem(repository);
223 226 for (auto child : repositoryItem->children()) {
224 227 if (child->type() == CATALOGUE_ITEM_TYPE) {
225 228 auto catalogueItem = static_cast<CatalogueTreeItem *>(child);
226 229 result << catalogueItem->catalogue();
227 230 }
228 231 else {
229 232 qCWarning(LOG_CatalogueSideBarWidget()) << "getCatalogues: invalid structure";
230 233 }
231 234 }
232 235
233 236 return result;
234 237 }
235 238
236 239 void CatalogueSideBarWidget::emitSelection()
237 240 {
238 241 auto selectionType = impl->selectionType(ui->treeView);
239 242
240 243 switch (selectionType) {
241 244 case CATALOGUE_ITEM_TYPE:
242 245 emit this->catalogueSelected(impl->selectedCatalogues(ui->treeView));
243 246 break;
244 247 case DATABASE_ITEM_TYPE:
245 248 emit this->databaseSelected(impl->selectedRepositories(ui->treeView));
246 249 break;
247 250 case ALL_EVENT_ITEM_TYPE:
248 251 emit this->allEventsSelected();
249 252 break;
250 253 case TRASH_ITEM_TYPE:
251 254 emit this->trashSelected();
252 255 break;
253 256 default:
254 257 emit this->selectionCleared();
255 258 break;
256 259 }
257 260 }
258 261
259 262 void CatalogueSideBarWidget::onContextMenuRequested(const QPoint &pos)
260 263 {
261 264 QMenu menu{this};
262 265
263 266 auto currentIndex = ui->treeView->currentIndex();
264 267 auto currentItem = impl->m_TreeModel->item(currentIndex);
265 268 if (!currentItem) {
266 269 return;
267 270 }
268 271
269 272 switch (currentItem->type()) {
270 273 case CATALOGUE_ITEM_TYPE:
271 274 menu.addAction("Rename", [this, currentIndex]() { ui->treeView->edit(currentIndex); });
272 275 break;
273 276 case DATABASE_ITEM_TYPE:
274 277 break;
275 278 case ALL_EVENT_ITEM_TYPE:
276 279 break;
277 280 case TRASH_ITEM_TYPE:
278 281 menu.addAction("Empty Trash", []() {
279 282 // TODO
280 283 });
281 284 break;
282 285 default:
283 286 break;
284 287 }
285 288
286 289 if (!menu.isEmpty()) {
287 290 menu.exec(ui->treeView->mapToGlobal(pos));
288 291 }
289 292 }
290 293
291 294 void CatalogueSideBarWidget::CatalogueSideBarWidgetPrivate::configureTreeWidget(QTreeView *treeView)
292 295 {
293 296 auto allEventsItem = new CatalogueTextTreeItem{QIcon{":/icones/allEvents.png"}, "All Events",
294 297 ALL_EVENT_ITEM_TYPE};
295 298 auto allEventIndex = m_TreeModel->addTopLevelItem(allEventsItem);
296 299 treeView->setCurrentIndex(allEventIndex);
297 300
298 301 auto trashItem
299 302 = new CatalogueTextTreeItem{QIcon{":/icones/trash.png"}, "Trash", TRASH_ITEM_TYPE};
300 303 m_TreeModel->addTopLevelItem(trashItem);
301 304
302 305 auto separator = new QFrame{treeView};
303 306 separator->setFrameShape(QFrame::HLine);
304 307 auto separatorItem
305 308 = new CatalogueTextTreeItem{QIcon{}, QString{}, CatalogueAbstractTreeItem::DEFAULT_TYPE};
306 309 separatorItem->setEnabled(false);
307 310 auto separatorIndex = m_TreeModel->addTopLevelItem(separatorItem);
308 311 treeView->setIndexWidget(separatorIndex, separator);
309 312
310 313 auto repositories = sqpApp->catalogueController().getRepositories();
311 314 for (auto dbname : repositories) {
312 315 auto dbIndex = addDatabaseItem(dbname);
313 316 auto catalogues = sqpApp->catalogueController().retrieveCatalogues(dbname);
314 317 for (auto catalogue : catalogues) {
315 318 addCatalogueItem(catalogue, dbIndex);
316 319 }
317 320 }
318 321
319 322 treeView->expandAll();
320 323 }
321 324
322 325 QModelIndex
323 326 CatalogueSideBarWidget::CatalogueSideBarWidgetPrivate::addDatabaseItem(const QString &name)
324 327 {
325 328 auto databaseItem
326 329 = new CatalogueTextTreeItem{QIcon{":/icones/database.png"}, {name}, DATABASE_ITEM_TYPE};
327 330 auto databaseIndex = m_TreeModel->addTopLevelItem(databaseItem);
328 331
329 332 return databaseIndex;
330 333 }
331 334
332 335 CatalogueAbstractTreeItem *
333 336 CatalogueSideBarWidget::CatalogueSideBarWidgetPrivate::getDatabaseItem(const QString &name)
334 337 {
335 338 for (auto item : m_TreeModel->topLevelItems()) {
336 339 if (item->type() == DATABASE_ITEM_TYPE && item->text() == name) {
337 340 return item;
338 341 }
339 342 }
340 343
341 344 return nullptr;
342 345 }
343 346
344 347 CatalogueAbstractTreeItem *CatalogueSideBarWidget::CatalogueSideBarWidgetPrivate::addCatalogueItem(
345 348 const std::shared_ptr<DBCatalogue> &catalogue, const QModelIndex &databaseIndex)
346 349 {
347 350 auto catalogueItem
348 351 = new CatalogueTreeItem{catalogue, QIcon{":/icones/catalogue.png"}, CATALOGUE_ITEM_TYPE};
349 352 m_TreeModel->addChildItem(catalogueItem, databaseIndex);
350 353
351 354 return catalogueItem;
352 355 }
353 356
354 357 CatalogueTreeItem *CatalogueSideBarWidget::CatalogueSideBarWidgetPrivate::getCatalogueItem(
355 358 const std::shared_ptr<DBCatalogue> &catalogue) const
356 359 {
357 360 for (auto item : m_TreeModel->topLevelItems()) {
358 361 if (item->type() == DATABASE_ITEM_TYPE) {
359 362 for (auto childItem : item->children()) {
360 363 if (childItem->type() == CATALOGUE_ITEM_TYPE) {
361 364 auto catalogueItem = static_cast<CatalogueTreeItem *>(childItem);
362 365 if (catalogueItem->catalogue()->getUniqId() == catalogue->getUniqId()) {
363 366 return catalogueItem;
364 367 }
365 368 }
366 369 else {
367 370 qCWarning(LOG_CatalogueSideBarWidget()) << "getCatalogueItem: Invalid tree "
368 371 "structure. A database item should "
369 372 "only contain catalogues.";
370 373 Q_ASSERT(false);
371 374 }
372 375 }
373 376 }
374 377 }
375 378
376 379 return nullptr;
377 380 }
378 381
379 382 void CatalogueSideBarWidget::CatalogueSideBarWidgetPrivate::setHasChanges(
380 383 bool value, const QModelIndex &index, CatalogueSideBarWidget *sideBarWidget)
381 384 {
382 385 std::shared_ptr<DBCatalogue> catalogue = nullptr;
383 386 auto item = m_TreeModel->item(index);
384 387 if (item && item->type() == CATALOGUE_ITEM_TYPE) {
385 388 catalogue = static_cast<CatalogueTreeItem *>(item)->catalogue();
386 389 }
387 390
388 391 auto validationIndex = index.sibling(index.row(), (int)CatalogueTreeModel::Column::Validation);
389 392 if (value) {
390 393 if (!hasChanges(validationIndex, sideBarWidget->ui->treeView)) {
391 394 auto widget = CatalogueExplorerHelper::buildValidationWidget(
392 395 sideBarWidget->ui->treeView,
393 396 [this, validationIndex, sideBarWidget, catalogue]() {
394 397 if (catalogue) {
395 398 sqpApp->catalogueController().saveCatalogue(catalogue);
399 emit sideBarWidget->catalogueSaved(catalogue);
396 400 }
397 401 setHasChanges(false, validationIndex, sideBarWidget);
398 402 },
399 403 [this, validationIndex, sideBarWidget, catalogue, item]() {
400 404 if (catalogue) {
401 405 bool removed;
402 406 sqpApp->catalogueController().discardCatalogue(catalogue, removed);
403 407
404 408 if (removed) {
405 409 m_TreeModel->removeChildItem(item,
406 410 m_TreeModel->indexOf(item->parent()));
407 411 }
408 412 else {
409 413 m_TreeModel->refresh(m_TreeModel->indexOf(item));
410 414 setHasChanges(false, validationIndex, sideBarWidget);
411 415 }
412 416 sideBarWidget->emitSelection();
413 417 }
414 418 });
415 419 sideBarWidget->ui->treeView->setIndexWidget(validationIndex, widget);
420 sideBarWidget->ui->treeView->header()->resizeSection(
421 (int)CatalogueTreeModel::Column::Validation, QHeaderView::ResizeToContents);
416 422 }
417 423 }
418 424 else {
419 425 // Note: the widget is destroyed
420 426 sideBarWidget->ui->treeView->setIndexWidget(validationIndex, nullptr);
421 427 }
422 428 }
423 429
424 430 bool CatalogueSideBarWidget::CatalogueSideBarWidgetPrivate::hasChanges(const QModelIndex &index,
425 431 QTreeView *treeView)
426 432 {
427 433 auto validationIndex = index.sibling(index.row(), (int)CatalogueTreeModel::Column::Validation);
428 434 return treeView->indexWidget(validationIndex) != nullptr;
429 435 }
436
437
438 void CatalogueSideBarWidget::keyPressEvent(QKeyEvent *event)
439 {
440 switch (event->key()) {
441 case Qt::Key_Delete: {
442 ui->btnRemove->click();
443 }
444 default:
445 break;
446 }
447 }
@@ -1,129 +1,126
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 79 // Check that the event is not dropped on the same catalogue
80 80 auto sourceCatalogues = sqpApp->catalogueController().cataloguesForMimeData(
81 81 data->data(MIME_TYPE_SOURCE_CATALOGUE_LIST));
82 82 for (auto catalogue : sourceCatalogues) {
83 83 if (catalogue->getUniqId() == impl->m_Catalogue->getUniqId()) {
84 84 return false;
85 85 }
86 86 }
87 87
88 88 auto events = sqpApp->catalogueController().eventsForMimeData(data->data(MIME_TYPE_EVENT_LIST));
89 89 auto canDrop = data->hasFormat(MIME_TYPE_EVENT_LIST);
90 90
91 91 for (auto event : events) {
92 92 canDrop &= (event->getRepository() == impl->m_Catalogue->getRepository());
93 93 }
94 94
95 95 return canDrop;
96 96 }
97 97
98 98 bool CatalogueTreeItem::dropMimeData(const QMimeData *data, Qt::DropAction action)
99 99 {
100 100 Q_ASSERT(canDropMimeData(data, action));
101 101 // Warning: Check that the events aren't already in the catalogue
102 102 // No need to check check for the repository: inter-repository drop is forbidden in
103 103 // canDropMimeData
104 104
105 105 auto events = sqpApp->catalogueController().eventsForMimeData(data->data(MIME_TYPE_EVENT_LIST));
106 106 auto sourceCatalogues = sqpApp->catalogueController().cataloguesForMimeData(
107 107 data->data(MIME_TYPE_SOURCE_CATALOGUE_LIST));
108 108
109 109 for (auto event : events) {
110 110
111 111 if (action == Qt::MoveAction) {
112 112 for (auto catalogue : sourceCatalogues) {
113 113 catalogue->removeEvent(event->getUniqId());
114 sqpApp->catalogueController().updateCatalogue(catalogue);
114 115 }
115 116 }
116 117
117 118 impl->m_Catalogue->addEvent(event->getUniqId());
119 sqpApp->catalogueController().updateCatalogue(impl->m_Catalogue);
118 120 }
119
120 for (auto catalogue : sourceCatalogues) {
121 sqpApp->catalogueController().updateCatalogue(catalogue);
122 }
123 sqpApp->catalogueController().updateCatalogue(impl->m_Catalogue);
124 121 }
125 122
126 123 std::shared_ptr<DBCatalogue> CatalogueTreeItem::catalogue() const
127 124 {
128 125 return impl->m_Catalogue;
129 126 }
@@ -1,207 +1,205
1 1 #include "Visualization/AxisRenderingUtils.h"
2 2
3 3 #include <Data/ScalarSeries.h>
4 4 #include <Data/SpectrogramSeries.h>
5 5 #include <Data/VectorSeries.h>
6 6
7 7 #include <Variable/Variable.h>
8 8
9 9 #include <Visualization/SqpColorScale.h>
10 10 #include <Visualization/qcustomplot.h>
11 11
12 12 Q_LOGGING_CATEGORY(LOG_AxisRenderingUtils, "AxisRenderingUtils")
13 13
14 14 namespace {
15 15
16 const auto DATETIME_FORMAT = QStringLiteral("yyyy/MM/dd hh:mm:ss:zzz");
17
18 16 /// Format for datetimes on a axis
19 17 const auto DATETIME_TICKER_FORMAT = QStringLiteral("yyyy/MM/dd \nhh:mm:ss");
20 18
21 19 const auto NUMBER_FORMAT = 'g';
22 20 const auto NUMBER_PRECISION = 9;
23 21
24 22 /// Generates the appropriate ticker for an axis, depending on whether the axis displays time or
25 23 /// non-time data
26 24 QSharedPointer<QCPAxisTicker> axisTicker(bool isTimeAxis, QCPAxis::ScaleType scaleType)
27 25 {
28 26 if (isTimeAxis) {
29 27 auto dateTicker = QSharedPointer<QCPAxisTickerDateTime>::create();
30 28 dateTicker->setDateTimeFormat(DATETIME_TICKER_FORMAT);
31 29 dateTicker->setDateTimeSpec(Qt::UTC);
32 30
33 31 return dateTicker;
34 32 }
35 33 else if (scaleType == QCPAxis::stLogarithmic) {
36 34 return QSharedPointer<QCPAxisTickerLog>::create();
37 35 }
38 36 else {
39 37 // default ticker
40 38 return QSharedPointer<QCPAxisTicker>::create();
41 39 }
42 40 }
43 41
44 42 /**
45 43 * Sets properties of the axis passed as parameter
46 44 * @param axis the axis to set
47 45 * @param unit the unit to set for the axis
48 46 * @param scaleType the scale type to set for the axis
49 47 */
50 48 void setAxisProperties(QCPAxis &axis, const Unit &unit,
51 49 QCPAxis::ScaleType scaleType = QCPAxis::stLinear)
52 50 {
53 51 // label (unit name)
54 52 axis.setLabel(unit.m_Name);
55 53
56 54 // scale type
57 55 axis.setScaleType(scaleType);
58 56 if (scaleType == QCPAxis::stLogarithmic) {
59 57 // Scientific notation
60 58 axis.setNumberPrecision(0);
61 59 axis.setNumberFormat("eb");
62 60 }
63 61
64 62 // ticker (depending on the type of unit)
65 63 axis.setTicker(axisTicker(unit.m_TimeUnit, scaleType));
66 64 }
67 65
68 66 /**
69 67 * Delegate used to set axes properties
70 68 */
71 69 template <typename T, typename Enabled = void>
72 70 struct AxisSetter {
73 71 static void setProperties(QCustomPlot &, SqpColorScale &)
74 72 {
75 73 // Default implementation does nothing
76 74 qCCritical(LOG_AxisRenderingUtils()) << "Can't set axis properties: unmanaged type of data";
77 75 }
78 76
79 77 static void setUnits(T &, QCustomPlot &, SqpColorScale &)
80 78 {
81 79 // Default implementation does nothing
82 80 qCCritical(LOG_AxisRenderingUtils()) << "Can't set axis units: unmanaged type of data";
83 81 }
84 82 };
85 83
86 84 /**
87 85 * Specialization of AxisSetter for scalars and vectors
88 86 * @sa ScalarSeries
89 87 * @sa VectorSeries
90 88 */
91 89 template <typename T>
92 90 struct AxisSetter<T, typename std::enable_if_t<std::is_base_of<ScalarSeries, T>::value
93 91 or std::is_base_of<VectorSeries, T>::value> > {
94 92 static void setProperties(QCustomPlot &, SqpColorScale &)
95 93 {
96 94 // Nothing to do
97 95 }
98 96
99 97 static void setUnits(T &dataSeries, QCustomPlot &plot, SqpColorScale &)
100 98 {
101 99 dataSeries.lockRead();
102 100 auto xAxisUnit = dataSeries.xAxisUnit();
103 101 auto valuesUnit = dataSeries.valuesUnit();
104 102 dataSeries.unlock();
105 103
106 104 setAxisProperties(*plot.xAxis, xAxisUnit);
107 105 setAxisProperties(*plot.yAxis, valuesUnit);
108 106 }
109 107 };
110 108
111 109 /**
112 110 * Specialization of AxisSetter for spectrograms
113 111 * @sa SpectrogramSeries
114 112 */
115 113 template <typename T>
116 114 struct AxisSetter<T, typename std::enable_if_t<std::is_base_of<SpectrogramSeries, T>::value> > {
117 115 static void setProperties(QCustomPlot &plot, SqpColorScale &colorScale)
118 116 {
119 117 // Displays color scale in plot
120 118 plot.plotLayout()->insertRow(0);
121 119 plot.plotLayout()->addElement(0, 0, colorScale.m_Scale);
122 120 colorScale.m_Scale->setType(QCPAxis::atTop);
123 121 colorScale.m_Scale->setMinimumMargins(QMargins{0, 0, 0, 0});
124 122
125 123 // Aligns color scale with axes
126 124 auto marginGroups = plot.axisRect()->marginGroups();
127 125 for (auto it = marginGroups.begin(), end = marginGroups.end(); it != end; ++it) {
128 126 colorScale.m_Scale->setMarginGroup(it.key(), it.value());
129 127 }
130 128
131 129 // Set color scale properties
132 130 colorScale.m_AutomaticThreshold = true;
133 131 }
134 132
135 133 static void setUnits(T &dataSeries, QCustomPlot &plot, SqpColorScale &colorScale)
136 134 {
137 135 dataSeries.lockRead();
138 136 auto xAxisUnit = dataSeries.xAxisUnit();
139 137 auto yAxisUnit = dataSeries.yAxisUnit();
140 138 auto valuesUnit = dataSeries.valuesUnit();
141 139 dataSeries.unlock();
142 140
143 141 setAxisProperties(*plot.xAxis, xAxisUnit);
144 142 setAxisProperties(*plot.yAxis, yAxisUnit, QCPAxis::stLogarithmic);
145 143 setAxisProperties(*colorScale.m_Scale->axis(), valuesUnit, QCPAxis::stLogarithmic);
146 144 }
147 145 };
148 146
149 147 /**
150 148 * Default implementation of IAxisHelper, which takes data series to set axes properties
151 149 * @tparam T the data series' type
152 150 */
153 151 template <typename T>
154 152 struct AxisHelper : public IAxisHelper {
155 153 explicit AxisHelper(std::shared_ptr<T> dataSeries) : m_DataSeries{dataSeries} {}
156 154
157 155 void setProperties(QCustomPlot &plot, SqpColorScale &colorScale) override
158 156 {
159 157 AxisSetter<T>::setProperties(plot, colorScale);
160 158 }
161 159
162 160 void setUnits(QCustomPlot &plot, SqpColorScale &colorScale) override
163 161 {
164 162 if (m_DataSeries) {
165 163 AxisSetter<T>::setUnits(*m_DataSeries, plot, colorScale);
166 164 }
167 165 else {
168 166 qCCritical(LOG_AxisRenderingUtils()) << "Can't set units: inconsistency between the "
169 167 "type of data series and the type supposed";
170 168 }
171 169 }
172 170
173 171 std::shared_ptr<T> m_DataSeries;
174 172 };
175 173
176 174 } // namespace
177 175
178 176 QString formatValue(double value, const QCPAxis &axis)
179 177 {
180 178 // If the axis is a time axis, formats the value as a date
181 179 if (auto axisTicker = qSharedPointerDynamicCast<QCPAxisTickerDateTime>(axis.ticker())) {
182 180 return DateUtils::dateTime(value, axisTicker->dateTimeSpec()).toString(DATETIME_FORMAT);
183 181 }
184 182 else {
185 183 return QString::number(value, NUMBER_FORMAT, NUMBER_PRECISION);
186 184 }
187 185 }
188 186
189 187 std::unique_ptr<IAxisHelper> IAxisHelperFactory::create(const Variable &variable) noexcept
190 188 {
191 189 switch (variable.type()) {
192 190 case DataSeriesType::SCALAR:
193 191 return std::make_unique<AxisHelper<ScalarSeries> >(
194 192 std::dynamic_pointer_cast<ScalarSeries>(variable.dataSeries()));
195 193 case DataSeriesType::SPECTROGRAM:
196 194 return std::make_unique<AxisHelper<SpectrogramSeries> >(
197 195 std::dynamic_pointer_cast<SpectrogramSeries>(variable.dataSeries()));
198 196 case DataSeriesType::VECTOR:
199 197 return std::make_unique<AxisHelper<VectorSeries> >(
200 198 std::dynamic_pointer_cast<VectorSeries>(variable.dataSeries()));
201 199 default:
202 200 // Creates default helper
203 201 break;
204 202 }
205 203
206 204 return std::make_unique<AxisHelper<IDataSeries> >(nullptr);
207 205 }
@@ -1,69 +1,69
1 1 #include "Visualization/VisualizationMultiZoneSelectionDialog.h"
2 2 #include "ui_VisualizationMultiZoneSelectionDialog.h"
3 3
4 4 #include "Common/DateUtils.h"
5 5 #include "Visualization/VisualizationSelectionZoneItem.h"
6 6
7 const auto DATETIME_FORMAT = QStringLiteral("yyyy/MM/dd hh:mm:ss");
7 const auto DATETIME_FORMAT_S = QStringLiteral("yyyy/MM/dd hh:mm:ss");
8 8
9 9 struct VisualizationMultiZoneSelectionDialog::VisualizationMultiZoneSelectionDialogPrivate {
10 10 QVector<VisualizationSelectionZoneItem *> m_Zones;
11 11 };
12 12
13 13 VisualizationMultiZoneSelectionDialog::VisualizationMultiZoneSelectionDialog(QWidget *parent)
14 14 : QDialog(parent, Qt::Tool),
15 15 ui(new Ui::VisualizationMultiZoneSelectionDialog),
16 16 impl{spimpl::make_unique_impl<VisualizationMultiZoneSelectionDialogPrivate>()}
17 17 {
18 18 ui->setupUi(this);
19 19
20 20 connect(ui->buttonBox, &QDialogButtonBox::accepted, this,
21 21 &VisualizationMultiZoneSelectionDialog::accept);
22 22 connect(ui->buttonBox, &QDialogButtonBox::rejected, this,
23 23 &VisualizationMultiZoneSelectionDialog::reject);
24 24 }
25 25
26 26 VisualizationMultiZoneSelectionDialog::~VisualizationMultiZoneSelectionDialog()
27 27 {
28 28 delete ui;
29 29 }
30 30
31 31 void VisualizationMultiZoneSelectionDialog::setZones(
32 32 const QVector<VisualizationSelectionZoneItem *> &zones)
33 33 {
34 34 impl->m_Zones = zones;
35 35
36 36 // Sorts the zones to display them in temporal order
37 37 std::sort(impl->m_Zones.begin(), impl->m_Zones.end(), [](auto zone1, auto zone2) {
38 38 return zone1->range().m_TStart < zone2->range().m_TStart;
39 39 });
40 40
41 41 // Adds the zones in the listwidget
42 42 for (auto zone : impl->m_Zones) {
43 43 auto name = zone->name();
44 44 if (!name.isEmpty()) {
45 45 name += tr(": ");
46 46 }
47 47
48 48 auto range = zone->range();
49 name += DateUtils::dateTime(range.m_TStart).toString(DATETIME_FORMAT);
49 name += DateUtils::dateTime(range.m_TStart).toString(DATETIME_FORMAT_S);
50 50 name += " - ";
51 name += DateUtils::dateTime(range.m_TEnd).toString(DATETIME_FORMAT);
51 name += DateUtils::dateTime(range.m_TEnd).toString(DATETIME_FORMAT_S);
52 52
53 53 auto item = new QListWidgetItem(name, ui->listWidget);
54 54 item->setSelected(zone->selected());
55 55 }
56 56 }
57 57
58 58 QMap<VisualizationSelectionZoneItem *, bool>
59 59 VisualizationMultiZoneSelectionDialog::selectedZones() const
60 60 {
61 61 QMap<VisualizationSelectionZoneItem *, bool> selectedZones;
62 62
63 63 for (auto i = 0; i < ui->listWidget->count(); ++i) {
64 64 auto item = ui->listWidget->item(i);
65 65 selectedZones[impl->m_Zones[i]] = item->isSelected();
66 66 }
67 67
68 68 return selectedZones;
69 69 }
@@ -1,658 +1,658
1 1 #include "Visualization/VisualizationZoneWidget.h"
2 2
3 3 #include "Visualization/IVisualizationWidgetVisitor.h"
4 4 #include "Visualization/QCustomPlotSynchronizer.h"
5 5 #include "Visualization/VisualizationGraphWidget.h"
6 6 #include "Visualization/VisualizationWidget.h"
7 7 #include "ui_VisualizationZoneWidget.h"
8 8
9 9 #include "Common/MimeTypesDef.h"
10 10 #include "Common/VisualizationDef.h"
11 11
12 12 #include <Data/SqpRange.h>
13 13 #include <DataSource/DataSourceController.h>
14 14 #include <Time/TimeController.h>
15 15 #include <Variable/Variable.h>
16 16 #include <Variable/VariableController.h>
17 17
18 18 #include <Visualization/operations/FindVariableOperation.h>
19 19
20 20 #include <DragAndDrop/DragDropGuiController.h>
21 21 #include <QUuid>
22 22 #include <SqpApplication.h>
23 23 #include <cmath>
24 24
25 25 #include <QLayout>
26 26
27 27 Q_LOGGING_CATEGORY(LOG_VisualizationZoneWidget, "VisualizationZoneWidget")
28 28
29 29 namespace {
30 30
31 31 /**
32 32 * Applies a function to all graphs of the zone represented by its layout
33 33 * @param layout the layout that contains graphs
34 34 * @param fun the function to apply to each graph
35 35 */
36 36 template <typename Fun>
37 37 void processGraphs(QLayout &layout, Fun fun)
38 38 {
39 39 for (auto i = 0; i < layout.count(); ++i) {
40 40 if (auto item = layout.itemAt(i)) {
41 41 if (auto visualizationGraphWidget
42 42 = qobject_cast<VisualizationGraphWidget *>(item->widget())) {
43 43 fun(*visualizationGraphWidget);
44 44 }
45 45 }
46 46 }
47 47 }
48 48
49 49 /// Generates a default name for a new graph, according to the number of graphs already displayed in
50 50 /// the zone
51 51 QString defaultGraphName(QLayout &layout)
52 52 {
53 53 QSet<QString> existingNames;
54 54 processGraphs(
55 55 layout, [&existingNames](auto &graphWidget) { existingNames.insert(graphWidget.name()); });
56 56
57 57 int zoneNum = 1;
58 58 QString name;
59 59 do {
60 60 name = QObject::tr("Graph ").append(QString::number(zoneNum));
61 61 ++zoneNum;
62 62 } while (existingNames.contains(name));
63 63
64 64 return name;
65 65 }
66 66
67 67 } // namespace
68 68
69 69 struct VisualizationZoneWidget::VisualizationZoneWidgetPrivate {
70 70
71 71 explicit VisualizationZoneWidgetPrivate()
72 72 : m_SynchronisationGroupId{QUuid::createUuid()},
73 73 m_Synchronizer{std::make_unique<QCustomPlotSynchronizer>()}
74 74 {
75 75 }
76 76 QUuid m_SynchronisationGroupId;
77 77 std::unique_ptr<IGraphSynchronizer> m_Synchronizer;
78 78
79 79 void dropGraph(int index, VisualizationZoneWidget *zoneWidget);
80 80 void dropVariables(const QList<std::shared_ptr<Variable> > &variables, int index,
81 81 VisualizationZoneWidget *zoneWidget);
82 82 void dropProducts(const QVariantList &productsData, int index,
83 83 VisualizationZoneWidget *zoneWidget);
84 84 };
85 85
86 86 VisualizationZoneWidget::VisualizationZoneWidget(const QString &name, QWidget *parent)
87 87 : VisualizationDragWidget{parent},
88 88 ui{new Ui::VisualizationZoneWidget},
89 89 impl{spimpl::make_unique_impl<VisualizationZoneWidgetPrivate>()}
90 90 {
91 91 ui->setupUi(this);
92 92
93 93 ui->zoneNameLabel->setText(name);
94 94
95 95 ui->dragDropContainer->setPlaceHolderType(DragDropGuiController::PlaceHolderType::Graph);
96 96 ui->dragDropContainer->setMimeType(MIME_TYPE_GRAPH,
97 97 VisualizationDragDropContainer::DropBehavior::Inserted);
98 98 ui->dragDropContainer->setMimeType(
99 99 MIME_TYPE_VARIABLE_LIST, VisualizationDragDropContainer::DropBehavior::InsertedAndMerged);
100 100 ui->dragDropContainer->setMimeType(
101 101 MIME_TYPE_PRODUCT_LIST, VisualizationDragDropContainer::DropBehavior::InsertedAndMerged);
102 102 ui->dragDropContainer->setMimeType(MIME_TYPE_TIME_RANGE,
103 103 VisualizationDragDropContainer::DropBehavior::Merged);
104 104 ui->dragDropContainer->setMimeType(MIME_TYPE_ZONE,
105 105 VisualizationDragDropContainer::DropBehavior::Forbidden);
106 106 ui->dragDropContainer->setMimeType(MIME_TYPE_SELECTION_ZONE,
107 107 VisualizationDragDropContainer::DropBehavior::Forbidden);
108 108 ui->dragDropContainer->setAcceptMimeDataFunction([this](auto mimeData) {
109 109 return sqpApp->dragDropGuiController().checkMimeDataForVisualization(mimeData,
110 110 ui->dragDropContainer);
111 111 });
112 112
113 113 auto acceptDragWidgetFun = [](auto dragWidget, auto mimeData) {
114 114 if (!mimeData) {
115 115 return false;
116 116 }
117 117
118 118 if (mimeData->hasFormat(MIME_TYPE_VARIABLE_LIST)) {
119 119 auto variables = sqpApp->variableController().variablesForMimeData(
120 120 mimeData->data(MIME_TYPE_VARIABLE_LIST));
121 121
122 122 if (variables.count() != 1) {
123 123 return false;
124 124 }
125 125 auto variable = variables.first();
126 126
127 127 if (auto graphWidget = dynamic_cast<const VisualizationGraphWidget *>(dragWidget)) {
128 128 return graphWidget->canDrop(*variable);
129 129 }
130 130 }
131 131
132 132 return true;
133 133 };
134 134 ui->dragDropContainer->setAcceptDragWidgetFunction(acceptDragWidgetFun);
135 135
136 136 connect(ui->dragDropContainer, &VisualizationDragDropContainer::dropOccuredInContainer, this,
137 137 &VisualizationZoneWidget::dropMimeData);
138 138 connect(ui->dragDropContainer, &VisualizationDragDropContainer::dropOccuredOnWidget, this,
139 139 &VisualizationZoneWidget::dropMimeDataOnGraph);
140 140
141 141 // 'Close' options : widget is deleted when closed
142 142 setAttribute(Qt::WA_DeleteOnClose);
143 143 connect(ui->closeButton, &QToolButton::clicked, this, &VisualizationZoneWidget::close);
144 144 ui->closeButton->setIcon(sqpApp->style()->standardIcon(QStyle::SP_TitleBarCloseButton));
145 145
146 146 // Synchronisation id
147 147 QMetaObject::invokeMethod(&sqpApp->variableController(), "onAddSynchronizationGroupId",
148 148 Qt::QueuedConnection, Q_ARG(QUuid, impl->m_SynchronisationGroupId));
149 149 }
150 150
151 151 VisualizationZoneWidget::~VisualizationZoneWidget()
152 152 {
153 153 delete ui;
154 154 }
155 155
156 156 void VisualizationZoneWidget::setZoneRange(const SqpRange &range)
157 157 {
158 158 if (auto graph = firstGraph()) {
159 159 graph->setGraphRange(range);
160 160 }
161 161 else {
162 162 qCWarning(LOG_VisualizationZoneWidget())
163 163 << tr("setZoneRange:Cannot set the range of an empty zone.");
164 164 }
165 165 }
166 166
167 167 void VisualizationZoneWidget::addGraph(VisualizationGraphWidget *graphWidget)
168 168 {
169 169 // Synchronize new graph with others in the zone
170 170 impl->m_Synchronizer->addGraph(*graphWidget);
171 171
172 172 ui->dragDropContainer->addDragWidget(graphWidget);
173 173 }
174 174
175 175 void VisualizationZoneWidget::insertGraph(int index, VisualizationGraphWidget *graphWidget)
176 176 {
177 177 // Synchronize new graph with others in the zone
178 178 impl->m_Synchronizer->addGraph(*graphWidget);
179 179
180 180 ui->dragDropContainer->insertDragWidget(index, graphWidget);
181 181 }
182 182
183 183 VisualizationGraphWidget *VisualizationZoneWidget::createGraph(std::shared_ptr<Variable> variable)
184 184 {
185 185 return createGraph(variable, -1);
186 186 }
187 187
188 188 VisualizationGraphWidget *VisualizationZoneWidget::createGraph(std::shared_ptr<Variable> variable,
189 189 int index)
190 190 {
191 191 auto graphWidget
192 192 = new VisualizationGraphWidget{defaultGraphName(*ui->dragDropContainer->layout()), this};
193 193
194 194
195 195 // Set graph properties
196 196 graphWidget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::MinimumExpanding);
197 197 graphWidget->setMinimumHeight(GRAPH_MINIMUM_HEIGHT);
198 198
199 199
200 200 // Lambda to synchronize zone widget
201 201 auto synchronizeZoneWidget = [this, graphWidget](const SqpRange &graphRange,
202 202 const SqpRange &oldGraphRange) {
203 203
204 204 auto zoomType = VariableController::getZoomType(graphRange, oldGraphRange);
205 205 auto frameLayout = ui->dragDropContainer->layout();
206 206 for (auto i = 0; i < frameLayout->count(); ++i) {
207 207 auto graphChild
208 208 = dynamic_cast<VisualizationGraphWidget *>(frameLayout->itemAt(i)->widget());
209 209 if (graphChild && (graphChild != graphWidget)) {
210 210
211 211 auto graphChildRange = graphChild->graphRange();
212 212 switch (zoomType) {
213 213 case AcquisitionZoomType::ZoomIn: {
214 214 auto deltaLeft = graphRange.m_TStart - oldGraphRange.m_TStart;
215 215 auto deltaRight = oldGraphRange.m_TEnd - graphRange.m_TEnd;
216 216 graphChildRange.m_TStart += deltaLeft;
217 217 graphChildRange.m_TEnd -= deltaRight;
218 218 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: ZoomIn");
219 219 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: deltaLeft")
220 220 << deltaLeft;
221 221 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: deltaRight")
222 222 << deltaRight;
223 223 qCDebug(LOG_VisualizationZoneWidget())
224 224 << tr("TORM: dt") << graphRange.m_TEnd - graphRange.m_TStart;
225 225
226 226 break;
227 227 }
228 228
229 229 case AcquisitionZoomType::ZoomOut: {
230 230 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: ZoomOut");
231 231 auto deltaLeft = oldGraphRange.m_TStart - graphRange.m_TStart;
232 232 auto deltaRight = graphRange.m_TEnd - oldGraphRange.m_TEnd;
233 233 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: deltaLeft")
234 234 << deltaLeft;
235 235 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: deltaRight")
236 236 << deltaRight;
237 237 qCDebug(LOG_VisualizationZoneWidget())
238 238 << tr("TORM: dt") << graphRange.m_TEnd - graphRange.m_TStart;
239 239 graphChildRange.m_TStart -= deltaLeft;
240 240 graphChildRange.m_TEnd += deltaRight;
241 241 break;
242 242 }
243 243 case AcquisitionZoomType::PanRight: {
244 244 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: PanRight");
245 245 auto deltaLeft = graphRange.m_TStart - oldGraphRange.m_TStart;
246 246 auto deltaRight = graphRange.m_TEnd - oldGraphRange.m_TEnd;
247 247 graphChildRange.m_TStart += deltaLeft;
248 248 graphChildRange.m_TEnd += deltaRight;
249 249 qCDebug(LOG_VisualizationZoneWidget())
250 250 << tr("TORM: dt") << graphRange.m_TEnd - graphRange.m_TStart;
251 251 break;
252 252 }
253 253 case AcquisitionZoomType::PanLeft: {
254 254 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: PanLeft");
255 255 auto deltaLeft = oldGraphRange.m_TStart - graphRange.m_TStart;
256 256 auto deltaRight = oldGraphRange.m_TEnd - graphRange.m_TEnd;
257 257 graphChildRange.m_TStart -= deltaLeft;
258 258 graphChildRange.m_TEnd -= deltaRight;
259 259 break;
260 260 }
261 261 case AcquisitionZoomType::Unknown: {
262 262 qCDebug(LOG_VisualizationZoneWidget())
263 263 << tr("Impossible to synchronize: zoom type unknown");
264 264 break;
265 265 }
266 266 default:
267 267 qCCritical(LOG_VisualizationZoneWidget())
268 268 << tr("Impossible to synchronize: zoom type not take into account");
269 269 // No action
270 270 break;
271 271 }
272 272 graphChild->setFlags(GraphFlag::DisableAll);
273 qCDebug(LOG_VisualizationZoneWidget())
274 << tr("TORM: Range before: ") << graphChild->graphRange();
275 qCDebug(LOG_VisualizationZoneWidget())
276 << tr("TORM: Range after : ") << graphChildRange;
273 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: Range before: ")
274 << graphChild->graphRange();
275 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: Range after : ")
276 << graphChildRange;
277 277 qCDebug(LOG_VisualizationZoneWidget())
278 278 << tr("TORM: child dt") << graphChildRange.m_TEnd - graphChildRange.m_TStart;
279 279 graphChild->setGraphRange(graphChildRange);
280 280 graphChild->setFlags(GraphFlag::EnableAll);
281 281 }
282 282 }
283 283 };
284 284
285 285 // connection for synchronization
286 286 connect(graphWidget, &VisualizationGraphWidget::synchronize, synchronizeZoneWidget);
287 287 connect(graphWidget, &VisualizationGraphWidget::variableAdded, this,
288 288 &VisualizationZoneWidget::onVariableAdded);
289 289 connect(graphWidget, &VisualizationGraphWidget::variableAboutToBeRemoved, this,
290 290 &VisualizationZoneWidget::onVariableAboutToBeRemoved);
291 291
292 292 auto range = SqpRange{};
293 293 if (auto firstGraph = this->firstGraph()) {
294 294 // Case of a new graph in a existant zone
295 295 range = firstGraph->graphRange();
296 296 }
297 297 else {
298 298 // Case of a new graph as the first of the zone
299 299 range = variable->range();
300 300 }
301 301
302 302 this->insertGraph(index, graphWidget);
303 303
304 304 graphWidget->addVariable(variable, range);
305 305 graphWidget->setYRange(variable);
306 306
307 307 return graphWidget;
308 308 }
309 309
310 310 VisualizationGraphWidget *
311 311 VisualizationZoneWidget::createGraph(const QList<std::shared_ptr<Variable> > variables, int index)
312 312 {
313 313 if (variables.isEmpty()) {
314 314 return nullptr;
315 315 }
316 316
317 317 auto graphWidget = createGraph(variables.first(), index);
318 318 for (auto variableIt = variables.cbegin() + 1; variableIt != variables.cend(); ++variableIt) {
319 319 graphWidget->addVariable(*variableIt, graphWidget->graphRange());
320 320 }
321 321
322 322 return graphWidget;
323 323 }
324 324
325 325 VisualizationGraphWidget *VisualizationZoneWidget::firstGraph() const
326 326 {
327 327 VisualizationGraphWidget *firstGraph = nullptr;
328 328 auto layout = ui->dragDropContainer->layout();
329 329 if (layout->count() > 0) {
330 330 if (auto visualizationGraphWidget
331 331 = qobject_cast<VisualizationGraphWidget *>(layout->itemAt(0)->widget())) {
332 332 firstGraph = visualizationGraphWidget;
333 333 }
334 334 }
335 335
336 336 return firstGraph;
337 337 }
338 338
339 339 void VisualizationZoneWidget::closeAllGraphs()
340 340 {
341 341 processGraphs(*ui->dragDropContainer->layout(),
342 342 [](VisualizationGraphWidget &graphWidget) { graphWidget.close(); });
343 343 }
344 344
345 345 void VisualizationZoneWidget::accept(IVisualizationWidgetVisitor *visitor)
346 346 {
347 347 if (visitor) {
348 348 visitor->visitEnter(this);
349 349
350 350 // Apply visitor to graph children: widgets different from graphs are not visited (no
351 351 // action)
352 352 processGraphs(
353 353 *ui->dragDropContainer->layout(),
354 354 [visitor](VisualizationGraphWidget &graphWidget) { graphWidget.accept(visitor); });
355 355
356 356 visitor->visitLeave(this);
357 357 }
358 358 else {
359 359 qCCritical(LOG_VisualizationZoneWidget()) << tr("Can't visit widget : the visitor is null");
360 360 }
361 361 }
362 362
363 363 bool VisualizationZoneWidget::canDrop(const Variable &variable) const
364 364 {
365 365 // A tab can always accomodate a variable
366 366 Q_UNUSED(variable);
367 367 return true;
368 368 }
369 369
370 370 bool VisualizationZoneWidget::contains(const Variable &variable) const
371 371 {
372 372 Q_UNUSED(variable);
373 373 return false;
374 374 }
375 375
376 376 QString VisualizationZoneWidget::name() const
377 377 {
378 378 return ui->zoneNameLabel->text();
379 379 }
380 380
381 381 QMimeData *VisualizationZoneWidget::mimeData(const QPoint &position) const
382 382 {
383 383 Q_UNUSED(position);
384 384
385 385 auto mimeData = new QMimeData;
386 386 mimeData->setData(MIME_TYPE_ZONE, QByteArray{});
387 387
388 388 if (auto firstGraph = this->firstGraph()) {
389 389 auto timeRangeData = TimeController::mimeDataForTimeRange(firstGraph->graphRange());
390 390 mimeData->setData(MIME_TYPE_TIME_RANGE, timeRangeData);
391 391 }
392 392
393 393 return mimeData;
394 394 }
395 395
396 396 bool VisualizationZoneWidget::isDragAllowed() const
397 397 {
398 398 return true;
399 399 }
400 400
401 401 void VisualizationZoneWidget::notifyMouseMoveInGraph(const QPointF &graphPosition,
402 402 const QPointF &plotPosition,
403 403 VisualizationGraphWidget *graphWidget)
404 404 {
405 405 processGraphs(*ui->dragDropContainer->layout(), [&graphPosition, &plotPosition, &graphWidget](
406 406 VisualizationGraphWidget &processedGraph) {
407 407
408 408 switch (sqpApp->plotsCursorMode()) {
409 409 case SqpApplication::PlotsCursorMode::Vertical:
410 410 processedGraph.removeHorizontalCursor();
411 411 processedGraph.addVerticalCursorAtViewportPosition(graphPosition.x());
412 412 break;
413 413 case SqpApplication::PlotsCursorMode::Temporal:
414 414 processedGraph.addVerticalCursor(plotPosition.x());
415 415 processedGraph.removeHorizontalCursor();
416 416 break;
417 417 case SqpApplication::PlotsCursorMode::Horizontal:
418 418 processedGraph.removeVerticalCursor();
419 419 if (&processedGraph == graphWidget) {
420 420 processedGraph.addHorizontalCursorAtViewportPosition(graphPosition.y());
421 421 }
422 422 else {
423 423 processedGraph.removeHorizontalCursor();
424 424 }
425 425 break;
426 426 case SqpApplication::PlotsCursorMode::Cross:
427 427 if (&processedGraph == graphWidget) {
428 428 processedGraph.addVerticalCursorAtViewportPosition(graphPosition.x());
429 429 processedGraph.addHorizontalCursorAtViewportPosition(graphPosition.y());
430 430 }
431 431 else {
432 432 processedGraph.removeHorizontalCursor();
433 433 processedGraph.removeVerticalCursor();
434 434 }
435 435 break;
436 436 case SqpApplication::PlotsCursorMode::NoCursor:
437 437 processedGraph.removeHorizontalCursor();
438 438 processedGraph.removeVerticalCursor();
439 439 break;
440 440 }
441 441
442 442
443 443 });
444 444 }
445 445
446 446 void VisualizationZoneWidget::notifyMouseLeaveGraph(VisualizationGraphWidget *graphWidget)
447 447 {
448 448 processGraphs(*ui->dragDropContainer->layout(), [](VisualizationGraphWidget &processedGraph) {
449 449 processedGraph.removeHorizontalCursor();
450 450 processedGraph.removeVerticalCursor();
451 451 });
452 452 }
453 453
454 454 void VisualizationZoneWidget::closeEvent(QCloseEvent *event)
455 455 {
456 456 // Closes graphs in the zone
457 457 processGraphs(*ui->dragDropContainer->layout(),
458 458 [](VisualizationGraphWidget &graphWidget) { graphWidget.close(); });
459 459
460 460 // Delete synchronization group from variable controller
461 461 QMetaObject::invokeMethod(&sqpApp->variableController(), "onRemoveSynchronizationGroupId",
462 462 Qt::QueuedConnection, Q_ARG(QUuid, impl->m_SynchronisationGroupId));
463 463
464 464 QWidget::closeEvent(event);
465 465 }
466 466
467 467 void VisualizationZoneWidget::onVariableAdded(std::shared_ptr<Variable> variable)
468 468 {
469 469 QMetaObject::invokeMethod(&sqpApp->variableController(), "onAddSynchronized",
470 470 Qt::QueuedConnection, Q_ARG(std::shared_ptr<Variable>, variable),
471 471 Q_ARG(QUuid, impl->m_SynchronisationGroupId));
472 472 }
473 473
474 474 void VisualizationZoneWidget::onVariableAboutToBeRemoved(std::shared_ptr<Variable> variable)
475 475 {
476 476 QMetaObject::invokeMethod(&sqpApp->variableController(), "desynchronize", Qt::QueuedConnection,
477 477 Q_ARG(std::shared_ptr<Variable>, variable),
478 478 Q_ARG(QUuid, impl->m_SynchronisationGroupId));
479 479 }
480 480
481 481 void VisualizationZoneWidget::dropMimeData(int index, const QMimeData *mimeData)
482 482 {
483 483 if (mimeData->hasFormat(MIME_TYPE_GRAPH)) {
484 484 impl->dropGraph(index, this);
485 485 }
486 486 else if (mimeData->hasFormat(MIME_TYPE_VARIABLE_LIST)) {
487 487 auto variables = sqpApp->variableController().variablesForMimeData(
488 488 mimeData->data(MIME_TYPE_VARIABLE_LIST));
489 489 impl->dropVariables(variables, index, this);
490 490 }
491 491 else if (mimeData->hasFormat(MIME_TYPE_PRODUCT_LIST)) {
492 492 auto products = sqpApp->dataSourceController().productsDataForMimeData(
493 493 mimeData->data(MIME_TYPE_PRODUCT_LIST));
494 494 impl->dropProducts(products, index, this);
495 495 }
496 496 else {
497 497 qCWarning(LOG_VisualizationZoneWidget())
498 498 << tr("VisualizationZoneWidget::dropMimeData, unknown MIME data received.");
499 499 }
500 500 }
501 501
502 502 void VisualizationZoneWidget::dropMimeDataOnGraph(VisualizationDragWidget *dragWidget,
503 503 const QMimeData *mimeData)
504 504 {
505 505 auto graphWidget = qobject_cast<VisualizationGraphWidget *>(dragWidget);
506 506 if (!graphWidget) {
507 507 qCWarning(LOG_VisualizationZoneWidget())
508 508 << tr("VisualizationZoneWidget::dropMimeDataOnGraph, dropping in an unknown widget, "
509 509 "drop aborted");
510 510 Q_ASSERT(false);
511 511 return;
512 512 }
513 513
514 514 if (mimeData->hasFormat(MIME_TYPE_VARIABLE_LIST)) {
515 515 auto variables = sqpApp->variableController().variablesForMimeData(
516 516 mimeData->data(MIME_TYPE_VARIABLE_LIST));
517 517 for (const auto &var : variables) {
518 518 graphWidget->addVariable(var, graphWidget->graphRange());
519 519 }
520 520 }
521 521 else if (mimeData->hasFormat(MIME_TYPE_PRODUCT_LIST)) {
522 522 auto products = sqpApp->dataSourceController().productsDataForMimeData(
523 523 mimeData->data(MIME_TYPE_PRODUCT_LIST));
524 524
525 525 auto context = new QObject{this};
526 526 connect(&sqpApp->variableController(), &VariableController::variableAdded, context,
527 527 [this, graphWidget, context](auto variable) {
528 528 graphWidget->addVariable(variable, graphWidget->graphRange());
529 529 delete context; // removes the connection
530 530 },
531 531 Qt::QueuedConnection);
532 532
533 533 auto productData = products.first().toHash();
534 534 QMetaObject::invokeMethod(&sqpApp->dataSourceController(), "requestVariable",
535 535 Qt::QueuedConnection, Q_ARG(QVariantHash, productData));
536 536 }
537 537 else if (mimeData->hasFormat(MIME_TYPE_TIME_RANGE)) {
538 538 auto range = TimeController::timeRangeForMimeData(mimeData->data(MIME_TYPE_TIME_RANGE));
539 539 graphWidget->setGraphRange(range);
540 540 }
541 541 else {
542 542 qCWarning(LOG_VisualizationZoneWidget())
543 543 << tr("VisualizationZoneWidget::dropMimeDataOnGraph, unknown MIME data received.");
544 544 }
545 545 }
546 546
547 547 void VisualizationZoneWidget::VisualizationZoneWidgetPrivate::dropGraph(
548 548 int index, VisualizationZoneWidget *zoneWidget)
549 549 {
550 550 auto &helper = sqpApp->dragDropGuiController();
551 551
552 552 auto graphWidget = qobject_cast<VisualizationGraphWidget *>(helper.getCurrentDragWidget());
553 553 if (!graphWidget) {
554 554 qCWarning(LOG_VisualizationZoneWidget())
555 555 << tr("VisualizationZoneWidget::dropGraph, drop aborted, the dropped graph is not "
556 556 "found or invalid.");
557 557 Q_ASSERT(false);
558 558 return;
559 559 }
560 560
561 561 auto parentDragDropContainer
562 562 = qobject_cast<VisualizationDragDropContainer *>(graphWidget->parentWidget());
563 563 if (!parentDragDropContainer) {
564 564 qCWarning(LOG_VisualizationZoneWidget())
565 565 << tr("VisualizationZoneWidget::dropGraph, drop aborted, the parent container of "
566 566 "the dropped graph is not found.");
567 567 Q_ASSERT(false);
568 568 return;
569 569 }
570 570
571 571 const auto &variables = graphWidget->variables();
572 572
573 573 if (parentDragDropContainer != zoneWidget->ui->dragDropContainer && !variables.isEmpty()) {
574 574 // The drop didn't occur in the same zone
575 575
576 576 // Abort the requests for the variables (if any)
577 577 // Commented, because it's not sure if it's needed or not
578 578 // for (const auto& var : variables)
579 579 //{
580 580 // sqpApp->variableController().onAbortProgressRequested(var);
581 581 //}
582 582
583 583 auto previousParentZoneWidget = graphWidget->parentZoneWidget();
584 584 auto nbGraph = parentDragDropContainer->countDragWidget();
585 585 if (nbGraph == 1) {
586 586 // This is the only graph in the previous zone, close the zone
587 587 helper.delayedCloseWidget(previousParentZoneWidget);
588 588 }
589 589 else {
590 590 // Close the graph
591 591 helper.delayedCloseWidget(graphWidget);
592 592 }
593 593
594 594 // Creates the new graph in the zone
595 595 auto newGraphWidget = zoneWidget->createGraph(variables, index);
596 596 newGraphWidget->addSelectionZones(graphWidget->selectionZoneRanges());
597 597 }
598 598 else {
599 599 // The drop occurred in the same zone or the graph is empty
600 600 // Simple move of the graph, no variable operation associated
601 601 parentDragDropContainer->layout()->removeWidget(graphWidget);
602 602
603 603 if (variables.isEmpty() && parentDragDropContainer != zoneWidget->ui->dragDropContainer) {
604 604 // The graph is empty and dropped in a different zone.
605 605 // Take the range of the first graph in the zone (if existing).
606 606 auto layout = zoneWidget->ui->dragDropContainer->layout();
607 607 if (layout->count() > 0) {
608 608 if (auto visualizationGraphWidget
609 609 = qobject_cast<VisualizationGraphWidget *>(layout->itemAt(0)->widget())) {
610 610 graphWidget->setGraphRange(visualizationGraphWidget->graphRange());
611 611 }
612 612 }
613 613 }
614 614
615 615 zoneWidget->ui->dragDropContainer->insertDragWidget(index, graphWidget);
616 616 }
617 617 }
618 618
619 619 void VisualizationZoneWidget::VisualizationZoneWidgetPrivate::dropVariables(
620 620 const QList<std::shared_ptr<Variable> > &variables, int index,
621 621 VisualizationZoneWidget *zoneWidget)
622 622 {
623 623 // Note: the AcceptMimeDataFunction (set on the drop container) ensure there is a single and
624 624 // compatible variable here
625 625 if (variables.count() > 1) {
626 626 qCWarning(LOG_VisualizationZoneWidget())
627 627 << tr("VisualizationZoneWidget::dropVariables, dropping multiple variables, operation "
628 628 "aborted.");
629 629 return;
630 630 }
631 631
632 632 zoneWidget->createGraph(variables, index);
633 633 }
634 634
635 635 void VisualizationZoneWidget::VisualizationZoneWidgetPrivate::dropProducts(
636 636 const QVariantList &productsData, int index, VisualizationZoneWidget *zoneWidget)
637 637 {
638 638 // Note: the AcceptMimeDataFunction (set on the drop container) ensure there is a single and
639 639 // compatible variable here
640 640 if (productsData.count() != 1) {
641 641 qCWarning(LOG_VisualizationZoneWidget())
642 642 << tr("VisualizationTabWidget::dropProducts, dropping multiple products, operation "
643 643 "aborted.");
644 644 return;
645 645 }
646 646
647 647 auto context = new QObject{zoneWidget};
648 648 connect(&sqpApp->variableController(), &VariableController::variableAdded, context,
649 649 [this, index, zoneWidget, context](auto variable) {
650 650 zoneWidget->createGraph(variable, index);
651 651 delete context; // removes the connection
652 652 },
653 653 Qt::QueuedConnection);
654 654
655 655 auto productData = productsData.first().toHash();
656 656 QMetaObject::invokeMethod(&sqpApp->dataSourceController(), "requestVariable",
657 657 Qt::QueuedConnection, Q_ARG(QVariantHash, productData));
658 658 }
@@ -1,145 +1,144
1 1 <?xml version="1.0" encoding="UTF-8"?>
2 2 <ui version="4.0">
3 3 <class>CatalogueEventsWidget</class>
4 4 <widget class="QWidget" name="CatalogueEventsWidget">
5 5 <property name="geometry">
6 6 <rect>
7 7 <x>0</x>
8 8 <y>0</y>
9 9 <width>566</width>
10 10 <height>258</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 34 <bool>false</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="text">
51 51 <string> - </string>
52 52 </property>
53 53 <property name="icon">
54 54 <iconset resource="../../resources/sqpguiresources.qrc">
55 55 <normaloff>:/icones/remove.png</normaloff>:/icones/remove.png</iconset>
56 56 </property>
57 57 <property name="autoRaise">
58 58 <bool>true</bool>
59 59 </property>
60 60 </widget>
61 61 </item>
62 62 <item>
63 63 <widget class="Line" name="line">
64 64 <property name="orientation">
65 65 <enum>Qt::Vertical</enum>
66 66 </property>
67 67 </widget>
68 68 </item>
69 69 <item>
70 70 <widget class="QToolButton" name="btnTime">
71 71 <property name="text">
72 72 <string>T</string>
73 73 </property>
74 74 <property name="icon">
75 75 <iconset resource="../../resources/sqpguiresources.qrc">
76 76 <normaloff>:/icones/time.png</normaloff>:/icones/time.png</iconset>
77 77 </property>
78 78 <property name="checkable">
79 79 <bool>true</bool>
80 80 </property>
81 81 <property name="autoRaise">
82 82 <bool>true</bool>
83 83 </property>
84 84 </widget>
85 85 </item>
86 86 <item>
87 87 <widget class="QToolButton" name="btnChart">
88 88 <property name="text">
89 89 <string>G</string>
90 90 </property>
91 91 <property name="icon">
92 92 <iconset resource="../../resources/sqpguiresources.qrc">
93 93 <normaloff>:/icones/chart.png</normaloff>:/icones/chart.png</iconset>
94 94 </property>
95 95 <property name="checkable">
96 96 <bool>true</bool>
97 97 </property>
98 98 <property name="autoRaise">
99 99 <bool>true</bool>
100 100 </property>
101 101 </widget>
102 102 </item>
103 103 <item>
104 104 <widget class="Line" name="line_2">
105 105 <property name="orientation">
106 106 <enum>Qt::Vertical</enum>
107 107 </property>
108 108 </widget>
109 109 </item>
110 110 <item>
111 111 <widget class="QLineEdit" name="lineEdit">
112 112 <property name="enabled">
113 113 <bool>false</bool>
114 114 </property>
115 115 </widget>
116 116 </item>
117 117 </layout>
118 118 </item>
119 119 <item>
120 120 <widget class="QTreeView" name="treeView">
121 121 <property name="dragEnabled">
122 122 <bool>true</bool>
123 123 </property>
124 124 <property name="dragDropMode">
125 125 <enum>QAbstractItemView::DragDrop</enum>
126 126 </property>
127 127 <property name="selectionMode">
128 128 <enum>QAbstractItemView::ExtendedSelection</enum>
129 129 </property>
130 130 <property name="selectionBehavior">
131 131 <enum>QAbstractItemView::SelectRows</enum>
132 132 </property>
133 133 <attribute name="headerStretchLastSection">
134 134 <bool>false</bool>
135 135 </attribute>
136 136 </widget>
137 137 </item>
138 138 </layout>
139 139 </widget>
140 140 <resources>
141 141 <include location="../../resources/sqpguiresources.qrc"/>
142 <include location="../../resources/sqpguiresources.qrc"/>
143 142 </resources>
144 143 <connections/>
145 144 </ui>
General Comments 0
You need to be logged in to leave comments. Login now