##// END OF EJS Templates
fix refresh of events after a discard
trabillard -
r1300:f0b824bb0975
parent child
Show More
@@ -1,68 +1,68
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 9 class DBEvent;
10 10 class DBEventProduct;
11 11
12 12 Q_DECLARE_LOGGING_CATEGORY(LOG_CatalogueEventsModel)
13 13
14 14 class CatalogueEventsModel : public QAbstractItemModel {
15 15 Q_OBJECT
16 16
17 17 signals:
18 18 void modelSorted();
19 19
20 20 public:
21 21 CatalogueEventsModel(QObject *parent = nullptr);
22 22
23 23 enum class Column { Name, TStart, TEnd, Tags, Product, Validation, NbColumn };
24 24
25 25 void setEvents(const QVector<std::shared_ptr<DBEvent> > &events);
26 26 void addEvent(const std::shared_ptr<DBEvent> &event);
27 27 void removeEvent(const std::shared_ptr<DBEvent> &event);
28 28 QVector<std::shared_ptr<DBEvent> > events() const;
29 29
30 30 enum class ItemType { Root, Event, EventProduct };
31 31 ItemType itemTypeOf(const QModelIndex &index) const;
32 32 std::shared_ptr<DBEvent> getEvent(const QModelIndex &index) const;
33 33 std::shared_ptr<DBEvent> getParentEvent(const QModelIndex &index) const;
34 34 std::shared_ptr<DBEventProduct> getEventProduct(const QModelIndex &index) const;
35 35
36 36 /// Refresh the data for the specified event
37 void refreshEvent(const std::shared_ptr<DBEvent> &event);
37 void refreshEvent(const std::shared_ptr<DBEvent> &event, bool refreshEventProducts = false);
38 38
39 39 /// Returns a QModelIndex which represent the specified event
40 40 QModelIndex indexOf(const std::shared_ptr<DBEvent> &event) const;
41 41
42 42 /// Marks a change flag on the specified event to allow sorting on the validation column
43 43 void setEventHasChanges(const std::shared_ptr<DBEvent> &event, bool hasChanges);
44 44
45 45 /// Returns true if the specified event has unsaved changes
46 46 bool eventsHasChanges(const std::shared_ptr<DBEvent> &event) const;
47 47
48 48 // Model
49 49 QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const;
50 50 QModelIndex parent(const QModelIndex &index) const;
51 51 int rowCount(const QModelIndex &parent = QModelIndex()) const override;
52 52 int columnCount(const QModelIndex &parent = QModelIndex()) const override;
53 53 Qt::ItemFlags flags(const QModelIndex &index) const override;
54 54 QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
55 55 QVariant headerData(int section, Qt::Orientation orientation,
56 56 int role = Qt::DisplayRole) const override;
57 57 void sort(int column, Qt::SortOrder order = Qt::AscendingOrder) override;
58 58
59 59 Qt::DropActions supportedDragActions() const override;
60 60 QStringList mimeTypes() const override;
61 61 QMimeData *mimeData(const QModelIndexList &indexes) const override;
62 62
63 63 private:
64 64 class CatalogueEventsModelPrivate;
65 65 spimpl::unique_impl_ptr<CatalogueEventsModelPrivate> impl;
66 66 };
67 67
68 68 #endif // SCIQLOP_CATALOGUEEVENTSMODEL_H
@@ -1,55 +1,58
1 1 #ifndef SCIQLOP_CATALOGUEEVENTSWIDGET_H
2 2 #define SCIQLOP_CATALOGUEEVENTSWIDGET_H
3 3
4 4 #include <Common/spimpl.h>
5 5 #include <QLoggingCategory>
6 6 #include <QWidget>
7 7
8 8 class DBCatalogue;
9 9 class DBEvent;
10 10 class DBEventProduct;
11 11 class VisualizationWidget;
12 12
13 13 namespace Ui {
14 14 class CatalogueEventsWidget;
15 15 }
16 16
17 17 Q_DECLARE_LOGGING_CATEGORY(LOG_CatalogueEventsWidget)
18 18
19 19 class CatalogueEventsWidget : public QWidget {
20 20 Q_OBJECT
21 21
22 22 signals:
23 23 void eventsSelected(const QVector<std::shared_ptr<DBEvent> > &event);
24 24 void eventProductsSelected(
25 25 const QVector<QPair<std::shared_ptr<DBEvent>, std::shared_ptr<DBEventProduct> > >
26 26 &eventproducts);
27 27 void selectionCleared();
28 28
29 29 public:
30 30 explicit CatalogueEventsWidget(QWidget *parent = 0);
31 31 virtual ~CatalogueEventsWidget();
32 32
33 33 void setVisualizationWidget(VisualizationWidget *visualization);
34 34
35 35 void addEvent(const std::shared_ptr<DBEvent> &event);
36 36 void setEventChanges(const std::shared_ptr<DBEvent> &event, bool hasChanges);
37 37
38 38 QVector<std::shared_ptr<DBCatalogue> > displayedCatalogues() const;
39 39 bool isAllEventsDisplayed() const;
40 40 bool isEventDisplayed(const std::shared_ptr<DBEvent> &event) const;
41 41
42 42 public slots:
43 43 void populateWithCatalogues(const QVector<std::shared_ptr<DBCatalogue> > &catalogues);
44 44 void populateWithAllEvents();
45 45 void clear();
46 46 void refresh();
47 47
48 48 private:
49 49 Ui::CatalogueEventsWidget *ui;
50 50
51 51 class CatalogueEventsWidgetPrivate;
52 52 spimpl::unique_impl_ptr<CatalogueEventsWidgetPrivate> impl;
53
54 private slots:
55 void emitSelection();
53 56 };
54 57
55 58 #endif // SCIQLOP_CATALOGUEEVENTSWIDGET_H
@@ -1,437 +1,461
1 1 #include "Catalogue/CatalogueEventsModel.h"
2 2
3 3 #include <Catalogue/CatalogueController.h>
4 4 #include <Common/DateUtils.h>
5 5 #include <Common/MimeTypesDef.h>
6 6 #include <DBEvent.h>
7 7 #include <DBEventProduct.h>
8 8 #include <DBTag.h>
9 9 #include <Data/SqpRange.h>
10 10 #include <SqpApplication.h>
11 11 #include <Time/TimeController.h>
12 12
13 13 #include <list>
14 14 #include <unordered_map>
15 15
16 16 #include <QHash>
17 17 #include <QMimeData>
18 18
19 19 Q_LOGGING_CATEGORY(LOG_CatalogueEventsModel, "CatalogueEventsModel")
20 20
21 21 const auto EVENT_ITEM_TYPE = 1;
22 22 const auto EVENT_PRODUCT_ITEM_TYPE = 2;
23 23
24 24 struct CatalogueEventsModel::CatalogueEventsModelPrivate {
25 25 QVector<std::shared_ptr<DBEvent> > m_Events;
26 26 std::unordered_map<DBEvent *, QVector<std::shared_ptr<DBEventProduct> > > m_EventProducts;
27 27
28 28 QStringList columnNames()
29 29 {
30 30 return QStringList{tr("Event"), tr("TStart"), tr("TEnd"),
31 31 tr("Tags"), tr("Product"), tr("")};
32 32 }
33 33
34 34 QVariant sortData(int col, const std::shared_ptr<DBEvent> &event) const
35 35 {
36 36 if (col == (int)CatalogueEventsModel::Column::Validation) {
37 37 auto hasChanges = sqpApp->catalogueController().eventHasChanges(event);
38 38 return hasChanges ? true : QVariant();
39 39 }
40 40
41 41 return eventData(col, event);
42 42 }
43 43
44 44 QVariant eventData(int col, const std::shared_ptr<DBEvent> &event) const
45 45 {
46 46 switch (static_cast<Column>(col)) {
47 47 case CatalogueEventsModel::Column::Name:
48 48 return event->getName();
49 49 case CatalogueEventsModel::Column::TStart:
50 50 return nbEventProducts(event) > 0 ? DateUtils::dateTime(event->getTStart())
51 51 : QVariant{};
52 52 case CatalogueEventsModel::Column::TEnd:
53 53 return nbEventProducts(event) > 0 ? DateUtils::dateTime(event->getTEnd())
54 54 : QVariant{};
55 55 case CatalogueEventsModel::Column::Product:
56 56 return QString::number(nbEventProducts(event)) + " product(s)";
57 57 case CatalogueEventsModel::Column::Tags: {
58 58 QString tagList;
59 59 auto tags = event->getTags();
60 60 for (auto tag : tags) {
61 61 tagList += tag.getName();
62 62 tagList += ' ';
63 63 }
64 64
65 65 return tagList;
66 66 }
67 67 case CatalogueEventsModel::Column::Validation:
68 68 return QVariant();
69 69 default:
70 70 break;
71 71 }
72 72
73 73 Q_ASSERT(false);
74 74 return QStringLiteral("Unknown Data");
75 75 }
76 76
77 77 void parseEventProduct(const std::shared_ptr<DBEvent> &event)
78 78 {
79 79 for (auto product : event->getEventProducts()) {
80 80 m_EventProducts[event.get()].append(std::make_shared<DBEventProduct>(product));
81 81 }
82 82 }
83 83
84 84 int nbEventProducts(const std::shared_ptr<DBEvent> &event) const
85 85 {
86 86 auto eventProductsIt = m_EventProducts.find(event.get());
87 87 if (eventProductsIt != m_EventProducts.cend()) {
88 88 return m_EventProducts.at(event.get()).count();
89 89 }
90 90 else {
91 91 return 0;
92 92 }
93 93 }
94 94
95 95 QVariant eventProductData(int col, const std::shared_ptr<DBEventProduct> &eventProduct) const
96 96 {
97 97 switch (static_cast<Column>(col)) {
98 98 case CatalogueEventsModel::Column::Name:
99 99 return eventProduct->getProductId();
100 100 case CatalogueEventsModel::Column::TStart:
101 101 return DateUtils::dateTime(eventProduct->getTStart());
102 102 case CatalogueEventsModel::Column::TEnd:
103 103 return DateUtils::dateTime(eventProduct->getTEnd());
104 104 case CatalogueEventsModel::Column::Product:
105 105 return eventProduct->getProductId();
106 106 case CatalogueEventsModel::Column::Tags:
107 107 return QString();
108 108 case CatalogueEventsModel::Column::Validation:
109 109 return QVariant();
110 110 default:
111 111 break;
112 112 }
113 113
114 114 Q_ASSERT(false);
115 115 return QStringLiteral("Unknown Data");
116 116 }
117 117
118 118 void refreshChildrenOfIndex(CatalogueEventsModel *model, const QModelIndex &index) const
119 119 {
120 120 auto childCount = model->rowCount(index);
121 121 auto colCount = model->columnCount();
122 122 emit model->dataChanged(model->index(0, 0, index),
123 123 model->index(childCount, colCount, index));
124 124 }
125 125 };
126 126
127 127 CatalogueEventsModel::CatalogueEventsModel(QObject *parent)
128 128 : QAbstractItemModel(parent), impl{spimpl::make_unique_impl<CatalogueEventsModelPrivate>()}
129 129 {
130 130 }
131 131
132 132 void CatalogueEventsModel::setEvents(const QVector<std::shared_ptr<DBEvent> > &events)
133 133 {
134 134 beginResetModel();
135 135
136 136 impl->m_Events = events;
137 137 impl->m_EventProducts.clear();
138 138 for (auto event : events) {
139 139 impl->parseEventProduct(event);
140 140 }
141 141
142 142 endResetModel();
143 143 }
144 144
145 145 std::shared_ptr<DBEvent> CatalogueEventsModel::getEvent(const QModelIndex &index) const
146 146 {
147 147 if (itemTypeOf(index) == CatalogueEventsModel::ItemType::Event) {
148 148 return impl->m_Events.value(index.row());
149 149 }
150 150 else {
151 151 return nullptr;
152 152 }
153 153 }
154 154
155 155 std::shared_ptr<DBEvent> CatalogueEventsModel::getParentEvent(const QModelIndex &index) const
156 156 {
157 157 if (itemTypeOf(index) == CatalogueEventsModel::ItemType::EventProduct) {
158 158 return getEvent(index.parent());
159 159 }
160 160 else {
161 161 return nullptr;
162 162 }
163 163 }
164 164
165 165 std::shared_ptr<DBEventProduct>
166 166 CatalogueEventsModel::getEventProduct(const QModelIndex &index) const
167 167 {
168 168 if (itemTypeOf(index) == CatalogueEventsModel::ItemType::EventProduct) {
169 169 auto event = static_cast<DBEvent *>(index.internalPointer());
170 170 return impl->m_EventProducts.at(event).value(index.row());
171 171 }
172 172 else {
173 173 return nullptr;
174 174 }
175 175 }
176 176
177 177 void CatalogueEventsModel::addEvent(const std::shared_ptr<DBEvent> &event)
178 178 {
179 179 beginInsertRows(QModelIndex(), impl->m_Events.count(), impl->m_Events.count());
180 180 impl->m_Events.append(event);
181 181 impl->parseEventProduct(event);
182 182 endInsertRows();
183 183
184 184 // Also refreshes its children event products
185 185 auto eventIndex = index(impl->m_Events.count(), 0);
186 186 impl->refreshChildrenOfIndex(this, eventIndex);
187 187 }
188 188
189 189 void CatalogueEventsModel::removeEvent(const std::shared_ptr<DBEvent> &event)
190 190 {
191 191 auto index = impl->m_Events.indexOf(event);
192 192 if (index >= 0) {
193 193 beginRemoveRows(QModelIndex(), index, index);
194 194 impl->m_Events.removeAt(index);
195 195 impl->m_EventProducts.erase(event.get());
196 196 endRemoveRows();
197 197 }
198 198 }
199 199
200 200 QVector<std::shared_ptr<DBEvent> > CatalogueEventsModel::events() const
201 201 {
202 202 return impl->m_Events;
203 203 }
204 204
205 void CatalogueEventsModel::refreshEvent(const std::shared_ptr<DBEvent> &event)
205 void CatalogueEventsModel::refreshEvent(const std::shared_ptr<DBEvent> &event,
206 bool refreshEventProducts)
206 207 {
207 208 auto eventIndex = indexOf(event);
208 209 if (eventIndex.isValid()) {
209 210
211 if (refreshEventProducts) {
212 // Reparse the associated event products
213
214 auto nbEventProducts = impl->nbEventProducts(event);
215 auto newNbOfEventProducts = event->getEventProducts().size();
216 if (newNbOfEventProducts < nbEventProducts) {
217 beginRemoveRows(eventIndex, newNbOfEventProducts, nbEventProducts - 1);
218 impl->m_EventProducts.erase(event.get());
219 impl->parseEventProduct(event);
220 endRemoveRows();
221 }
222 else if (newNbOfEventProducts > nbEventProducts) {
223 beginInsertRows(eventIndex, nbEventProducts, newNbOfEventProducts - 1);
224 impl->m_EventProducts.erase(event.get());
225 impl->parseEventProduct(event);
226 endInsertRows();
227 }
228 else { // newNbOfEventProducts == nbEventProducts
229 impl->m_EventProducts.erase(event.get());
230 impl->parseEventProduct(event);
231 }
232 }
233
210 234 // Refreshes the event line
211 235 auto colCount = columnCount();
212 236 emit dataChanged(eventIndex, index(eventIndex.row(), colCount));
213 237
214 238 // Also refreshes its children event products
215 239 impl->refreshChildrenOfIndex(this, eventIndex);
216 240 }
217 241 else {
218 242 qCWarning(LOG_CatalogueEventsModel()) << "refreshEvent: event not found.";
219 243 }
220 244 }
221 245
222 246 QModelIndex CatalogueEventsModel::indexOf(const std::shared_ptr<DBEvent> &event) const
223 247 {
224 248 auto row = impl->m_Events.indexOf(event);
225 249 if (row >= 0) {
226 250 return index(row, 0);
227 251 }
228 252
229 253 return QModelIndex();
230 254 }
231 255
232 256 QModelIndex CatalogueEventsModel::index(int row, int column, const QModelIndex &parent) const
233 257 {
234 258 if (!hasIndex(row, column, parent)) {
235 259 return QModelIndex();
236 260 }
237 261
238 262 switch (itemTypeOf(parent)) {
239 263 case CatalogueEventsModel::ItemType::Root:
240 264 return createIndex(row, column);
241 265 case CatalogueEventsModel::ItemType::Event: {
242 266 auto event = getEvent(parent);
243 267 return createIndex(row, column, event.get());
244 268 }
245 269 case CatalogueEventsModel::ItemType::EventProduct:
246 270 break;
247 271 default:
248 272 break;
249 273 }
250 274
251 275 return QModelIndex();
252 276 }
253 277
254 278 QModelIndex CatalogueEventsModel::parent(const QModelIndex &index) const
255 279 {
256 280 switch (itemTypeOf(index)) {
257 281 case CatalogueEventsModel::ItemType::EventProduct: {
258 282 auto parentEvent = static_cast<DBEvent *>(index.internalPointer());
259 283 auto it
260 284 = std::find_if(impl->m_Events.cbegin(), impl->m_Events.cend(),
261 285 [parentEvent](auto event) { return event.get() == parentEvent; });
262 286
263 287 if (it != impl->m_Events.cend()) {
264 288 return createIndex(it - impl->m_Events.cbegin(), 0);
265 289 }
266 290 else {
267 291 return QModelIndex();
268 292 }
269 293 }
270 294 case CatalogueEventsModel::ItemType::Root:
271 295 break;
272 296 case CatalogueEventsModel::ItemType::Event:
273 297 break;
274 298 default:
275 299 break;
276 300 }
277 301
278 302 return QModelIndex();
279 303 }
280 304
281 305 int CatalogueEventsModel::rowCount(const QModelIndex &parent) const
282 306 {
283 307 if (parent.column() > 0) {
284 308 return 0;
285 309 }
286 310
287 311 switch (itemTypeOf(parent)) {
288 312 case CatalogueEventsModel::ItemType::Root:
289 313 return impl->m_Events.count();
290 314 case CatalogueEventsModel::ItemType::Event: {
291 315 auto event = getEvent(parent);
292 316 return impl->m_EventProducts[event.get()].count();
293 317 }
294 318 case CatalogueEventsModel::ItemType::EventProduct:
295 319 break;
296 320 default:
297 321 break;
298 322 }
299 323
300 324 return 0;
301 325 }
302 326
303 327 int CatalogueEventsModel::columnCount(const QModelIndex &parent) const
304 328 {
305 329 return static_cast<int>(CatalogueEventsModel::Column::NbColumn);
306 330 }
307 331
308 332 Qt::ItemFlags CatalogueEventsModel::flags(const QModelIndex &index) const
309 333 {
310 334 return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled;
311 335 }
312 336
313 337 QVariant CatalogueEventsModel::data(const QModelIndex &index, int role) const
314 338 {
315 339 if (index.isValid()) {
316 340
317 341 auto type = itemTypeOf(index);
318 342 if (type == CatalogueEventsModel::ItemType::Event) {
319 343 auto event = getEvent(index);
320 344 switch (role) {
321 345 case Qt::DisplayRole:
322 346 return impl->eventData(index.column(), event);
323 347 break;
324 348 }
325 349 }
326 350 else if (type == CatalogueEventsModel::ItemType::EventProduct) {
327 351 auto product = getEventProduct(index);
328 352 switch (role) {
329 353 case Qt::DisplayRole:
330 354 return impl->eventProductData(index.column(), product);
331 355 break;
332 356 }
333 357 }
334 358 }
335 359
336 360 return QVariant{};
337 361 }
338 362
339 363 QVariant CatalogueEventsModel::headerData(int section, Qt::Orientation orientation, int role) const
340 364 {
341 365 if (orientation == Qt::Horizontal && role == Qt::DisplayRole) {
342 366 return impl->columnNames().value(section);
343 367 }
344 368
345 369 return QVariant();
346 370 }
347 371
348 372 void CatalogueEventsModel::sort(int column, Qt::SortOrder order)
349 373 {
350 374 beginResetModel();
351 375 std::sort(impl->m_Events.begin(), impl->m_Events.end(),
352 376 [this, column, order](auto e1, auto e2) {
353 377 auto data1 = impl->sortData(column, e1);
354 378 auto data2 = impl->sortData(column, e2);
355 379
356 380 auto result = data1.toString() < data2.toString();
357 381
358 382 return order == Qt::AscendingOrder ? result : !result;
359 383 });
360 384
361 385 endResetModel();
362 386 emit modelSorted();
363 387 }
364 388
365 389 Qt::DropActions CatalogueEventsModel::supportedDragActions() const
366 390 {
367 391 return Qt::CopyAction;
368 392 }
369 393
370 394 QStringList CatalogueEventsModel::mimeTypes() const
371 395 {
372 396 return {MIME_TYPE_EVENT_LIST, MIME_TYPE_TIME_RANGE};
373 397 }
374 398
375 399 QMimeData *CatalogueEventsModel::mimeData(const QModelIndexList &indexes) const
376 400 {
377 401 auto mimeData = new QMimeData;
378 402
379 403 bool isFirst = true;
380 404
381 405 QVector<std::shared_ptr<DBEvent> > eventList;
382 406 QVector<std::shared_ptr<DBEventProduct> > eventProductList;
383 407
384 408 SqpRange firstTimeRange;
385 409 for (const auto &index : indexes) {
386 410 if (index.column() == 0) { // only the first column
387 411
388 412 auto type = itemTypeOf(index);
389 413 if (type == ItemType::Event) {
390 414 auto event = getEvent(index);
391 415 eventList << event;
392 416
393 417 if (isFirst) {
394 418 isFirst = false;
395 419 firstTimeRange.m_TStart = event->getTStart();
396 420 firstTimeRange.m_TEnd = event->getTEnd();
397 421 }
398 422 }
399 423 else if (type == ItemType::EventProduct) {
400 424 auto product = getEventProduct(index);
401 425 eventProductList << product;
402 426
403 427 if (isFirst) {
404 428 isFirst = false;
405 429 firstTimeRange.m_TStart = product->getTStart();
406 430 firstTimeRange.m_TEnd = product->getTEnd();
407 431 }
408 432 }
409 433 }
410 434 }
411 435
412 436 if (!eventList.isEmpty() && eventProductList.isEmpty()) {
413 437 auto eventsEncodedData = sqpApp->catalogueController().mimeDataForEvents(eventList);
414 438 mimeData->setData(MIME_TYPE_EVENT_LIST, eventsEncodedData);
415 439 }
416 440
417 441 if (eventList.count() + eventProductList.count() == 1) {
418 442 // No time range MIME data if multiple events are dragged
419 443 auto timeEncodedData = TimeController::mimeDataForTimeRange(firstTimeRange);
420 444 mimeData->setData(MIME_TYPE_TIME_RANGE, timeEncodedData);
421 445 }
422 446
423 447 return mimeData;
424 448 }
425 449
426 450 CatalogueEventsModel::ItemType CatalogueEventsModel::itemTypeOf(const QModelIndex &index) const
427 451 {
428 452 if (!index.isValid()) {
429 453 return ItemType::Root;
430 454 }
431 455 else if (index.internalPointer() == nullptr) {
432 456 return ItemType::Event;
433 457 }
434 458 else {
435 459 return ItemType::EventProduct;
436 460 }
437 461 }
@@ -1,457 +1,460
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 <SqpApplication.h>
10 10 #include <Visualization/VisualizationTabWidget.h>
11 11 #include <Visualization/VisualizationWidget.h>
12 12 #include <Visualization/VisualizationZoneWidget.h>
13 13
14 14 #include <QDialog>
15 15 #include <QDialogButtonBox>
16 16 #include <QListWidget>
17 17 #include <QMessageBox>
18 18
19 19 Q_LOGGING_CATEGORY(LOG_CatalogueEventsWidget, "CatalogueEventsWidget")
20 20
21 21 /// Fixed size of the validation column
22 22 const auto VALIDATION_COLUMN_SIZE = 35;
23 23
24 24 struct CatalogueEventsWidget::CatalogueEventsWidgetPrivate {
25 25
26 26 CatalogueEventsModel *m_Model = nullptr;
27 27 QStringList m_ZonesForTimeMode;
28 28 QString m_ZoneForGraphMode;
29 29 QVector<std::shared_ptr<DBCatalogue> > m_DisplayedCatalogues;
30 30
31 31 VisualizationWidget *m_VisualizationWidget = nullptr;
32 32
33 33 void setEvents(const QVector<std::shared_ptr<DBEvent> > &events, CatalogueEventsWidget *widget)
34 34 {
35 35 widget->ui->treeView->setSortingEnabled(false);
36 36 m_Model->setEvents(events);
37 37 widget->ui->treeView->setSortingEnabled(true);
38 38
39 39 for (auto event : events) {
40 40 if (sqpApp->catalogueController().eventHasChanges(event)) {
41 41 auto index = m_Model->indexOf(event);
42 42 widget->setEventChanges(event, true);
43 43 }
44 44 }
45 45 }
46 46
47 47 void addEvent(const std::shared_ptr<DBEvent> &event, QTreeView *treeView)
48 48 {
49 49 treeView->setSortingEnabled(false);
50 50 m_Model->addEvent(event);
51 51 treeView->setSortingEnabled(true);
52 52 }
53 53
54 54 void removeEvent(const std::shared_ptr<DBEvent> &event, QTreeView *treeView)
55 55 {
56 56 treeView->setSortingEnabled(false);
57 57 m_Model->removeEvent(event);
58 58 treeView->setSortingEnabled(true);
59 59 }
60 60
61 61 QStringList getAvailableVisualizationZoneList() const
62 62 {
63 63 if (m_VisualizationWidget) {
64 64 if (auto tab = m_VisualizationWidget->currentTabWidget()) {
65 65 return tab->availableZoneWidgets();
66 66 }
67 67 }
68 68
69 69 return QStringList{};
70 70 }
71 71
72 72 QStringList selectZone(QWidget *parent, const QStringList &selectedZones,
73 73 bool allowMultiSelection, const QPoint &location)
74 74 {
75 75 auto availableZones = getAvailableVisualizationZoneList();
76 76 if (availableZones.isEmpty()) {
77 77 return QStringList{};
78 78 }
79 79
80 80 QDialog d(parent, Qt::Tool);
81 81 d.setWindowTitle("Choose a zone");
82 82 auto layout = new QVBoxLayout{&d};
83 83 layout->setContentsMargins(0, 0, 0, 0);
84 84 auto listWidget = new QListWidget{&d};
85 85 layout->addWidget(listWidget);
86 86
87 87 QSet<QListWidgetItem *> checkedItems;
88 88 for (auto zone : availableZones) {
89 89 auto item = new QListWidgetItem{zone};
90 90 item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsUserCheckable);
91 91 if (selectedZones.contains(zone)) {
92 92 item->setCheckState(Qt::Checked);
93 93 checkedItems << item;
94 94 }
95 95 else {
96 96 item->setCheckState(Qt::Unchecked);
97 97 }
98 98
99 99 listWidget->addItem(item);
100 100 }
101 101
102 102 auto buttonBox = new QDialogButtonBox{QDialogButtonBox::Ok, &d};
103 103 layout->addWidget(buttonBox);
104 104
105 105 QObject::connect(buttonBox, &QDialogButtonBox::accepted, &d, &QDialog::accept);
106 106 QObject::connect(buttonBox, &QDialogButtonBox::rejected, &d, &QDialog::reject);
107 107
108 108 QObject::connect(listWidget, &QListWidget::itemChanged,
109 109 [&checkedItems, allowMultiSelection, listWidget](auto item) {
110 110 if (item->checkState() == Qt::Checked) {
111 111 if (!allowMultiSelection) {
112 112 for (auto checkedItem : checkedItems) {
113 113 listWidget->blockSignals(true);
114 114 checkedItem->setCheckState(Qt::Unchecked);
115 115 listWidget->blockSignals(false);
116 116 }
117 117
118 118 checkedItems.clear();
119 119 }
120 120 checkedItems << item;
121 121 }
122 122 else {
123 123 checkedItems.remove(item);
124 124 }
125 125 });
126 126
127 127 QStringList result;
128 128
129 129 d.setMinimumWidth(120);
130 130 d.resize(d.minimumSizeHint());
131 131 d.move(location);
132 132 if (d.exec() == QDialog::Accepted) {
133 133 for (auto item : checkedItems) {
134 134 result += item->text();
135 135 }
136 136 }
137 137 else {
138 138 result = selectedZones;
139 139 }
140 140
141 141 return result;
142 142 }
143 143
144 144 void updateForTimeMode(QTreeView *treeView)
145 145 {
146 146 auto selectedRows = treeView->selectionModel()->selectedRows();
147 147
148 148 if (selectedRows.count() == 1) {
149 149 auto event = m_Model->getEvent(selectedRows.first());
150 150 if (event) {
151 151 if (m_VisualizationWidget) {
152 152 if (auto tab = m_VisualizationWidget->currentTabWidget()) {
153 153
154 154 for (auto zoneName : m_ZonesForTimeMode) {
155 155 if (auto zone = tab->getZoneWithName(zoneName)) {
156 156 SqpRange eventRange;
157 157 eventRange.m_TStart = event->getTStart();
158 158 eventRange.m_TEnd = event->getTEnd();
159 159 zone->setZoneRange(eventRange);
160 160 }
161 161 }
162 162 }
163 163 else {
164 164 qCWarning(LOG_CatalogueEventsWidget())
165 165 << "updateTimeZone: no tab found in the visualization";
166 166 }
167 167 }
168 168 else {
169 169 qCWarning(LOG_CatalogueEventsWidget())
170 170 << "updateTimeZone: visualization widget not found";
171 171 }
172 172 }
173 173 }
174 174 else {
175 175 qCWarning(LOG_CatalogueEventsWidget())
176 176 << "updateTimeZone: not compatible with multiple events selected";
177 177 }
178 178 }
179 179
180 180 void updateForGraphMode(QTreeView *treeView)
181 181 {
182 182 auto selectedRows = treeView->selectionModel()->selectedRows();
183 183
184 184 if (selectedRows.count() == 1) {
185 185 auto event = m_Model->getEvent(selectedRows.first());
186 186 if (m_VisualizationWidget) {
187 187 if (auto tab = m_VisualizationWidget->currentTabWidget()) {
188 188 if (auto zone = tab->getZoneWithName(m_ZoneForGraphMode)) {
189 189 // TODO
190 190 }
191 191 }
192 192 else {
193 193 qCWarning(LOG_CatalogueEventsWidget())
194 194 << "updateGraphMode: no tab found in the visualization";
195 195 }
196 196 }
197 197 else {
198 198 qCWarning(LOG_CatalogueEventsWidget())
199 199 << "updateGraphMode: visualization widget not found";
200 200 }
201 201 }
202 202 else {
203 203 qCWarning(LOG_CatalogueEventsWidget())
204 204 << "updateGraphMode: not compatible with multiple events selected";
205 205 }
206 206 }
207 207
208 208 void getSelectedItems(
209 209 QTreeView *treeView, QVector<std::shared_ptr<DBEvent> > &events,
210 210 QVector<QPair<std::shared_ptr<DBEvent>, std::shared_ptr<DBEventProduct> > > &eventProducts)
211 211 {
212 212 for (auto rowIndex : treeView->selectionModel()->selectedRows()) {
213 213 auto itemType = m_Model->itemTypeOf(rowIndex);
214 214 if (itemType == CatalogueEventsModel::ItemType::Event) {
215 215 events << m_Model->getEvent(rowIndex);
216 216 }
217 217 else if (itemType == CatalogueEventsModel::ItemType::EventProduct) {
218 218 eventProducts << qMakePair(m_Model->getParentEvent(rowIndex),
219 219 m_Model->getEventProduct(rowIndex));
220 220 }
221 221 }
222 222 }
223 223 };
224 224
225 225 CatalogueEventsWidget::CatalogueEventsWidget(QWidget *parent)
226 226 : QWidget(parent),
227 227 ui(new Ui::CatalogueEventsWidget),
228 228 impl{spimpl::make_unique_impl<CatalogueEventsWidgetPrivate>()}
229 229 {
230 230 ui->setupUi(this);
231 231
232 232 impl->m_Model = new CatalogueEventsModel{this};
233 233 ui->treeView->setModel(impl->m_Model);
234 234
235 235 ui->treeView->setSortingEnabled(true);
236 236 ui->treeView->setDragDropMode(QAbstractItemView::DragDrop);
237 237 ui->treeView->setDragEnabled(true);
238 238
239 239 connect(ui->btnTime, &QToolButton::clicked, [this](auto checked) {
240 240 if (checked) {
241 241 ui->btnChart->setChecked(false);
242 242 impl->m_ZonesForTimeMode
243 243 = impl->selectZone(this, impl->m_ZonesForTimeMode, true,
244 244 this->mapToGlobal(ui->btnTime->frameGeometry().center()));
245 245
246 246 impl->updateForTimeMode(ui->treeView);
247 247 }
248 248 });
249 249
250 250 connect(ui->btnChart, &QToolButton::clicked, [this](auto checked) {
251 251 if (checked) {
252 252 ui->btnTime->setChecked(false);
253 253 impl->m_ZoneForGraphMode
254 254 = impl->selectZone(this, {impl->m_ZoneForGraphMode}, false,
255 255 this->mapToGlobal(ui->btnChart->frameGeometry().center()))
256 256 .value(0);
257 257
258 258 impl->updateForGraphMode(ui->treeView);
259 259 }
260 260 });
261 261
262 262 connect(ui->btnRemove, &QToolButton::clicked, [this]() {
263 263 QVector<std::shared_ptr<DBEvent> > events;
264 264 QVector<QPair<std::shared_ptr<DBEvent>, std::shared_ptr<DBEventProduct> > > eventProducts;
265 265 impl->getSelectedItems(ui->treeView, events, eventProducts);
266 266
267 267 if (!events.isEmpty() && eventProducts.isEmpty()) {
268 268
269 269 if (QMessageBox::warning(this, tr("Remove Event(s)"),
270 270 tr("The selected event(s) will be completly removed "
271 271 "from the repository!\nAre you sure you want to continue?"),
272 272 QMessageBox::Yes | QMessageBox::No, QMessageBox::No)
273 273 == QMessageBox::Yes) {
274 274
275 275 for (auto event : events) {
276 276 sqpApp->catalogueController().removeEvent(event);
277 277 impl->removeEvent(event, ui->treeView);
278 278 }
279 279 }
280 280 }
281 281 });
282 282
283 auto emitSelection = [this]() {
284 QVector<std::shared_ptr<DBEvent> > events;
285 QVector<QPair<std::shared_ptr<DBEvent>, std::shared_ptr<DBEventProduct> > > eventProducts;
286 impl->getSelectedItems(ui->treeView, events, eventProducts);
287
288 if (!events.isEmpty() && eventProducts.isEmpty()) {
289 emit this->eventsSelected(events);
290 }
291 else if (events.isEmpty() && !eventProducts.isEmpty()) {
292 emit this->eventProductsSelected(eventProducts);
293 }
294 else {
295 emit this->selectionCleared();
296 }
297 };
298
299 connect(ui->treeView, &QTreeView::clicked, emitSelection);
300 connect(ui->treeView->selectionModel(), &QItemSelectionModel::selectionChanged, emitSelection);
283 connect(ui->treeView, &QTreeView::clicked, this, &CatalogueEventsWidget::emitSelection);
284 connect(ui->treeView->selectionModel(), &QItemSelectionModel::selectionChanged, this,
285 &CatalogueEventsWidget::emitSelection);
301 286
302 287 ui->btnRemove->setEnabled(false); // Disabled by default when nothing is selected
303 288 connect(ui->treeView->selectionModel(), &QItemSelectionModel::selectionChanged, [this]() {
304 289 auto isNotMultiSelection = ui->treeView->selectionModel()->selectedRows().count() <= 1;
305 290 ui->btnChart->setEnabled(isNotMultiSelection);
306 291 ui->btnTime->setEnabled(isNotMultiSelection);
307 292
308 293 if (isNotMultiSelection && ui->btnTime->isChecked()) {
309 294 impl->updateForTimeMode(ui->treeView);
310 295 }
311 296 else if (isNotMultiSelection && ui->btnChart->isChecked()) {
312 297 impl->updateForGraphMode(ui->treeView);
313 298 }
314 299
315 300 QVector<std::shared_ptr<DBEvent> > events;
316 301 QVector<QPair<std::shared_ptr<DBEvent>, std::shared_ptr<DBEventProduct> > > eventProducts;
317 302 impl->getSelectedItems(ui->treeView, events, eventProducts);
318 303 ui->btnRemove->setEnabled(!events.isEmpty() && eventProducts.isEmpty());
319 304 });
320 305
321 306 ui->treeView->header()->setSectionResizeMode(QHeaderView::ResizeToContents);
322 307 ui->treeView->header()->setSectionResizeMode((int)CatalogueEventsModel::Column::Tags,
323 308 QHeaderView::Stretch);
324 309 ui->treeView->header()->setSectionResizeMode((int)CatalogueEventsModel::Column::Validation,
325 310 QHeaderView::Fixed);
326 311 ui->treeView->header()->setSectionResizeMode((int)CatalogueEventsModel::Column::Name,
327 312 QHeaderView::Interactive);
328 313 ui->treeView->header()->resizeSection((int)CatalogueEventsModel::Column::Validation,
329 314 VALIDATION_COLUMN_SIZE);
330 315 ui->treeView->header()->setSortIndicatorShown(true);
331 316
332 317 connect(impl->m_Model, &CatalogueEventsModel::modelSorted, [this]() {
333 318 auto allEvents = impl->m_Model->events();
334 319 for (auto event : allEvents) {
335 320 setEventChanges(event, sqpApp->catalogueController().eventHasChanges(event));
336 321 }
337 322 });
338 323
339 324 populateWithAllEvents();
340 325 }
341 326
342 327 CatalogueEventsWidget::~CatalogueEventsWidget()
343 328 {
344 329 delete ui;
345 330 }
346 331
347 332 void CatalogueEventsWidget::setVisualizationWidget(VisualizationWidget *visualization)
348 333 {
349 334 impl->m_VisualizationWidget = visualization;
350 335 }
351 336
352 337 void CatalogueEventsWidget::addEvent(const std::shared_ptr<DBEvent> &event)
353 338 {
354 339 impl->addEvent(event, ui->treeView);
355 340 }
356 341
357 342 void CatalogueEventsWidget::setEventChanges(const std::shared_ptr<DBEvent> &event, bool hasChanges)
358 343 {
359 344 impl->m_Model->refreshEvent(event);
360 345
361 346 auto eventIndex = impl->m_Model->indexOf(event);
362 347 auto validationIndex
363 348 = eventIndex.sibling(eventIndex.row(), (int)CatalogueEventsModel::Column::Validation);
364 349
365 350 if (validationIndex.isValid()) {
366 351 if (hasChanges) {
367 352 if (ui->treeView->indexWidget(validationIndex) == nullptr) {
368 353 auto widget = CatalogueExplorerHelper::buildValidationWidget(
369 354 ui->treeView,
370 355 [this, event]() {
371 356 sqpApp->catalogueController().saveEvent(event);
372 357 setEventChanges(event, false);
373 358 },
374 359 [this, event]() {
375 360 sqpApp->catalogueController().discardEvent(event);
376 361 setEventChanges(event, false);
377 impl->m_Model->refreshEvent(event);
362 impl->m_Model->refreshEvent(event, true);
363 emitSelection();
378 364 });
379 365 ui->treeView->setIndexWidget(validationIndex, widget);
380 366 }
381 367 }
382 368 else {
383 369 // Note: the widget is destroyed
384 370 ui->treeView->setIndexWidget(validationIndex, nullptr);
385 371 }
386 372 }
387 373 else {
388 374 qCWarning(LOG_CatalogueEventsWidget())
389 375 << "setEventChanges: the event is not displayed in the model.";
390 376 }
391 377 }
392 378
393 379 QVector<std::shared_ptr<DBCatalogue> > CatalogueEventsWidget::displayedCatalogues() const
394 380 {
395 381 return impl->m_DisplayedCatalogues;
396 382 }
397 383
398 384 bool CatalogueEventsWidget::isAllEventsDisplayed() const
399 385 {
400 386 return impl->m_DisplayedCatalogues.isEmpty() && !impl->m_Model->events().isEmpty();
401 387 }
402 388
403 389 bool CatalogueEventsWidget::isEventDisplayed(const std::shared_ptr<DBEvent> &event) const
404 390 {
405 391 return impl->m_Model->indexOf(event).isValid();
406 392 }
407 393
408 394 void CatalogueEventsWidget::populateWithCatalogues(
409 395 const QVector<std::shared_ptr<DBCatalogue> > &catalogues)
410 396 {
411 397 impl->m_DisplayedCatalogues = catalogues;
412 398
413 399 QSet<QUuid> eventIds;
414 400 QVector<std::shared_ptr<DBEvent> > events;
415 401
416 402 for (auto catalogue : catalogues) {
417 403 auto catalogueEvents = sqpApp->catalogueController().retrieveEventsFromCatalogue(catalogue);
418 404 for (auto event : catalogueEvents) {
419 405 if (!eventIds.contains(event->getUniqId())) {
420 406 events << event;
421 407 eventIds.insert(event->getUniqId());
422 408 }
423 409 }
424 410 }
425 411
426 412 impl->setEvents(events, this);
427 413 }
428 414
429 415 void CatalogueEventsWidget::populateWithAllEvents()
430 416 {
431 417 impl->m_DisplayedCatalogues.clear();
432 418
433 419 auto allEvents = sqpApp->catalogueController().retrieveAllEvents();
434 420
435 421 QVector<std::shared_ptr<DBEvent> > events;
436 422 for (auto event : allEvents) {
437 423 events << event;
438 424 }
439 425
440 426 impl->setEvents(events, this);
441 427 }
442 428
443 429 void CatalogueEventsWidget::clear()
444 430 {
445 431 impl->m_DisplayedCatalogues.clear();
446 432 impl->setEvents({}, this);
447 433 }
448 434
449 435 void CatalogueEventsWidget::refresh()
450 436 {
451 437 if (impl->m_DisplayedCatalogues.isEmpty()) {
452 438 populateWithAllEvents();
453 439 }
454 440 else {
455 441 populateWithCatalogues(impl->m_DisplayedCatalogues);
456 442 }
457 443 }
444
445 void CatalogueEventsWidget::emitSelection()
446 {
447 QVector<std::shared_ptr<DBEvent> > events;
448 QVector<QPair<std::shared_ptr<DBEvent>, std::shared_ptr<DBEventProduct> > > eventProducts;
449 impl->getSelectedItems(ui->treeView, events, eventProducts);
450
451 if (!events.isEmpty() && eventProducts.isEmpty()) {
452 emit eventsSelected(events);
453 }
454 else if (events.isEmpty() && !eventProducts.isEmpty()) {
455 emit eventProductsSelected(eventProducts);
456 }
457 else {
458 emit selectionCleared();
459 }
460 }
General Comments 3
Under Review
author

Auto status change to "Under Review"

Approved
author

Status change > Approved

You need to be logged in to leave comments. Login now