##// END OF EJS Templates
Fix sorting with event products
trabillard -
r1264:17c80e548bb4
parent child
Show More
@@ -1,444 +1,456
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 std::unordered_set<std::shared_ptr<DBEvent> > m_EventsWithChanges;
28 28
29 29 QStringList columnNames()
30 30 {
31 31 return QStringList{tr("Event"), tr("TStart"), tr("TEnd"),
32 32 tr("Tags"), tr("Product"), tr("")};
33 33 }
34 34
35 35 QVariant sortData(int col, const std::shared_ptr<DBEvent> &event) const
36 36 {
37 37 if (col == (int)CatalogueEventsModel::Column::Validation) {
38 38 return m_EventsWithChanges.find(event) != m_EventsWithChanges.cend() ? true
39 39 : QVariant();
40 40 }
41 41
42 42 return eventData(col, event);
43 43 }
44 44
45 45 QVariant eventData(int col, const std::shared_ptr<DBEvent> &event) const
46 46 {
47 47 switch (static_cast<Column>(col)) {
48 48 case CatalogueEventsModel::Column::Name:
49 49 return event->getName();
50 50 case CatalogueEventsModel::Column::TStart:
51 51 return nbEventProducts(event) > 0 ? DateUtils::dateTime(event->getTStart())
52 52 : QVariant{};
53 53 case CatalogueEventsModel::Column::TEnd:
54 54 return nbEventProducts(event) > 0 ? DateUtils::dateTime(event->getTEnd())
55 55 : QVariant{};
56 56 case CatalogueEventsModel::Column::Product:
57 57 return QString::number(nbEventProducts(event)) + " product(s)";
58 58 case CatalogueEventsModel::Column::Tags: {
59 59 QString tagList;
60 60 auto tags = event->getTags();
61 61 for (auto tag : tags) {
62 62 tagList += tag.getName();
63 63 tagList += ' ';
64 64 }
65 65
66 66 return tagList;
67 67 }
68 68 case CatalogueEventsModel::Column::Validation:
69 69 return QVariant();
70 70 default:
71 71 break;
72 72 }
73 73
74 74 Q_ASSERT(false);
75 75 return QStringLiteral("Unknown Data");
76 76 }
77 77
78 78 void parseEventProduct(const std::shared_ptr<DBEvent> &event)
79 79 {
80 80 for (auto product : event->getEventProducts()) {
81 81 m_EventProducts[event.get()].append(std::make_shared<DBEventProduct>(product));
82 82 }
83 83 }
84 84
85 85 int nbEventProducts(const std::shared_ptr<DBEvent> &event) const
86 86 {
87 87 auto eventProductsIt = m_EventProducts.find(event.get());
88 88 if (eventProductsIt != m_EventProducts.cend()) {
89 89 return m_EventProducts.at(event.get()).count();
90 90 }
91 91 else {
92 92 return 0;
93 93 }
94 94 }
95 95
96 96 QVariant eventProductData(int col, const std::shared_ptr<DBEventProduct> &eventProduct) const
97 97 {
98 98 switch (static_cast<Column>(col)) {
99 99 case CatalogueEventsModel::Column::Name:
100 100 return eventProduct->getProductId();
101 101 case CatalogueEventsModel::Column::TStart:
102 102 return DateUtils::dateTime(eventProduct->getTStart());
103 103 case CatalogueEventsModel::Column::TEnd:
104 104 return DateUtils::dateTime(eventProduct->getTEnd());
105 105 case CatalogueEventsModel::Column::Product:
106 106 return eventProduct->getProductId();
107 107 case CatalogueEventsModel::Column::Tags:
108 108 return QString();
109 109 case CatalogueEventsModel::Column::Validation:
110 110 return QVariant();
111 111 default:
112 112 break;
113 113 }
114 114
115 115 Q_ASSERT(false);
116 116 return QStringLiteral("Unknown Data");
117 117 }
118
119 void refreshChildrenOfIndex(CatalogueEventsModel *model, const QModelIndex &index) const
120 {
121 auto childCount = model->rowCount(index);
122 auto colCount = model->columnCount();
123 emit model->dataChanged(model->index(0, 0, index),
124 model->index(childCount, colCount, index));
125 }
118 126 };
119 127
120 128 CatalogueEventsModel::CatalogueEventsModel(QObject *parent)
121 129 : QAbstractItemModel(parent), impl{spimpl::make_unique_impl<CatalogueEventsModelPrivate>()}
122 130 {
123 131 }
124 132
125 133 void CatalogueEventsModel::setEvents(const QVector<std::shared_ptr<DBEvent> > &events)
126 134 {
127 135 beginResetModel();
128 136
129 137 impl->m_Events = events;
130 138 impl->m_EventProducts.clear();
131 139 impl->m_EventsWithChanges.clear();
132 140 for (auto event : events) {
133 141 impl->parseEventProduct(event);
134 142 }
135 143
136 144 endResetModel();
137 145 }
138 146
139 147 std::shared_ptr<DBEvent> CatalogueEventsModel::getEvent(const QModelIndex &index) const
140 148 {
141 149 if (itemTypeOf(index) == CatalogueEventsModel::ItemType::Event) {
142 150 return impl->m_Events.value(index.row());
143 151 }
144 152 else {
145 153 return nullptr;
146 154 }
147 155 }
148 156
149 157 std::shared_ptr<DBEvent> CatalogueEventsModel::getParentEvent(const QModelIndex &index) const
150 158 {
151 159 if (itemTypeOf(index) == CatalogueEventsModel::ItemType::EventProduct) {
152 160 return getEvent(index.parent());
153 161 }
154 162 else {
155 163 return nullptr;
156 164 }
157 165 }
158 166
159 167 std::shared_ptr<DBEventProduct>
160 168 CatalogueEventsModel::getEventProduct(const QModelIndex &index) const
161 169 {
162 170 if (itemTypeOf(index) == CatalogueEventsModel::ItemType::EventProduct) {
163 171 auto event = static_cast<DBEvent *>(index.internalPointer());
164 172 return impl->m_EventProducts.at(event).value(index.row());
165 173 }
166 174 else {
167 175 return nullptr;
168 176 }
169 177 }
170 178
171 179 void CatalogueEventsModel::addEvent(const std::shared_ptr<DBEvent> &event)
172 180 {
173 beginInsertRows(QModelIndex(), impl->m_Events.count() - 1, impl->m_Events.count() - 1);
181 beginInsertRows(QModelIndex(), impl->m_Events.count(), impl->m_Events.count());
174 182 impl->m_Events.append(event);
175 183 impl->parseEventProduct(event);
176 184 endInsertRows();
185
186 // Also refreshes its children event products
187 auto eventIndex = index(impl->m_Events.count(), 0);
188 impl->refreshChildrenOfIndex(this, eventIndex);
177 189 }
178 190
179 191 void CatalogueEventsModel::removeEvent(const std::shared_ptr<DBEvent> &event)
180 192 {
181 193 auto index = impl->m_Events.indexOf(event);
182 194 if (index >= 0) {
183 195 beginRemoveRows(QModelIndex(), index, index);
184 196 impl->m_Events.removeAt(index);
185 197 impl->m_EventProducts.erase(event.get());
186 198 impl->m_EventsWithChanges.erase(event);
187 199 endRemoveRows();
188 200 }
189 201 }
190 202
191 203 QVector<std::shared_ptr<DBEvent> > CatalogueEventsModel::events() const
192 204 {
193 205 return impl->m_Events;
194 206 }
195 207
196 208 void CatalogueEventsModel::refreshEvent(const std::shared_ptr<DBEvent> &event)
197 209 {
198 210 auto eventIndex = indexOf(event);
199 211 if (eventIndex.isValid()) {
200 212
201 213 // Refreshes the event line
202 214 auto colCount = columnCount();
203 215 emit dataChanged(eventIndex, index(eventIndex.row(), colCount));
204 216
205 217 // Also refreshes its children event products
206 auto childCount = rowCount(eventIndex);
207 emit dataChanged(index(0, 0, eventIndex), index(childCount, colCount, eventIndex));
218 impl->refreshChildrenOfIndex(this, eventIndex);
208 219 }
209 220 else {
210 221 qCWarning(LOG_CatalogueEventsModel()) << "refreshEvent: event not found.";
211 222 }
212 223 }
213 224
214 225 QModelIndex CatalogueEventsModel::indexOf(const std::shared_ptr<DBEvent> &event) const
215 226 {
216 227 auto row = impl->m_Events.indexOf(event);
217 228 if (row >= 0) {
218 229 return index(row, 0);
219 230 }
220 231
221 232 return QModelIndex();
222 233 }
223 234
224 235 void CatalogueEventsModel::setEventHasChanges(const std::shared_ptr<DBEvent> &event,
225 236 bool hasChanges)
226 237 {
227 238 if (hasChanges) {
228 239 impl->m_EventsWithChanges.insert(event);
229 240 }
230 241 else {
231 242 impl->m_EventsWithChanges.erase(event);
232 243 }
233 244 }
234 245
235 246 bool CatalogueEventsModel::eventsHasChanges(const std::shared_ptr<DBEvent> &event) const
236 247 {
237 248 return impl->m_EventsWithChanges.find(event) != impl->m_EventsWithChanges.cend();
238 249 }
239 250
240 251 QModelIndex CatalogueEventsModel::index(int row, int column, const QModelIndex &parent) const
241 252 {
242 253 if (!hasIndex(row, column, parent)) {
243 254 return QModelIndex();
244 255 }
245 256
246 257 switch (itemTypeOf(parent)) {
247 258 case CatalogueEventsModel::ItemType::Root:
248 259 return createIndex(row, column);
249 260 case CatalogueEventsModel::ItemType::Event: {
250 261 auto event = getEvent(parent);
251 262 return createIndex(row, column, event.get());
252 263 }
253 264 case CatalogueEventsModel::ItemType::EventProduct:
254 265 break;
255 266 default:
256 267 break;
257 268 }
258 269
259 270 return QModelIndex();
260 271 }
261 272
262 273 QModelIndex CatalogueEventsModel::parent(const QModelIndex &index) const
263 274 {
264 275 switch (itemTypeOf(index)) {
265 276 case CatalogueEventsModel::ItemType::EventProduct: {
266 277 auto parentEvent = static_cast<DBEvent *>(index.internalPointer());
267 278 auto it
268 279 = std::find_if(impl->m_Events.cbegin(), impl->m_Events.cend(),
269 280 [parentEvent](auto event) { return event.get() == parentEvent; });
270 281
271 282 if (it != impl->m_Events.cend()) {
272 283 return createIndex(it - impl->m_Events.cbegin(), 0);
273 284 }
274 285 else {
275 286 return QModelIndex();
276 287 }
277 288 }
278 289 case CatalogueEventsModel::ItemType::Root:
279 290 break;
280 291 case CatalogueEventsModel::ItemType::Event:
281 292 break;
282 293 default:
283 294 break;
284 295 }
285 296
286 297 return QModelIndex();
287 298 }
288 299
289 300 int CatalogueEventsModel::rowCount(const QModelIndex &parent) const
290 301 {
291 302 if (parent.column() > 0) {
292 303 return 0;
293 304 }
294 305
295 306 switch (itemTypeOf(parent)) {
296 307 case CatalogueEventsModel::ItemType::Root:
297 308 return impl->m_Events.count();
298 309 case CatalogueEventsModel::ItemType::Event: {
299 310 auto event = getEvent(parent);
300 311 return impl->m_EventProducts[event.get()].count();
301 312 }
302 313 case CatalogueEventsModel::ItemType::EventProduct:
303 314 break;
304 315 default:
305 316 break;
306 317 }
307 318
308 319 return 0;
309 320 }
310 321
311 322 int CatalogueEventsModel::columnCount(const QModelIndex &parent) const
312 323 {
313 324 return static_cast<int>(CatalogueEventsModel::Column::NbColumn);
314 325 }
315 326
316 327 Qt::ItemFlags CatalogueEventsModel::flags(const QModelIndex &index) const
317 328 {
318 329 return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled;
319 330 }
320 331
321 332 QVariant CatalogueEventsModel::data(const QModelIndex &index, int role) const
322 333 {
323 334 if (index.isValid()) {
324 335
325 336 auto type = itemTypeOf(index);
326 337 if (type == CatalogueEventsModel::ItemType::Event) {
327 338 auto event = getEvent(index);
328 339 switch (role) {
329 340 case Qt::DisplayRole:
330 341 return impl->eventData(index.column(), event);
331 342 break;
332 343 }
333 344 }
334 345 else if (type == CatalogueEventsModel::ItemType::EventProduct) {
335 346 auto product = getEventProduct(index);
336 347 switch (role) {
337 348 case Qt::DisplayRole:
338 349 return impl->eventProductData(index.column(), product);
339 350 break;
340 351 }
341 352 }
342 353 }
343 354
344 355 return QVariant{};
345 356 }
346 357
347 358 QVariant CatalogueEventsModel::headerData(int section, Qt::Orientation orientation, int role) const
348 359 {
349 360 if (orientation == Qt::Horizontal && role == Qt::DisplayRole) {
350 361 return impl->columnNames().value(section);
351 362 }
352 363
353 364 return QVariant();
354 365 }
355 366
356 367 void CatalogueEventsModel::sort(int column, Qt::SortOrder order)
357 368 {
369 beginResetModel();
358 370 std::sort(impl->m_Events.begin(), impl->m_Events.end(),
359 371 [this, column, order](auto e1, auto e2) {
360 372 auto data1 = impl->sortData(column, e1);
361 373 auto data2 = impl->sortData(column, e2);
362 374
363 375 auto result = data1.toString() < data2.toString();
364 376
365 377 return order == Qt::AscendingOrder ? result : !result;
366 378 });
367 379
368 emit dataChanged(QModelIndex(), QModelIndex());
380 endResetModel();
369 381 emit modelSorted();
370 382 }
371 383
372 384 Qt::DropActions CatalogueEventsModel::supportedDragActions() const
373 385 {
374 386 return Qt::CopyAction;
375 387 }
376 388
377 389 QStringList CatalogueEventsModel::mimeTypes() const
378 390 {
379 391 return {MIME_TYPE_EVENT_LIST, MIME_TYPE_TIME_RANGE};
380 392 }
381 393
382 394 QMimeData *CatalogueEventsModel::mimeData(const QModelIndexList &indexes) const
383 395 {
384 396 auto mimeData = new QMimeData;
385 397
386 398 bool isFirst = true;
387 399
388 400 QVector<std::shared_ptr<DBEvent> > eventList;
389 401 QVector<std::shared_ptr<DBEventProduct> > eventProductList;
390 402
391 403 SqpRange firstTimeRange;
392 404 for (const auto &index : indexes) {
393 405 if (index.column() == 0) { // only the first column
394 406
395 407 auto type = itemTypeOf(index);
396 408 if (type == ItemType::Event) {
397 409 auto event = getEvent(index);
398 410 eventList << event;
399 411
400 412 if (isFirst) {
401 413 isFirst = false;
402 414 firstTimeRange.m_TStart = event->getTStart();
403 415 firstTimeRange.m_TEnd = event->getTEnd();
404 416 }
405 417 }
406 418 else if (type == ItemType::EventProduct) {
407 419 auto product = getEventProduct(index);
408 420 eventProductList << product;
409 421
410 422 if (isFirst) {
411 423 isFirst = false;
412 424 firstTimeRange.m_TStart = product->getTStart();
413 425 firstTimeRange.m_TEnd = product->getTEnd();
414 426 }
415 427 }
416 428 }
417 429 }
418 430
419 431 if (!eventList.isEmpty() && eventProductList.isEmpty()) {
420 432 auto eventsEncodedData = sqpApp->catalogueController().mimeDataForEvents(eventList);
421 433 mimeData->setData(MIME_TYPE_EVENT_LIST, eventsEncodedData);
422 434 }
423 435
424 436 if (eventList.count() + eventProductList.count() == 1) {
425 437 // No time range MIME data if multiple events are dragged
426 438 auto timeEncodedData = TimeController::mimeDataForTimeRange(firstTimeRange);
427 439 mimeData->setData(MIME_TYPE_TIME_RANGE, timeEncodedData);
428 440 }
429 441
430 442 return mimeData;
431 443 }
432 444
433 445 CatalogueEventsModel::ItemType CatalogueEventsModel::itemTypeOf(const QModelIndex &index) const
434 446 {
435 447 if (!index.isValid()) {
436 448 return ItemType::Root;
437 449 }
438 450 else if (index.internalPointer() == nullptr) {
439 451 return ItemType::Event;
440 452 }
441 453 else {
442 454 return ItemType::EventProduct;
443 455 }
444 456 }
General Comments 0
You need to be logged in to leave comments. Login now