##// END OF EJS Templates
Merge branch 'feature/CataloguePart6' into develop
trabillard -
r1363:faf6baef791f merge
parent child
Show More
@@ -1,86 +1,93
1 1 #ifndef SCIQLOP_CATALOGUECONTROLLER_H
2 2 #define SCIQLOP_CATALOGUECONTROLLER_H
3 3
4 4 #include "CoreGlobal.h"
5 5
6 6 #include <Data/SqpRange.h>
7 7
8 8 #include <QLoggingCategory>
9 9 #include <QObject>
10 10 #include <QUuid>
11 11
12 12 #include <Common/spimpl.h>
13 13
14 14 #include <memory>
15 15
16 16 class DBCatalogue;
17 17 class DBEvent;
18 18 class DBEventProduct;
19 19
20 20 Q_DECLARE_LOGGING_CATEGORY(LOG_CatalogueController)
21 21
22 22 class DataSourceItem;
23 23 class Variable;
24 24
25 25 /**
26 26 * @brief The CatalogueController class aims to handle catalogues and event using the CatalogueAPI
27 27 * library.
28 28 */
29 29 class SCIQLOP_CORE_EXPORT CatalogueController : public QObject {
30 30 Q_OBJECT
31 31 public:
32 32 explicit CatalogueController(QObject *parent = 0);
33 33 virtual ~CatalogueController();
34 34
35 35 // DB
36 36 QStringList getRepositories() const;
37 37 void addDB(const QString &dbPath);
38 38 void saveDB(const QString &destinationPath, const QString &repository);
39 39
40 40 // Event
41 41 /// retrieveEvents with empty repository retrieve them from the default repository
42 42 std::list<std::shared_ptr<DBEvent> > retrieveEvents(const QString &repository) const;
43 43 std::list<std::shared_ptr<DBEvent> > retrieveAllEvents() const;
44 44
45 45 void addEvent(std::shared_ptr<DBEvent> event);
46 46 void updateEvent(std::shared_ptr<DBEvent> event);
47 47 void updateEventProduct(std::shared_ptr<DBEventProduct> eventProduct);
48 48 void removeEvent(std::shared_ptr<DBEvent> event);
49 49 // void trashEvent(std::shared_ptr<DBEvent> event);
50 50 // void restore(std::shared_ptr<DBEvent> event);
51 51 void saveEvent(std::shared_ptr<DBEvent> event);
52 52 void discardEvent(std::shared_ptr<DBEvent> event, bool &removed);
53 53 bool eventHasChanges(std::shared_ptr<DBEvent> event) const;
54 54
55 55 // Catalogue
56 56 std::list<std::shared_ptr<DBEvent> >
57 57 retrieveEventsFromCatalogue(std::shared_ptr<DBCatalogue> catalogue) const;
58 58
59 59 /// retrieveEvents with empty repository retrieve them from the default repository
60 60 std::list<std::shared_ptr<DBCatalogue> > retrieveCatalogues(const QString &repository
61 61 = QString()) const;
62 62 void addCatalogue(std::shared_ptr<DBCatalogue> catalogue);
63 63 void updateCatalogue(std::shared_ptr<DBCatalogue> catalogue);
64 64 void removeCatalogue(std::shared_ptr<DBCatalogue> catalogue);
65 65 void saveCatalogue(std::shared_ptr<DBCatalogue> catalogue);
66 66 void discardCatalogue(std::shared_ptr<DBCatalogue> catalogue, bool &removed);
67 67
68 68 void saveAll();
69 69 bool hasChanges() const;
70 70
71 71 /// Returns the MIME data associated to a list of variables
72 72 QByteArray mimeDataForEvents(const QVector<std::shared_ptr<DBEvent> > &events) const;
73 73
74 74 /// Returns the list of variables contained in a MIME data
75 75 QVector<std::shared_ptr<DBEvent> > eventsForMimeData(const QByteArray &mimeData) const;
76 76
77 /// Returns the MIME data associated to a list of variables
78 QByteArray
79 mimeDataForCatalogues(const QVector<std::shared_ptr<DBCatalogue> > &catalogues) const;
80
81 /// Returns the list of variables contained in a MIME data
82 QVector<std::shared_ptr<DBCatalogue> > cataloguesForMimeData(const QByteArray &mimeData) const;
83
77 84 public slots:
78 85 /// Manage init/end of the controller
79 86 void initialize();
80 87
81 88 private:
82 89 class CatalogueControllerPrivate;
83 90 spimpl::unique_impl_ptr<CatalogueControllerPrivate> impl;
84 91 };
85 92
86 93 #endif // SCIQLOP_CATALOGUECONTROLLER_H
@@ -1,21 +1,22
1 1 #ifndef SCIQLOP_MIMETYPESDEF_H
2 2 #define SCIQLOP_MIMETYPESDEF_H
3 3
4 4 #include "CoreGlobal.h"
5 5
6 6 #include <QString>
7 7
8 8 // ////////////////// //
9 9 // SciQlop Mime Types //
10 10 // ////////////////// //
11 11
12 12 extern SCIQLOP_CORE_EXPORT const QString MIME_TYPE_GRAPH;
13 13 extern SCIQLOP_CORE_EXPORT const QString MIME_TYPE_ZONE;
14 14 extern SCIQLOP_CORE_EXPORT const QString MIME_TYPE_VARIABLE_LIST;
15 15 extern SCIQLOP_CORE_EXPORT const QString MIME_TYPE_PRODUCT_LIST;
16 16 extern SCIQLOP_CORE_EXPORT const QString MIME_TYPE_TIME_RANGE;
17 17 extern SCIQLOP_CORE_EXPORT const QString MIME_TYPE_SELECTION_ZONE;
18 18 extern SCIQLOP_CORE_EXPORT const QString MIME_TYPE_EVENT_LIST;
19 extern SCIQLOP_CORE_EXPORT const QString MIME_TYPE_SOURCE_CATALOGUE_LIST;
19 20
20 21
21 22 #endif // SCIQLOP_MIMETYPESDEF_H
@@ -1,526 +1,566
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 293 }
294 294
295 295 void CatalogueController::discardCatalogue(std::shared_ptr<DBCatalogue> catalogue, bool &removed)
296 296 {
297 297 auto syncPred
298 298 = impl->createFinder(catalogue->getUniqId(), catalogue->getRepository(), DBType::SYNC);
299 299 auto workPred
300 300 = impl->createFinder(catalogue->getUniqId(), catalogue->getRepository(), DBType::WORK);
301 301
302 302 auto syncCatalogue = impl->m_CatalogueDao.getCatalogue(syncPred);
303 303 if (!syncCatalogue.getUniqId().isNull()) {
304 304 removed = false;
305 305 impl->m_CatalogueDao.copyCatalogue(
306 306 syncCatalogue, impl->toWorkRepository(catalogue->getRepository()), true);
307 307
308 308 auto workCatalogue = impl->m_CatalogueDao.getCatalogue(workPred);
309 309 *catalogue = workCatalogue;
310 310 impl->m_KeysWithChanges.remove(impl->catalogueUniqueKey(catalogue));
311 311 }
312 312 else {
313 313 removed = true;
314 314 // Since the element wasn't in sync repository. Discard it means remove it
315 315 catalogue->setRepository(impl->toWorkRepository(catalogue->getRepository()));
316 316 impl->m_CatalogueDao.removeCatalogue(*catalogue);
317 317 }
318 318 }
319 319
320 320 void CatalogueController::saveAll()
321 321 {
322 322 for (auto repository : impl->m_RepositoryList) {
323 323 // Save Event
324 324 auto events = this->retrieveEvents(repository);
325 325 for (auto event : events) {
326 326 impl->saveEvent(event, false);
327 327 }
328 328
329 329 // Save Catalogue
330 330 auto catalogues = this->retrieveCatalogues(repository);
331 331 for (auto catalogue : catalogues) {
332 332 impl->saveCatalogue(catalogue, false);
333 333 }
334 334 }
335 335
336 336 impl->savAllDB();
337 337 impl->m_KeysWithChanges.clear();
338 338 }
339 339
340 340 bool CatalogueController::hasChanges() const
341 341 {
342 342 return !impl->m_KeysWithChanges.isEmpty();
343 343 }
344 344
345 345 QByteArray
346 346 CatalogueController::mimeDataForEvents(const QVector<std::shared_ptr<DBEvent> > &events) const
347 347 {
348 348 auto encodedData = QByteArray{};
349 349
350 350 QMap<QString, QVariantList> idsPerRepository;
351 351 for (auto event : events) {
352 352 idsPerRepository[event->getRepository()] << event->getUniqId();
353 353 }
354 354
355 355 QDataStream stream{&encodedData, QIODevice::WriteOnly};
356 356 stream << idsPerRepository;
357 357
358 358 return encodedData;
359 359 }
360 360
361 361 QVector<std::shared_ptr<DBEvent> >
362 362 CatalogueController::eventsForMimeData(const QByteArray &mimeData) const
363 363 {
364 364 auto events = QVector<std::shared_ptr<DBEvent> >{};
365 365 QDataStream stream{mimeData};
366 366
367 367 QMap<QString, QVariantList> idsPerRepository;
368 368 stream >> idsPerRepository;
369 369
370 370 for (auto it = idsPerRepository.cbegin(); it != idsPerRepository.cend(); ++it) {
371 371 auto repository = it.key();
372 372 auto allRepositoryEvent = retrieveEvents(repository);
373 373 for (auto uuid : it.value()) {
374 374 for (auto repositoryEvent : allRepositoryEvent) {
375 375 if (uuid.toUuid() == repositoryEvent->getUniqId()) {
376 376 events << repositoryEvent;
377 377 }
378 378 }
379 379 }
380 380 }
381 381
382 382 return events;
383 383 }
384 384
385 QByteArray CatalogueController::mimeDataForCatalogues(
386 const QVector<std::shared_ptr<DBCatalogue> > &catalogues) const
387 {
388 auto encodedData = QByteArray{};
389
390 QMap<QString, QVariantList> idsPerRepository;
391 for (auto catalogue : catalogues) {
392 idsPerRepository[catalogue->getRepository()] << catalogue->getUniqId();
393 }
394
395 QDataStream stream{&encodedData, QIODevice::WriteOnly};
396 stream << idsPerRepository;
397
398 return encodedData;
399 }
400
401 QVector<std::shared_ptr<DBCatalogue> >
402 CatalogueController::cataloguesForMimeData(const QByteArray &mimeData) const
403 {
404 auto catalogues = QVector<std::shared_ptr<DBCatalogue> >{};
405 QDataStream stream{mimeData};
406
407 QMap<QString, QVariantList> idsPerRepository;
408 stream >> idsPerRepository;
409
410 for (auto it = idsPerRepository.cbegin(); it != idsPerRepository.cend(); ++it) {
411 auto repository = it.key();
412 auto allRepositoryCatalogues = retrieveCatalogues(repository);
413 for (auto uuid : it.value()) {
414 for (auto repositoryCatalogues : allRepositoryCatalogues) {
415 if (uuid.toUuid() == repositoryCatalogues->getUniqId()) {
416 catalogues << repositoryCatalogues;
417 }
418 }
419 }
420 }
421
422 return catalogues;
423 }
424
385 425 void CatalogueController::initialize()
386 426 {
387 427 qCDebug(LOG_CatalogueController()) << tr("CatalogueController init")
388 428 << QThread::currentThread();
389 429
390 430 impl->m_CatalogueDao.initialize();
391 431 auto defaultRepositoryLocation
392 432 = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
393 433
394 434 QDir defaultRepositoryLocationDir;
395 435 if (defaultRepositoryLocationDir.mkpath(defaultRepositoryLocation)) {
396 436 defaultRepositoryLocationDir.cd(defaultRepositoryLocation);
397 437 auto defaultRepository = defaultRepositoryLocationDir.absoluteFilePath(REPOSITORY_DEFAULT);
398 438
399 439 qCInfo(LOG_CatalogueController()) << tr("Persistant data loading from: ")
400 440 << defaultRepository;
401 441
402 442 QDir dbDir(defaultRepository);
403 443 impl->m_RepositoryList << REPOSITORY_DEFAULT;
404 444 if (dbDir.exists()) {
405 445 auto dirName = dbDir.dirName();
406 446
407 447 if (impl->m_CatalogueDao.addDB(defaultRepository, dirName)) {
408 448 impl->copyDBtoDB(dirName, impl->toWorkRepository(dirName));
409 449 }
410 450 }
411 451 else {
412 452 qCInfo(LOG_CatalogueController()) << tr("Initialisation of Default repository detected")
413 453 << defaultRepository;
414 454 }
415 455 }
416 456 else {
417 457 qCWarning(LOG_CatalogueController())
418 458 << tr("Cannot load the persistent default repository from ")
419 459 << defaultRepositoryLocation;
420 460 }
421 461
422 462 qCDebug(LOG_CatalogueController()) << tr("CatalogueController init END");
423 463 }
424 464
425 465 QString CatalogueController::CatalogueControllerPrivate::eventUniqueKey(
426 466 const std::shared_ptr<DBEvent> &event) const
427 467 {
428 468 return event->getUniqId().toString().append(event->getRepository());
429 469 }
430 470
431 471 QString CatalogueController::CatalogueControllerPrivate::catalogueUniqueKey(
432 472 const std::shared_ptr<DBCatalogue> &catalogue) const
433 473 {
434 474 return catalogue->getUniqId().toString().append(catalogue->getRepository());
435 475 }
436 476
437 477 void CatalogueController::CatalogueControllerPrivate::copyDBtoDB(const QString &dbFrom,
438 478 const QString &dbTo)
439 479 {
440 480 // auto cataloguesShared = std::list<std::shared_ptr<DBCatalogue> >{};
441 481 auto catalogues = m_CatalogueDao.getCatalogues(dbFrom);
442 482 auto events = m_CatalogueDao.getEvents(dbFrom);
443 483 for (auto catalogue : catalogues) {
444 484 m_CatalogueDao.copyCatalogue(catalogue, dbTo, true);
445 485 }
446 486
447 487 for (auto event : events) {
448 488 m_CatalogueDao.copyEvent(event, dbTo, true);
449 489 }
450 490 }
451 491
452 492 QString CatalogueController::CatalogueControllerPrivate::toWorkRepository(QString repository)
453 493 {
454 494 auto syncRepository = toSyncRepository(repository);
455 495
456 496 return QString("%1%2").arg(syncRepository, REPOSITORY_WORK_SUFFIX);
457 497 }
458 498
459 499 QString CatalogueController::CatalogueControllerPrivate::toSyncRepository(QString repository)
460 500 {
461 501 auto syncRepository = repository;
462 502 if (repository.endsWith(REPOSITORY_WORK_SUFFIX)) {
463 503 syncRepository.remove(REPOSITORY_WORK_SUFFIX);
464 504 }
465 505 else if (repository.endsWith(REPOSITORY_TRASH_SUFFIX)) {
466 506 syncRepository.remove(REPOSITORY_TRASH_SUFFIX);
467 507 }
468 508 return syncRepository;
469 509 }
470 510
471 511 void CatalogueController::CatalogueControllerPrivate::savAllDB()
472 512 {
473 513 for (auto repository : m_RepositoryList) {
474 514 auto defaultRepositoryLocation
475 515 = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
476 516 m_CatalogueDao.saveDB(defaultRepositoryLocation, repository);
477 517 }
478 518 }
479 519
480 520 void CatalogueController::CatalogueControllerPrivate::saveEvent(std::shared_ptr<DBEvent> event,
481 521 bool persist)
482 522 {
483 523 m_CatalogueDao.copyEvent(*event, toSyncRepository(event->getRepository()), true);
484 524 if (persist) {
485 525 savAllDB();
486 526 }
487 527 }
488 528
489 529 void CatalogueController::CatalogueControllerPrivate::saveCatalogue(
490 530 std::shared_ptr<DBCatalogue> catalogue, bool persist)
491 531 {
492 532 m_CatalogueDao.copyCatalogue(*catalogue, toSyncRepository(catalogue->getRepository()), true);
493 533 if (persist) {
494 534 savAllDB();
495 535 }
496 536 }
497 537
498 538 std::shared_ptr<IRequestPredicate> CatalogueController::CatalogueControllerPrivate::createFinder(
499 539 const QUuid &uniqId, const QString &repository, DBType type)
500 540 {
501 541 // update catalogue parameter
502 542 auto uniqIdPredicate = std::make_shared<ComparaisonPredicate>(QString{"uniqId"}, uniqId,
503 543 ComparaisonOperation::EQUALEQUAL);
504 544
505 545 auto repositoryType = repository;
506 546 switch (type) {
507 547 case DBType::SYNC:
508 548 repositoryType = toSyncRepository(repositoryType);
509 549 break;
510 550 case DBType::WORK:
511 551 repositoryType = toWorkRepository(repositoryType);
512 552 break;
513 553 case DBType::TRASH:
514 554 default:
515 555 break;
516 556 }
517 557
518 558 auto repositoryPredicate = std::make_shared<ComparaisonPredicate>(
519 559 QString{"repository"}, repositoryType, ComparaisonOperation::EQUALEQUAL);
520 560
521 561 auto finderPred = std::make_shared<CompoundPredicate>(CompoundOperation::AND);
522 562 finderPred->AddRequestPredicate(uniqIdPredicate);
523 563 finderPred->AddRequestPredicate(repositoryPredicate);
524 564
525 565 return finderPred;
526 566 }
@@ -1,9 +1,10
1 1 #include "Common/MimeTypesDef.h"
2 2
3 3 const QString MIME_TYPE_GRAPH = QStringLiteral("sciqlop/graph");
4 4 const QString MIME_TYPE_ZONE = QStringLiteral("sciqlop/zone");
5 5 const QString MIME_TYPE_VARIABLE_LIST = QStringLiteral("sciqlop/var-list");
6 6 const QString MIME_TYPE_PRODUCT_LIST = QStringLiteral("sciqlop/product-list");
7 7 const QString MIME_TYPE_TIME_RANGE = QStringLiteral("sciqlop/time-range");
8 8 const QString MIME_TYPE_SELECTION_ZONE = QStringLiteral("sciqlop/selection-zone");
9 9 const QString MIME_TYPE_EVENT_LIST = QStringLiteral("sciqlop/event-list");
10 const QString MIME_TYPE_SOURCE_CATALOGUE_LIST = QStringLiteral("sciqlop/source-catalogue-list");
@@ -1,68 +1,70
1 1 #ifndef SCIQLOP_CATALOGUEEVENTSMODEL_H
2 2 #define SCIQLOP_CATALOGUEEVENTSMODEL_H
3 3
4 4 #include <Common/spimpl.h>
5 5 #include <QAbstractItemModel>
6 6 #include <QLoggingCategory>
7 7 #include <unordered_set>
8 8
9 class DBCatalogue;
9 10 class DBEvent;
10 11 class DBEventProduct;
11 12
12 13 Q_DECLARE_LOGGING_CATEGORY(LOG_CatalogueEventsModel)
13 14
14 15 class CatalogueEventsModel : public QAbstractItemModel {
15 16 Q_OBJECT
16 17
17 18 signals:
18 19 void modelSorted();
19 20
20 21 public:
21 22 CatalogueEventsModel(QObject *parent = nullptr);
22 23
23 24 enum class Column { Name, TStart, TEnd, Tags, Product, Validation, NbColumn };
24 25
26 void setSourceCatalogues(const QVector<std::shared_ptr<DBCatalogue> > &catalogues);
25 27 void setEvents(const QVector<std::shared_ptr<DBEvent> > &events);
26 28 void addEvent(const std::shared_ptr<DBEvent> &event);
27 29 void removeEvent(const std::shared_ptr<DBEvent> &event);
28 30 QVector<std::shared_ptr<DBEvent> > events() const;
29 31
30 32 enum class ItemType { Root, Event, EventProduct };
31 33 ItemType itemTypeOf(const QModelIndex &index) const;
32 34 std::shared_ptr<DBEvent> getEvent(const QModelIndex &index) const;
33 35 std::shared_ptr<DBEvent> getParentEvent(const QModelIndex &index) const;
34 36 std::shared_ptr<DBEventProduct> getEventProduct(const QModelIndex &index) const;
35 37
36 38 /// Refresh the data for the specified event
37 39 void refreshEvent(const std::shared_ptr<DBEvent> &event, bool refreshEventProducts = false);
38 40
39 41 /// Returns a QModelIndex which represent the specified event
40 42 QModelIndex indexOf(const std::shared_ptr<DBEvent> &event) const;
41 43
42 44 /// Marks a change flag on the specified event to allow sorting on the validation column
43 45 void setEventHasChanges(const std::shared_ptr<DBEvent> &event, bool hasChanges);
44 46
45 47 /// Returns true if the specified event has unsaved changes
46 48 bool eventsHasChanges(const std::shared_ptr<DBEvent> &event) const;
47 49
48 50 // Model
49 51 QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const;
50 52 QModelIndex parent(const QModelIndex &index) const;
51 53 int rowCount(const QModelIndex &parent = QModelIndex()) const override;
52 54 int columnCount(const QModelIndex &parent = QModelIndex()) const override;
53 55 Qt::ItemFlags flags(const QModelIndex &index) const override;
54 56 QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
55 57 QVariant headerData(int section, Qt::Orientation orientation,
56 58 int role = Qt::DisplayRole) const override;
57 59 void sort(int column, Qt::SortOrder order = Qt::AscendingOrder) override;
58 60
59 61 Qt::DropActions supportedDragActions() const override;
60 62 QStringList mimeTypes() const override;
61 63 QMimeData *mimeData(const QModelIndexList &indexes) const override;
62 64
63 65 private:
64 66 class CatalogueEventsModelPrivate;
65 67 spimpl::unique_impl_ptr<CatalogueEventsModelPrivate> impl;
66 68 };
67 69
68 70 #endif // SCIQLOP_CATALOGUEEVENTSMODEL_H
@@ -1,59 +1,59
1 1 #ifndef SCIQLOP_CATALOGUETREEMODEL_H
2 2 #define SCIQLOP_CATALOGUETREEMODEL_H
3 3
4 4 #include <Common/spimpl.h>
5 5 #include <QAbstractItemModel>
6 6
7 7 class CatalogueAbstractTreeItem;
8 8
9 9 /**
10 10 * @brief Model to display catalogue items based on QTreeWidgetItem
11 11 * @warning Do not use the method QTreeWidgetItem::treeWidget for an item added to this model or the
12 12 * application will crash
13 13 */
14 14 class CatalogueTreeModel : public QAbstractItemModel {
15 15 Q_OBJECT
16 16
17 17 signals:
18 18 void itemRenamed(const QModelIndex &index);
19 void itemDropped(const QModelIndex &parentIndex);
19 void itemDropped(const QModelIndex &parentIndex, const QMimeData *data, Qt::DropAction action);
20 20
21 21 public:
22 22 CatalogueTreeModel(QObject *parent = nullptr);
23 23
24 24 enum class Column { Name, Validation, Count };
25 25
26 26 QModelIndex addTopLevelItem(CatalogueAbstractTreeItem *item);
27 27 QVector<CatalogueAbstractTreeItem *> topLevelItems() const;
28 28
29 29 void addChildItem(CatalogueAbstractTreeItem *child, const QModelIndex &parentIndex);
30 30 void removeChildItem(CatalogueAbstractTreeItem *child, const QModelIndex &parentIndex);
31 31 /// Refresh the data for the specified index
32 32 void refresh(const QModelIndex &index);
33 33
34 34 CatalogueAbstractTreeItem *item(const QModelIndex &index) const;
35 35 QModelIndex indexOf(CatalogueAbstractTreeItem *item, int column = 0) const;
36 36
37 37 // model
38 38 QModelIndex index(int row, int column,
39 39 const QModelIndex &parent = QModelIndex()) const override;
40 40 QModelIndex parent(const QModelIndex &index) const override;
41 41 int rowCount(const QModelIndex &parent) const override;
42 42 int columnCount(const QModelIndex &parent) const override;
43 43 Qt::ItemFlags flags(const QModelIndex &index) const override;
44 44 QVariant data(const QModelIndex &index, int role) const override;
45 45 bool setData(const QModelIndex &index, const QVariant &value, int role) override;
46 46
47 47 bool canDropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column,
48 48 const QModelIndex &parent) const override;
49 49 bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column,
50 50 const QModelIndex &parent) override;
51 51 Qt::DropActions supportedDropActions() const;
52 52 QStringList mimeTypes() const;
53 53
54 54 private:
55 55 class CatalogueTreeModelPrivate;
56 56 spimpl::unique_impl_ptr<CatalogueTreeModelPrivate> impl;
57 57 };
58 58
59 59 #endif // CATALOGUETREEMODEL_H
@@ -1,103 +1,106
1 1 #ifndef SCIQLOP_VISUALIZATIONZONEWIDGET_H
2 2 #define SCIQLOP_VISUALIZATIONZONEWIDGET_H
3 3
4 4 #include "Data/SqpRange.h"
5 5 #include "Visualization/IVisualizationWidget.h"
6 6 #include "Visualization/VisualizationDragWidget.h"
7 7
8 8 #include <QLoggingCategory>
9 9 #include <QWidget>
10 10
11 11 #include <memory>
12 12
13 13 #include <Common/spimpl.h>
14 14
15 15 Q_DECLARE_LOGGING_CATEGORY(LOG_VisualizationZoneWidget)
16 16
17 17 namespace Ui {
18 18 class VisualizationZoneWidget;
19 19 } // namespace Ui
20 20
21 21 class Variable;
22 22 class VisualizationGraphWidget;
23 23
24 24 class VisualizationZoneWidget : public VisualizationDragWidget, public IVisualizationWidget {
25 25 Q_OBJECT
26 26
27 27 public:
28 28 explicit VisualizationZoneWidget(const QString &name = {}, QWidget *parent = 0);
29 29 virtual ~VisualizationZoneWidget();
30 30
31 31 /// Sets the range of the zone, only works if there is at least one graph in the zone
32 32 /// Note: calibrations between graphs are lost.
33 33 void setZoneRange(const SqpRange &range);
34 34
35 35 /// Adds a graph widget
36 36 void addGraph(VisualizationGraphWidget *graphWidget);
37 37
38 38 /// Inserts a graph widget
39 39 void insertGraph(int index, VisualizationGraphWidget *graphWidget);
40 40
41 41 /**
42 42 * Creates a graph using a variable. The variable will be displayed in the new graph.
43 43 * The graph is added at the end.
44 44 * @param variable the variable for which to create the graph
45 45 * @return the pointer to the created graph
46 46 */
47 47 VisualizationGraphWidget *createGraph(std::shared_ptr<Variable> variable);
48 48
49 49 /**
50 50 * Creates a graph using a variable. The variable will be displayed in the new graph.
51 51 * The graph is inserted at the specified index.
52 52 * @param variable the variable for which to create the graph
53 53 * @param index The index where the graph should be inserted in the layout
54 54 * @return the pointer to the created graph
55 55 */
56 56 VisualizationGraphWidget *createGraph(std::shared_ptr<Variable> variable, int index);
57 57
58 58 /**
59 59 * Creates a graph using a list of variables. The variables will be displayed in the new graph.
60 60 * The graph is inserted at the specified index.
61 61 * @param variables List of variables to be added to the graph
62 62 * @param index The index where the graph should be inserted in the layout
63 63 * @return the pointer to the created graph
64 64 */
65 65 VisualizationGraphWidget *createGraph(const QList<std::shared_ptr<Variable> > variables,
66 66 int index);
67 67
68 68 /// Returns the first graph in the zone or nullptr if there is no graph inside
69 69 VisualizationGraphWidget *firstGraph() const;
70 70
71 /// Closes all graphes inside the zone
72 void closeAllGraphs();
73
71 74 // IVisualizationWidget interface
72 75 void accept(IVisualizationWidgetVisitor *visitor) override;
73 76 bool canDrop(const Variable &variable) const override;
74 77 bool contains(const Variable &variable) const override;
75 78 QString name() const override;
76 79
77 80 // VisualisationDragWidget
78 81 QMimeData *mimeData(const QPoint &position) const override;
79 82 bool isDragAllowed() const override;
80 83
81 84 void notifyMouseMoveInGraph(const QPointF &graphPosition, const QPointF &plotPosition,
82 85 VisualizationGraphWidget *graphWidget);
83 86 void notifyMouseLeaveGraph(VisualizationGraphWidget *graphWidget);
84 87
85 88 protected:
86 89 void closeEvent(QCloseEvent *event) override;
87 90
88 91 private:
89 92 Ui::VisualizationZoneWidget *ui;
90 93
91 94 class VisualizationZoneWidgetPrivate;
92 95 spimpl::unique_impl_ptr<VisualizationZoneWidgetPrivate> impl;
93 96
94 97 private slots:
95 98 void onVariableAdded(std::shared_ptr<Variable> variable);
96 99 /// Slot called when a variable is about to be removed from a graph contained in the zone
97 100 void onVariableAboutToBeRemoved(std::shared_ptr<Variable> variable);
98 101
99 102 void dropMimeData(int index, const QMimeData *mimeData);
100 103 void dropMimeDataOnGraph(VisualizationDragWidget *dragWidget, const QMimeData *mimeData);
101 104 };
102 105
103 106 #endif // SCIQLOP_VISUALIZATIONZONEWIDGET_H
@@ -1,147 +1,145
1 1 #include "Catalogue/CatalogueActionManager.h"
2 2
3 3 #include <Actions/ActionsGuiController.h>
4 4 #include <Catalogue/CatalogueController.h>
5 5 #include <DataSource/DataSourceItem.h>
6 6 #include <SqpApplication.h>
7 7 #include <Variable/Variable.h>
8 8 #include <Visualization/VisualizationGraphWidget.h>
9 9 #include <Visualization/VisualizationSelectionZoneItem.h>
10 10
11 11 #include <Catalogue/CatalogueEventsWidget.h>
12 12 #include <Catalogue/CatalogueExplorer.h>
13 13 #include <Catalogue/CatalogueSideBarWidget.h>
14 14 #include <Catalogue/CreateEventDialog.h>
15 15
16 16 #include <CatalogueDao.h>
17 17 #include <DBCatalogue.h>
18 18 #include <DBEvent.h>
19 19 #include <DBEventProduct.h>
20 20
21 21 #include <QBoxLayout>
22 22 #include <QComboBox>
23 23 #include <QDialog>
24 24 #include <QDialogButtonBox>
25 25 #include <QLineEdit>
26 26 #include <memory>
27 27
28 28 struct CatalogueActionManager::CatalogueActionManagerPrivate {
29 29
30 30 CatalogueExplorer *m_CatalogueExplorer = nullptr;
31 31
32 32 CatalogueActionManagerPrivate(CatalogueExplorer *catalogueExplorer)
33 33 : m_CatalogueExplorer(catalogueExplorer)
34 34 {
35 35 }
36 36
37 37 void createEventFromZones(const QString &eventName,
38 38 const QVector<VisualizationSelectionZoneItem *> &zones,
39 39 const std::shared_ptr<DBCatalogue> &catalogue = nullptr)
40 40 {
41 41 auto event = std::make_shared<DBEvent>();
42 42 event->setName(eventName);
43 43
44 44 std::list<DBEventProduct> productList;
45 45 for (auto zone : zones) {
46 46 auto graph = zone->parentGraphWidget();
47 47 for (auto var : graph->variables()) {
48 48 auto eventProduct = std::make_shared<DBEventProduct>();
49 49 eventProduct->setEvent(*event);
50 50
51 51 auto productId
52 52 = var->metadata().value(DataSourceItem::ID_DATA_KEY, "UnknownID").toString();
53 53
54 54 auto zoneRange = zone->range();
55 55 eventProduct->setTStart(zoneRange.m_TStart);
56 56 eventProduct->setTEnd(zoneRange.m_TEnd);
57 57
58 58 eventProduct->setProductId(productId);
59 59
60 60 productList.push_back(*eventProduct);
61
62 m_CatalogueExplorer->addSelectionZoneItem(event, productId, zone);
63 61 }
64 62 }
65 63
66 64 event->setEventProducts(productList);
67 65
68 66 sqpApp->catalogueController().addEvent(event);
69 67
70 68
71 69 if (catalogue) {
72 70 catalogue->addEvent(event->getUniqId());
73 71 sqpApp->catalogueController().updateCatalogue(catalogue);
74 72 m_CatalogueExplorer->sideBarWidget().setCatalogueChanges(catalogue, true);
75 73 if (m_CatalogueExplorer->eventsWidget().displayedCatalogues().contains(catalogue)) {
76 74 m_CatalogueExplorer->eventsWidget().addEvent(event);
77 75 m_CatalogueExplorer->eventsWidget().setEventChanges(event, true);
78 76 }
79 77 }
80 78 else if (m_CatalogueExplorer->eventsWidget().isAllEventsDisplayed()) {
81 79 m_CatalogueExplorer->eventsWidget().addEvent(event);
82 80 m_CatalogueExplorer->eventsWidget().setEventChanges(event, true);
83 81 }
84 82 }
85 83 };
86 84
87 85 CatalogueActionManager::CatalogueActionManager(CatalogueExplorer *catalogueExplorer)
88 86 : impl{spimpl::make_unique_impl<CatalogueActionManagerPrivate>(catalogueExplorer)}
89 87 {
90 88 }
91 89
92 90 void CatalogueActionManager::installSelectionZoneActions()
93 91 {
94 92 auto &actionController = sqpApp->actionsGuiController();
95 93
96 94 auto createEventEnableFuntion = [](auto zones) {
97 95
98 96 // Checks that all variables in the zones doesn't refer to the same product
99 97 QSet<QString> usedDatasource;
100 98 for (auto zone : zones) {
101 99 auto graph = zone->parentGraphWidget();
102 100 auto variables = graph->variables();
103 101
104 102 for (auto var : variables) {
105 103 auto datasourceId = var->metadata().value(DataSourceItem::ID_DATA_KEY).toString();
106 104 if (!usedDatasource.contains(datasourceId)) {
107 105 usedDatasource.insert(datasourceId);
108 106 }
109 107 else {
110 108 return false;
111 109 }
112 110 }
113 111 }
114 112
115 113 return true;
116 114 };
117 115
118 116 auto createEventAction = actionController.addSectionZoneAction(
119 117 {QObject::tr("Catalogues")}, QObject::tr("New Event..."), [this](auto zones) {
120 118 CreateEventDialog dialog(
121 119 impl->m_CatalogueExplorer->sideBarWidget().getCatalogues(REPOSITORY_DEFAULT));
122 120 dialog.hideCatalogueChoice();
123 121 if (dialog.exec() == QDialog::Accepted) {
124 122 impl->createEventFromZones(dialog.eventName(), zones);
125 123 }
126 124 });
127 125 createEventAction->setEnableFunction(createEventEnableFuntion);
128 126
129 127 auto createEventInCatalogueAction = actionController.addSectionZoneAction(
130 128 {QObject::tr("Catalogues")}, QObject::tr("New Event in Catalogue..."), [this](auto zones) {
131 129 CreateEventDialog dialog(
132 130 impl->m_CatalogueExplorer->sideBarWidget().getCatalogues(REPOSITORY_DEFAULT));
133 131 if (dialog.exec() == QDialog::Accepted) {
134 132 auto selectedCatalogue = dialog.selectedCatalogue();
135 133 if (!selectedCatalogue) {
136 134 selectedCatalogue = std::make_shared<DBCatalogue>();
137 135 selectedCatalogue->setName(dialog.catalogueName());
138 136 sqpApp->catalogueController().addCatalogue(selectedCatalogue);
139 137 impl->m_CatalogueExplorer->sideBarWidget().addCatalogue(selectedCatalogue,
140 138 REPOSITORY_DEFAULT);
141 139 }
142 140
143 141 impl->createEventFromZones(dialog.eventName(), zones, selectedCatalogue);
144 142 }
145 143 });
146 144 createEventInCatalogueAction->setEnableFunction(createEventEnableFuntion);
147 145 }
@@ -1,461 +1,472
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 QVector<std::shared_ptr<DBCatalogue> > m_SourceCatalogue;
27 28
28 29 QStringList columnNames()
29 30 {
30 31 return QStringList{tr("Event"), tr("TStart"), tr("TEnd"),
31 32 tr("Tags"), tr("Product"), tr("")};
32 33 }
33 34
34 35 QVariant sortData(int col, const std::shared_ptr<DBEvent> &event) const
35 36 {
36 37 if (col == (int)CatalogueEventsModel::Column::Validation) {
37 38 auto hasChanges = sqpApp->catalogueController().eventHasChanges(event);
38 39 return hasChanges ? true : QVariant();
39 40 }
40 41
41 42 return eventData(col, event);
42 43 }
43 44
44 45 QVariant eventData(int col, const std::shared_ptr<DBEvent> &event) const
45 46 {
46 47 switch (static_cast<Column>(col)) {
47 48 case CatalogueEventsModel::Column::Name:
48 49 return event->getName();
49 50 case CatalogueEventsModel::Column::TStart:
50 51 return nbEventProducts(event) > 0 ? DateUtils::dateTime(event->getTStart())
51 52 : QVariant{};
52 53 case CatalogueEventsModel::Column::TEnd:
53 54 return nbEventProducts(event) > 0 ? DateUtils::dateTime(event->getTEnd())
54 55 : QVariant{};
55 56 case CatalogueEventsModel::Column::Product:
56 57 return QString::number(nbEventProducts(event)) + " product(s)";
57 58 case CatalogueEventsModel::Column::Tags: {
58 59 QString tagList;
59 60 auto tags = event->getTags();
60 61 for (auto tag : tags) {
61 62 tagList += tag.getName();
62 63 tagList += ' ';
63 64 }
64 65
65 66 return tagList;
66 67 }
67 68 case CatalogueEventsModel::Column::Validation:
68 69 return QVariant();
69 70 default:
70 71 break;
71 72 }
72 73
73 74 Q_ASSERT(false);
74 75 return QStringLiteral("Unknown Data");
75 76 }
76 77
77 78 void parseEventProduct(const std::shared_ptr<DBEvent> &event)
78 79 {
79 80 for (auto product : event->getEventProducts()) {
80 81 m_EventProducts[event.get()].append(std::make_shared<DBEventProduct>(product));
81 82 }
82 83 }
83 84
84 85 int nbEventProducts(const std::shared_ptr<DBEvent> &event) const
85 86 {
86 87 auto eventProductsIt = m_EventProducts.find(event.get());
87 88 if (eventProductsIt != m_EventProducts.cend()) {
88 89 return m_EventProducts.at(event.get()).count();
89 90 }
90 91 else {
91 92 return 0;
92 93 }
93 94 }
94 95
95 96 QVariant eventProductData(int col, const std::shared_ptr<DBEventProduct> &eventProduct) const
96 97 {
97 98 switch (static_cast<Column>(col)) {
98 99 case CatalogueEventsModel::Column::Name:
99 100 return eventProduct->getProductId();
100 101 case CatalogueEventsModel::Column::TStart:
101 102 return DateUtils::dateTime(eventProduct->getTStart());
102 103 case CatalogueEventsModel::Column::TEnd:
103 104 return DateUtils::dateTime(eventProduct->getTEnd());
104 105 case CatalogueEventsModel::Column::Product:
105 106 return eventProduct->getProductId();
106 107 case CatalogueEventsModel::Column::Tags:
107 108 return QString();
108 109 case CatalogueEventsModel::Column::Validation:
109 110 return QVariant();
110 111 default:
111 112 break;
112 113 }
113 114
114 115 Q_ASSERT(false);
115 116 return QStringLiteral("Unknown Data");
116 117 }
117 118
118 119 void refreshChildrenOfIndex(CatalogueEventsModel *model, const QModelIndex &index) const
119 120 {
120 121 auto childCount = model->rowCount(index);
121 122 auto colCount = model->columnCount();
122 123 emit model->dataChanged(model->index(0, 0, index),
123 124 model->index(childCount, colCount, index));
124 125 }
125 126 };
126 127
127 128 CatalogueEventsModel::CatalogueEventsModel(QObject *parent)
128 129 : QAbstractItemModel(parent), impl{spimpl::make_unique_impl<CatalogueEventsModelPrivate>()}
129 130 {
130 131 }
131 132
133 void CatalogueEventsModel::setSourceCatalogues(
134 const QVector<std::shared_ptr<DBCatalogue> > &catalogues)
135 {
136 impl->m_SourceCatalogue = catalogues;
137 }
138
132 139 void CatalogueEventsModel::setEvents(const QVector<std::shared_ptr<DBEvent> > &events)
133 140 {
134 141 beginResetModel();
135 142
136 143 impl->m_Events = events;
137 144 impl->m_EventProducts.clear();
138 145 for (auto event : events) {
139 146 impl->parseEventProduct(event);
140 147 }
141 148
142 149 endResetModel();
143 150 }
144 151
145 152 std::shared_ptr<DBEvent> CatalogueEventsModel::getEvent(const QModelIndex &index) const
146 153 {
147 154 if (itemTypeOf(index) == CatalogueEventsModel::ItemType::Event) {
148 155 return impl->m_Events.value(index.row());
149 156 }
150 157 else {
151 158 return nullptr;
152 159 }
153 160 }
154 161
155 162 std::shared_ptr<DBEvent> CatalogueEventsModel::getParentEvent(const QModelIndex &index) const
156 163 {
157 164 if (itemTypeOf(index) == CatalogueEventsModel::ItemType::EventProduct) {
158 165 return getEvent(index.parent());
159 166 }
160 167 else {
161 168 return nullptr;
162 169 }
163 170 }
164 171
165 172 std::shared_ptr<DBEventProduct>
166 173 CatalogueEventsModel::getEventProduct(const QModelIndex &index) const
167 174 {
168 175 if (itemTypeOf(index) == CatalogueEventsModel::ItemType::EventProduct) {
169 176 auto event = static_cast<DBEvent *>(index.internalPointer());
170 177 return impl->m_EventProducts.at(event).value(index.row());
171 178 }
172 179 else {
173 180 return nullptr;
174 181 }
175 182 }
176 183
177 184 void CatalogueEventsModel::addEvent(const std::shared_ptr<DBEvent> &event)
178 185 {
179 186 beginInsertRows(QModelIndex(), impl->m_Events.count(), impl->m_Events.count());
180 187 impl->m_Events.append(event);
181 188 impl->parseEventProduct(event);
182 189 endInsertRows();
183 190
184 191 // Also refreshes its children event products
185 192 auto eventIndex = index(impl->m_Events.count(), 0);
186 193 impl->refreshChildrenOfIndex(this, eventIndex);
187 194 }
188 195
189 196 void CatalogueEventsModel::removeEvent(const std::shared_ptr<DBEvent> &event)
190 197 {
191 198 auto index = impl->m_Events.indexOf(event);
192 199 if (index >= 0) {
193 200 beginRemoveRows(QModelIndex(), index, index);
194 201 impl->m_Events.removeAt(index);
195 202 impl->m_EventProducts.erase(event.get());
196 203 endRemoveRows();
197 204 }
198 205 }
199 206
200 207 QVector<std::shared_ptr<DBEvent> > CatalogueEventsModel::events() const
201 208 {
202 209 return impl->m_Events;
203 210 }
204 211
205 212 void CatalogueEventsModel::refreshEvent(const std::shared_ptr<DBEvent> &event,
206 213 bool refreshEventProducts)
207 214 {
208 215 auto eventIndex = indexOf(event);
209 216 if (eventIndex.isValid()) {
210 217
211 218 if (refreshEventProducts) {
212 219 // Reparse the associated event products
213 220
214 221 auto nbEventProducts = impl->nbEventProducts(event);
215 222 auto newNbOfEventProducts = event->getEventProducts().size();
216 223 if (newNbOfEventProducts < nbEventProducts) {
217 224 beginRemoveRows(eventIndex, newNbOfEventProducts, nbEventProducts - 1);
218 225 impl->m_EventProducts.erase(event.get());
219 226 impl->parseEventProduct(event);
220 227 endRemoveRows();
221 228 }
222 229 else if (newNbOfEventProducts > nbEventProducts) {
223 230 beginInsertRows(eventIndex, nbEventProducts, newNbOfEventProducts - 1);
224 231 impl->m_EventProducts.erase(event.get());
225 232 impl->parseEventProduct(event);
226 233 endInsertRows();
227 234 }
228 235 else { // newNbOfEventProducts == nbEventProducts
229 236 impl->m_EventProducts.erase(event.get());
230 237 impl->parseEventProduct(event);
231 238 }
232 239 }
233 240
234 241 // Refreshes the event line
235 242 auto colCount = columnCount();
236 243 emit dataChanged(eventIndex, index(eventIndex.row(), colCount));
237 244
238 245 // Also refreshes its children event products
239 246 impl->refreshChildrenOfIndex(this, eventIndex);
240 247 }
241 248 else {
242 249 qCWarning(LOG_CatalogueEventsModel()) << "refreshEvent: event not found.";
243 250 }
244 251 }
245 252
246 253 QModelIndex CatalogueEventsModel::indexOf(const std::shared_ptr<DBEvent> &event) const
247 254 {
248 255 auto row = impl->m_Events.indexOf(event);
249 256 if (row >= 0) {
250 257 return index(row, 0);
251 258 }
252 259
253 260 return QModelIndex();
254 261 }
255 262
256 263 QModelIndex CatalogueEventsModel::index(int row, int column, const QModelIndex &parent) const
257 264 {
258 265 if (!hasIndex(row, column, parent)) {
259 266 return QModelIndex();
260 267 }
261 268
262 269 switch (itemTypeOf(parent)) {
263 270 case CatalogueEventsModel::ItemType::Root:
264 271 return createIndex(row, column);
265 272 case CatalogueEventsModel::ItemType::Event: {
266 273 auto event = getEvent(parent);
267 274 return createIndex(row, column, event.get());
268 275 }
269 276 case CatalogueEventsModel::ItemType::EventProduct:
270 277 break;
271 278 default:
272 279 break;
273 280 }
274 281
275 282 return QModelIndex();
276 283 }
277 284
278 285 QModelIndex CatalogueEventsModel::parent(const QModelIndex &index) const
279 286 {
280 287 switch (itemTypeOf(index)) {
281 288 case CatalogueEventsModel::ItemType::EventProduct: {
282 289 auto parentEvent = static_cast<DBEvent *>(index.internalPointer());
283 290 auto it
284 291 = std::find_if(impl->m_Events.cbegin(), impl->m_Events.cend(),
285 292 [parentEvent](auto event) { return event.get() == parentEvent; });
286 293
287 294 if (it != impl->m_Events.cend()) {
288 295 return createIndex(it - impl->m_Events.cbegin(), 0);
289 296 }
290 297 else {
291 298 return QModelIndex();
292 299 }
293 300 }
294 301 case CatalogueEventsModel::ItemType::Root:
295 302 break;
296 303 case CatalogueEventsModel::ItemType::Event:
297 304 break;
298 305 default:
299 306 break;
300 307 }
301 308
302 309 return QModelIndex();
303 310 }
304 311
305 312 int CatalogueEventsModel::rowCount(const QModelIndex &parent) const
306 313 {
307 314 if (parent.column() > 0) {
308 315 return 0;
309 316 }
310 317
311 318 switch (itemTypeOf(parent)) {
312 319 case CatalogueEventsModel::ItemType::Root:
313 320 return impl->m_Events.count();
314 321 case CatalogueEventsModel::ItemType::Event: {
315 322 auto event = getEvent(parent);
316 323 return impl->m_EventProducts[event.get()].count();
317 324 }
318 325 case CatalogueEventsModel::ItemType::EventProduct:
319 326 break;
320 327 default:
321 328 break;
322 329 }
323 330
324 331 return 0;
325 332 }
326 333
327 334 int CatalogueEventsModel::columnCount(const QModelIndex &parent) const
328 335 {
329 336 return static_cast<int>(CatalogueEventsModel::Column::NbColumn);
330 337 }
331 338
332 339 Qt::ItemFlags CatalogueEventsModel::flags(const QModelIndex &index) const
333 340 {
334 341 return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled;
335 342 }
336 343
337 344 QVariant CatalogueEventsModel::data(const QModelIndex &index, int role) const
338 345 {
339 346 if (index.isValid()) {
340 347
341 348 auto type = itemTypeOf(index);
342 349 if (type == CatalogueEventsModel::ItemType::Event) {
343 350 auto event = getEvent(index);
344 351 switch (role) {
345 352 case Qt::DisplayRole:
346 353 return impl->eventData(index.column(), event);
347 354 break;
348 355 }
349 356 }
350 357 else if (type == CatalogueEventsModel::ItemType::EventProduct) {
351 358 auto product = getEventProduct(index);
352 359 switch (role) {
353 360 case Qt::DisplayRole:
354 361 return impl->eventProductData(index.column(), product);
355 362 break;
356 363 }
357 364 }
358 365 }
359 366
360 367 return QVariant{};
361 368 }
362 369
363 370 QVariant CatalogueEventsModel::headerData(int section, Qt::Orientation orientation, int role) const
364 371 {
365 372 if (orientation == Qt::Horizontal && role == Qt::DisplayRole) {
366 373 return impl->columnNames().value(section);
367 374 }
368 375
369 376 return QVariant();
370 377 }
371 378
372 379 void CatalogueEventsModel::sort(int column, Qt::SortOrder order)
373 380 {
374 381 beginResetModel();
375 382 std::sort(impl->m_Events.begin(), impl->m_Events.end(),
376 383 [this, column, order](auto e1, auto e2) {
377 384 auto data1 = impl->sortData(column, e1);
378 385 auto data2 = impl->sortData(column, e2);
379 386
380 387 auto result = data1.toString() < data2.toString();
381 388
382 389 return order == Qt::AscendingOrder ? result : !result;
383 390 });
384 391
385 392 endResetModel();
386 393 emit modelSorted();
387 394 }
388 395
389 396 Qt::DropActions CatalogueEventsModel::supportedDragActions() const
390 397 {
391 return Qt::CopyAction;
398 return Qt::CopyAction | Qt::MoveAction;
392 399 }
393 400
394 401 QStringList CatalogueEventsModel::mimeTypes() const
395 402 {
396 return {MIME_TYPE_EVENT_LIST, MIME_TYPE_TIME_RANGE};
403 return {MIME_TYPE_EVENT_LIST, MIME_TYPE_SOURCE_CATALOGUE_LIST, MIME_TYPE_TIME_RANGE};
397 404 }
398 405
399 406 QMimeData *CatalogueEventsModel::mimeData(const QModelIndexList &indexes) const
400 407 {
401 408 auto mimeData = new QMimeData;
402 409
403 410 bool isFirst = true;
404 411
405 412 QVector<std::shared_ptr<DBEvent> > eventList;
406 413 QVector<std::shared_ptr<DBEventProduct> > eventProductList;
407 414
408 415 SqpRange firstTimeRange;
409 416 for (const auto &index : indexes) {
410 417 if (index.column() == 0) { // only the first column
411 418
412 419 auto type = itemTypeOf(index);
413 420 if (type == ItemType::Event) {
414 421 auto event = getEvent(index);
415 422 eventList << event;
416 423
417 424 if (isFirst) {
418 425 isFirst = false;
419 426 firstTimeRange.m_TStart = event->getTStart();
420 427 firstTimeRange.m_TEnd = event->getTEnd();
421 428 }
422 429 }
423 430 else if (type == ItemType::EventProduct) {
424 431 auto product = getEventProduct(index);
425 432 eventProductList << product;
426 433
427 434 if (isFirst) {
428 435 isFirst = false;
429 436 firstTimeRange.m_TStart = product->getTStart();
430 437 firstTimeRange.m_TEnd = product->getTEnd();
431 438 }
432 439 }
433 440 }
434 441 }
435 442
436 443 if (!eventList.isEmpty() && eventProductList.isEmpty()) {
437 444 auto eventsEncodedData = sqpApp->catalogueController().mimeDataForEvents(eventList);
438 445 mimeData->setData(MIME_TYPE_EVENT_LIST, eventsEncodedData);
446
447 auto sourceCataloguesEncodedData
448 = sqpApp->catalogueController().mimeDataForCatalogues(impl->m_SourceCatalogue);
449 mimeData->setData(MIME_TYPE_SOURCE_CATALOGUE_LIST, sourceCataloguesEncodedData);
439 450 }
440 451
441 452 if (eventList.count() + eventProductList.count() == 1) {
442 453 // No time range MIME data if multiple events are dragged
443 454 auto timeEncodedData = TimeController::mimeDataForTimeRange(firstTimeRange);
444 455 mimeData->setData(MIME_TYPE_TIME_RANGE, timeEncodedData);
445 456 }
446 457
447 458 return mimeData;
448 459 }
449 460
450 461 CatalogueEventsModel::ItemType CatalogueEventsModel::itemTypeOf(const QModelIndex &index) const
451 462 {
452 463 if (!index.isValid()) {
453 464 return ItemType::Root;
454 465 }
455 466 else if (index.internalPointer() == nullptr) {
456 467 return ItemType::Event;
457 468 }
458 469 else {
459 470 return ItemType::EventProduct;
460 471 }
461 472 }
@@ -1,590 +1,594
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 22 #include <QListWidget>
23 23 #include <QMessageBox>
24 24
25 25 Q_LOGGING_CATEGORY(LOG_CatalogueEventsWidget, "CatalogueEventsWidget")
26 26
27 27 /// Fixed size of the validation column
28 28 const auto VALIDATION_COLUMN_SIZE = 35;
29 29
30 30 /// Percentage added to the range of a event when it is displayed
31 31 const auto EVENT_RANGE_MARGE = 30; // in %
32 32
33 33 struct CatalogueEventsWidget::CatalogueEventsWidgetPrivate {
34 34
35 35 CatalogueEventsModel *m_Model = nullptr;
36 36 QStringList m_ZonesForTimeMode;
37 37 QString m_ZoneForGraphMode;
38 38 QVector<std::shared_ptr<DBCatalogue> > m_DisplayedCatalogues;
39 39 bool m_AllEventDisplayed = false;
40 40 QVector<VisualizationGraphWidget *> m_CustomGraphs;
41 41
42 42 VisualizationWidget *m_VisualizationWidget = nullptr;
43 43
44 44 void setEvents(const QVector<std::shared_ptr<DBEvent> > &events, CatalogueEventsWidget *widget)
45 45 {
46 46 widget->ui->treeView->setSortingEnabled(false);
47 m_Model->setSourceCatalogues(m_DisplayedCatalogues);
47 48 m_Model->setEvents(events);
48 49 widget->ui->treeView->setSortingEnabled(true);
49 50
50 51 for (auto event : events) {
51 52 if (sqpApp->catalogueController().eventHasChanges(event)) {
52 53 auto index = m_Model->indexOf(event);
53 54 widget->setEventChanges(event, true);
54 55 }
55 56 }
56 57 }
57 58
58 59 void addEvent(const std::shared_ptr<DBEvent> &event, QTreeView *treeView)
59 60 {
60 61 treeView->setSortingEnabled(false);
61 62 m_Model->addEvent(event);
62 63 treeView->setSortingEnabled(true);
63 64 }
64 65
65 66 void removeEvent(const std::shared_ptr<DBEvent> &event, QTreeView *treeView)
66 67 {
67 68 treeView->setSortingEnabled(false);
68 69 m_Model->removeEvent(event);
69 70 treeView->setSortingEnabled(true);
70 71 }
71 72
72 73 QStringList getAvailableVisualizationZoneList() const
73 74 {
74 75 if (m_VisualizationWidget) {
75 76 if (auto tab = m_VisualizationWidget->currentTabWidget()) {
76 77 return tab->availableZoneWidgets();
77 78 }
78 79 }
79 80
80 81 return QStringList{};
81 82 }
82 83
83 84 QStringList selectZone(QWidget *parent, const QStringList &selectedZones,
84 85 bool allowMultiSelection, const QPoint &location)
85 86 {
86 87 auto availableZones = getAvailableVisualizationZoneList();
87 88 if (availableZones.isEmpty()) {
88 89 return QStringList{};
89 90 }
90 91
91 92 QDialog d(parent, Qt::Tool);
92 93 d.setWindowTitle("Choose a zone");
93 94 auto layout = new QVBoxLayout{&d};
94 95 layout->setContentsMargins(0, 0, 0, 0);
95 96 auto listWidget = new QListWidget{&d};
96 97 layout->addWidget(listWidget);
97 98
98 99 QSet<QListWidgetItem *> checkedItems;
99 100 for (auto zone : availableZones) {
100 101 auto item = new QListWidgetItem{zone};
101 102 item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsUserCheckable);
102 103 if (selectedZones.contains(zone)) {
103 104 item->setCheckState(Qt::Checked);
104 105 checkedItems << item;
105 106 }
106 107 else {
107 108 item->setCheckState(Qt::Unchecked);
108 109 }
109 110
110 111 listWidget->addItem(item);
111 112 }
112 113
113 114 auto buttonBox = new QDialogButtonBox{QDialogButtonBox::Ok, &d};
114 115 layout->addWidget(buttonBox);
115 116
116 117 QObject::connect(buttonBox, &QDialogButtonBox::accepted, &d, &QDialog::accept);
117 118 QObject::connect(buttonBox, &QDialogButtonBox::rejected, &d, &QDialog::reject);
118 119
119 120 QObject::connect(listWidget, &QListWidget::itemChanged,
120 121 [&checkedItems, allowMultiSelection, listWidget](auto item) {
121 122 if (item->checkState() == Qt::Checked) {
122 123 if (!allowMultiSelection) {
123 124 for (auto checkedItem : checkedItems) {
124 125 listWidget->blockSignals(true);
125 126 checkedItem->setCheckState(Qt::Unchecked);
126 127 listWidget->blockSignals(false);
127 128 }
128 129
129 130 checkedItems.clear();
130 131 }
131 132 checkedItems << item;
132 133 }
133 134 else {
134 135 checkedItems.remove(item);
135 136 }
136 137 });
137 138
138 139 QStringList result;
139 140
140 141 d.setMinimumWidth(120);
141 142 d.resize(d.minimumSizeHint());
142 143 d.move(location);
143 144 if (d.exec() == QDialog::Accepted) {
144 145 for (auto item : checkedItems) {
145 146 result += item->text();
146 147 }
147 148 }
148 149 else {
149 150 result = selectedZones;
150 151 }
151 152
152 153 return result;
153 154 }
154 155
155 156 void updateForTimeMode(QTreeView *treeView)
156 157 {
157 158 auto selectedRows = treeView->selectionModel()->selectedRows();
158 159
159 160 if (selectedRows.count() == 1) {
160 161 auto event = m_Model->getEvent(selectedRows.first());
161 162 if (event) {
162 163 if (m_VisualizationWidget) {
163 164 if (auto tab = m_VisualizationWidget->currentTabWidget()) {
164 165
165 166 for (auto zoneName : m_ZonesForTimeMode) {
166 167 if (auto zone = tab->getZoneWithName(zoneName)) {
167 168 SqpRange eventRange;
168 169 eventRange.m_TStart = event->getTStart();
169 170 eventRange.m_TEnd = event->getTEnd();
170 171 zone->setZoneRange(eventRange);
171 172 }
172 173 }
173 174 }
174 175 else {
175 176 qCWarning(LOG_CatalogueEventsWidget())
176 177 << "updateTimeZone: no tab found in the visualization";
177 178 }
178 179 }
179 180 else {
180 181 qCWarning(LOG_CatalogueEventsWidget())
181 182 << "updateTimeZone: visualization widget not found";
182 183 }
183 184 }
184 185 }
185 186 else {
186 187 qCWarning(LOG_CatalogueEventsWidget())
187 188 << "updateTimeZone: not compatible with multiple events selected";
188 189 }
189 190 }
190 191
191 192 QVector<SqpRange> getGraphRanges(const std::shared_ptr<DBEvent> &event)
192 193 {
193 194 // Retrieves the range of each product and the maximum size
194 195 QVector<SqpRange> graphRanges;
195 196 double maxDt = 0;
196 197 for (auto eventProduct : event->getEventProducts()) {
197 198 SqpRange eventRange;
198 199 eventRange.m_TStart = eventProduct.getTStart();
199 200 eventRange.m_TEnd = eventProduct.getTEnd();
200 201 graphRanges << eventRange;
201 202
202 203 auto dt = eventRange.m_TEnd - eventRange.m_TStart;
203 204 if (dt > maxDt) {
204 205 maxDt = dt;
205 206 }
206 207 }
207 208
208 209 // Adds the marge
209 210 maxDt *= (100.0 + EVENT_RANGE_MARGE) / 100.0;
210 211
211 212 // Corrects the graph ranges so that they all have the same size
212 213 QVector<SqpRange> correctedGraphRanges;
213 214 for (auto range : graphRanges) {
214 215 auto dt = range.m_TEnd - range.m_TStart;
215 216 auto diff = qAbs((maxDt - dt) / 2.0);
216 217
217 218 SqpRange correctedRange;
218 219 correctedRange.m_TStart = range.m_TStart - diff;
219 220 correctedRange.m_TEnd = range.m_TEnd + diff;
220 221
221 222 correctedGraphRanges << correctedRange;
222 223 }
223 224
224 225 return correctedGraphRanges;
225 226 }
226 227
227 228 void updateForGraphMode(CatalogueEventsWidget *catalogueEventWidget)
228 229 {
229 230 auto selectedRows = catalogueEventWidget->ui->treeView->selectionModel()->selectedRows();
230 231 if (selectedRows.count() != 1) {
231 232 qCWarning(LOG_CatalogueEventsWidget())
232 233 << "updateGraphMode: not compatible with multiple events selected";
233 234 return;
234 235 }
235 236
236 237 if (!m_VisualizationWidget) {
237 238 qCWarning(LOG_CatalogueEventsWidget())
238 239 << "updateGraphMode: visualization widget not found";
239 240 return;
240 241 }
241 242
242 243 auto event = m_Model->getEvent(selectedRows.first());
243 244 if (!event) {
244 245 // A event product is probably selected
245 246 qCInfo(LOG_CatalogueEventsWidget()) << "updateGraphMode: no events are selected";
246 247 return;
247 248 }
248 249
249 250 auto tab = m_VisualizationWidget->currentTabWidget();
250 251 if (!tab) {
251 252 qCWarning(LOG_CatalogueEventsWidget())
252 253 << "updateGraphMode: no tab found in the visualization";
253 254 return;
254 255 }
255 256
256 257 auto zone = tab->getZoneWithName(m_ZoneForGraphMode);
257 258 if (!zone) {
258 259 qCWarning(LOG_CatalogueEventsWidget()) << "updateGraphMode: zone not found";
259 260 return;
260 261 }
261 262
262 // Close the previous graph and delete the asociated variables
263 // Closes the previous graph and delete the asociated variables
263 264 for (auto graph : m_CustomGraphs) {
264 265 graph->close();
265 266 auto variables = graph->variables().toVector();
266 267
267 268 QMetaObject::invokeMethod(&sqpApp->variableController(), "deleteVariables",
268 269 Qt::QueuedConnection,
269 270 Q_ARG(QVector<std::shared_ptr<Variable> >, variables));
270 271 }
271 272 m_CustomGraphs.clear();
272 273
274 // Closes the remaining graphs inside the zone
275 zone->closeAllGraphs();
276
273 277 // Calculates the range of each graph which will be created
274 278 auto graphRange = getGraphRanges(event);
275 279
276 280 // Loops through the event products and create the graph
277 281 auto itRange = graphRange.cbegin();
278 282 for (auto eventProduct : event->getEventProducts()) {
279 283 auto productId = eventProduct.getProductId();
280 284
281 285 auto range = *itRange;
282 286 ++itRange;
283 287
284 288 SqpRange productRange;
285 289 productRange.m_TStart = eventProduct.getTStart();
286 290 productRange.m_TEnd = eventProduct.getTEnd();
287 291
288 292 auto context = new QObject{catalogueEventWidget};
289 293 QObject::connect(
290 294 &sqpApp->variableController(), &VariableController::variableAdded, context,
291 295 [this, catalogueEventWidget, zone, context, event, range, productRange,
292 296 productId](auto variable) {
293 297
294 298 if (variable->metadata().value(DataSourceItem::ID_DATA_KEY).toString()
295 299 == productId) {
296 300 auto graph = zone->createGraph(variable);
297 301 graph->setAutoRangeOnVariableInitialization(false);
298 302
299 303 auto selectionZone
300 304 = graph->addSelectionZone(event->getName(), productRange);
301 305 emit catalogueEventWidget->selectionZoneAdded(event, productId,
302 306 selectionZone);
303 307 m_CustomGraphs << graph;
304 308
305 309 graph->setGraphRange(range, true);
306 310
307 311 // Removes the graph from the graph list if it is closed manually
308 312 QObject::connect(graph, &VisualizationGraphWidget::destroyed,
309 313 [this, graph]() { m_CustomGraphs.removeAll(graph); });
310 314
311 315 delete context; // removes the connection
312 316 }
313 317 },
314 318 Qt::QueuedConnection);
315 319
316 320 QMetaObject::invokeMethod(&sqpApp->dataSourceController(),
317 321 "requestVariableFromProductIdKey", Qt::QueuedConnection,
318 322 Q_ARG(QString, productId));
319 323 }
320 324 }
321 325
322 326 void getSelectedItems(
323 327 QTreeView *treeView, QVector<std::shared_ptr<DBEvent> > &events,
324 328 QVector<QPair<std::shared_ptr<DBEvent>, std::shared_ptr<DBEventProduct> > > &eventProducts)
325 329 {
326 330 for (auto rowIndex : treeView->selectionModel()->selectedRows()) {
327 331 auto itemType = m_Model->itemTypeOf(rowIndex);
328 332 if (itemType == CatalogueEventsModel::ItemType::Event) {
329 333 events << m_Model->getEvent(rowIndex);
330 334 }
331 335 else if (itemType == CatalogueEventsModel::ItemType::EventProduct) {
332 336 eventProducts << qMakePair(m_Model->getParentEvent(rowIndex),
333 337 m_Model->getEventProduct(rowIndex));
334 338 }
335 339 }
336 340 }
337 341 };
338 342
339 343 CatalogueEventsWidget::CatalogueEventsWidget(QWidget *parent)
340 344 : QWidget(parent),
341 345 ui(new Ui::CatalogueEventsWidget),
342 346 impl{spimpl::make_unique_impl<CatalogueEventsWidgetPrivate>()}
343 347 {
344 348 ui->setupUi(this);
345 349
346 350 impl->m_Model = new CatalogueEventsModel{this};
347 351 ui->treeView->setModel(impl->m_Model);
348 352
349 353 ui->treeView->setSortingEnabled(true);
350 354 ui->treeView->setDragDropMode(QAbstractItemView::DragDrop);
351 355 ui->treeView->setDragEnabled(true);
352 356
353 357 connect(ui->btnTime, &QToolButton::clicked, [this](auto checked) {
354 358 if (checked) {
355 359 ui->btnChart->setChecked(false);
356 360 impl->m_ZonesForTimeMode
357 361 = impl->selectZone(this, impl->m_ZonesForTimeMode, true,
358 362 this->mapToGlobal(ui->btnTime->frameGeometry().center()));
359 363
360 364 impl->updateForTimeMode(ui->treeView);
361 365 }
362 366 });
363 367
364 368 connect(ui->btnChart, &QToolButton::clicked, [this](auto checked) {
365 369 if (checked) {
366 370 ui->btnTime->setChecked(false);
367 371 impl->m_ZoneForGraphMode
368 372 = impl->selectZone(this, {impl->m_ZoneForGraphMode}, false,
369 373 this->mapToGlobal(ui->btnChart->frameGeometry().center()))
370 374 .value(0);
371 375
372 376 impl->updateForGraphMode(this);
373 377 }
374 378 });
375 379
376 380 connect(ui->btnRemove, &QToolButton::clicked, [this]() {
377 381 QVector<std::shared_ptr<DBEvent> > events;
378 382 QVector<QPair<std::shared_ptr<DBEvent>, std::shared_ptr<DBEventProduct> > > eventProducts;
379 383 impl->getSelectedItems(ui->treeView, events, eventProducts);
380 384
381 385 if (!events.isEmpty() && eventProducts.isEmpty()) {
382 386
383 387 if (QMessageBox::warning(this, tr("Remove Event(s)"),
384 388 tr("The selected event(s) will be permanently removed "
385 389 "from the repository!\nAre you sure you want to continue?"),
386 390 QMessageBox::Yes | QMessageBox::No, QMessageBox::No)
387 391 == QMessageBox::Yes) {
388 392
389 393 for (auto event : events) {
390 394 sqpApp->catalogueController().removeEvent(event);
391 395 impl->removeEvent(event, ui->treeView);
392 396 }
393 397
394 398 emit this->eventsRemoved(events);
395 399 }
396 400 }
397 401 });
398 402
399 403 connect(ui->treeView, &QTreeView::clicked, this, &CatalogueEventsWidget::emitSelection);
400 404 connect(ui->treeView->selectionModel(), &QItemSelectionModel::selectionChanged, this,
401 405 &CatalogueEventsWidget::emitSelection);
402 406
403 407 ui->btnRemove->setEnabled(false); // Disabled by default when nothing is selected
404 408 connect(ui->treeView->selectionModel(), &QItemSelectionModel::selectionChanged, [this]() {
405 409 auto isNotMultiSelection = ui->treeView->selectionModel()->selectedRows().count() <= 1;
406 410 ui->btnChart->setEnabled(isNotMultiSelection);
407 411 ui->btnTime->setEnabled(isNotMultiSelection);
408 412
409 413 if (isNotMultiSelection && ui->btnTime->isChecked()) {
410 414 impl->updateForTimeMode(ui->treeView);
411 415 }
412 416 else if (isNotMultiSelection && ui->btnChart->isChecked()) {
413 417 impl->updateForGraphMode(this);
414 418 }
415 419
416 420 QVector<std::shared_ptr<DBEvent> > events;
417 421 QVector<QPair<std::shared_ptr<DBEvent>, std::shared_ptr<DBEventProduct> > > eventProducts;
418 422 impl->getSelectedItems(ui->treeView, events, eventProducts);
419 423 ui->btnRemove->setEnabled(!events.isEmpty() && eventProducts.isEmpty());
420 424 });
421 425
422 426 ui->treeView->header()->setSectionResizeMode(QHeaderView::ResizeToContents);
423 427 ui->treeView->header()->setSectionResizeMode((int)CatalogueEventsModel::Column::Tags,
424 428 QHeaderView::Stretch);
425 429 ui->treeView->header()->setSectionResizeMode((int)CatalogueEventsModel::Column::Validation,
426 430 QHeaderView::Fixed);
427 431 ui->treeView->header()->setSectionResizeMode((int)CatalogueEventsModel::Column::Name,
428 432 QHeaderView::Interactive);
429 433 ui->treeView->header()->resizeSection((int)CatalogueEventsModel::Column::Validation,
430 434 VALIDATION_COLUMN_SIZE);
431 435 ui->treeView->header()->setSortIndicatorShown(true);
432 436
433 437 connect(impl->m_Model, &CatalogueEventsModel::modelSorted, [this]() {
434 438 auto allEvents = impl->m_Model->events();
435 439 for (auto event : allEvents) {
436 440 setEventChanges(event, sqpApp->catalogueController().eventHasChanges(event));
437 441 }
438 442 });
439 443
440 444 populateWithAllEvents();
441 445 }
442 446
443 447 CatalogueEventsWidget::~CatalogueEventsWidget()
444 448 {
445 449 delete ui;
446 450 }
447 451
448 452 void CatalogueEventsWidget::setVisualizationWidget(VisualizationWidget *visualization)
449 453 {
450 454 impl->m_VisualizationWidget = visualization;
451 455 }
452 456
453 457 void CatalogueEventsWidget::addEvent(const std::shared_ptr<DBEvent> &event)
454 458 {
455 459 impl->addEvent(event, ui->treeView);
456 460 }
457 461
458 462 void CatalogueEventsWidget::setEventChanges(const std::shared_ptr<DBEvent> &event, bool hasChanges)
459 463 {
460 464 impl->m_Model->refreshEvent(event);
461 465
462 466 auto eventIndex = impl->m_Model->indexOf(event);
463 467 auto validationIndex
464 468 = eventIndex.sibling(eventIndex.row(), (int)CatalogueEventsModel::Column::Validation);
465 469
466 470 if (validationIndex.isValid()) {
467 471 if (hasChanges) {
468 472 if (ui->treeView->indexWidget(validationIndex) == nullptr) {
469 473 auto widget = CatalogueExplorerHelper::buildValidationWidget(
470 474 ui->treeView,
471 475 [this, event]() {
472 476 sqpApp->catalogueController().saveEvent(event);
473 477 setEventChanges(event, false);
474 478 },
475 479 [this, event]() {
476 480 bool removed = false;
477 481 sqpApp->catalogueController().discardEvent(event, removed);
478 482 if (removed) {
479 483 impl->m_Model->removeEvent(event);
480 484 }
481 485 else {
482 486 setEventChanges(event, false);
483 487 impl->m_Model->refreshEvent(event, true);
484 488 }
485 489 emitSelection();
486 490 });
487 491 ui->treeView->setIndexWidget(validationIndex, widget);
488 492 }
489 493 }
490 494 else {
491 495 // Note: the widget is destroyed
492 496 ui->treeView->setIndexWidget(validationIndex, nullptr);
493 497 }
494 498 }
495 499 else {
496 500 qCWarning(LOG_CatalogueEventsWidget())
497 501 << "setEventChanges: the event is not displayed in the model.";
498 502 }
499 503 }
500 504
501 505 QVector<std::shared_ptr<DBCatalogue> > CatalogueEventsWidget::displayedCatalogues() const
502 506 {
503 507 return impl->m_DisplayedCatalogues;
504 508 }
505 509
506 510 bool CatalogueEventsWidget::isAllEventsDisplayed() const
507 511 {
508 512 return impl->m_AllEventDisplayed;
509 513 }
510 514
511 515 bool CatalogueEventsWidget::isEventDisplayed(const std::shared_ptr<DBEvent> &event) const
512 516 {
513 517 return impl->m_Model->indexOf(event).isValid();
514 518 }
515 519
516 520 void CatalogueEventsWidget::refreshEvent(const std::shared_ptr<DBEvent> &event)
517 521 {
518 522 impl->m_Model->refreshEvent(event, true);
519 523 }
520 524
521 525 void CatalogueEventsWidget::populateWithCatalogues(
522 526 const QVector<std::shared_ptr<DBCatalogue> > &catalogues)
523 527 {
524 528 impl->m_DisplayedCatalogues = catalogues;
525 529 impl->m_AllEventDisplayed = false;
526 530
527 531 QSet<QUuid> eventIds;
528 532 QVector<std::shared_ptr<DBEvent> > events;
529 533
530 534 for (auto catalogue : catalogues) {
531 535 auto catalogueEvents = sqpApp->catalogueController().retrieveEventsFromCatalogue(catalogue);
532 536 for (auto event : catalogueEvents) {
533 537 if (!eventIds.contains(event->getUniqId())) {
534 538 events << event;
535 539 eventIds.insert(event->getUniqId());
536 540 }
537 541 }
538 542 }
539 543
540 544 impl->setEvents(events, this);
541 545 }
542 546
543 547 void CatalogueEventsWidget::populateWithAllEvents()
544 548 {
545 549 impl->m_DisplayedCatalogues.clear();
546 550 impl->m_AllEventDisplayed = true;
547 551
548 552 auto allEvents = sqpApp->catalogueController().retrieveAllEvents();
549 553
550 554 QVector<std::shared_ptr<DBEvent> > events;
551 555 for (auto event : allEvents) {
552 556 events << event;
553 557 }
554 558
555 559 impl->setEvents(events, this);
556 560 }
557 561
558 562 void CatalogueEventsWidget::clear()
559 563 {
560 564 impl->m_DisplayedCatalogues.clear();
561 565 impl->m_AllEventDisplayed = false;
562 566 impl->setEvents({}, this);
563 567 }
564 568
565 569 void CatalogueEventsWidget::refresh()
566 570 {
567 571 if (isAllEventsDisplayed()) {
568 572 populateWithAllEvents();
569 573 }
570 574 else if (!impl->m_DisplayedCatalogues.isEmpty()) {
571 575 populateWithCatalogues(impl->m_DisplayedCatalogues);
572 576 }
573 577 }
574 578
575 579 void CatalogueEventsWidget::emitSelection()
576 580 {
577 581 QVector<std::shared_ptr<DBEvent> > events;
578 582 QVector<QPair<std::shared_ptr<DBEvent>, std::shared_ptr<DBEventProduct> > > eventProducts;
579 583 impl->getSelectedItems(ui->treeView, events, eventProducts);
580 584
581 585 if (!events.isEmpty() && eventProducts.isEmpty()) {
582 586 emit eventsSelected(events);
583 587 }
584 588 else if (events.isEmpty() && !eventProducts.isEmpty()) {
585 589 emit eventProductsSelected(eventProducts);
586 590 }
587 591 else {
588 592 emit selectionCleared();
589 593 }
590 594 }
@@ -1,414 +1,429
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 #include <Common/MimeTypesDef.h>
11 12 #include <ComparaisonPredicate.h>
12 13 #include <DBCatalogue.h>
13 14
14 15 #include <QMenu>
15 16 #include <QMessageBox>
17 #include <QMimeData>
16 18
17 19 Q_LOGGING_CATEGORY(LOG_CatalogueSideBarWidget, "CatalogueSideBarWidget")
18 20
19 21
20 22 constexpr auto ALL_EVENT_ITEM_TYPE = CatalogueAbstractTreeItem::DEFAULT_TYPE + 1;
21 23 constexpr auto TRASH_ITEM_TYPE = CatalogueAbstractTreeItem::DEFAULT_TYPE + 2;
22 24 constexpr auto CATALOGUE_ITEM_TYPE = CatalogueAbstractTreeItem::DEFAULT_TYPE + 3;
23 25 constexpr auto DATABASE_ITEM_TYPE = CatalogueAbstractTreeItem::DEFAULT_TYPE + 4;
24 26
25 27
26 28 struct CatalogueSideBarWidget::CatalogueSideBarWidgetPrivate {
27 29
28 30 CatalogueTreeModel *m_TreeModel = nullptr;
29 31
30 32 void configureTreeWidget(QTreeView *treeView);
31 33 QModelIndex addDatabaseItem(const QString &name);
32 34 CatalogueAbstractTreeItem *getDatabaseItem(const QString &name);
33 35 CatalogueAbstractTreeItem *addCatalogueItem(const std::shared_ptr<DBCatalogue> &catalogue,
34 36 const QModelIndex &databaseIndex);
35 37
36 38 CatalogueTreeItem *getCatalogueItem(const std::shared_ptr<DBCatalogue> &catalogue) const;
37 39 void setHasChanges(bool value, const QModelIndex &index, CatalogueSideBarWidget *sideBarWidget);
38 40 bool hasChanges(const QModelIndex &index, QTreeView *treeView);
39 41
40 42 int selectionType(QTreeView *treeView) const
41 43 {
42 44 auto selectedItems = treeView->selectionModel()->selectedRows();
43 45 if (selectedItems.isEmpty()) {
44 46 return CatalogueAbstractTreeItem::DEFAULT_TYPE;
45 47 }
46 48 else {
47 49 auto firstIndex = selectedItems.first();
48 50 auto firstItem = m_TreeModel->item(firstIndex);
49 51 if (!firstItem) {
50 52 Q_ASSERT(false);
51 53 return CatalogueAbstractTreeItem::DEFAULT_TYPE;
52 54 }
53 55 auto selectionType = firstItem->type();
54 56
55 57 for (auto itemIndex : selectedItems) {
56 58 auto item = m_TreeModel->item(itemIndex);
57 59 if (!item || item->type() != selectionType) {
58 60 // Incoherent multi selection
59 61 selectionType = CatalogueAbstractTreeItem::DEFAULT_TYPE;
60 62 break;
61 63 }
62 64 }
63 65
64 66 return selectionType;
65 67 }
66 68 }
67 69
68 70 QVector<std::shared_ptr<DBCatalogue> > selectedCatalogues(QTreeView *treeView) const
69 71 {
70 72 QVector<std::shared_ptr<DBCatalogue> > catalogues;
71 73 auto selectedItems = treeView->selectionModel()->selectedRows();
72 74 for (auto itemIndex : selectedItems) {
73 75 auto item = m_TreeModel->item(itemIndex);
74 76 if (item && item->type() == CATALOGUE_ITEM_TYPE) {
75 77 catalogues.append(static_cast<CatalogueTreeItem *>(item)->catalogue());
76 78 }
77 79 }
78 80
79 81 return catalogues;
80 82 }
81 83
82 84 QStringList selectedRepositories(QTreeView *treeView) const
83 85 {
84 86 QStringList repositories;
85 87 auto selectedItems = treeView->selectionModel()->selectedRows();
86 88 for (auto itemIndex : selectedItems) {
87 89 auto item = m_TreeModel->item(itemIndex);
88 90 if (item && item->type() == DATABASE_ITEM_TYPE) {
89 91 repositories.append(item->text());
90 92 }
91 93 }
92 94
93 95 return repositories;
94 96 }
95 97 };
96 98
97 99 CatalogueSideBarWidget::CatalogueSideBarWidget(QWidget *parent)
98 100 : QWidget(parent),
99 101 ui(new Ui::CatalogueSideBarWidget),
100 102 impl{spimpl::make_unique_impl<CatalogueSideBarWidgetPrivate>()}
101 103 {
102 104 ui->setupUi(this);
103 105
104 106 impl->m_TreeModel = new CatalogueTreeModel(this);
105 107 ui->treeView->setModel(impl->m_TreeModel);
106 108
107 109 impl->configureTreeWidget(ui->treeView);
108 110
109 111 ui->treeView->header()->setStretchLastSection(false);
110 112 ui->treeView->header()->setSectionResizeMode(QHeaderView::ResizeToContents);
111 113 ui->treeView->header()->setSectionResizeMode(0, QHeaderView::Stretch);
112 114
113 115 connect(ui->treeView, &QTreeView::clicked, this, &CatalogueSideBarWidget::emitSelection);
114 116 connect(ui->treeView->selectionModel(), &QItemSelectionModel::currentChanged, this,
115 117 &CatalogueSideBarWidget::emitSelection);
116 118
117 119
118 120 connect(ui->btnAdd, &QToolButton::clicked, [this]() {
119 121 auto catalogue = std::make_shared<DBCatalogue>();
120 122 catalogue->setName(QString("Cat"));
121 123 sqpApp->catalogueController().addCatalogue(catalogue);
122 124 auto item = this->addCatalogue(catalogue, REPOSITORY_DEFAULT);
123 125 this->setCatalogueChanges(catalogue, true);
124 126 ui->treeView->edit(impl->m_TreeModel->indexOf(item));
125 127
126 128 });
127 129
128 130
129 connect(impl->m_TreeModel, &CatalogueTreeModel::itemDropped, [this](auto index) {
130 auto item = impl->m_TreeModel->item(index);
131 if (item && item->type() == CATALOGUE_ITEM_TYPE) {
132 auto catalogue = static_cast<CatalogueTreeItem *>(item)->catalogue();
133 this->setCatalogueChanges(catalogue, true);
134 }
135 });
131 connect(impl->m_TreeModel, &CatalogueTreeModel::itemDropped,
132 [this](auto index, auto mimeData, auto action) {
133 auto item = impl->m_TreeModel->item(index);
134 if (item && item->type() == CATALOGUE_ITEM_TYPE) {
135 auto catalogue = static_cast<CatalogueTreeItem *>(item)->catalogue();
136 this->setCatalogueChanges(catalogue, true);
137 }
138
139 if (action == Qt::MoveAction) {
140 /// Display a save button on source catalogues
141 auto sourceCatalogues = sqpApp->catalogueController().cataloguesForMimeData(
142 mimeData->data(MIME_TYPE_SOURCE_CATALOGUE_LIST));
143 for (auto catalogue : sourceCatalogues) {
144 if (auto catalogueItem = impl->getCatalogueItem(catalogue)) {
145 this->setCatalogueChanges(catalogue, true);
146 }
147 }
148 }
149 });
150
136 151 connect(ui->btnRemove, &QToolButton::clicked, [this]() {
137 152 QVector<QPair<std::shared_ptr<DBCatalogue>, CatalogueAbstractTreeItem *> >
138 153 cataloguesToItems;
139 154 auto selectedIndexes = ui->treeView->selectionModel()->selectedRows();
140 155
141 156 for (auto index : selectedIndexes) {
142 157 auto item = impl->m_TreeModel->item(index);
143 158 if (item && item->type() == CATALOGUE_ITEM_TYPE) {
144 159 auto catalogue = static_cast<CatalogueTreeItem *>(item)->catalogue();
145 160 cataloguesToItems << qMakePair(catalogue, item);
146 161 }
147 162 }
148 163
149 164 if (!cataloguesToItems.isEmpty()) {
150 165
151 166 if (QMessageBox::warning(this, tr("Remove Catalogue(s)"),
152 167 tr("The selected catalogues(s) will be completly removed "
153 168 "from the repository!\nAre you sure you want to continue?"),
154 169 QMessageBox::Yes | QMessageBox::No, QMessageBox::No)
155 170 == QMessageBox::Yes) {
156 171
157 172 for (auto catalogueToItem : cataloguesToItems) {
158 173 sqpApp->catalogueController().removeCatalogue(catalogueToItem.first);
159 174 impl->m_TreeModel->removeChildItem(
160 175 catalogueToItem.second,
161 176 impl->m_TreeModel->indexOf(catalogueToItem.second->parent()));
162 177 }
163 178 }
164 179 }
165 180 });
166 181
167 182 connect(impl->m_TreeModel, &CatalogueTreeModel::itemRenamed, [this](auto index) {
168 183 auto selectedIndexes = ui->treeView->selectionModel()->selectedRows();
169 184 if (selectedIndexes.contains(index)) {
170 185 this->emitSelection();
171 186 }
172 187 impl->setHasChanges(true, index, this);
173 188 });
174 189
175 190 ui->treeView->setContextMenuPolicy(Qt::CustomContextMenu);
176 191 connect(ui->treeView, &QTreeView::customContextMenuRequested, this,
177 192 &CatalogueSideBarWidget::onContextMenuRequested);
178 193 }
179 194
180 195 CatalogueSideBarWidget::~CatalogueSideBarWidget()
181 196 {
182 197 delete ui;
183 198 }
184 199
185 200 CatalogueAbstractTreeItem *
186 201 CatalogueSideBarWidget::addCatalogue(const std::shared_ptr<DBCatalogue> &catalogue,
187 202 const QString &repository)
188 203 {
189 204 auto repositoryItem = impl->getDatabaseItem(repository);
190 205 return impl->addCatalogueItem(catalogue, impl->m_TreeModel->indexOf(repositoryItem));
191 206 }
192 207
193 208 void CatalogueSideBarWidget::setCatalogueChanges(const std::shared_ptr<DBCatalogue> &catalogue,
194 209 bool hasChanges)
195 210 {
196 211 if (auto catalogueItem = impl->getCatalogueItem(catalogue)) {
197 212 auto index = impl->m_TreeModel->indexOf(catalogueItem);
198 213 impl->setHasChanges(hasChanges, index, this);
199 214 // catalogueItem->refresh();
200 215 }
201 216 }
202 217
203 218 QVector<std::shared_ptr<DBCatalogue> >
204 219 CatalogueSideBarWidget::getCatalogues(const QString &repository) const
205 220 {
206 221 QVector<std::shared_ptr<DBCatalogue> > result;
207 222 auto repositoryItem = impl->getDatabaseItem(repository);
208 223 for (auto child : repositoryItem->children()) {
209 224 if (child->type() == CATALOGUE_ITEM_TYPE) {
210 225 auto catalogueItem = static_cast<CatalogueTreeItem *>(child);
211 226 result << catalogueItem->catalogue();
212 227 }
213 228 else {
214 229 qCWarning(LOG_CatalogueSideBarWidget()) << "getCatalogues: invalid structure";
215 230 }
216 231 }
217 232
218 233 return result;
219 234 }
220 235
221 236 void CatalogueSideBarWidget::emitSelection()
222 237 {
223 238 auto selectionType = impl->selectionType(ui->treeView);
224 239
225 240 switch (selectionType) {
226 241 case CATALOGUE_ITEM_TYPE:
227 242 emit this->catalogueSelected(impl->selectedCatalogues(ui->treeView));
228 243 break;
229 244 case DATABASE_ITEM_TYPE:
230 245 emit this->databaseSelected(impl->selectedRepositories(ui->treeView));
231 246 break;
232 247 case ALL_EVENT_ITEM_TYPE:
233 248 emit this->allEventsSelected();
234 249 break;
235 250 case TRASH_ITEM_TYPE:
236 251 emit this->trashSelected();
237 252 break;
238 253 default:
239 254 emit this->selectionCleared();
240 255 break;
241 256 }
242 257 }
243 258
244 259 void CatalogueSideBarWidget::onContextMenuRequested(const QPoint &pos)
245 260 {
246 261 QMenu menu{this};
247 262
248 263 auto currentIndex = ui->treeView->currentIndex();
249 264 auto currentItem = impl->m_TreeModel->item(currentIndex);
250 265 if (!currentItem) {
251 266 return;
252 267 }
253 268
254 269 switch (currentItem->type()) {
255 270 case CATALOGUE_ITEM_TYPE:
256 271 menu.addAction("Rename", [this, currentIndex]() { ui->treeView->edit(currentIndex); });
257 272 break;
258 273 case DATABASE_ITEM_TYPE:
259 274 break;
260 275 case ALL_EVENT_ITEM_TYPE:
261 276 break;
262 277 case TRASH_ITEM_TYPE:
263 278 menu.addAction("Empty Trash", []() {
264 279 // TODO
265 280 });
266 281 break;
267 282 default:
268 283 break;
269 284 }
270 285
271 286 if (!menu.isEmpty()) {
272 287 menu.exec(ui->treeView->mapToGlobal(pos));
273 288 }
274 289 }
275 290
276 291 void CatalogueSideBarWidget::CatalogueSideBarWidgetPrivate::configureTreeWidget(QTreeView *treeView)
277 292 {
278 293 auto allEventsItem = new CatalogueTextTreeItem{QIcon{":/icones/allEvents.png"}, "All Events",
279 294 ALL_EVENT_ITEM_TYPE};
280 295 auto allEventIndex = m_TreeModel->addTopLevelItem(allEventsItem);
281 296 treeView->setCurrentIndex(allEventIndex);
282 297
283 298 auto trashItem
284 299 = new CatalogueTextTreeItem{QIcon{":/icones/trash.png"}, "Trash", TRASH_ITEM_TYPE};
285 300 m_TreeModel->addTopLevelItem(trashItem);
286 301
287 302 auto separator = new QFrame{treeView};
288 303 separator->setFrameShape(QFrame::HLine);
289 304 auto separatorItem
290 305 = new CatalogueTextTreeItem{QIcon{}, QString{}, CatalogueAbstractTreeItem::DEFAULT_TYPE};
291 306 separatorItem->setEnabled(false);
292 307 auto separatorIndex = m_TreeModel->addTopLevelItem(separatorItem);
293 308 treeView->setIndexWidget(separatorIndex, separator);
294 309
295 310 auto repositories = sqpApp->catalogueController().getRepositories();
296 311 for (auto dbname : repositories) {
297 312 auto dbIndex = addDatabaseItem(dbname);
298 313 auto catalogues = sqpApp->catalogueController().retrieveCatalogues(dbname);
299 314 for (auto catalogue : catalogues) {
300 315 addCatalogueItem(catalogue, dbIndex);
301 316 }
302 317 }
303 318
304 319 treeView->expandAll();
305 320 }
306 321
307 322 QModelIndex
308 323 CatalogueSideBarWidget::CatalogueSideBarWidgetPrivate::addDatabaseItem(const QString &name)
309 324 {
310 325 auto databaseItem
311 326 = new CatalogueTextTreeItem{QIcon{":/icones/database.png"}, {name}, DATABASE_ITEM_TYPE};
312 327 auto databaseIndex = m_TreeModel->addTopLevelItem(databaseItem);
313 328
314 329 return databaseIndex;
315 330 }
316 331
317 332 CatalogueAbstractTreeItem *
318 333 CatalogueSideBarWidget::CatalogueSideBarWidgetPrivate::getDatabaseItem(const QString &name)
319 334 {
320 335 for (auto item : m_TreeModel->topLevelItems()) {
321 336 if (item->type() == DATABASE_ITEM_TYPE && item->text() == name) {
322 337 return item;
323 338 }
324 339 }
325 340
326 341 return nullptr;
327 342 }
328 343
329 344 CatalogueAbstractTreeItem *CatalogueSideBarWidget::CatalogueSideBarWidgetPrivate::addCatalogueItem(
330 345 const std::shared_ptr<DBCatalogue> &catalogue, const QModelIndex &databaseIndex)
331 346 {
332 347 auto catalogueItem
333 348 = new CatalogueTreeItem{catalogue, QIcon{":/icones/catalogue.png"}, CATALOGUE_ITEM_TYPE};
334 349 m_TreeModel->addChildItem(catalogueItem, databaseIndex);
335 350
336 351 return catalogueItem;
337 352 }
338 353
339 354 CatalogueTreeItem *CatalogueSideBarWidget::CatalogueSideBarWidgetPrivate::getCatalogueItem(
340 355 const std::shared_ptr<DBCatalogue> &catalogue) const
341 356 {
342 357 for (auto item : m_TreeModel->topLevelItems()) {
343 358 if (item->type() == DATABASE_ITEM_TYPE) {
344 359 for (auto childItem : item->children()) {
345 360 if (childItem->type() == CATALOGUE_ITEM_TYPE) {
346 361 auto catalogueItem = static_cast<CatalogueTreeItem *>(childItem);
347 if (catalogueItem->catalogue() == catalogue) {
362 if (catalogueItem->catalogue()->getUniqId() == catalogue->getUniqId()) {
348 363 return catalogueItem;
349 364 }
350 365 }
351 366 else {
352 367 qCWarning(LOG_CatalogueSideBarWidget()) << "getCatalogueItem: Invalid tree "
353 368 "structure. A database item should "
354 369 "only contain catalogues.";
355 370 Q_ASSERT(false);
356 371 }
357 372 }
358 373 }
359 374 }
360 375
361 376 return nullptr;
362 377 }
363 378
364 379 void CatalogueSideBarWidget::CatalogueSideBarWidgetPrivate::setHasChanges(
365 380 bool value, const QModelIndex &index, CatalogueSideBarWidget *sideBarWidget)
366 381 {
367 382 std::shared_ptr<DBCatalogue> catalogue = nullptr;
368 383 auto item = m_TreeModel->item(index);
369 384 if (item && item->type() == CATALOGUE_ITEM_TYPE) {
370 385 catalogue = static_cast<CatalogueTreeItem *>(item)->catalogue();
371 386 }
372 387
373 388 auto validationIndex = index.sibling(index.row(), (int)CatalogueTreeModel::Column::Validation);
374 389 if (value) {
375 390 if (!hasChanges(validationIndex, sideBarWidget->ui->treeView)) {
376 391 auto widget = CatalogueExplorerHelper::buildValidationWidget(
377 392 sideBarWidget->ui->treeView,
378 393 [this, validationIndex, sideBarWidget, catalogue]() {
379 394 if (catalogue) {
380 395 sqpApp->catalogueController().saveCatalogue(catalogue);
381 396 }
382 397 setHasChanges(false, validationIndex, sideBarWidget);
383 398 },
384 399 [this, validationIndex, sideBarWidget, catalogue, item]() {
385 400 if (catalogue) {
386 401 bool removed;
387 402 sqpApp->catalogueController().discardCatalogue(catalogue, removed);
388 403
389 404 if (removed) {
390 405 m_TreeModel->removeChildItem(item,
391 406 m_TreeModel->indexOf(item->parent()));
392 407 }
393 408 else {
394 409 m_TreeModel->refresh(m_TreeModel->indexOf(item));
395 410 setHasChanges(false, validationIndex, sideBarWidget);
396 411 }
397 412 sideBarWidget->emitSelection();
398 413 }
399 414 });
400 415 sideBarWidget->ui->treeView->setIndexWidget(validationIndex, widget);
401 416 }
402 417 }
403 418 else {
404 419 // Note: the widget is destroyed
405 420 sideBarWidget->ui->treeView->setIndexWidget(validationIndex, nullptr);
406 421 }
407 422 }
408 423
409 424 bool CatalogueSideBarWidget::CatalogueSideBarWidgetPrivate::hasChanges(const QModelIndex &index,
410 425 QTreeView *treeView)
411 426 {
412 427 auto validationIndex = index.sibling(index.row(), (int)CatalogueTreeModel::Column::Validation);
413 428 return treeView->indexWidget(validationIndex) != nullptr;
414 429 }
@@ -1,105 +1,129
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 // Check that the event is not dropped on the same catalogue
80 auto sourceCatalogues = sqpApp->catalogueController().cataloguesForMimeData(
81 data->data(MIME_TYPE_SOURCE_CATALOGUE_LIST));
82 for (auto catalogue : sourceCatalogues) {
83 if (catalogue->getUniqId() == impl->m_Catalogue->getUniqId()) {
84 return false;
85 }
86 }
87
79 88 auto events = sqpApp->catalogueController().eventsForMimeData(data->data(MIME_TYPE_EVENT_LIST));
80 89 auto canDrop = data->hasFormat(MIME_TYPE_EVENT_LIST);
81 90
82 91 for (auto event : events) {
83 92 canDrop &= (event->getRepository() == impl->m_Catalogue->getRepository());
84 93 }
94
85 95 return canDrop;
86 96 }
87 97
88 98 bool CatalogueTreeItem::dropMimeData(const QMimeData *data, Qt::DropAction action)
89 99 {
90 100 Q_ASSERT(canDropMimeData(data, action));
91 101 // Warning: Check that the events aren't already in the catalogue
92 // Also check for the repository !!!
102 // No need to check check for the repository: inter-repository drop is forbidden in
103 // canDropMimeData
93 104
94 105 auto events = sqpApp->catalogueController().eventsForMimeData(data->data(MIME_TYPE_EVENT_LIST));
106 auto sourceCatalogues = sqpApp->catalogueController().cataloguesForMimeData(
107 data->data(MIME_TYPE_SOURCE_CATALOGUE_LIST));
95 108
96 109 for (auto event : events) {
110
111 if (action == Qt::MoveAction) {
112 for (auto catalogue : sourceCatalogues) {
113 catalogue->removeEvent(event->getUniqId());
114 }
115 }
116
97 117 impl->m_Catalogue->addEvent(event->getUniqId());
98 sqpApp->catalogueController().updateCatalogue(impl->m_Catalogue);
99 118 }
119
120 for (auto catalogue : sourceCatalogues) {
121 sqpApp->catalogueController().updateCatalogue(catalogue);
122 }
123 sqpApp->catalogueController().updateCatalogue(impl->m_Catalogue);
100 124 }
101 125
102 126 std::shared_ptr<DBCatalogue> CatalogueTreeItem::catalogue() const
103 127 {
104 128 return impl->m_Catalogue;
105 129 }
@@ -1,218 +1,219
1 1 #include "Catalogue/CatalogueTreeModel.h"
2 2 #include <Catalogue/CatalogueTreeItems/CatalogueAbstractTreeItem.h>
3 3
4 4 #include <QMimeData>
5 5 #include <memory>
6 6
7 7 #include <Common/MimeTypesDef.h>
8 8
9 9 struct CatalogueTreeModel::CatalogueTreeModelPrivate {
10 10 std::unique_ptr<CatalogueAbstractTreeItem> m_RootItem = nullptr;
11 11
12 12 CatalogueTreeModelPrivate() : m_RootItem{std::make_unique<CatalogueAbstractTreeItem>()} {}
13 13 };
14 14
15 15 CatalogueTreeModel::CatalogueTreeModel(QObject *parent)
16 16 : QAbstractItemModel(parent), impl{spimpl::make_unique_impl<CatalogueTreeModelPrivate>()}
17 17 {
18 18 }
19 19
20 20 QModelIndex CatalogueTreeModel::addTopLevelItem(CatalogueAbstractTreeItem *item)
21 21 {
22 22 auto nbTopLevelItems = impl->m_RootItem->children().count();
23 23 beginInsertRows(QModelIndex(), nbTopLevelItems, nbTopLevelItems);
24 24 impl->m_RootItem->addChild(item);
25 25 endInsertRows();
26 26
27 27 emit dataChanged(QModelIndex(), QModelIndex());
28 28
29 29 return index(nbTopLevelItems, 0);
30 30 }
31 31
32 32 QVector<CatalogueAbstractTreeItem *> CatalogueTreeModel::topLevelItems() const
33 33 {
34 34 return impl->m_RootItem->children();
35 35 }
36 36
37 37 void CatalogueTreeModel::addChildItem(CatalogueAbstractTreeItem *child,
38 38 const QModelIndex &parentIndex)
39 39 {
40 40 auto parentItem = item(parentIndex);
41 41 int c = parentItem->children().count();
42 42 beginInsertRows(parentIndex, c, c);
43 43 parentItem->addChild(child);
44 44 endInsertRows();
45 45
46 46 emit dataChanged(parentIndex, parentIndex);
47 47 }
48 48
49 49 void CatalogueTreeModel::removeChildItem(CatalogueAbstractTreeItem *child,
50 50 const QModelIndex &parentIndex)
51 51 {
52 52 auto parentItem = item(parentIndex);
53 53 int i = parentItem->children().indexOf(child);
54 54 beginRemoveRows(parentIndex, i, i);
55 55 parentItem->removeChild(child);
56 56 endRemoveRows();
57 57
58 58 emit dataChanged(parentIndex, parentIndex);
59 59 }
60 60
61 61 void CatalogueTreeModel::refresh(const QModelIndex &index)
62 62 {
63 63 emit dataChanged(index, index);
64 64 }
65 65
66 66 CatalogueAbstractTreeItem *CatalogueTreeModel::item(const QModelIndex &index) const
67 67 {
68 68 return static_cast<CatalogueAbstractTreeItem *>(index.internalPointer());
69 69 }
70 70
71 71 QModelIndex CatalogueTreeModel::indexOf(CatalogueAbstractTreeItem *item, int column) const
72 72 {
73 73 auto parentItem = item->parent();
74 74 if (!parentItem) {
75 75 return QModelIndex();
76 76 }
77 77
78 78 auto row = parentItem->children().indexOf(item);
79 79 return createIndex(row, column, item);
80 80 }
81 81
82 82 QModelIndex CatalogueTreeModel::index(int row, int column, const QModelIndex &parent) const
83 83 {
84 84 if (column > 0) {
85 85 int a = 0;
86 86 }
87 87
88 88 if (!hasIndex(row, column, parent)) {
89 89 return QModelIndex();
90 90 }
91 91
92 92 CatalogueAbstractTreeItem *parentItem = nullptr;
93 93
94 94 if (!parent.isValid()) {
95 95 parentItem = impl->m_RootItem.get();
96 96 }
97 97 else {
98 98 parentItem = item(parent);
99 99 }
100 100
101 101 auto childItem = parentItem->children().value(row);
102 102 if (childItem) {
103 103 return createIndex(row, column, childItem);
104 104 }
105 105
106 106 return QModelIndex();
107 107 }
108 108
109 109
110 110 QModelIndex CatalogueTreeModel::parent(const QModelIndex &index) const
111 111 {
112 112 if (!index.isValid()) {
113 113 return QModelIndex();
114 114 }
115 115
116 116 auto childItem = item(index);
117 117 auto parentItem = childItem->parent();
118 118
119 119 if (parentItem == nullptr || parentItem->parent() == nullptr) {
120 120 return QModelIndex();
121 121 }
122 122
123 123 auto row = parentItem->parent()->children().indexOf(parentItem);
124 124 return createIndex(row, 0, parentItem);
125 125 }
126 126
127 127 int CatalogueTreeModel::rowCount(const QModelIndex &parent) const
128 128 {
129 129 CatalogueAbstractTreeItem *parentItem = nullptr;
130 130
131 131 if (!parent.isValid()) {
132 132 parentItem = impl->m_RootItem.get();
133 133 }
134 134 else {
135 135 parentItem = item(parent);
136 136 }
137 137
138 138 return parentItem->children().count();
139 139 }
140 140
141 141 int CatalogueTreeModel::columnCount(const QModelIndex &parent) const
142 142 {
143 143 return (int)Column::Count;
144 144 }
145 145
146 146 Qt::ItemFlags CatalogueTreeModel::flags(const QModelIndex &index) const
147 147 {
148 148 auto treeItem = item(index);
149 149 if (treeItem) {
150 150 return treeItem->flags(index.column());
151 151 }
152 152
153 153 return Qt::NoItemFlags;
154 154 }
155 155
156 156 QVariant CatalogueTreeModel::data(const QModelIndex &index, int role) const
157 157 {
158 158 auto treeItem = item(index);
159 159 if (treeItem) {
160 160 return treeItem->data(index.column(), role);
161 161 }
162 162
163 163 return QModelIndex();
164 164 }
165 165
166 166 bool CatalogueTreeModel::setData(const QModelIndex &index, const QVariant &value, int role)
167 167 {
168 168 auto treeItem = item(index);
169 169 if (treeItem) {
170 170 auto result = treeItem->setData(index.column(), role, value);
171 171
172 172 if (result && index.column() == (int)Column::Name) {
173 173 emit itemRenamed(index);
174 174 }
175 175
176 176 return result;
177 177 }
178 178
179 179 return false;
180 180 }
181
181 182 bool CatalogueTreeModel::canDropMimeData(const QMimeData *data, Qt::DropAction action, int row,
182 183 int column, const QModelIndex &parent) const
183 184 {
184 185 auto draggedIndex = parent;
185 186 auto draggedItem = item(draggedIndex);
186 187 if (draggedItem) {
187 188 return draggedItem->canDropMimeData(data, action);
188 189 }
189 190
190 191 return false;
191 192 }
192 193
193 194 bool CatalogueTreeModel::dropMimeData(const QMimeData *data, Qt::DropAction action, int row,
194 195 int column, const QModelIndex &parent)
195 196 {
196 197 bool result = false;
197 198
198 199 auto draggedIndex = parent;
199 200 auto draggedItem = item(draggedIndex);
200 201 if (draggedItem) {
201 202 result = draggedItem->dropMimeData(data, action);
202 203 if (result) {
203 emit itemDropped(draggedIndex);
204 emit itemDropped(draggedIndex, data, action);
204 205 }
205 206 }
206 207
207 208 return result;
208 209 }
209 210
210 211 Qt::DropActions CatalogueTreeModel::supportedDropActions() const
211 212 {
212 213 return Qt::CopyAction | Qt::MoveAction;
213 214 }
214 215
215 216 QStringList CatalogueTreeModel::mimeTypes() const
216 217 {
217 return {MIME_TYPE_EVENT_LIST};
218 return {MIME_TYPE_EVENT_LIST, MIME_TYPE_SOURCE_CATALOGUE_LIST};
218 219 }
@@ -1,652 +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 273 qCDebug(LOG_VisualizationZoneWidget())
274 274 << tr("TORM: Range before: ") << graphChild->graphRange();
275 275 qCDebug(LOG_VisualizationZoneWidget())
276 276 << tr("TORM: Range after : ") << 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 void VisualizationZoneWidget::closeAllGraphs()
340 {
341 processGraphs(*ui->dragDropContainer->layout(),
342 [](VisualizationGraphWidget &graphWidget) { graphWidget.close(); });
343 }
344
339 345 void VisualizationZoneWidget::accept(IVisualizationWidgetVisitor *visitor)
340 346 {
341 347 if (visitor) {
342 348 visitor->visitEnter(this);
343 349
344 350 // Apply visitor to graph children: widgets different from graphs are not visited (no
345 351 // action)
346 352 processGraphs(
347 353 *ui->dragDropContainer->layout(),
348 354 [visitor](VisualizationGraphWidget &graphWidget) { graphWidget.accept(visitor); });
349 355
350 356 visitor->visitLeave(this);
351 357 }
352 358 else {
353 359 qCCritical(LOG_VisualizationZoneWidget()) << tr("Can't visit widget : the visitor is null");
354 360 }
355 361 }
356 362
357 363 bool VisualizationZoneWidget::canDrop(const Variable &variable) const
358 364 {
359 365 // A tab can always accomodate a variable
360 366 Q_UNUSED(variable);
361 367 return true;
362 368 }
363 369
364 370 bool VisualizationZoneWidget::contains(const Variable &variable) const
365 371 {
366 372 Q_UNUSED(variable);
367 373 return false;
368 374 }
369 375
370 376 QString VisualizationZoneWidget::name() const
371 377 {
372 378 return ui->zoneNameLabel->text();
373 379 }
374 380
375 381 QMimeData *VisualizationZoneWidget::mimeData(const QPoint &position) const
376 382 {
377 383 Q_UNUSED(position);
378 384
379 385 auto mimeData = new QMimeData;
380 386 mimeData->setData(MIME_TYPE_ZONE, QByteArray{});
381 387
382 388 if (auto firstGraph = this->firstGraph()) {
383 389 auto timeRangeData = TimeController::mimeDataForTimeRange(firstGraph->graphRange());
384 390 mimeData->setData(MIME_TYPE_TIME_RANGE, timeRangeData);
385 391 }
386 392
387 393 return mimeData;
388 394 }
389 395
390 396 bool VisualizationZoneWidget::isDragAllowed() const
391 397 {
392 398 return true;
393 399 }
394 400
395 401 void VisualizationZoneWidget::notifyMouseMoveInGraph(const QPointF &graphPosition,
396 402 const QPointF &plotPosition,
397 403 VisualizationGraphWidget *graphWidget)
398 404 {
399 405 processGraphs(*ui->dragDropContainer->layout(), [&graphPosition, &plotPosition, &graphWidget](
400 406 VisualizationGraphWidget &processedGraph) {
401 407
402 408 switch (sqpApp->plotsCursorMode()) {
403 409 case SqpApplication::PlotsCursorMode::Vertical:
404 410 processedGraph.removeHorizontalCursor();
405 411 processedGraph.addVerticalCursorAtViewportPosition(graphPosition.x());
406 412 break;
407 413 case SqpApplication::PlotsCursorMode::Temporal:
408 414 processedGraph.addVerticalCursor(plotPosition.x());
409 415 processedGraph.removeHorizontalCursor();
410 416 break;
411 417 case SqpApplication::PlotsCursorMode::Horizontal:
412 418 processedGraph.removeVerticalCursor();
413 419 if (&processedGraph == graphWidget) {
414 420 processedGraph.addHorizontalCursorAtViewportPosition(graphPosition.y());
415 421 }
416 422 else {
417 423 processedGraph.removeHorizontalCursor();
418 424 }
419 425 break;
420 426 case SqpApplication::PlotsCursorMode::Cross:
421 427 if (&processedGraph == graphWidget) {
422 428 processedGraph.addVerticalCursorAtViewportPosition(graphPosition.x());
423 429 processedGraph.addHorizontalCursorAtViewportPosition(graphPosition.y());
424 430 }
425 431 else {
426 432 processedGraph.removeHorizontalCursor();
427 433 processedGraph.removeVerticalCursor();
428 434 }
429 435 break;
430 436 case SqpApplication::PlotsCursorMode::NoCursor:
431 437 processedGraph.removeHorizontalCursor();
432 438 processedGraph.removeVerticalCursor();
433 439 break;
434 440 }
435 441
436 442
437 443 });
438 444 }
439 445
440 446 void VisualizationZoneWidget::notifyMouseLeaveGraph(VisualizationGraphWidget *graphWidget)
441 447 {
442 448 processGraphs(*ui->dragDropContainer->layout(), [](VisualizationGraphWidget &processedGraph) {
443 449 processedGraph.removeHorizontalCursor();
444 450 processedGraph.removeVerticalCursor();
445 451 });
446 452 }
447 453
448 454 void VisualizationZoneWidget::closeEvent(QCloseEvent *event)
449 455 {
450 456 // Closes graphs in the zone
451 457 processGraphs(*ui->dragDropContainer->layout(),
452 458 [](VisualizationGraphWidget &graphWidget) { graphWidget.close(); });
453 459
454 460 // Delete synchronization group from variable controller
455 461 QMetaObject::invokeMethod(&sqpApp->variableController(), "onRemoveSynchronizationGroupId",
456 462 Qt::QueuedConnection, Q_ARG(QUuid, impl->m_SynchronisationGroupId));
457 463
458 464 QWidget::closeEvent(event);
459 465 }
460 466
461 467 void VisualizationZoneWidget::onVariableAdded(std::shared_ptr<Variable> variable)
462 468 {
463 469 QMetaObject::invokeMethod(&sqpApp->variableController(), "onAddSynchronized",
464 470 Qt::QueuedConnection, Q_ARG(std::shared_ptr<Variable>, variable),
465 471 Q_ARG(QUuid, impl->m_SynchronisationGroupId));
466 472 }
467 473
468 474 void VisualizationZoneWidget::onVariableAboutToBeRemoved(std::shared_ptr<Variable> variable)
469 475 {
470 476 QMetaObject::invokeMethod(&sqpApp->variableController(), "desynchronize", Qt::QueuedConnection,
471 477 Q_ARG(std::shared_ptr<Variable>, variable),
472 478 Q_ARG(QUuid, impl->m_SynchronisationGroupId));
473 479 }
474 480
475 481 void VisualizationZoneWidget::dropMimeData(int index, const QMimeData *mimeData)
476 482 {
477 483 if (mimeData->hasFormat(MIME_TYPE_GRAPH)) {
478 484 impl->dropGraph(index, this);
479 485 }
480 486 else if (mimeData->hasFormat(MIME_TYPE_VARIABLE_LIST)) {
481 487 auto variables = sqpApp->variableController().variablesForMimeData(
482 488 mimeData->data(MIME_TYPE_VARIABLE_LIST));
483 489 impl->dropVariables(variables, index, this);
484 490 }
485 491 else if (mimeData->hasFormat(MIME_TYPE_PRODUCT_LIST)) {
486 492 auto products = sqpApp->dataSourceController().productsDataForMimeData(
487 493 mimeData->data(MIME_TYPE_PRODUCT_LIST));
488 494 impl->dropProducts(products, index, this);
489 495 }
490 496 else {
491 497 qCWarning(LOG_VisualizationZoneWidget())
492 498 << tr("VisualizationZoneWidget::dropMimeData, unknown MIME data received.");
493 499 }
494 500 }
495 501
496 502 void VisualizationZoneWidget::dropMimeDataOnGraph(VisualizationDragWidget *dragWidget,
497 503 const QMimeData *mimeData)
498 504 {
499 505 auto graphWidget = qobject_cast<VisualizationGraphWidget *>(dragWidget);
500 506 if (!graphWidget) {
501 507 qCWarning(LOG_VisualizationZoneWidget())
502 508 << tr("VisualizationZoneWidget::dropMimeDataOnGraph, dropping in an unknown widget, "
503 509 "drop aborted");
504 510 Q_ASSERT(false);
505 511 return;
506 512 }
507 513
508 514 if (mimeData->hasFormat(MIME_TYPE_VARIABLE_LIST)) {
509 515 auto variables = sqpApp->variableController().variablesForMimeData(
510 516 mimeData->data(MIME_TYPE_VARIABLE_LIST));
511 517 for (const auto &var : variables) {
512 518 graphWidget->addVariable(var, graphWidget->graphRange());
513 519 }
514 520 }
515 521 else if (mimeData->hasFormat(MIME_TYPE_PRODUCT_LIST)) {
516 522 auto products = sqpApp->dataSourceController().productsDataForMimeData(
517 523 mimeData->data(MIME_TYPE_PRODUCT_LIST));
518 524
519 525 auto context = new QObject{this};
520 526 connect(&sqpApp->variableController(), &VariableController::variableAdded, context,
521 527 [this, graphWidget, context](auto variable) {
522 528 graphWidget->addVariable(variable, graphWidget->graphRange());
523 529 delete context; // removes the connection
524 530 },
525 531 Qt::QueuedConnection);
526 532
527 533 auto productData = products.first().toHash();
528 534 QMetaObject::invokeMethod(&sqpApp->dataSourceController(), "requestVariable",
529 535 Qt::QueuedConnection, Q_ARG(QVariantHash, productData));
530 536 }
531 537 else if (mimeData->hasFormat(MIME_TYPE_TIME_RANGE)) {
532 538 auto range = TimeController::timeRangeForMimeData(mimeData->data(MIME_TYPE_TIME_RANGE));
533 539 graphWidget->setGraphRange(range);
534 540 }
535 541 else {
536 542 qCWarning(LOG_VisualizationZoneWidget())
537 543 << tr("VisualizationZoneWidget::dropMimeDataOnGraph, unknown MIME data received.");
538 544 }
539 545 }
540 546
541 547 void VisualizationZoneWidget::VisualizationZoneWidgetPrivate::dropGraph(
542 548 int index, VisualizationZoneWidget *zoneWidget)
543 549 {
544 550 auto &helper = sqpApp->dragDropGuiController();
545 551
546 552 auto graphWidget = qobject_cast<VisualizationGraphWidget *>(helper.getCurrentDragWidget());
547 553 if (!graphWidget) {
548 554 qCWarning(LOG_VisualizationZoneWidget())
549 555 << tr("VisualizationZoneWidget::dropGraph, drop aborted, the dropped graph is not "
550 556 "found or invalid.");
551 557 Q_ASSERT(false);
552 558 return;
553 559 }
554 560
555 561 auto parentDragDropContainer
556 562 = qobject_cast<VisualizationDragDropContainer *>(graphWidget->parentWidget());
557 563 if (!parentDragDropContainer) {
558 564 qCWarning(LOG_VisualizationZoneWidget())
559 565 << tr("VisualizationZoneWidget::dropGraph, drop aborted, the parent container of "
560 566 "the dropped graph is not found.");
561 567 Q_ASSERT(false);
562 568 return;
563 569 }
564 570
565 571 const auto &variables = graphWidget->variables();
566 572
567 573 if (parentDragDropContainer != zoneWidget->ui->dragDropContainer && !variables.isEmpty()) {
568 574 // The drop didn't occur in the same zone
569 575
570 576 // Abort the requests for the variables (if any)
571 577 // Commented, because it's not sure if it's needed or not
572 578 // for (const auto& var : variables)
573 579 //{
574 580 // sqpApp->variableController().onAbortProgressRequested(var);
575 581 //}
576 582
577 583 auto previousParentZoneWidget = graphWidget->parentZoneWidget();
578 584 auto nbGraph = parentDragDropContainer->countDragWidget();
579 585 if (nbGraph == 1) {
580 586 // This is the only graph in the previous zone, close the zone
581 587 helper.delayedCloseWidget(previousParentZoneWidget);
582 588 }
583 589 else {
584 590 // Close the graph
585 591 helper.delayedCloseWidget(graphWidget);
586 592 }
587 593
588 594 // Creates the new graph in the zone
589 595 auto newGraphWidget = zoneWidget->createGraph(variables, index);
590 596 newGraphWidget->addSelectionZones(graphWidget->selectionZoneRanges());
591 597 }
592 598 else {
593 599 // The drop occurred in the same zone or the graph is empty
594 600 // Simple move of the graph, no variable operation associated
595 601 parentDragDropContainer->layout()->removeWidget(graphWidget);
596 602
597 603 if (variables.isEmpty() && parentDragDropContainer != zoneWidget->ui->dragDropContainer) {
598 604 // The graph is empty and dropped in a different zone.
599 605 // Take the range of the first graph in the zone (if existing).
600 606 auto layout = zoneWidget->ui->dragDropContainer->layout();
601 607 if (layout->count() > 0) {
602 608 if (auto visualizationGraphWidget
603 609 = qobject_cast<VisualizationGraphWidget *>(layout->itemAt(0)->widget())) {
604 610 graphWidget->setGraphRange(visualizationGraphWidget->graphRange());
605 611 }
606 612 }
607 613 }
608 614
609 615 zoneWidget->ui->dragDropContainer->insertDragWidget(index, graphWidget);
610 616 }
611 617 }
612 618
613 619 void VisualizationZoneWidget::VisualizationZoneWidgetPrivate::dropVariables(
614 620 const QList<std::shared_ptr<Variable> > &variables, int index,
615 621 VisualizationZoneWidget *zoneWidget)
616 622 {
617 623 // Note: the AcceptMimeDataFunction (set on the drop container) ensure there is a single and
618 624 // compatible variable here
619 625 if (variables.count() > 1) {
620 626 qCWarning(LOG_VisualizationZoneWidget())
621 627 << tr("VisualizationZoneWidget::dropVariables, dropping multiple variables, operation "
622 628 "aborted.");
623 629 return;
624 630 }
625 631
626 632 zoneWidget->createGraph(variables, index);
627 633 }
628 634
629 635 void VisualizationZoneWidget::VisualizationZoneWidgetPrivate::dropProducts(
630 636 const QVariantList &productsData, int index, VisualizationZoneWidget *zoneWidget)
631 637 {
632 638 // Note: the AcceptMimeDataFunction (set on the drop container) ensure there is a single and
633 639 // compatible variable here
634 640 if (productsData.count() != 1) {
635 641 qCWarning(LOG_VisualizationZoneWidget())
636 642 << tr("VisualizationTabWidget::dropProducts, dropping multiple products, operation "
637 643 "aborted.");
638 644 return;
639 645 }
640 646
641 647 auto context = new QObject{zoneWidget};
642 648 connect(&sqpApp->variableController(), &VariableController::variableAdded, context,
643 649 [this, index, zoneWidget, context](auto variable) {
644 650 zoneWidget->createGraph(variable, index);
645 651 delete context; // removes the connection
646 652 },
647 653 Qt::QueuedConnection);
648 654
649 655 auto productData = productsData.first().toHash();
650 656 QMetaObject::invokeMethod(&sqpApp->dataSourceController(), "requestVariable",
651 657 Qt::QueuedConnection, Q_ARG(QVariantHash, productData));
652 658 }
General Comments 0
You need to be logged in to leave comments. Login now