1 /* ============================================================
2 *
3 * This file is a part of digiKam project
4 * https://www.digikam.org
5 *
6 * Date : 2009-03-24
7 * Description : Qt Model for Albums - filter model
8 *
9 * Copyright (C) 2008-2011 by Marcel Wiesweg <marcel dot wiesweg at gmx dot de>
10 * Copyright (C) 2009 by Johannes Wienke <languitar at semipol dot de>
11 * Copyright (C) 2014-2015 by Mohamed_Anwer <m_dot_anwer at gmx dot com>
12 *
13 * This program is free software; you can redistribute it
14 * and/or modify it under the terms of the GNU General
15 * Public License as published by the Free Software Foundation;
16 * either version 2, or (at your option)
17 * any later version.
18 *
19 * This program is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
23 *
24 * ============================================================ */
25
26 #include "albumfiltermodel.h"
27
28 // Qt includes
29
30 #include <QSortFilterProxyModel>
31 #include <QHeaderView>
32
33 // Local includes
34
35 #include "digikam_debug.h"
36 #include "albummanager.h"
37 #include "albummodel.h"
38 #include "applicationsettings.h"
39 #include "itemsortcollator.h"
40 #include "facetags.h"
41
42 namespace Digikam
43 {
44
AlbumFilterModel(QObject * const parent)45 AlbumFilterModel::AlbumFilterModel(QObject* const parent)
46 : QSortFilterProxyModel(parent),
47 m_filterBehavior (FullFiltering),
48 m_chainedModel (nullptr),
49 m_parent (parent)
50 {
51 setSortRole(AbstractAlbumModel::AlbumSortRole);
52 setSortCaseSensitivity(Qt::CaseInsensitive);
53
54 // sorting may have changed when the string comparison is different
55
56 connect(ApplicationSettings::instance(), SIGNAL(setupChanged()),
57 this, SLOT(invalidate()));
58
59 // dynamicSortFilter does not work well for us: a dataChange may, because of our way of filtering,
60 // also affect parents and children of the changed index, which is not handled by QSortFilterProxyModel.
61
62 setDynamicSortFilter(false);
63
64 // Instead, we listen directly to AlbumManager's relevant change signals
65
66 connect(AlbumManager::instance(), SIGNAL(signalAlbumRenamed(Album*)),
67 this, SLOT(slotAlbumRenamed(Album*)));
68
69 connect(AlbumManager::instance(), SIGNAL(signalAlbumsUpdated(int)),
70 this, SLOT(slotAlbumsHaveBeenUpdated(int)));
71 }
72
setFilterBehavior(FilterBehavior behavior)73 void AlbumFilterModel::setFilterBehavior(FilterBehavior behavior)
74 {
75 if (behavior == m_filterBehavior)
76 {
77 return;
78 }
79
80 m_filterBehavior = behavior;
81 invalidateFilter();
82 }
83
setSearchTextSettings(const SearchTextSettings & settings)84 void AlbumFilterModel::setSearchTextSettings(const SearchTextSettings& settings)
85 {
86 if (!sourceModel())
87 {
88 return;
89 }
90
91 // don't use isFiltering here because it may be reimplemented
92
93 bool wasSearching = settingsFilter(m_settings);
94 bool willSearch = settingsFilter(settings);
95 emit searchTextSettingsAboutToChange(wasSearching, willSearch);
96
97 m_settings = settings;
98 invalidateFilter();
99 emit signalFilterChanged();
100
101 emit searchTextSettingsChanged(wasSearching, willSearch);
102
103 if (sourceAlbumModel()->albumType() == Album::PHYSICAL)
104 {
105 // find out if this setting has some results or not
106
107 int validRows = 0;
108
109 // for every collection we got
110
111 for (int i = 0 ; i < rowCount(rootAlbumIndex()) ; ++i)
112 {
113 QModelIndex collectionIndex = index(i, 0, rootAlbumIndex());
114
115 // count the number of rows
116
117 validRows += rowCount(collectionIndex);
118 }
119
120 bool hasResult = validRows > 0;
121 qCDebug(DIGIKAM_GENERAL_LOG) << "new search text settings:" << settings.text
122 << ": hasResult =" << hasResult
123 << ", validRows =" << validRows;
124 emit hasSearchResult(hasResult);
125 }
126 else
127 {
128 QModelIndex head = rootAlbumIndex(); // either root, or invalid, thus toplevel
129 emit hasSearchResult(rowCount(head));
130 }
131 }
132
settingsFilter(const SearchTextSettings & settings) const133 bool AlbumFilterModel::settingsFilter(const SearchTextSettings& settings) const
134 {
135 return !settings.text.isEmpty();
136 }
137
isFiltering() const138 bool AlbumFilterModel::isFiltering() const
139 {
140 return settingsFilter(m_settings);
141 }
142
updateFilter()143 void AlbumFilterModel::updateFilter()
144 {
145 if (isFiltering())
146 {
147 invalidateFilter();
148 }
149 }
150
searchTextSettings() const151 SearchTextSettings AlbumFilterModel::searchTextSettings() const
152 {
153 return m_settings;
154 }
155
setSourceAlbumModel(AbstractAlbumModel * const source)156 void AlbumFilterModel::setSourceAlbumModel(AbstractAlbumModel* const source)
157 {
158 if (m_chainedModel)
159 {
160 m_chainedModel->setSourceAlbumModel(source);
161 }
162 else
163 {
164 if (source != sourceModel())
165 {
166 setSourceModel(source);
167 }
168 }
169 }
170
setSourceFilterModel(AlbumFilterModel * const source)171 void AlbumFilterModel::setSourceFilterModel(AlbumFilterModel* const source)
172 {
173 if (source)
174 {
175 AbstractAlbumModel* const model = sourceAlbumModel();
176
177 if (model)
178 {
179 source->setSourceAlbumModel(model);
180 }
181 }
182
183 if ((m_chainedModel != source) || (sourceModel() != source))
184 {
185 m_chainedModel = source;
186 setSourceModel(source);
187 }
188 }
189
setSourceModel(QAbstractItemModel * const model)190 void AlbumFilterModel::setSourceModel(QAbstractItemModel* const model)
191 {
192 // made it protected, only setSourceAlbumModel is public
193
194 QSortFilterProxyModel::setSourceModel(model);
195 }
196
sourceAlbumModel() const197 AbstractAlbumModel* AlbumFilterModel::sourceAlbumModel() const
198 {
199 if (m_chainedModel)
200 {
201 return m_chainedModel->sourceAlbumModel();
202 }
203
204 return static_cast<AbstractAlbumModel*>(sourceModel());
205 }
206
sourceFilterModel() const207 AlbumFilterModel* AlbumFilterModel::sourceFilterModel() const
208 {
209 return m_chainedModel;
210 }
211
mapToSourceAlbumModel(const QModelIndex & index) const212 QModelIndex AlbumFilterModel::mapToSourceAlbumModel(const QModelIndex& index) const
213 {
214 if (!index.isValid())
215 {
216 return QModelIndex();
217 }
218
219 if (m_chainedModel)
220 {
221 return m_chainedModel->mapToSourceAlbumModel(mapToSource(index));
222 }
223
224 return mapToSource(index);
225 }
226
mapFromSourceAlbumModel(const QModelIndex & albummodel_index) const227 QModelIndex AlbumFilterModel::mapFromSourceAlbumModel(const QModelIndex& albummodel_index) const
228 {
229 if (!albummodel_index.isValid())
230 {
231 return QModelIndex();
232 }
233
234 if (m_chainedModel)
235 {
236 return mapFromSource(m_chainedModel->mapFromSourceAlbumModel(albummodel_index));
237 }
238
239 return mapFromSource(albummodel_index);
240 }
241
albumForIndex(const QModelIndex & index) const242 Album* AlbumFilterModel::albumForIndex(const QModelIndex& index) const
243 {
244 return AbstractAlbumModel::retrieveAlbum(index);
245 }
246
indexForAlbum(Album * album) const247 QModelIndex AlbumFilterModel::indexForAlbum(Album* album) const
248 {
249 AbstractAlbumModel* const model = sourceAlbumModel();
250
251 if (!model)
252 {
253 return QModelIndex();
254 }
255
256 return mapFromSourceAlbumModel(model->indexForAlbum(album));
257 }
258
rootAlbumIndex() const259 QModelIndex AlbumFilterModel::rootAlbumIndex() const
260 {
261 AbstractAlbumModel* const model = sourceAlbumModel();
262
263 if (!model)
264 {
265 return QModelIndex();
266 }
267
268 return mapFromSourceAlbumModel(model->rootAlbumIndex());
269 }
270
dataForCurrentSortRole(Album * album) const271 QVariant AlbumFilterModel::dataForCurrentSortRole(Album* album) const
272 {
273 if (album)
274 {
275 if (album->type() == Album::PHYSICAL)
276 {
277 PAlbum* const a = static_cast<PAlbum*>(album);
278
279 ApplicationSettings::AlbumSortRole sortRole = ApplicationSettings::instance()->getAlbumSortRole();
280
281 switch (sortRole)
282 {
283 case ApplicationSettings::ByFolder:
284 return a->title();
285
286 case ApplicationSettings::ByDate:
287 return a->date();
288
289 default:
290 return a->category();
291 }
292 }
293 else if (album->type() == Album::TAG)
294 {
295 return static_cast<TAlbum*>(album)->title();
296 }
297 else if (album->type() == Album::DATE)
298 {
299 return static_cast<DAlbum*>(album)->date();
300 }
301 else if (album->type() == Album::SEARCH)
302 {
303 return static_cast<SAlbum*>(album)->title();
304 }
305 }
306
307 return QVariant();
308 }
309
matches(Album * album) const310 bool AlbumFilterModel::matches(Album* album) const
311 {
312 // We want to work on the visual representation, so we use model data with AlbumTitleRole,
313 // not a direct Album method.
314 // We use direct source's index, not our index,
315 // because if the item is currently filtered out, we won't have an index for this album.
316
317 QModelIndex source_index = sourceAlbumModel()->indexForAlbum(album);
318
319 if (m_chainedModel)
320 {
321 source_index = m_chainedModel->mapFromSourceAlbumModel(source_index);
322 }
323
324 QString displayTitle = source_index.data(AbstractAlbumModel::AlbumTitleRole).toString();
325
326 return displayTitle.contains(m_settings.text, m_settings.caseSensitive);
327 }
328
matchResult(const QModelIndex & index) const329 AlbumFilterModel::MatchResult AlbumFilterModel::matchResult(const QModelIndex& index) const
330 {
331 return matchResult(albumForIndex(index));
332 }
333
matchResult(Album * album) const334 AlbumFilterModel::MatchResult AlbumFilterModel::matchResult(Album* album) const
335 {
336 if (!album)
337 {
338 return NoMatch;
339 }
340
341 PAlbum* const palbum = dynamic_cast<PAlbum*>(album);
342
343 if (album->isRoot() || (palbum && palbum->isAlbumRoot()))
344 {
345 return SpecialMatch;
346 }
347
348 TAlbum* const talbum = dynamic_cast<TAlbum*>(album);
349
350 if (talbum && talbum->isInternalTag())
351 {
352 return NoMatch;
353 }
354
355 if (matches(album))
356 {
357 return DirectMatch;
358 }
359
360 if (m_filterBehavior == SimpleFiltering)
361 {
362 return NoMatch;
363 }
364
365 if (m_filterBehavior == FullFiltering)
366 {
367 // check if any of the parents match the search
368
369 Album* parent = album->parent();
370 PAlbum* const pparent = palbum ? static_cast<PAlbum*>(parent) : nullptr;
371
372 while (parent && !(parent->isRoot() || (pparent && pparent->isAlbumRoot())))
373 {
374 if (matches(parent))
375 {
376 return ParentMatch;
377 }
378
379 parent = parent->parent();
380 }
381 }
382
383 // check if any of the children match the search
384
385 AlbumIterator it(album);
386
387 while (it.current())
388 {
389 if (matches(*it))
390 {
391 return ChildMatch;
392 }
393
394 ++it;
395 }
396
397 return NoMatch;
398 }
399
filterAcceptsRow(int source_row,const QModelIndex & source_parent) const400 bool AlbumFilterModel::filterAcceptsRow(int source_row, const QModelIndex& source_parent) const
401 {
402 QModelIndex index = sourceModel()->index(source_row, 0, source_parent);
403 Album* const album = AbstractAlbumModel::retrieveAlbum(index);
404 MatchResult result = matchResult(album);
405
406 return result;
407 }
408
lessThan(const QModelIndex & left,const QModelIndex & right) const409 bool AlbumFilterModel::lessThan(const QModelIndex& left, const QModelIndex& right) const
410 {
411 Album* leftAlbum = albumForIndex(left);
412 Album* rightAlbum = albumForIndex(right);
413
414 if (!leftAlbum || !rightAlbum)
415 {
416 return QSortFilterProxyModel::lessThan(left, right);
417 }
418
419 if ((leftAlbum->type() == Album::TAG) && (rightAlbum->type() == Album::TAG))
420 {
421 if ((leftAlbum->id() == FaceTags::unconfirmedPersonTagId()) != (rightAlbum->id() == FaceTags::unconfirmedPersonTagId()))
422 {
423 // unconfirmed tag album go to the top, regardless of sort role
424
425 return (sortOrder() == Qt::AscendingOrder) ? (leftAlbum->id() == FaceTags::unconfirmedPersonTagId())
426 : (leftAlbum->id() != FaceTags::unconfirmedPersonTagId());
427 }
428
429 if ((leftAlbum->id() == FaceTags::unknownPersonTagId()) != (rightAlbum->id() == FaceTags::unknownPersonTagId()))
430 {
431 // unknown tag albums go to the top, regardless of sort role
432
433 return (sortOrder() == Qt::AscendingOrder) ? (leftAlbum->id() == FaceTags::unknownPersonTagId())
434 : (leftAlbum->id() != FaceTags::unknownPersonTagId());
435 }
436
437 // Verify this, to prevent auto-creation of Ignored Tag.
438
439 if (FaceTags::existsIgnoredPerson())
440 {
441 if ((leftAlbum->id() == FaceTags::ignoredPersonTagId()) != (rightAlbum->id() == FaceTags::ignoredPersonTagId()))
442 {
443 // ignored tag albums go to the top, regardless of sort role
444
445 return (sortOrder() == Qt::AscendingOrder) ? (leftAlbum->id() == FaceTags::ignoredPersonTagId())
446 : (leftAlbum->id() != FaceTags::ignoredPersonTagId());
447 }
448 }
449
450 /**
451 * Implementation to sort Tags that contain
452 * Unconfirmed Faces, according to the Unconfirmed
453 * Face Count.
454 */
455
456 if (sourceAlbumModel() && sourceAlbumModel()->isFaceTagModel())
457 {
458 QMap<int, int> unconfirmedFaceCount = AlbumManager::instance()->getUnconfirmedFaceCount();
459 int leftFaceCount = unconfirmedFaceCount.value(leftAlbum->id(), 0);
460 int rightFaceCount = unconfirmedFaceCount.value(rightAlbum->id(), 0);
461
462 if ((leftFaceCount != 0) || (rightFaceCount != 0))
463 {
464 return (sortOrder() == Qt::AscendingOrder) ? (leftFaceCount > rightFaceCount)
465 : (leftFaceCount < rightFaceCount);
466 }
467 }
468 }
469
470 if (leftAlbum->isTrashAlbum() != rightAlbum->isTrashAlbum())
471 {
472 // trash albums go to the bottom, regardless of sort role
473
474 return (sortOrder() == Qt::AscendingOrder) ? !leftAlbum->isTrashAlbum()
475 : leftAlbum->isTrashAlbum();
476 }
477
478 QVariant valLeft = dataForCurrentSortRole(leftAlbum);
479 QVariant valRight = dataForCurrentSortRole(rightAlbum);
480
481 ApplicationSettings::AlbumSortRole role = ApplicationSettings::instance()->getAlbumSortRole();
482
483 if (((role == ApplicationSettings::ByDate) || (role == ApplicationSettings::ByCategory)) &&
484 (valLeft == valRight))
485 {
486 return QSortFilterProxyModel::lessThan(left, right);
487 }
488
489 if ((valLeft.type() == QVariant::String) && (valRight.type() == QVariant::String))
490 {
491 ItemSortCollator* const sorter = ItemSortCollator::instance();
492 bool natural = ApplicationSettings::instance()->isStringTypeNatural();
493
494 return (sorter->albumCompare(valLeft.toString(), valRight.toString(), sortCaseSensitivity(), natural) < 0);
495 }
496 else if ((valLeft.type() == QVariant::Date) && (valRight.type() == QVariant::Date))
497 {
498 return (compareByOrder(valLeft.toDate(), valRight.toDate(), Qt::AscendingOrder) < 0);
499 }
500
501 return QSortFilterProxyModel::lessThan(left, right);
502 }
503
slotAlbumRenamed(Album * album)504 void AlbumFilterModel::slotAlbumRenamed(Album* album)
505 {
506 if (album)
507 {
508 slotAlbumsHaveBeenUpdated(album->type());
509 }
510 }
511
slotAlbumsHaveBeenUpdated(int type)512 void AlbumFilterModel::slotAlbumsHaveBeenUpdated(int type)
513 {
514 if (sourceAlbumModel() && sourceAlbumModel()->albumType() == type)
515 {
516 invalidate();
517 }
518 }
519
520 // -----------------------------------------------------------------------------
521
CheckableAlbumFilterModel(QObject * const parent)522 CheckableAlbumFilterModel::CheckableAlbumFilterModel(QObject* const parent) :
523 AlbumFilterModel(parent),
524 m_filterChecked(false),
525 m_filterPartiallyChecked(false)
526 {
527 }
528
setSourceAlbumModel(AbstractCheckableAlbumModel * const source)529 void CheckableAlbumFilterModel::setSourceAlbumModel(AbstractCheckableAlbumModel* const source)
530 {
531 AlbumFilterModel::setSourceAlbumModel(source);
532 }
533
setSourceFilterModel(CheckableAlbumFilterModel * const source)534 void CheckableAlbumFilterModel::setSourceFilterModel(CheckableAlbumFilterModel* const source)
535 {
536 AlbumFilterModel::setSourceFilterModel(source);
537 }
538
sourceAlbumModel() const539 AbstractCheckableAlbumModel* CheckableAlbumFilterModel::sourceAlbumModel() const
540 {
541 return static_cast<AbstractCheckableAlbumModel*>(AlbumFilterModel::sourceAlbumModel());
542 }
543
setFilterChecked(bool filter)544 void CheckableAlbumFilterModel::setFilterChecked(bool filter)
545 {
546 m_filterChecked = filter;
547 invalidateFilter();
548 emit signalFilterChanged();
549 }
550
setFilterPartiallyChecked(bool filter)551 void CheckableAlbumFilterModel::setFilterPartiallyChecked(bool filter)
552 {
553 m_filterPartiallyChecked = filter;
554 invalidateFilter();
555 emit signalFilterChanged();
556 }
557
isFiltering() const558 bool CheckableAlbumFilterModel::isFiltering() const
559 {
560 return (AlbumFilterModel::isFiltering() || m_filterChecked || m_filterPartiallyChecked);
561 }
562
matches(Album * album) const563 bool CheckableAlbumFilterModel::matches(Album* album) const
564 {
565 bool accepted = AlbumFilterModel::matches(album);
566
567 if (!m_filterChecked && !m_filterPartiallyChecked)
568 {
569 return accepted;
570 }
571
572 Qt::CheckState state = sourceAlbumModel()->checkState(album);
573
574 bool stateAccepted = false;
575
576 if (m_filterPartiallyChecked)
577 {
578 stateAccepted |= state == Qt::PartiallyChecked;
579 }
580
581 if (m_filterChecked)
582 {
583 stateAccepted |= state == Qt::Checked;
584 }
585
586 return (accepted && stateAccepted);
587 }
588
589 // -----------------------------------------------------------------------------
590
SearchFilterModel(QObject * const parent)591 SearchFilterModel::SearchFilterModel(QObject* const parent)
592 : CheckableAlbumFilterModel(parent),
593 m_searchType (-1),
594 m_listTemporary (false)
595 {
596 }
597
setSourceSearchModel(SearchModel * const source)598 void SearchFilterModel::setSourceSearchModel(SearchModel* const source)
599 {
600 setSourceAlbumModel(source);
601 }
602
sourceSearchModel() const603 SearchModel* SearchFilterModel::sourceSearchModel() const
604 {
605 return dynamic_cast<SearchModel*> (sourceModel());
606 }
607
setFilterSearchType(DatabaseSearch::Type type)608 void SearchFilterModel::setFilterSearchType(DatabaseSearch::Type type)
609 {
610 setTypeFilter(type);
611 }
612
listNormalSearches()613 void SearchFilterModel::listNormalSearches()
614 {
615 setTypeFilter(-1);
616 }
617
listAllSearches()618 void SearchFilterModel::listAllSearches()
619 {
620 setTypeFilter(-2);
621 }
622
listTimelineSearches()623 void SearchFilterModel::listTimelineSearches()
624 {
625 setTypeFilter(DatabaseSearch::TimeLineSearch);
626 }
627
listHaarSearches()628 void SearchFilterModel::listHaarSearches()
629 {
630 setTypeFilter(DatabaseSearch::HaarSearch);
631 }
632
listMapSearches()633 void SearchFilterModel::listMapSearches()
634 {
635 setTypeFilter(DatabaseSearch::MapSearch);
636 }
637
listDuplicatesSearches()638 void SearchFilterModel::listDuplicatesSearches()
639 {
640 setTypeFilter(DatabaseSearch::DuplicatesSearch);
641 }
642
setTypeFilter(int type)643 void SearchFilterModel::setTypeFilter(int type)
644 {
645 m_searchType = type;
646 invalidateFilter();
647 emit signalFilterChanged();
648 }
649
setListTemporarySearches(bool list)650 void SearchFilterModel::setListTemporarySearches(bool list)
651 {
652 m_listTemporary = list;
653 invalidateFilter();
654 emit signalFilterChanged();
655 }
656
isFiltering() const657 bool SearchFilterModel::isFiltering() const
658 {
659 return ((m_searchType != -2) || !m_listTemporary);
660 }
661
matches(Album * album) const662 bool SearchFilterModel::matches(Album* album) const
663 {
664 if (!CheckableAlbumFilterModel::matches(album))
665 {
666 return false;
667 }
668
669 SAlbum* const salbum = static_cast<SAlbum*>(album);
670
671 if (m_searchType == -1)
672 {
673 if (!salbum->isNormalSearch())
674 {
675 return false;
676 }
677 }
678 else if (m_searchType == -2)
679 {
680 }
681 else
682 {
683 if (salbum->searchType() != (DatabaseSearch::Type)m_searchType)
684 {
685 return false;
686 }
687 }
688
689 if (!m_listTemporary && salbum->isTemporarySearch())
690 {
691 return false;
692 }
693
694 return true;
695 }
696
setSourceAlbumModel(AbstractAlbumModel * const source)697 void SearchFilterModel::setSourceAlbumModel(AbstractAlbumModel* const source)
698 {
699 AlbumFilterModel::setSourceAlbumModel(source);
700 }
701
702 // -----------------------------------------------------------------------------
703
TagPropertiesFilterModel(QObject * const parent)704 TagPropertiesFilterModel::TagPropertiesFilterModel(QObject* const parent)
705 : CheckableAlbumFilterModel(parent)
706 {
707 connect(AlbumManager::instance(), SIGNAL(signalTagPropertiesChanged(TAlbum*)),
708 this, SLOT(tagPropertiesChanged(TAlbum*)));
709 }
710
setSourceAlbumModel(TagModel * const source)711 void TagPropertiesFilterModel::setSourceAlbumModel(TagModel* const source)
712 {
713 CheckableAlbumFilterModel::setSourceAlbumModel(source);
714 }
715
sourceTagModel() const716 TagModel* TagPropertiesFilterModel::sourceTagModel() const
717 {
718 return dynamic_cast<TagModel*>(sourceModel());
719 }
720
listOnlyTagsWithProperty(const QString & property)721 void TagPropertiesFilterModel::listOnlyTagsWithProperty(const QString& property)
722 {
723 if (m_propertiesWhiteList.contains(property))
724 {
725 return;
726 }
727
728 m_propertiesWhiteList << property;
729 invalidateFilter();
730 emit signalFilterChanged();
731 }
732
removeListOnlyProperty(const QString & property)733 void TagPropertiesFilterModel::removeListOnlyProperty(const QString& property)
734 {
735 if (!m_propertiesWhiteList.contains(property))
736 {
737 return;
738 }
739
740 m_propertiesWhiteList.remove(property);
741 invalidateFilter();
742 emit signalFilterChanged();
743 }
744
doNotListTagsWithProperty(const QString & property)745 void TagPropertiesFilterModel::doNotListTagsWithProperty(const QString& property)
746 {
747 if (m_propertiesBlackList.contains(property))
748 {
749 return;
750 }
751
752 m_propertiesBlackList << property;
753 invalidateFilter();
754 emit signalFilterChanged();
755 }
756
removeDoNotListProperty(const QString & property)757 void TagPropertiesFilterModel::removeDoNotListProperty(const QString& property)
758 {
759 if (!m_propertiesBlackList.contains(property))
760 {
761 return;
762 }
763
764 m_propertiesBlackList.remove(property);
765 invalidateFilter();
766 emit signalFilterChanged();
767 }
768
isFiltering() const769 bool TagPropertiesFilterModel::isFiltering() const
770 {
771 return (!m_propertiesWhiteList.isEmpty() || !m_propertiesBlackList.isEmpty());
772 }
773
tagPropertiesChanged(TAlbum *)774 void TagPropertiesFilterModel::tagPropertiesChanged(TAlbum*)
775 {
776 // I do not expect batch changes. Otherwise we'll need a timer.
777
778 if (isFiltering())
779 {
780 invalidateFilter();
781
782 // Sort new when tag properties change.
783
784 invalidate();
785 }
786 }
787
matches(Album * album) const788 bool TagPropertiesFilterModel::matches(Album* album) const
789 {
790 if (!CheckableAlbumFilterModel::matches(album))
791 {
792 return false;
793 }
794
795 TAlbum* const talbum = static_cast<TAlbum*>(album);
796
797 foreach (const QString& prop, m_propertiesBlackList)
798 {
799 if (talbum->hasProperty(prop))
800 {
801 return false;
802 }
803 }
804
805 foreach (const QString& prop, m_propertiesWhiteList)
806 {
807 if (!talbum->hasProperty(prop))
808 {
809 return false;
810 }
811 }
812
813 return true;
814 }
815
816 // -----------------------------------------------------------------------
817
TagsManagerFilterModel(QObject * const parent)818 TagsManagerFilterModel::TagsManagerFilterModel(QObject* const parent)
819 : TagPropertiesFilterModel(parent)
820 {
821 }
822
setQuickListTags(const QList<int> & tags)823 void TagsManagerFilterModel::setQuickListTags(const QList<int>& tags)
824 {
825 m_keywords.clear();
826
827 foreach (int tag, tags)
828 {
829 m_keywords << tag;
830 }
831
832 invalidateFilter();
833 emit signalFilterChanged();
834 }
835
matches(Album * album) const836 bool TagsManagerFilterModel::matches(Album* album) const
837 {
838 if (!TagPropertiesFilterModel::matches(album))
839 {
840 return false;
841 }
842
843 if (m_keywords.isEmpty())
844 {
845 return true;
846 }
847
848 bool dirty = false;
849
850 for (QSet<int>::const_iterator it = m_keywords.begin() ;
851 it != m_keywords.end() ; ++it)
852 {
853 TAlbum* const talbum = AlbumManager::instance()->findTAlbum(*it);
854
855 if (!talbum)
856 {
857 continue;
858 }
859
860 if (talbum->title().compare(album->title()) == 0)
861 {
862 dirty = true;
863 }
864 }
865
866 return dirty;
867 }
868
869 } // namespace Digikam
870