##// END OF EJS Templates
Fix hardcorded icon size for save and discard tool button
perrinel -
r1318:79bf37fd2aff
parent child
Show More
@@ -1,632 +1,629
1 1 #include "Catalogue/CatalogueEventsWidget.h"
2 2 #include "ui_CatalogueEventsWidget.h"
3 3
4 4 #include <Catalogue/CatalogueController.h>
5 5 #include <Catalogue/CatalogueEventsModel.h>
6 6 #include <Catalogue/CatalogueExplorerHelper.h>
7 7 #include <CatalogueDao.h>
8 8 #include <DBCatalogue.h>
9 9 #include <DBEventProduct.h>
10 10 #include <DataSource/DataSourceController.h>
11 11 #include <DataSource/DataSourceItem.h>
12 12 #include <SqpApplication.h>
13 13 #include <Variable/Variable.h>
14 14 #include <Variable/VariableController.h>
15 15 #include <Visualization/VisualizationGraphWidget.h>
16 16 #include <Visualization/VisualizationTabWidget.h>
17 17 #include <Visualization/VisualizationWidget.h>
18 18 #include <Visualization/VisualizationZoneWidget.h>
19 19
20 20 #include <QDialog>
21 21 #include <QDialogButtonBox>
22 22 #include <QKeyEvent>
23 23 #include <QListWidget>
24 24 #include <QMessageBox>
25 25
26 26 Q_LOGGING_CATEGORY(LOG_CatalogueEventsWidget, "CatalogueEventsWidget")
27 27
28 /// Fixed size of the validation column
29 const auto VALIDATION_COLUMN_SIZE = 35;
30
31 28 /// Percentage added to the range of a event when it is displayed
32 29 const auto EVENT_RANGE_MARGE = 30; // in %
33 30
34 31 struct CatalogueEventsWidget::CatalogueEventsWidgetPrivate {
35 32
36 33 CatalogueEventsModel *m_Model = nullptr;
37 34 QStringList m_ZonesForTimeMode;
38 35 QString m_ZoneForGraphMode;
39 36 QVector<std::shared_ptr<DBCatalogue> > m_DisplayedCatalogues;
40 37 bool m_AllEventDisplayed = false;
41 38 QVector<VisualizationGraphWidget *> m_CustomGraphs;
42 39
43 40 VisualizationWidget *m_VisualizationWidget = nullptr;
44 41
45 42 void setEvents(const QVector<std::shared_ptr<DBEvent> > &events, CatalogueEventsWidget *widget)
46 43 {
47 44 widget->ui->treeView->setSortingEnabled(false);
48 45 m_Model->setSourceCatalogues(m_DisplayedCatalogues);
49 46 m_Model->setEvents(events);
50 47 widget->ui->treeView->setSortingEnabled(true);
51 48
52 49 for (auto event : events) {
53 50 if (sqpApp->catalogueController().eventHasChanges(event)) {
54 51 auto index = m_Model->indexOf(event);
55 52 widget->setEventChanges(event, true);
56 53 }
57 54 }
58 55 }
59 56
60 57 void addEvent(const std::shared_ptr<DBEvent> &event, QTreeView *treeView)
61 58 {
62 59 treeView->setSortingEnabled(false);
63 60 m_Model->addEvent(event);
64 61 treeView->setSortingEnabled(true);
65 62 }
66 63
67 64 void removeEvent(const std::shared_ptr<DBEvent> &event, QTreeView *treeView)
68 65 {
69 66 treeView->setSortingEnabled(false);
70 67 m_Model->removeEvent(event);
71 68 treeView->setSortingEnabled(true);
72 69 }
73 70
74 71 QStringList getAvailableVisualizationZoneList() const
75 72 {
76 73 if (m_VisualizationWidget) {
77 74 if (auto tab = m_VisualizationWidget->currentTabWidget()) {
78 75 return tab->availableZoneWidgets();
79 76 }
80 77 }
81 78
82 79 return QStringList{};
83 80 }
84 81
85 82 QStringList selectZone(QWidget *parent, const QStringList &selectedZones,
86 83 bool allowMultiSelection, const QPoint &location)
87 84 {
88 85 auto availableZones = getAvailableVisualizationZoneList();
89 86 if (availableZones.isEmpty()) {
90 87 return QStringList{};
91 88 }
92 89
93 90 QDialog d(parent, Qt::Tool);
94 91 d.setWindowTitle("Choose a zone");
95 92 auto layout = new QVBoxLayout{&d};
96 93 layout->setContentsMargins(0, 0, 0, 0);
97 94 auto listWidget = new QListWidget{&d};
98 95 layout->addWidget(listWidget);
99 96
100 97 QSet<QListWidgetItem *> checkedItems;
101 98 for (auto zone : availableZones) {
102 99 auto item = new QListWidgetItem{zone};
103 100 item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsUserCheckable);
104 101 if (selectedZones.contains(zone)) {
105 102 item->setCheckState(Qt::Checked);
106 103 checkedItems << item;
107 104 }
108 105 else {
109 106 item->setCheckState(Qt::Unchecked);
110 107 }
111 108
112 109 listWidget->addItem(item);
113 110 }
114 111
115 112 auto buttonBox = new QDialogButtonBox{QDialogButtonBox::Ok, &d};
116 113 layout->addWidget(buttonBox);
117 114
118 115 QObject::connect(buttonBox, &QDialogButtonBox::accepted, &d, &QDialog::accept);
119 116 QObject::connect(buttonBox, &QDialogButtonBox::rejected, &d, &QDialog::reject);
120 117
121 118 QObject::connect(listWidget, &QListWidget::itemChanged,
122 119 [&checkedItems, allowMultiSelection, listWidget](auto item) {
123 120 if (item->checkState() == Qt::Checked) {
124 121 if (!allowMultiSelection) {
125 122 for (auto checkedItem : checkedItems) {
126 123 listWidget->blockSignals(true);
127 124 checkedItem->setCheckState(Qt::Unchecked);
128 125 listWidget->blockSignals(false);
129 126 }
130 127
131 128 checkedItems.clear();
132 129 }
133 130 checkedItems << item;
134 131 }
135 132 else {
136 133 checkedItems.remove(item);
137 134 }
138 135 });
139 136
140 137 QStringList result;
141 138
142 139 d.setMinimumWidth(120);
143 140 d.resize(d.minimumSizeHint());
144 141 d.move(location);
145 142 if (d.exec() == QDialog::Accepted) {
146 143 for (auto item : checkedItems) {
147 144 result += item->text();
148 145 }
149 146 }
150 147 else {
151 148 result = selectedZones;
152 149 }
153 150
154 151 return result;
155 152 }
156 153
157 154 void updateForTimeMode(QTreeView *treeView)
158 155 {
159 156 auto selectedRows = treeView->selectionModel()->selectedRows();
160 157
161 158 if (selectedRows.count() == 1) {
162 159 auto event = m_Model->getEvent(selectedRows.first());
163 160 if (event) {
164 161 if (m_VisualizationWidget) {
165 162 if (auto tab = m_VisualizationWidget->currentTabWidget()) {
166 163
167 164 for (auto zoneName : m_ZonesForTimeMode) {
168 165 if (auto zone = tab->getZoneWithName(zoneName)) {
169 166 SqpRange eventRange;
170 167 eventRange.m_TStart = event->getTStart();
171 168 eventRange.m_TEnd = event->getTEnd();
172 169 zone->setZoneRange(eventRange);
173 170 }
174 171 }
175 172 }
176 173 else {
177 174 qCWarning(LOG_CatalogueEventsWidget())
178 175 << "updateTimeZone: no tab found in the visualization";
179 176 }
180 177 }
181 178 else {
182 179 qCWarning(LOG_CatalogueEventsWidget())
183 180 << "updateTimeZone: visualization widget not found";
184 181 }
185 182 }
186 183 }
187 184 else {
188 185 qCWarning(LOG_CatalogueEventsWidget())
189 186 << "updateTimeZone: not compatible with multiple events selected";
190 187 }
191 188 }
192 189
193 190 QVector<SqpRange> getGraphRanges(const std::shared_ptr<DBEvent> &event)
194 191 {
195 192 // Retrieves the range of each product and the maximum size
196 193 QVector<SqpRange> graphRanges;
197 194 double maxDt = 0;
198 195 for (auto eventProduct : event->getEventProducts()) {
199 196 SqpRange eventRange;
200 197 eventRange.m_TStart = eventProduct.getTStart();
201 198 eventRange.m_TEnd = eventProduct.getTEnd();
202 199 graphRanges << eventRange;
203 200
204 201 auto dt = eventRange.m_TEnd - eventRange.m_TStart;
205 202 if (dt > maxDt) {
206 203 maxDt = dt;
207 204 }
208 205 }
209 206
210 207 // Adds the marge
211 208 maxDt *= (100.0 + EVENT_RANGE_MARGE) / 100.0;
212 209
213 210 // Corrects the graph ranges so that they all have the same size
214 211 QVector<SqpRange> correctedGraphRanges;
215 212 for (auto range : graphRanges) {
216 213 auto dt = range.m_TEnd - range.m_TStart;
217 214 auto diff = qAbs((maxDt - dt) / 2.0);
218 215
219 216 SqpRange correctedRange;
220 217 correctedRange.m_TStart = range.m_TStart - diff;
221 218 correctedRange.m_TEnd = range.m_TEnd + diff;
222 219
223 220 correctedGraphRanges << correctedRange;
224 221 }
225 222
226 223 return correctedGraphRanges;
227 224 }
228 225
229 226 void updateForGraphMode(CatalogueEventsWidget *catalogueEventWidget)
230 227 {
231 228 auto selectedRows = catalogueEventWidget->ui->treeView->selectionModel()->selectedRows();
232 229 if (selectedRows.count() != 1) {
233 230 qCWarning(LOG_CatalogueEventsWidget())
234 231 << "updateGraphMode: not compatible with multiple events selected";
235 232 return;
236 233 }
237 234
238 235 if (!m_VisualizationWidget) {
239 236 qCWarning(LOG_CatalogueEventsWidget())
240 237 << "updateGraphMode: visualization widget not found";
241 238 return;
242 239 }
243 240
244 241 auto event = m_Model->getEvent(selectedRows.first());
245 242 if (!event) {
246 243 // A event product is probably selected
247 244 qCInfo(LOG_CatalogueEventsWidget()) << "updateGraphMode: no events are selected";
248 245 return;
249 246 }
250 247
251 248 auto tab = m_VisualizationWidget->currentTabWidget();
252 249 if (!tab) {
253 250 qCWarning(LOG_CatalogueEventsWidget())
254 251 << "updateGraphMode: no tab found in the visualization";
255 252 return;
256 253 }
257 254
258 255 auto zone = tab->getZoneWithName(m_ZoneForGraphMode);
259 256 if (!zone) {
260 257 qCWarning(LOG_CatalogueEventsWidget()) << "updateGraphMode: zone not found";
261 258 return;
262 259 }
263 260
264 261 // Closes the previous graph and delete the asociated variables
265 262 for (auto graph : m_CustomGraphs) {
266 263 graph->close();
267 264 auto variables = graph->variables().toVector();
268 265
269 266 QMetaObject::invokeMethod(&sqpApp->variableController(), "deleteVariables",
270 267 Qt::QueuedConnection,
271 268 Q_ARG(QVector<std::shared_ptr<Variable> >, variables));
272 269 }
273 270 m_CustomGraphs.clear();
274 271
275 272 // Closes the remaining graphs inside the zone
276 273 zone->closeAllGraphs();
277 274
278 275 // Calculates the range of each graph which will be created
279 276 auto graphRange = getGraphRanges(event);
280 277
281 278 // Loops through the event products and create the graph
282 279 auto itRange = graphRange.cbegin();
283 280 for (auto eventProduct : event->getEventProducts()) {
284 281 auto productId = eventProduct.getProductId();
285 282
286 283 auto range = *itRange;
287 284 ++itRange;
288 285
289 286 SqpRange productRange;
290 287 productRange.m_TStart = eventProduct.getTStart();
291 288 productRange.m_TEnd = eventProduct.getTEnd();
292 289
293 290 auto context = new QObject{catalogueEventWidget};
294 291 QObject::connect(
295 292 &sqpApp->variableController(), &VariableController::variableAdded, context,
296 293 [this, catalogueEventWidget, zone, context, event, range, productRange,
297 294 productId](auto variable) {
298 295
299 296 if (variable->metadata().value(DataSourceItem::ID_DATA_KEY).toString()
300 297 == productId) {
301 298 auto graph = zone->createGraph(variable);
302 299 graph->setAutoRangeOnVariableInitialization(false);
303 300
304 301 auto selectionZone
305 302 = graph->addSelectionZone(event->getName(), productRange);
306 303 emit catalogueEventWidget->selectionZoneAdded(event, productId,
307 304 selectionZone);
308 305 m_CustomGraphs << graph;
309 306
310 307 graph->setGraphRange(range, true);
311 308
312 309 // Removes the graph from the graph list if it is closed manually
313 310 QObject::connect(graph, &VisualizationGraphWidget::destroyed,
314 311 [this, graph]() { m_CustomGraphs.removeAll(graph); });
315 312
316 313 delete context; // removes the connection
317 314 }
318 315 },
319 316 Qt::QueuedConnection);
320 317
321 318 QMetaObject::invokeMethod(&sqpApp->dataSourceController(),
322 319 "requestVariableFromProductIdKey", Qt::QueuedConnection,
323 320 Q_ARG(QString, productId));
324 321 }
325 322 }
326 323
327 324 void getSelectedItems(
328 325 QTreeView *treeView, QVector<std::shared_ptr<DBEvent> > &events,
329 326 QVector<QPair<std::shared_ptr<DBEvent>, std::shared_ptr<DBEventProduct> > > &eventProducts)
330 327 {
331 328 for (auto rowIndex : treeView->selectionModel()->selectedRows()) {
332 329 auto itemType = m_Model->itemTypeOf(rowIndex);
333 330 if (itemType == CatalogueEventsModel::ItemType::Event) {
334 331 events << m_Model->getEvent(rowIndex);
335 332 }
336 333 else if (itemType == CatalogueEventsModel::ItemType::EventProduct) {
337 334 eventProducts << qMakePair(m_Model->getParentEvent(rowIndex),
338 335 m_Model->getEventProduct(rowIndex));
339 336 }
340 337 }
341 338 }
342 339 };
343 340
344 341 CatalogueEventsWidget::CatalogueEventsWidget(QWidget *parent)
345 342 : QWidget(parent),
346 343 ui(new Ui::CatalogueEventsWidget),
347 344 impl{spimpl::make_unique_impl<CatalogueEventsWidgetPrivate>()}
348 345 {
349 346 ui->setupUi(this);
350 347
351 348 impl->m_Model = new CatalogueEventsModel{this};
352 349 ui->treeView->setModel(impl->m_Model);
353 350
354 351 ui->treeView->setSortingEnabled(true);
355 352 ui->treeView->setDragDropMode(QAbstractItemView::DragDrop);
356 353 ui->treeView->setDragEnabled(true);
357 354
358 355
359 356 connect(ui->btnTime, &QToolButton::clicked, [this](auto checked) {
360 357 if (checked) {
361 358 ui->btnChart->setChecked(false);
362 359 impl->m_ZonesForTimeMode
363 360 = impl->selectZone(this, impl->m_ZonesForTimeMode, true,
364 361 this->mapToGlobal(ui->btnTime->frameGeometry().center()));
365 362
366 363 impl->updateForTimeMode(ui->treeView);
367 364 }
368 365 });
369 366
370 367 connect(ui->btnChart, &QToolButton::clicked, [this](auto checked) {
371 368 if (checked) {
372 369 ui->btnTime->setChecked(false);
373 370 impl->m_ZoneForGraphMode
374 371 = impl->selectZone(this, {impl->m_ZoneForGraphMode}, false,
375 372 this->mapToGlobal(ui->btnChart->frameGeometry().center()))
376 373 .value(0);
377 374
378 375 impl->updateForGraphMode(this);
379 376 }
380 377 });
381 378
382 379 connect(ui->btnRemove, &QToolButton::clicked, [this]() {
383 380 QVector<std::shared_ptr<DBEvent> > events;
384 381 QVector<QPair<std::shared_ptr<DBEvent>, std::shared_ptr<DBEventProduct> > > eventProducts;
385 382 impl->getSelectedItems(ui->treeView, events, eventProducts);
386 383
387 384 if (!events.isEmpty() && eventProducts.isEmpty()) {
388 385
389 386 auto canRemoveEvent
390 387 = !this->isAllEventsDisplayed()
391 388 || (QMessageBox::warning(
392 389 this, tr("Remove Event(s)"),
393 390 tr("The selected event(s) will be permanently removed "
394 391 "from the repository!\nAre you sure you want to continue?"),
395 392 QMessageBox::Yes | QMessageBox::No, QMessageBox::No)
396 393 == QMessageBox::Yes);
397 394
398 395 if (canRemoveEvent) {
399 396 for (auto event : events) {
400 397 if (this->isAllEventsDisplayed()) {
401 398 sqpApp->catalogueController().removeEvent(event);
402 399 impl->removeEvent(event, ui->treeView);
403 400 }
404 401 else {
405 402 QVector<std::shared_ptr<DBCatalogue> > modifiedCatalogues;
406 403 for (auto catalogue : this->displayedCatalogues()) {
407 404 if (catalogue->removeEvent(event->getUniqId())) {
408 405 sqpApp->catalogueController().updateCatalogue(catalogue);
409 406 modifiedCatalogues << catalogue;
410 407 }
411 408 }
412 409 if (!modifiedCatalogues.empty()) {
413 410 emit eventCataloguesModified(modifiedCatalogues);
414 411 }
415 412 }
416 413 impl->m_Model->removeEvent(event);
417 414 }
418 415
419 416
420 417 emit this->eventsRemoved(events);
421 418 }
422 419 }
423 420 });
424 421
425 422 connect(ui->treeView, &QTreeView::clicked, this, &CatalogueEventsWidget::emitSelection);
426 423 connect(ui->treeView->selectionModel(), &QItemSelectionModel::selectionChanged, this,
427 424 &CatalogueEventsWidget::emitSelection);
428 425
429 426 ui->btnRemove->setEnabled(false); // Disabled by default when nothing is selected
430 427 connect(ui->treeView->selectionModel(), &QItemSelectionModel::selectionChanged, [this]() {
431 428 auto isNotMultiSelection = ui->treeView->selectionModel()->selectedRows().count() <= 1;
432 429 ui->btnChart->setEnabled(isNotMultiSelection);
433 430 ui->btnTime->setEnabled(isNotMultiSelection);
434 431
435 432 if (isNotMultiSelection && ui->btnTime->isChecked()) {
436 433 impl->updateForTimeMode(ui->treeView);
437 434 }
438 435 else if (isNotMultiSelection && ui->btnChart->isChecked()) {
439 436 impl->updateForGraphMode(this);
440 437 }
441 438
442 439 QVector<std::shared_ptr<DBEvent> > events;
443 440 QVector<QPair<std::shared_ptr<DBEvent>, std::shared_ptr<DBEventProduct> > > eventProducts;
444 441 impl->getSelectedItems(ui->treeView, events, eventProducts);
445 442 ui->btnRemove->setEnabled(!events.isEmpty() && eventProducts.isEmpty());
446 443 });
447 444
448 445 ui->treeView->header()->setSectionResizeMode(QHeaderView::ResizeToContents);
449 446 ui->treeView->header()->setSectionResizeMode((int)CatalogueEventsModel::Column::Tags,
450 447 QHeaderView::Stretch);
451 448 ui->treeView->header()->setSectionResizeMode((int)CatalogueEventsModel::Column::Validation,
452 QHeaderView::Fixed);
449 QHeaderView::ResizeToContents);
453 450 ui->treeView->header()->setSectionResizeMode((int)CatalogueEventsModel::Column::Name,
454 451 QHeaderView::Interactive);
455 ui->treeView->header()->resizeSection((int)CatalogueEventsModel::Column::Validation,
456 VALIDATION_COLUMN_SIZE);
457 452 ui->treeView->header()->setSectionResizeMode((int)CatalogueEventsModel::Column::TStart,
458 453 QHeaderView::ResizeToContents);
459 454 ui->treeView->header()->setSectionResizeMode((int)CatalogueEventsModel::Column::TEnd,
460 455 QHeaderView::ResizeToContents);
461 456 ui->treeView->header()->setSortIndicatorShown(true);
462 457
463 458 connect(impl->m_Model, &CatalogueEventsModel::modelSorted, [this]() {
464 459 auto allEvents = impl->m_Model->events();
465 460 for (auto event : allEvents) {
466 461 setEventChanges(event, sqpApp->catalogueController().eventHasChanges(event));
467 462 }
468 463 });
469 464
470 465 populateWithAllEvents();
471 466 }
472 467
473 468 CatalogueEventsWidget::~CatalogueEventsWidget()
474 469 {
475 470 delete ui;
476 471 }
477 472
478 473 void CatalogueEventsWidget::setVisualizationWidget(VisualizationWidget *visualization)
479 474 {
480 475 impl->m_VisualizationWidget = visualization;
481 476 }
482 477
483 478 void CatalogueEventsWidget::addEvent(const std::shared_ptr<DBEvent> &event)
484 479 {
485 480 impl->addEvent(event, ui->treeView);
486 481 }
487 482
488 483 void CatalogueEventsWidget::setEventChanges(const std::shared_ptr<DBEvent> &event, bool hasChanges)
489 484 {
490 485 impl->m_Model->refreshEvent(event);
491 486
492 487 auto eventIndex = impl->m_Model->indexOf(event);
493 488 auto validationIndex
494 489 = eventIndex.sibling(eventIndex.row(), (int)CatalogueEventsModel::Column::Validation);
495 490
496 491 if (validationIndex.isValid()) {
497 492 if (hasChanges) {
498 493 if (ui->treeView->indexWidget(validationIndex) == nullptr) {
499 494 auto widget = CatalogueExplorerHelper::buildValidationWidget(
500 495 ui->treeView,
501 496 [this, event]() {
502 497 sqpApp->catalogueController().saveEvent(event);
503 498 setEventChanges(event, false);
504 499 },
505 500 [this, event]() {
506 501 bool removed = false;
507 502 sqpApp->catalogueController().discardEvent(event, removed);
508 503 if (removed) {
509 504 impl->m_Model->removeEvent(event);
510 505 }
511 506 else {
512 507 setEventChanges(event, false);
513 508 impl->m_Model->refreshEvent(event, true);
514 509 }
515 510 emitSelection();
516 511 });
517 512 ui->treeView->setIndexWidget(validationIndex, widget);
513 ui->treeView->header()->resizeSection((int)CatalogueEventsModel::Column::Validation,
514 QHeaderView::ResizeToContents);
518 515 }
519 516 }
520 517 else {
521 518 // Note: the widget is destroyed
522 519 ui->treeView->setIndexWidget(validationIndex, nullptr);
523 520 }
524 521 }
525 522 else {
526 523 qCWarning(LOG_CatalogueEventsWidget())
527 524 << "setEventChanges: the event is not displayed in the model.";
528 525 }
529 526 }
530 527
531 528 QVector<std::shared_ptr<DBCatalogue> > CatalogueEventsWidget::displayedCatalogues() const
532 529 {
533 530 return impl->m_DisplayedCatalogues;
534 531 }
535 532
536 533 bool CatalogueEventsWidget::isAllEventsDisplayed() const
537 534 {
538 535 return impl->m_AllEventDisplayed;
539 536 }
540 537
541 538 bool CatalogueEventsWidget::isEventDisplayed(const std::shared_ptr<DBEvent> &event) const
542 539 {
543 540 return impl->m_Model->indexOf(event).isValid();
544 541 }
545 542
546 543 void CatalogueEventsWidget::refreshEvent(const std::shared_ptr<DBEvent> &event)
547 544 {
548 545 impl->m_Model->refreshEvent(event, true);
549 546 }
550 547
551 548 void CatalogueEventsWidget::populateWithCatalogues(
552 549 const QVector<std::shared_ptr<DBCatalogue> > &catalogues)
553 550 {
554 551 impl->m_DisplayedCatalogues = catalogues;
555 552 impl->m_AllEventDisplayed = false;
556 553
557 554 QSet<QUuid> eventIds;
558 555 QVector<std::shared_ptr<DBEvent> > events;
559 556
560 557 for (auto catalogue : catalogues) {
561 558 auto catalogueEvents = sqpApp->catalogueController().retrieveEventsFromCatalogue(catalogue);
562 559 for (auto event : catalogueEvents) {
563 560 if (!eventIds.contains(event->getUniqId())) {
564 561 events << event;
565 562 eventIds.insert(event->getUniqId());
566 563 }
567 564 }
568 565 }
569 566
570 567 impl->setEvents(events, this);
571 568 }
572 569
573 570 void CatalogueEventsWidget::populateWithAllEvents()
574 571 {
575 572 impl->m_DisplayedCatalogues.clear();
576 573 impl->m_AllEventDisplayed = true;
577 574
578 575 auto allEvents = sqpApp->catalogueController().retrieveAllEvents();
579 576
580 577 QVector<std::shared_ptr<DBEvent> > events;
581 578 for (auto event : allEvents) {
582 579 events << event;
583 580 }
584 581
585 582 impl->setEvents(events, this);
586 583 }
587 584
588 585 void CatalogueEventsWidget::clear()
589 586 {
590 587 impl->m_DisplayedCatalogues.clear();
591 588 impl->m_AllEventDisplayed = false;
592 589 impl->setEvents({}, this);
593 590 }
594 591
595 592 void CatalogueEventsWidget::refresh()
596 593 {
597 594 if (isAllEventsDisplayed()) {
598 595 populateWithAllEvents();
599 596 }
600 597 else if (!impl->m_DisplayedCatalogues.isEmpty()) {
601 598 populateWithCatalogues(impl->m_DisplayedCatalogues);
602 599 }
603 600 }
604 601
605 602 void CatalogueEventsWidget::emitSelection()
606 603 {
607 604 QVector<std::shared_ptr<DBEvent> > events;
608 605 QVector<QPair<std::shared_ptr<DBEvent>, std::shared_ptr<DBEventProduct> > > eventProducts;
609 606 impl->getSelectedItems(ui->treeView, events, eventProducts);
610 607
611 608 if (!events.isEmpty() && eventProducts.isEmpty()) {
612 609 emit eventsSelected(events);
613 610 }
614 611 else if (events.isEmpty() && !eventProducts.isEmpty()) {
615 612 emit eventProductsSelected(eventProducts);
616 613 }
617 614 else {
618 615 emit selectionCleared();
619 616 }
620 617 }
621 618
622 619
623 620 void CatalogueEventsWidget::keyPressEvent(QKeyEvent *event)
624 621 {
625 622 switch (event->key()) {
626 623 case Qt::Key_Delete: {
627 624 ui->btnRemove->click();
628 625 }
629 626 default:
630 627 break;
631 628 }
632 629 }
@@ -1,32 +1,30
1 1 #include "Catalogue/CatalogueExplorerHelper.h"
2 2
3 3 #include <QBoxLayout>
4 4 #include <QToolButton>
5 5
6 const auto VALIDATION_BUTTON_ICON_SIZE = 12;
7 6
8 7 QWidget *CatalogueExplorerHelper::buildValidationWidget(QWidget *parent, std::function<void()> save,
9 8 std::function<void()> discard)
10 9 {
11 10 auto widget = new QWidget{parent};
12 11
13 12 auto layout = new QHBoxLayout{widget};
14 layout->setContentsMargins(0, 0, 0, 0);
15 layout->setSpacing(0);
16 13
17 14 auto btnValid = new QToolButton{widget};
18 15 btnValid->setIcon(QIcon{":/icones/save"});
19 btnValid->setIconSize(QSize{VALIDATION_BUTTON_ICON_SIZE, VALIDATION_BUTTON_ICON_SIZE});
20 16 btnValid->setAutoRaise(true);
21 17 QObject::connect(btnValid, &QToolButton::clicked, save);
22 18 layout->addWidget(btnValid);
23 19
24 20 auto btnDiscard = new QToolButton{widget};
25 21 btnDiscard->setIcon(QIcon{":/icones/discard"});
26 btnDiscard->setIconSize(QSize{VALIDATION_BUTTON_ICON_SIZE, VALIDATION_BUTTON_ICON_SIZE});
27 22 btnDiscard->setAutoRaise(true);
28 23 QObject::connect(btnDiscard, &QToolButton::clicked, discard);
29 24 layout->addWidget(btnDiscard);
30 25
26 layout->setContentsMargins(0, 0, 0, 0);
27 layout->setSpacing(0);
28
31 29 return widget;
32 30 }
@@ -1,444 +1,447
1 1 #include "Catalogue/CatalogueSideBarWidget.h"
2 2 #include "ui_CatalogueSideBarWidget.h"
3 3 #include <SqpApplication.h>
4 4
5 5 #include <Catalogue/CatalogueController.h>
6 6 #include <Catalogue/CatalogueExplorerHelper.h>
7 7 #include <Catalogue/CatalogueTreeItems/CatalogueTextTreeItem.h>
8 8 #include <Catalogue/CatalogueTreeItems/CatalogueTreeItem.h>
9 9 #include <Catalogue/CatalogueTreeModel.h>
10 10 #include <CatalogueDao.h>
11 11 #include <Common/MimeTypesDef.h>
12 12 #include <ComparaisonPredicate.h>
13 13 #include <DBCatalogue.h>
14 14
15 15 #include <QKeyEvent>
16 16 #include <QMenu>
17 17 #include <QMessageBox>
18 18 #include <QMimeData>
19 19
20 20 Q_LOGGING_CATEGORY(LOG_CatalogueSideBarWidget, "CatalogueSideBarWidget")
21 21
22 22
23 23 constexpr auto ALL_EVENT_ITEM_TYPE = CatalogueAbstractTreeItem::DEFAULT_TYPE + 1;
24 24 constexpr auto TRASH_ITEM_TYPE = CatalogueAbstractTreeItem::DEFAULT_TYPE + 2;
25 25 constexpr auto CATALOGUE_ITEM_TYPE = CatalogueAbstractTreeItem::DEFAULT_TYPE + 3;
26 26 constexpr auto DATABASE_ITEM_TYPE = CatalogueAbstractTreeItem::DEFAULT_TYPE + 4;
27 27
28 28
29 29 struct CatalogueSideBarWidget::CatalogueSideBarWidgetPrivate {
30 30
31 31 CatalogueTreeModel *m_TreeModel = nullptr;
32 32
33 33 void configureTreeWidget(QTreeView *treeView);
34 34 QModelIndex addDatabaseItem(const QString &name);
35 35 CatalogueAbstractTreeItem *getDatabaseItem(const QString &name);
36 36 CatalogueAbstractTreeItem *addCatalogueItem(const std::shared_ptr<DBCatalogue> &catalogue,
37 37 const QModelIndex &databaseIndex);
38 38
39 39 CatalogueTreeItem *getCatalogueItem(const std::shared_ptr<DBCatalogue> &catalogue) const;
40 40 void setHasChanges(bool value, const QModelIndex &index, CatalogueSideBarWidget *sideBarWidget);
41 41 bool hasChanges(const QModelIndex &index, QTreeView *treeView);
42 42
43 43 int selectionType(QTreeView *treeView) const
44 44 {
45 45 auto selectedItems = treeView->selectionModel()->selectedRows();
46 46 if (selectedItems.isEmpty()) {
47 47 return CatalogueAbstractTreeItem::DEFAULT_TYPE;
48 48 }
49 49 else {
50 50 auto firstIndex = selectedItems.first();
51 51 auto firstItem = m_TreeModel->item(firstIndex);
52 52 if (!firstItem) {
53 53 Q_ASSERT(false);
54 54 return CatalogueAbstractTreeItem::DEFAULT_TYPE;
55 55 }
56 56 auto selectionType = firstItem->type();
57 57
58 58 for (auto itemIndex : selectedItems) {
59 59 auto item = m_TreeModel->item(itemIndex);
60 60 if (!item || item->type() != selectionType) {
61 61 // Incoherent multi selection
62 62 selectionType = CatalogueAbstractTreeItem::DEFAULT_TYPE;
63 63 break;
64 64 }
65 65 }
66 66
67 67 return selectionType;
68 68 }
69 69 }
70 70
71 71 QVector<std::shared_ptr<DBCatalogue> > selectedCatalogues(QTreeView *treeView) const
72 72 {
73 73 QVector<std::shared_ptr<DBCatalogue> > catalogues;
74 74 auto selectedItems = treeView->selectionModel()->selectedRows();
75 75 for (auto itemIndex : selectedItems) {
76 76 auto item = m_TreeModel->item(itemIndex);
77 77 if (item && item->type() == CATALOGUE_ITEM_TYPE) {
78 78 catalogues.append(static_cast<CatalogueTreeItem *>(item)->catalogue());
79 79 }
80 80 }
81 81
82 82 return catalogues;
83 83 }
84 84
85 85 QStringList selectedRepositories(QTreeView *treeView) const
86 86 {
87 87 QStringList repositories;
88 88 auto selectedItems = treeView->selectionModel()->selectedRows();
89 89 for (auto itemIndex : selectedItems) {
90 90 auto item = m_TreeModel->item(itemIndex);
91 91 if (item && item->type() == DATABASE_ITEM_TYPE) {
92 92 repositories.append(item->text());
93 93 }
94 94 }
95 95
96 96 return repositories;
97 97 }
98 98 };
99 99
100 100 CatalogueSideBarWidget::CatalogueSideBarWidget(QWidget *parent)
101 101 : QWidget(parent),
102 102 ui(new Ui::CatalogueSideBarWidget),
103 103 impl{spimpl::make_unique_impl<CatalogueSideBarWidgetPrivate>()}
104 104 {
105 105 ui->setupUi(this);
106 106
107 107 impl->m_TreeModel = new CatalogueTreeModel(this);
108 108 ui->treeView->setModel(impl->m_TreeModel);
109 109
110 110 impl->configureTreeWidget(ui->treeView);
111 111
112 112 ui->treeView->header()->setStretchLastSection(false);
113 113 ui->treeView->header()->setSectionResizeMode(QHeaderView::ResizeToContents);
114 ui->treeView->header()->setSectionResizeMode(0, QHeaderView::Stretch);
114 ui->treeView->header()->setSectionResizeMode((int)CatalogueTreeModel::Column::Name,
115 QHeaderView::Stretch);
115 116
116 117 connect(ui->treeView, &QTreeView::clicked, this, &CatalogueSideBarWidget::emitSelection);
117 118 connect(ui->treeView->selectionModel(), &QItemSelectionModel::currentChanged, this,
118 119 &CatalogueSideBarWidget::emitSelection);
119 120
120 121
121 122 connect(ui->btnAdd, &QToolButton::clicked, [this]() {
122 123 auto catalogue = std::make_shared<DBCatalogue>();
123 124 catalogue->setName(QString("Cat"));
124 125 sqpApp->catalogueController().addCatalogue(catalogue);
125 126 auto item = this->addCatalogue(catalogue, REPOSITORY_DEFAULT);
126 127 this->setCatalogueChanges(catalogue, true);
127 128 ui->treeView->edit(impl->m_TreeModel->indexOf(item));
128 129
129 130 });
130 131
131 132
132 133 connect(impl->m_TreeModel, &CatalogueTreeModel::itemDropped,
133 134 [this](auto index, auto mimeData, auto action) {
134 135 auto item = impl->m_TreeModel->item(index);
135 136 if (item && item->type() == CATALOGUE_ITEM_TYPE) {
136 137 auto catalogue = static_cast<CatalogueTreeItem *>(item)->catalogue();
137 138 this->setCatalogueChanges(catalogue, true);
138 139 }
139 140
140 141 if (action == Qt::MoveAction) {
141 142 /// Display a save button on source catalogues
142 143 auto sourceCatalogues = sqpApp->catalogueController().cataloguesForMimeData(
143 144 mimeData->data(MIME_TYPE_SOURCE_CATALOGUE_LIST));
144 145 for (auto catalogue : sourceCatalogues) {
145 146 if (auto catalogueItem = impl->getCatalogueItem(catalogue)) {
146 147 this->setCatalogueChanges(catalogue, true);
147 148 }
148 149 }
149 150 }
150 151 });
151 152
152 153 connect(ui->btnRemove, &QToolButton::clicked, [this]() {
153 154 QVector<QPair<std::shared_ptr<DBCatalogue>, CatalogueAbstractTreeItem *> >
154 155 cataloguesToItems;
155 156 auto selectedIndexes = ui->treeView->selectionModel()->selectedRows();
156 157
157 158 for (auto index : selectedIndexes) {
158 159 auto item = impl->m_TreeModel->item(index);
159 160 if (item && item->type() == CATALOGUE_ITEM_TYPE) {
160 161 auto catalogue = static_cast<CatalogueTreeItem *>(item)->catalogue();
161 162 cataloguesToItems << qMakePair(catalogue, item);
162 163 }
163 164 }
164 165
165 166 if (!cataloguesToItems.isEmpty()) {
166 167
167 168 if (QMessageBox::warning(this, tr("Remove Catalogue(s)"),
168 169 tr("The selected catalogues(s) will be completly removed "
169 170 "from the repository!\nAre you sure you want to continue?"),
170 171 QMessageBox::Yes | QMessageBox::No, QMessageBox::No)
171 172 == QMessageBox::Yes) {
172 173
173 174 for (auto catalogueToItem : cataloguesToItems) {
174 175 sqpApp->catalogueController().removeCatalogue(catalogueToItem.first);
175 176 impl->m_TreeModel->removeChildItem(
176 177 catalogueToItem.second,
177 178 impl->m_TreeModel->indexOf(catalogueToItem.second->parent()));
178 179 }
179 180 emitSelection();
180 181 }
181 182 }
182 183 });
183 184
184 185 connect(impl->m_TreeModel, &CatalogueTreeModel::itemRenamed, [this](auto index) {
185 186 auto selectedIndexes = ui->treeView->selectionModel()->selectedRows();
186 187 if (selectedIndexes.contains(index)) {
187 188 this->emitSelection();
188 189 }
189 190 impl->setHasChanges(true, index, this);
190 191 });
191 192
192 193 ui->treeView->setContextMenuPolicy(Qt::CustomContextMenu);
193 194 connect(ui->treeView, &QTreeView::customContextMenuRequested, this,
194 195 &CatalogueSideBarWidget::onContextMenuRequested);
195 196 }
196 197
197 198 CatalogueSideBarWidget::~CatalogueSideBarWidget()
198 199 {
199 200 delete ui;
200 201 }
201 202
202 203 CatalogueAbstractTreeItem *
203 204 CatalogueSideBarWidget::addCatalogue(const std::shared_ptr<DBCatalogue> &catalogue,
204 205 const QString &repository)
205 206 {
206 207 auto repositoryItem = impl->getDatabaseItem(repository);
207 208 return impl->addCatalogueItem(catalogue, impl->m_TreeModel->indexOf(repositoryItem));
208 209 }
209 210
210 211 void CatalogueSideBarWidget::setCatalogueChanges(const std::shared_ptr<DBCatalogue> &catalogue,
211 212 bool hasChanges)
212 213 {
213 214 if (auto catalogueItem = impl->getCatalogueItem(catalogue)) {
214 215 auto index = impl->m_TreeModel->indexOf(catalogueItem);
215 216 impl->setHasChanges(hasChanges, index, this);
216 217 // catalogueItem->refresh();
217 218 }
218 219 }
219 220
220 221 QVector<std::shared_ptr<DBCatalogue> >
221 222 CatalogueSideBarWidget::getCatalogues(const QString &repository) const
222 223 {
223 224 QVector<std::shared_ptr<DBCatalogue> > result;
224 225 auto repositoryItem = impl->getDatabaseItem(repository);
225 226 for (auto child : repositoryItem->children()) {
226 227 if (child->type() == CATALOGUE_ITEM_TYPE) {
227 228 auto catalogueItem = static_cast<CatalogueTreeItem *>(child);
228 229 result << catalogueItem->catalogue();
229 230 }
230 231 else {
231 232 qCWarning(LOG_CatalogueSideBarWidget()) << "getCatalogues: invalid structure";
232 233 }
233 234 }
234 235
235 236 return result;
236 237 }
237 238
238 239 void CatalogueSideBarWidget::emitSelection()
239 240 {
240 241 auto selectionType = impl->selectionType(ui->treeView);
241 242
242 243 switch (selectionType) {
243 244 case CATALOGUE_ITEM_TYPE:
244 245 emit this->catalogueSelected(impl->selectedCatalogues(ui->treeView));
245 246 break;
246 247 case DATABASE_ITEM_TYPE:
247 248 emit this->databaseSelected(impl->selectedRepositories(ui->treeView));
248 249 break;
249 250 case ALL_EVENT_ITEM_TYPE:
250 251 emit this->allEventsSelected();
251 252 break;
252 253 case TRASH_ITEM_TYPE:
253 254 emit this->trashSelected();
254 255 break;
255 256 default:
256 257 emit this->selectionCleared();
257 258 break;
258 259 }
259 260 }
260 261
261 262 void CatalogueSideBarWidget::onContextMenuRequested(const QPoint &pos)
262 263 {
263 264 QMenu menu{this};
264 265
265 266 auto currentIndex = ui->treeView->currentIndex();
266 267 auto currentItem = impl->m_TreeModel->item(currentIndex);
267 268 if (!currentItem) {
268 269 return;
269 270 }
270 271
271 272 switch (currentItem->type()) {
272 273 case CATALOGUE_ITEM_TYPE:
273 274 menu.addAction("Rename", [this, currentIndex]() { ui->treeView->edit(currentIndex); });
274 275 break;
275 276 case DATABASE_ITEM_TYPE:
276 277 break;
277 278 case ALL_EVENT_ITEM_TYPE:
278 279 break;
279 280 case TRASH_ITEM_TYPE:
280 281 menu.addAction("Empty Trash", []() {
281 282 // TODO
282 283 });
283 284 break;
284 285 default:
285 286 break;
286 287 }
287 288
288 289 if (!menu.isEmpty()) {
289 290 menu.exec(ui->treeView->mapToGlobal(pos));
290 291 }
291 292 }
292 293
293 294 void CatalogueSideBarWidget::CatalogueSideBarWidgetPrivate::configureTreeWidget(QTreeView *treeView)
294 295 {
295 296 auto allEventsItem = new CatalogueTextTreeItem{QIcon{":/icones/allEvents.png"}, "All Events",
296 297 ALL_EVENT_ITEM_TYPE};
297 298 auto allEventIndex = m_TreeModel->addTopLevelItem(allEventsItem);
298 299 treeView->setCurrentIndex(allEventIndex);
299 300
300 301 auto trashItem
301 302 = new CatalogueTextTreeItem{QIcon{":/icones/trash.png"}, "Trash", TRASH_ITEM_TYPE};
302 303 m_TreeModel->addTopLevelItem(trashItem);
303 304
304 305 auto separator = new QFrame{treeView};
305 306 separator->setFrameShape(QFrame::HLine);
306 307 auto separatorItem
307 308 = new CatalogueTextTreeItem{QIcon{}, QString{}, CatalogueAbstractTreeItem::DEFAULT_TYPE};
308 309 separatorItem->setEnabled(false);
309 310 auto separatorIndex = m_TreeModel->addTopLevelItem(separatorItem);
310 311 treeView->setIndexWidget(separatorIndex, separator);
311 312
312 313 auto repositories = sqpApp->catalogueController().getRepositories();
313 314 for (auto dbname : repositories) {
314 315 auto dbIndex = addDatabaseItem(dbname);
315 316 auto catalogues = sqpApp->catalogueController().retrieveCatalogues(dbname);
316 317 for (auto catalogue : catalogues) {
317 318 addCatalogueItem(catalogue, dbIndex);
318 319 }
319 320 }
320 321
321 322 treeView->expandAll();
322 323 }
323 324
324 325 QModelIndex
325 326 CatalogueSideBarWidget::CatalogueSideBarWidgetPrivate::addDatabaseItem(const QString &name)
326 327 {
327 328 auto databaseItem
328 329 = new CatalogueTextTreeItem{QIcon{":/icones/database.png"}, {name}, DATABASE_ITEM_TYPE};
329 330 auto databaseIndex = m_TreeModel->addTopLevelItem(databaseItem);
330 331
331 332 return databaseIndex;
332 333 }
333 334
334 335 CatalogueAbstractTreeItem *
335 336 CatalogueSideBarWidget::CatalogueSideBarWidgetPrivate::getDatabaseItem(const QString &name)
336 337 {
337 338 for (auto item : m_TreeModel->topLevelItems()) {
338 339 if (item->type() == DATABASE_ITEM_TYPE && item->text() == name) {
339 340 return item;
340 341 }
341 342 }
342 343
343 344 return nullptr;
344 345 }
345 346
346 347 CatalogueAbstractTreeItem *CatalogueSideBarWidget::CatalogueSideBarWidgetPrivate::addCatalogueItem(
347 348 const std::shared_ptr<DBCatalogue> &catalogue, const QModelIndex &databaseIndex)
348 349 {
349 350 auto catalogueItem
350 351 = new CatalogueTreeItem{catalogue, QIcon{":/icones/catalogue.png"}, CATALOGUE_ITEM_TYPE};
351 352 m_TreeModel->addChildItem(catalogueItem, databaseIndex);
352 353
353 354 return catalogueItem;
354 355 }
355 356
356 357 CatalogueTreeItem *CatalogueSideBarWidget::CatalogueSideBarWidgetPrivate::getCatalogueItem(
357 358 const std::shared_ptr<DBCatalogue> &catalogue) const
358 359 {
359 360 for (auto item : m_TreeModel->topLevelItems()) {
360 361 if (item->type() == DATABASE_ITEM_TYPE) {
361 362 for (auto childItem : item->children()) {
362 363 if (childItem->type() == CATALOGUE_ITEM_TYPE) {
363 364 auto catalogueItem = static_cast<CatalogueTreeItem *>(childItem);
364 365 if (catalogueItem->catalogue()->getUniqId() == catalogue->getUniqId()) {
365 366 return catalogueItem;
366 367 }
367 368 }
368 369 else {
369 370 qCWarning(LOG_CatalogueSideBarWidget()) << "getCatalogueItem: Invalid tree "
370 371 "structure. A database item should "
371 372 "only contain catalogues.";
372 373 Q_ASSERT(false);
373 374 }
374 375 }
375 376 }
376 377 }
377 378
378 379 return nullptr;
379 380 }
380 381
381 382 void CatalogueSideBarWidget::CatalogueSideBarWidgetPrivate::setHasChanges(
382 383 bool value, const QModelIndex &index, CatalogueSideBarWidget *sideBarWidget)
383 384 {
384 385 std::shared_ptr<DBCatalogue> catalogue = nullptr;
385 386 auto item = m_TreeModel->item(index);
386 387 if (item && item->type() == CATALOGUE_ITEM_TYPE) {
387 388 catalogue = static_cast<CatalogueTreeItem *>(item)->catalogue();
388 389 }
389 390
390 391 auto validationIndex = index.sibling(index.row(), (int)CatalogueTreeModel::Column::Validation);
391 392 if (value) {
392 393 if (!hasChanges(validationIndex, sideBarWidget->ui->treeView)) {
393 394 auto widget = CatalogueExplorerHelper::buildValidationWidget(
394 395 sideBarWidget->ui->treeView,
395 396 [this, validationIndex, sideBarWidget, catalogue]() {
396 397 if (catalogue) {
397 398 sqpApp->catalogueController().saveCatalogue(catalogue);
398 399 emit sideBarWidget->catalogueSaved(catalogue);
399 400 }
400 401 setHasChanges(false, validationIndex, sideBarWidget);
401 402 },
402 403 [this, validationIndex, sideBarWidget, catalogue, item]() {
403 404 if (catalogue) {
404 405 bool removed;
405 406 sqpApp->catalogueController().discardCatalogue(catalogue, removed);
406 407
407 408 if (removed) {
408 409 m_TreeModel->removeChildItem(item,
409 410 m_TreeModel->indexOf(item->parent()));
410 411 }
411 412 else {
412 413 m_TreeModel->refresh(m_TreeModel->indexOf(item));
413 414 setHasChanges(false, validationIndex, sideBarWidget);
414 415 }
415 416 sideBarWidget->emitSelection();
416 417 }
417 418 });
418 419 sideBarWidget->ui->treeView->setIndexWidget(validationIndex, widget);
420 sideBarWidget->ui->treeView->header()->resizeSection(
421 (int)CatalogueTreeModel::Column::Validation, QHeaderView::ResizeToContents);
419 422 }
420 423 }
421 424 else {
422 425 // Note: the widget is destroyed
423 426 sideBarWidget->ui->treeView->setIndexWidget(validationIndex, nullptr);
424 427 }
425 428 }
426 429
427 430 bool CatalogueSideBarWidget::CatalogueSideBarWidgetPrivate::hasChanges(const QModelIndex &index,
428 431 QTreeView *treeView)
429 432 {
430 433 auto validationIndex = index.sibling(index.row(), (int)CatalogueTreeModel::Column::Validation);
431 434 return treeView->indexWidget(validationIndex) != nullptr;
432 435 }
433 436
434 437
435 438 void CatalogueSideBarWidget::keyPressEvent(QKeyEvent *event)
436 439 {
437 440 switch (event->key()) {
438 441 case Qt::Key_Delete: {
439 442 ui->btnRemove->click();
440 443 }
441 444 default:
442 445 break;
443 446 }
444 447 }
General Comments 0
You need to be logged in to leave comments. Login now