1 /* ============================================================
2 *
3 * This file is a part of digiKam project
4 * https://www.digikam.org
5 *
6 * Date : 2012-21-06
7 * Description : Qt filter model for import items
8 *
9 * Copyright (C) 2012 by Islam Wazery <wazery at ubuntu dot com>
10 *
11 * This program is free software; you can redistribute it
12 * and/or modify it under the terms of the GNU General
13 * Public License as published by the Free Software Foundation;
14 * either version 2, or (at your option)
15 * any later version.
16 *
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
21 *
22 * ============================================================ */
23
24 #include "importfiltermodel.h"
25
26 // Local includes
27
28 #include "camiteminfo.h"
29 #include "filter.h"
30 #include "importimagemodel.h"
31
32 namespace Digikam
33 {
34
ImportSortFilterModel(QObject * const parent)35 ImportSortFilterModel::ImportSortFilterModel(QObject* const parent)
36 : DCategorizedSortFilterProxyModel(parent),
37 m_chainedModel (nullptr)
38 {
39 }
40
~ImportSortFilterModel()41 ImportSortFilterModel::~ImportSortFilterModel()
42 {
43 }
44
setSourceImportModel(ImportItemModel * const sourceModel)45 void ImportSortFilterModel::setSourceImportModel(ImportItemModel* const sourceModel)
46 {
47 if (m_chainedModel)
48 {
49 m_chainedModel->setSourceImportModel(sourceModel);
50 }
51 else
52 {
53 setDirectSourceImportModel(sourceModel);
54 }
55 }
56
sourceImportModel() const57 ImportItemModel* ImportSortFilterModel::sourceImportModel() const
58 {
59 if (m_chainedModel)
60 {
61 return m_chainedModel->sourceImportModel();
62 }
63
64 return static_cast<ImportItemModel*>(sourceModel());
65 }
66
setSourceFilterModel(ImportSortFilterModel * const sourceModel)67 void ImportSortFilterModel::setSourceFilterModel(ImportSortFilterModel* const sourceModel)
68 {
69 if (sourceModel)
70 {
71 ImportItemModel* const model = sourceImportModel();
72
73 if (model)
74 {
75 sourceModel->setSourceImportModel(model);
76 }
77 }
78
79 m_chainedModel = sourceModel;
80 setSourceModel(sourceModel);
81 }
82
sourceFilterModel() const83 ImportSortFilterModel* ImportSortFilterModel::sourceFilterModel() const
84 {
85 return m_chainedModel;
86 }
87
mapToSourceImportModel(const QModelIndex & proxyIndex) const88 QModelIndex ImportSortFilterModel::mapToSourceImportModel(const QModelIndex& proxyIndex) const
89 {
90 if (!proxyIndex.isValid())
91 {
92 return QModelIndex();
93 }
94
95 if (m_chainedModel)
96 {
97 return m_chainedModel->mapToSourceImportModel(mapToSource(proxyIndex));
98 }
99
100 return mapToSource(proxyIndex);
101 }
102
mapFromSourceImportModel(const QModelIndex & importModelIndex) const103 QModelIndex ImportSortFilterModel::mapFromSourceImportModel(const QModelIndex& importModelIndex) const
104 {
105 if (!importModelIndex.isValid())
106 {
107 return QModelIndex();
108 }
109
110 if (m_chainedModel)
111 {
112 return mapFromSource(m_chainedModel->mapFromSourceImportModel(importModelIndex));
113 }
114
115 return mapFromSource(importModelIndex);
116 }
117
mapFromDirectSourceToSourceImportModel(const QModelIndex & sourceModelIndex) const118 QModelIndex ImportSortFilterModel::mapFromDirectSourceToSourceImportModel(const QModelIndex& sourceModelIndex) const
119 {
120 if (!sourceModelIndex.isValid())
121 {
122 return QModelIndex();
123 }
124
125 if (m_chainedModel)
126 {
127 return m_chainedModel->mapToSourceImportModel(sourceModelIndex);
128 }
129
130 return sourceModelIndex;
131 }
132
mapListToSource(const QList<QModelIndex> & indexes) const133 QList<QModelIndex> ImportSortFilterModel::mapListToSource(const QList<QModelIndex>& indexes) const
134 {
135 QList<QModelIndex> sourceIndexes;
136
137 foreach (const QModelIndex& index, indexes)
138 {
139 sourceIndexes << mapToSourceImportModel(index);
140 }
141
142 return sourceIndexes;
143 }
144
mapListFromSource(const QList<QModelIndex> & sourceIndexes) const145 QList<QModelIndex> ImportSortFilterModel::mapListFromSource(const QList<QModelIndex>& sourceIndexes) const
146 {
147 QList<QModelIndex> indexes;
148
149 foreach (const QModelIndex& index, sourceIndexes)
150 {
151 indexes << mapFromSourceImportModel(index);
152 }
153
154 return indexes;
155 }
156
camItemInfo(const QModelIndex & index) const157 CamItemInfo ImportSortFilterModel::camItemInfo(const QModelIndex& index) const
158 {
159 return sourceImportModel()->camItemInfo(mapToSourceImportModel(index));
160 }
161
camItemId(const QModelIndex & index) const162 qlonglong ImportSortFilterModel::camItemId(const QModelIndex& index) const
163 {
164 return sourceImportModel()->camItemId(mapToSourceImportModel(index));
165 }
166
camItemInfos(const QList<QModelIndex> & indexes) const167 QList<CamItemInfo> ImportSortFilterModel::camItemInfos(const QList<QModelIndex>& indexes) const
168 {
169 QList<CamItemInfo> infos;
170
171 foreach (const QModelIndex& index, indexes)
172 {
173 infos << camItemInfo(index);
174 }
175
176 return infos;
177 }
178
camItemIds(const QList<QModelIndex> & indexes) const179 QList<qlonglong> ImportSortFilterModel::camItemIds(const QList<QModelIndex>& indexes) const
180 {
181 QList<qlonglong> ids;
182
183 foreach (const QModelIndex& index, indexes)
184 {
185 ids << camItemId(index);
186 }
187
188 return ids;
189 }
190
indexForPath(const QString & filePath) const191 QModelIndex ImportSortFilterModel::indexForPath(const QString& filePath) const
192 {
193 QUrl fileUrl = QUrl::fromLocalFile(filePath);
194
195 return mapFromSourceImportModel(sourceImportModel()->indexForUrl(fileUrl));
196 }
197
indexForCamItemInfo(const CamItemInfo & info) const198 QModelIndex ImportSortFilterModel::indexForCamItemInfo(const CamItemInfo& info) const
199 {
200 return mapFromSourceImportModel(sourceImportModel()->indexForCamItemInfo(info));
201 }
202
indexForCamItemId(qlonglong id) const203 QModelIndex ImportSortFilterModel::indexForCamItemId(qlonglong id) const
204 {
205 return mapFromSourceImportModel(sourceImportModel()->indexForCamItemId(id));
206 }
207
camItemInfosSorted() const208 QList<CamItemInfo> ImportSortFilterModel::camItemInfosSorted() const
209 {
210 QList<CamItemInfo> infos;
211 const int size = rowCount();
212
213 for (int i = 0 ; i < size ; ++i)
214 {
215 infos << camItemInfo(index(i, 0));
216 }
217
218 return infos;
219 }
220
importFilterModel() const221 ImportFilterModel* ImportSortFilterModel::importFilterModel() const
222 {
223 if (m_chainedModel)
224 {
225 return m_chainedModel->importFilterModel();
226 }
227
228 return nullptr;
229 }
230
setSourceModel(QAbstractItemModel * sourceModel)231 void ImportSortFilterModel::setSourceModel(QAbstractItemModel* sourceModel)
232 {
233 DCategorizedSortFilterProxyModel::setSourceModel(sourceModel);
234 }
235
setDirectSourceImportModel(ImportItemModel * const sourceModel)236 void ImportSortFilterModel::setDirectSourceImportModel(ImportItemModel* const sourceModel)
237 {
238 setSourceModel(sourceModel);
239 }
240
241 //--- ImportFilterModel methods ---------------------------------
242
243 class Q_DECL_HIDDEN ImportFilterModel::ImportFilterModelPrivate : public QObject
244 {
245 Q_OBJECT
246
247 public:
248
ImportFilterModelPrivate()249 ImportFilterModelPrivate()
250 : q (nullptr),
251 importItemModel(nullptr),
252 filter (nullptr)
253 {
254 }
255
256 void init(ImportFilterModel* const _q);
257
258 Q_SIGNALS:
259
260 void reAddCamItemInfos(const QList<CamItemInfo>&);
261 void reAddingFinished();
262
263 public:
264
265 ImportFilterModel* q;
266 ImportItemModel* importItemModel;
267 CamItemSortSettings sorter;
268 Filter* filter;
269
270 private:
271
272 // Disable
273 ImportFilterModelPrivate(QObject*);
274 };
275
init(ImportFilterModel * const _q)276 void ImportFilterModel::ImportFilterModelPrivate::init(ImportFilterModel* const _q)
277 {
278 q = _q;
279 }
280
ImportFilterModel(QObject * const parent)281 ImportFilterModel::ImportFilterModel(QObject* const parent)
282 : ImportSortFilterModel(parent),
283 d_ptr(new ImportFilterModelPrivate)
284 {
285 d_ptr->init(this);
286 }
287
~ImportFilterModel()288 ImportFilterModel::~ImportFilterModel()
289 {
290 Q_D(ImportFilterModel);
291 delete d;
292 }
293
data(const QModelIndex & index,int role) const294 QVariant ImportFilterModel::data(const QModelIndex& index, int role) const
295 {
296 Q_D(const ImportFilterModel);
297
298 if (!index.isValid())
299 {
300 return QVariant();
301 }
302
303 switch (role)
304 {
305 case DCategorizedSortFilterProxyModel::CategoryDisplayRole:
306 return categoryIdentifier(d->importItemModel->camItemInfoRef(mapToSource(index)));
307
308 case CategorizationModeRole:
309 return d->sorter.categorizationMode;
310
311 case SortOrderRole:
312 return d->sorter.sortRole;
313
314 case CategoryFormatRole:
315 return d->importItemModel->camItemInfoRef(mapToSource(index)).mime;
316
317 case CategoryDateRole:
318 return d->importItemModel->camItemInfoRef(mapToSource(index)).ctime;
319
320 case ImportFilterModelPointerRole:
321 return QVariant::fromValue(const_cast<ImportFilterModel*>(this));
322 }
323
324 return DCategorizedSortFilterProxyModel::data(index, role);
325 }
326
importFilterModel() const327 ImportFilterModel* ImportFilterModel::importFilterModel() const
328 {
329 return const_cast<ImportFilterModel*>(this);
330 }
331
332 // --- Sorting and Categorization ----------------------------------------------
333
setCamItemSortSettings(const CamItemSortSettings & sorter)334 void ImportFilterModel::setCamItemSortSettings(const CamItemSortSettings& sorter)
335 {
336 Q_D(ImportFilterModel);
337 d->sorter = sorter;
338 setCategorizedModel(d->sorter.categorizationMode != CamItemSortSettings::NoCategories);
339 invalidate();
340 }
341
setCategorizationMode(CamItemSortSettings::CategorizationMode mode)342 void ImportFilterModel::setCategorizationMode(CamItemSortSettings::CategorizationMode mode)
343 {
344 Q_D(ImportFilterModel);
345 d->sorter.setCategorizationMode(mode);
346 setCamItemSortSettings(d->sorter);
347 }
348
setSortRole(CamItemSortSettings::SortRole role)349 void ImportFilterModel::setSortRole(CamItemSortSettings::SortRole role)
350 {
351 Q_D(ImportFilterModel);
352 d->sorter.setSortRole(role);
353 setCamItemSortSettings(d->sorter);
354 }
355
setSortOrder(CamItemSortSettings::SortOrder order)356 void ImportFilterModel::setSortOrder(CamItemSortSettings::SortOrder order)
357 {
358 Q_D(ImportFilterModel);
359 d->sorter.setSortOrder(order);
360 setCamItemSortSettings(d->sorter);
361 }
362
setStringTypeNatural(bool natural)363 void ImportFilterModel::setStringTypeNatural(bool natural)
364 {
365 Q_D(ImportFilterModel);
366 d->sorter.setStringTypeNatural(natural);
367 setCamItemSortSettings(d->sorter);
368 }
369
setFilter(Digikam::Filter * filter)370 void ImportFilterModel::setFilter(Digikam::Filter* filter)
371 {
372 Q_D(ImportFilterModel);
373 d->filter = filter;
374 invalidateFilter();
375 }
376
setCameraThumbsController(CameraThumbsCtrl * const thumbsCtrl)377 void ImportFilterModel::setCameraThumbsController(CameraThumbsCtrl* const thumbsCtrl)
378 {
379 Q_D(ImportFilterModel);
380 d->importItemModel->setCameraThumbsController(thumbsCtrl);
381 }
382
setSendCamItemInfoSignals(bool sendSignals)383 void ImportFilterModel::setSendCamItemInfoSignals(bool sendSignals)
384 {
385 if (sendSignals)
386 {
387 connect(this, SIGNAL(rowsInserted(QModelIndex,int,int)),
388 this, SLOT(slotRowsInserted(QModelIndex,int,int)));
389
390 connect(this, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)),
391 this, SLOT(slotRowsAboutToBeRemoved(QModelIndex,int,int)));
392 }
393 else
394 {
395 disconnect(this, SIGNAL(rowsInserted(QModelIndex,int,int)),
396 this, SLOT(slotRowsInserted(QModelIndex,int,int)));
397
398 disconnect(this, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)),
399 this, SLOT(slotRowsAboutToBeRemoved(QModelIndex,int,int)));
400 }
401 }
402
slotRowsInserted(const QModelIndex &,int start,int end)403 void ImportFilterModel::slotRowsInserted(const QModelIndex& /*parent*/, int start, int end)
404 {
405 QList<CamItemInfo> infos;
406
407 for (int i = start ; i < end ; ++i)
408 {
409 infos << camItemInfo(index(i, 0));
410 }
411
412 emit camItemInfosAdded(infos);
413 }
414
slotRowsAboutToBeRemoved(const QModelIndex &,int start,int end)415 void ImportFilterModel::slotRowsAboutToBeRemoved(const QModelIndex& /*parent*/, int start, int end)
416 {
417 QList<CamItemInfo> infos;
418
419 for (int i = start ; i < end ; ++i)
420 {
421 infos << camItemInfo(index(i, 0));
422 }
423
424 emit camItemInfosAboutToBeRemoved(infos);
425 }
426
setDirectSourceImportModel(ImportItemModel * const sourceModel)427 void ImportFilterModel::setDirectSourceImportModel(ImportItemModel* const sourceModel)
428 {
429 Q_D(ImportFilterModel);
430
431 if (d->importItemModel)
432 {
433 /*
434 disconnect(d->importItemModel, SIGNAL(modelReset()),
435 this, SLOT(slotModelReset()));
436
437 // TODO: slotModelReset(); will be added when implementing filtering options
438 */
439 disconnect(d->importItemModel, SIGNAL(processAdded(QList<CamItemInfo>)),
440 this, SLOT(slotProcessAdded(QList<CamItemInfo>)));
441 }
442
443 // TODO do we need to delete the old one?
444
445 d->importItemModel = sourceModel;
446
447 if (d->importItemModel)
448 {
449 /*
450 connect(d, SIGNAL(reAddCamItemInfos(QList<CamItemInfo>)),
451 d->importItemModel, SLOT(reAddCamItemInfos(QList<CamItemInfo>)));
452
453 connect(d, SIGNAL(reAddingFinished()),
454 d->importItemModel, SLOT(reAddingFinished()));
455
456 connect(d->importItemModel, SIGNAL(modelReset()),
457 this, SLOT(slotModelReset()));
458 */
459 connect(d->importItemModel, SIGNAL(processAdded(QList<CamItemInfo>)),
460 this, SLOT(slotProcessAdded(QList<CamItemInfo>)));
461 }
462
463 setSourceModel(d->importItemModel);
464 }
465
slotProcessAdded(const QList<CamItemInfo> &)466 void ImportFilterModel::slotProcessAdded(const QList<CamItemInfo>&)
467 {
468 invalidate();
469 }
470
compareCategories(const QModelIndex & left,const QModelIndex & right) const471 int ImportFilterModel::compareCategories(const QModelIndex& left, const QModelIndex& right) const
472 {
473 Q_D(const ImportFilterModel);
474
475 if (!d->sorter.isCategorized())
476 {
477 return 0;
478 }
479
480 if (!left.isValid() || !right.isValid())
481 {
482 return -1;
483 }
484
485 return compareInfosCategories(d->importItemModel->camItemInfoRef(left), d->importItemModel->camItemInfoRef(right));
486 }
487
subSortLessThan(const QModelIndex & left,const QModelIndex & right) const488 bool ImportFilterModel::subSortLessThan(const QModelIndex& left, const QModelIndex& right) const
489 {
490 Q_D(const ImportFilterModel);
491
492 if (!left.isValid() || !right.isValid())
493 {
494 return true;
495 }
496
497 if (left == right)
498 {
499 return false;
500 }
501
502 const CamItemInfo& leftInfo = d->importItemModel->camItemInfoRef(left);
503 const CamItemInfo& rightInfo = d->importItemModel->camItemInfoRef(right);
504
505 if (leftInfo == rightInfo)
506 {
507 return d->sorter.lessThan(left.data(ImportItemModel::ExtraDataRole), right.data(ImportItemModel::ExtraDataRole));
508 }
509
510 return infosLessThan(leftInfo, rightInfo);
511 }
512
compareInfosCategories(const CamItemInfo & left,const CamItemInfo & right) const513 int ImportFilterModel::compareInfosCategories(const CamItemInfo& left, const CamItemInfo& right) const
514 {
515 Q_D(const ImportFilterModel);
516
517 return d->sorter.compareCategories(left, right);
518 }
519
infosLessThan(const CamItemInfo & left,const CamItemInfo & right) const520 bool ImportFilterModel::infosLessThan(const CamItemInfo& left, const CamItemInfo& right) const
521 {
522 Q_D(const ImportFilterModel);
523
524 return d->sorter.lessThan(left, right);
525 }
526
categoryIdentifier(const CamItemInfo & info) const527 QString ImportFilterModel::categoryIdentifier(const CamItemInfo& info) const
528 {
529 Q_D(const ImportFilterModel);
530
531 switch (d->sorter.categorizationMode)
532 {
533 case CamItemSortSettings::NoCategories:
534 return QString();
535
536 case CamItemSortSettings::CategoryByFolder:
537 return info.folder;
538
539 case CamItemSortSettings::CategoryByFormat:
540 return info.mime;
541
542 case CamItemSortSettings::CategoryByDate:
543 return info.ctime.date().toString(Qt::ISODate);
544
545 default:
546 return QString();
547 }
548 }
549
filterAcceptsRow(int source_row,const QModelIndex & source_parent) const550 bool ImportFilterModel::filterAcceptsRow(int source_row, const QModelIndex& source_parent) const
551 {
552 Q_D(const ImportFilterModel);
553
554 if (!d->filter)
555 {
556 return true;
557 }
558
559 QModelIndex idx = sourceModel()->index(source_row, 0, source_parent);
560 const CamItemInfo &info = d->importItemModel->camItemInfo(idx);
561
562 if (d->filter->matchesCurrentFilter(info))
563 {
564 return true;
565 }
566
567 return false;
568 }
569
570 // -------------------------------------------------------------------------------------------------------
571
NoDuplicatesImportFilterModel(QObject * const parent)572 NoDuplicatesImportFilterModel::NoDuplicatesImportFilterModel(QObject* const parent)
573 : ImportSortFilterModel(parent)
574 {
575 }
576
filterAcceptsRow(int source_row,const QModelIndex & source_parent) const577 bool NoDuplicatesImportFilterModel::filterAcceptsRow(int source_row, const QModelIndex& source_parent) const
578 {
579 QModelIndex index = sourceModel()->index(source_row, 0, source_parent);
580
581 if (index.data(ImportItemModel::ExtraDataDuplicateCount).toInt() <= 1)
582 {
583 return true;
584 }
585
586 QModelIndex previousIndex = sourceModel()->index(source_row - 1, 0, source_parent);
587
588 if (!previousIndex.isValid())
589 {
590 return true;
591 }
592
593 if (sourceImportModel()->camItemId(mapFromDirectSourceToSourceImportModel(index)) ==
594 sourceImportModel()->camItemId(mapFromDirectSourceToSourceImportModel(previousIndex)))
595 {
596 return false;
597 }
598
599 return true;
600 }
601
602 } // namespace Digikam
603
604 #include "importfiltermodel.moc"
605