1 /* ============================================================
2  *
3  * This file is a part of digiKam project
4  * https://www.digikam.org
5  *
6  * Date        : 2009-03-05
7  * Description : Filter values for use with ItemFilterModel
8  *
9  * Copyright (C) 2009-2011 by Marcel Wiesweg <marcel dot wiesweg at gmx dot de>
10  * Copyright (C) 2011-2021 by Gilles Caulier <caulier dot gilles at gmail dot com>
11  * Copyright (C)      2010 by Andi Clemens <andi dot clemens at gmail dot com>
12  * Copyright (C)      2011 by Michael G. Hansen <mike at mghansen dot de>
13  * Copyright (C)      2014 by Mohamed_Anwer <m_dot_anwer at gmx dot com>
14  *
15  * This program is free software; you can redistribute it
16  * and/or modify it under the terms of the GNU General
17  * Public License as published by the Free Software Foundation;
18  * either version 2, or (at your option)
19  * any later version.
20  *
21  * This program is distributed in the hope that it will be useful,
22  * but WITHOUT ANY WARRANTY; without even the implied warranty of
23  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
24  * GNU General Public License for more details.
25  *
26  * ============================================================ */
27 
28 #include "itemfiltersettings.h"
29 
30 // C++ includes
31 
32 #include <cmath>
33 
34 // Qt includes
35 
36 #include <QDateTime>
37 
38 // Local includes
39 
40 #include "digikam_debug.h"
41 #include "coredbfields.h"
42 #include "digikam_globals.h"
43 #include "iteminfo.h"
44 #include "tagscache.h"
45 #include "versionmanagersettings.h"
46 
47 namespace Digikam
48 {
49 
ItemFilterSettings()50 ItemFilterSettings::ItemFilterSettings()
51     : m_untaggedFilter      (false),
52       m_matchingCond        (OrCondition),
53       m_ratingFilter        (0),
54       m_ratingCond          (GreaterEqualCondition),
55       m_isUnratedExcluded   (false),
56       m_mimeTypeFilter      (MimeFilter::AllFiles),
57       m_geolocationCondition(GeolocationNoFilter)
58 {
59 }
60 
watchFlags() const61 DatabaseFields::Set ItemFilterSettings::watchFlags() const
62 {
63     DatabaseFields::Set set;
64 
65     if (isFilteringByDay())
66     {
67         set |= DatabaseFields::CreationDate;
68     }
69 
70     if (isFilteringByText())
71     {
72         set |= DatabaseFields::Name;
73         set |= DatabaseFields::Comment;
74     }
75 
76     if (isFilteringByRating())
77     {
78         set |= DatabaseFields::Rating;
79     }
80 
81     if (isFilteringByTypeMime())
82     {
83         set |= DatabaseFields::Category;
84         set |= DatabaseFields::Format;
85     }
86 
87     if (isFilteringByGeolocation())
88     {
89         set |= DatabaseFields::ItemPositionsAll;
90     }
91 
92     if (isFilteringByColorLabels())
93     {
94         set |= DatabaseFields::ColorLabel;
95     }
96 
97     if (isFilteringByPickLabels())
98     {
99         set |= DatabaseFields::PickLabel;
100     }
101 
102     return set;
103 }
104 
isFilteringByDay() const105 bool ItemFilterSettings::isFilteringByDay() const
106 {
107     if (!m_dayFilter.isEmpty())
108     {
109         return true;
110     }
111 
112     return false;
113 }
114 
isFilteringByTags() const115 bool ItemFilterSettings::isFilteringByTags() const
116 {
117     if (!m_includeTagFilter.isEmpty() || !m_excludeTagFilter.isEmpty() || m_untaggedFilter)
118     {
119         return true;
120     }
121 
122     return false;
123 }
124 
isFilteringByColorLabels() const125 bool ItemFilterSettings::isFilteringByColorLabels() const
126 {
127     if (!m_colorLabelTagFilter.isEmpty())
128     {
129         return true;
130     }
131 
132     return false;
133 }
134 
isFilteringByPickLabels() const135 bool ItemFilterSettings::isFilteringByPickLabels() const
136 {
137     if (!m_pickLabelTagFilter.isEmpty())
138     {
139         return true;
140     }
141 
142     return false;
143 }
144 
isFilteringByText() const145 bool ItemFilterSettings::isFilteringByText() const
146 {
147     if (!m_textFilterSettings.text.isEmpty())
148     {
149         return true;
150     }
151 
152     return false;
153 }
154 
isFilteringByTypeMime() const155 bool ItemFilterSettings::isFilteringByTypeMime() const
156 {
157     if (m_mimeTypeFilter != MimeFilter::AllFiles)
158     {
159         return true;
160     }
161 
162     return false;
163 }
164 
isFilteringByGeolocation() const165 bool ItemFilterSettings::isFilteringByGeolocation() const
166 {
167     return (m_geolocationCondition != GeolocationNoFilter);
168 }
169 
isFilteringByRating() const170 bool ItemFilterSettings::isFilteringByRating() const
171 {
172     if ((m_ratingFilter != 0) || (m_ratingCond != GreaterEqualCondition) || m_isUnratedExcluded)
173     {
174         return true;
175     }
176 
177     return false;
178 }
179 
isFilteringInternally() const180 bool ItemFilterSettings::isFilteringInternally() const
181 {
182     return (isFiltering() || !m_urlWhitelists.isEmpty() || !m_idWhitelists.isEmpty());
183 }
184 
isFiltering() const185 bool ItemFilterSettings::isFiltering() const
186 {
187     return (isFilteringByDay()         ||
188             isFilteringByTags()        ||
189             isFilteringByText()        ||
190             isFilteringByRating()      ||
191             isFilteringByTypeMime()    ||
192             isFilteringByColorLabels() ||
193             isFilteringByPickLabels()  ||
194             isFilteringByGeolocation());
195 }
196 
setDayFilter(const QList<QDateTime> & days)197 void ItemFilterSettings::setDayFilter(const QList<QDateTime>& days)
198 {
199     m_dayFilter.clear();
200 
201     for (QList<QDateTime>::const_iterator it = days.constBegin() ; it != days.constEnd() ; ++it)
202     {
203         m_dayFilter.insert(*it, true);
204     }
205 }
206 
setTagFilter(const QList<int> & includedTags,const QList<int> & excludedTags,MatchingCondition matchingCondition,bool showUnTagged,const QList<int> & clTagIds,const QList<int> & plTagIds)207 void ItemFilterSettings::setTagFilter(const QList<int>& includedTags,
208                                       const QList<int>& excludedTags,
209                                       MatchingCondition matchingCondition,
210                                       bool showUnTagged,
211                                       const QList<int>& clTagIds,
212                                       const QList<int>& plTagIds)
213 {
214     m_includeTagFilter    = includedTags;
215     m_excludeTagFilter    = excludedTags;
216     m_matchingCond        = matchingCondition;
217     m_untaggedFilter      = showUnTagged;
218     m_colorLabelTagFilter = clTagIds;
219     m_pickLabelTagFilter  = plTagIds;
220 }
221 
setRatingFilter(int rating,RatingCondition ratingCondition,bool isUnratedExcluded)222 void ItemFilterSettings::setRatingFilter(int rating, RatingCondition ratingCondition, bool isUnratedExcluded)
223 {
224     m_ratingFilter      = rating;
225     m_ratingCond        = ratingCondition;
226     m_isUnratedExcluded = isUnratedExcluded;
227 }
228 
setMimeTypeFilter(int mime)229 void ItemFilterSettings::setMimeTypeFilter(int mime)
230 {
231     m_mimeTypeFilter = (MimeFilter::TypeMimeFilter)mime;
232 }
233 
setGeolocationFilter(const GeolocationCondition & condition)234 void ItemFilterSettings::setGeolocationFilter(const GeolocationCondition& condition)
235 {
236     m_geolocationCondition = condition;
237 }
238 
setTextFilter(const SearchTextFilterSettings & settings)239 void ItemFilterSettings::setTextFilter(const SearchTextFilterSettings& settings)
240 {
241     m_textFilterSettings = settings;
242 }
243 
setTagNames(const QHash<int,QString> & hash)244 void ItemFilterSettings::setTagNames(const QHash<int, QString>& hash)
245 {
246     m_tagNameHash = hash;
247 }
248 
setAlbumNames(const QHash<int,QString> & hash)249 void ItemFilterSettings::setAlbumNames(const QHash<int, QString>& hash)
250 {
251     m_albumNameHash = hash;
252 }
253 
setUrlWhitelist(const QList<QUrl> & urlList,const QString & id)254 void ItemFilterSettings::setUrlWhitelist(const QList<QUrl>& urlList, const QString& id)
255 {
256     if (urlList.isEmpty())
257     {
258         m_urlWhitelists.remove(id);
259     }
260     else
261     {
262         m_urlWhitelists.insert(id, urlList);
263     }
264 }
265 
setIdWhitelist(const QList<qlonglong> & idList,const QString & id)266 void ItemFilterSettings::setIdWhitelist(const QList<qlonglong>& idList, const QString& id)
267 {
268     if (idList.isEmpty())
269     {
270         m_idWhitelists.remove(id);
271     }
272     else
273     {
274         m_idWhitelists.insert(id, idList);
275     }
276 }
277 
278 template <class ContainerA, class ContainerB>
containsAnyOf(const ContainerA & listA,const ContainerB & listB)279 bool containsAnyOf(const ContainerA& listA, const ContainerB& listB)
280 {
281     foreach (const typename ContainerA::value_type& a, listA)
282     {
283         if (listB.contains(a))
284         {
285             return true;
286         }
287     }
288 
289     return false;
290 }
291 
292 template <class ContainerA, typename Value, class ContainerB>
containsNoneOfExcept(const ContainerA & list,const ContainerB & noneOfList,const Value & exception)293 bool containsNoneOfExcept(const ContainerA& list, const ContainerB& noneOfList, const Value& exception)
294 {
295     foreach (const typename ContainerB::value_type& n, noneOfList)
296     {
297         if (n != exception && list.contains(n))
298         {
299             return false;
300         }
301     }
302 
303     return true;
304 }
305 
matches(const ItemInfo & info,bool * const foundText) const306 bool ItemFilterSettings::matches(const ItemInfo& info, bool* const foundText) const
307 {
308     if (foundText)
309     {
310         *foundText = false;
311     }
312 
313     if (!isFilteringInternally())
314     {
315         return true;
316     }
317 
318     bool match = false;
319 
320     if (!m_includeTagFilter.isEmpty() || !m_excludeTagFilter.isEmpty())
321     {
322         QList<int>                 tagIds = info.tagIds();
323         QList<int>::const_iterator it;
324 
325         match = m_includeTagFilter.isEmpty();
326 
327         if (m_matchingCond == OrCondition)
328         {
329             for (it = m_includeTagFilter.begin() ; it != m_includeTagFilter.end() ; ++it)
330             {
331                 if (tagIds.contains(*it))
332                 {
333                     match = true;
334                     break;
335                 }
336             }
337 
338             match |= (m_untaggedFilter && tagIds.isEmpty());
339         }
340         else // AND matching condition...
341         {
342             // m_untaggedFilter and non-empty tag filter, combined with AND, is logically no match
343             if (!m_untaggedFilter)
344             {
345                 for (it = m_includeTagFilter.begin() ; it != m_includeTagFilter.end() ; ++it)
346                 {
347                     if (!tagIds.contains(*it))
348                     {
349                         break;
350                     }
351                 }
352 
353                 if (it == m_includeTagFilter.end())
354                 {
355                     match = true;
356                 }
357             }
358         }
359 
360         for (it = m_excludeTagFilter.begin() ; it != m_excludeTagFilter.end() ; ++it)
361         {
362             if (tagIds.contains(*it))
363             {
364                 match = false;
365                 break;
366             }
367         }
368     }
369     else if (m_untaggedFilter)
370     {
371         match = !TagsCache::instance()->containsPublicTags(info.tagIds());
372     }
373     else
374     {
375         match = true;
376     }
377 
378     //-- Filter by pick labels ------------------------------------------------
379 
380     if (!m_pickLabelTagFilter.isEmpty())
381     {
382         QList<int> tagIds = info.tagIds();
383         bool matchPL      = false;
384 
385         if (containsAnyOf(m_pickLabelTagFilter, tagIds))
386         {
387             matchPL = true;
388         }
389         else
390         {
391             int noPickLabelTagId = TagsCache::instance()->tagForPickLabel(NoPickLabel);
392 
393             if (m_pickLabelTagFilter.contains(noPickLabelTagId))
394             {
395                 // Searching for "has no ColorLabel" requires special handling:
396                 // Scan that the tag ids contains none of the ColorLabel tags, except maybe the NoColorLabel tag
397 
398                 matchPL = containsNoneOfExcept(tagIds, TagsCache::instance()->pickLabelTags(), noPickLabelTagId);
399             }
400         }
401 
402         match &= matchPL;
403     }
404 
405     //-- Filter by color labels ------------------------------------------------
406 
407     if (!m_colorLabelTagFilter.isEmpty())
408     {
409         QList<int> tagIds = info.tagIds();
410         bool matchCL      = false;
411 
412         if (containsAnyOf(m_colorLabelTagFilter, tagIds))
413         {
414             matchCL = true;
415         }
416         else
417         {
418             int noColorLabelTagId = TagsCache::instance()->tagForColorLabel(NoColorLabel);
419 
420             if (m_colorLabelTagFilter.contains(noColorLabelTagId))
421             {
422                 // Searching for "has no ColorLabel" requires special handling:
423                 // Scan that the tag ids contains none of the ColorLabel tags, except maybe the NoColorLabel tag
424                 matchCL = containsNoneOfExcept(tagIds, TagsCache::instance()->colorLabelTags(), noColorLabelTagId);
425             }
426         }
427 
428         match &= matchCL;
429     }
430 
431     //-- Filter by date -----------------------------------------------------------
432 
433     if (!m_dayFilter.isEmpty())
434     {
435         match &= m_dayFilter.contains(QDateTime(info.dateTime().date(), QTime()));
436     }
437 
438     //-- Filter by rating ---------------------------------------------------------
439 
440     if (m_ratingFilter >= 0)
441     {
442         // for now we treat -1 (no rating) just like a rating of 0.
443 
444         int rating = info.rating();
445 
446         if (rating == -1)
447         {
448             rating = 0;
449         }
450 
451         if (m_isUnratedExcluded && rating == 0)
452         {
453             match = false;
454         }
455         else
456         {
457             if (m_ratingCond == GreaterEqualCondition)
458             {
459                 // If the rating is not >=, i.e it is <, then it does not match.
460 
461                 if (rating < m_ratingFilter)
462                 {
463                     match = false;
464                 }
465             }
466             else if (m_ratingCond == EqualCondition)
467             {
468                 // If the rating is not =, i.e it is !=, then it does not match.
469 
470                 if (rating != m_ratingFilter)
471                 {
472                     match = false;
473                 }
474             }
475             else
476             {
477                 // If the rating is not <=, i.e it is >, then it does not match.
478 
479                 if (rating > m_ratingFilter)
480                 {
481                     match = false;
482                 }
483             }
484         }
485     }
486 
487     // -- Filter by mime type -----------------------------------------------------
488 
489     switch (m_mimeTypeFilter)
490     {
491         // info.format is a standardized string: Only one possibility per mime type
492 
493         case MimeFilter::ImageFiles:
494         {
495             if (info.category() != DatabaseItem::Image)
496             {
497                 match = false;
498             }
499 
500             break;
501         }
502 
503         case MimeFilter::JPGFiles:
504         {
505             if (info.format() != QLatin1String("JPG"))
506             {
507                 match = false;
508             }
509 
510             break;
511         }
512 
513         case MimeFilter::PNGFiles:
514         {
515             if (info.format() != QLatin1String("PNG"))
516             {
517                 match = false;
518             }
519 
520             break;
521         }
522 
523         case MimeFilter::HEIFFiles:
524         {
525             if (info.format() != QLatin1String("HEIF"))
526             {
527                 match = false;
528             }
529 
530             break;
531         }
532 
533         case MimeFilter::PGFFiles:
534         {
535             if (info.format() != QLatin1String("PGF"))
536             {
537                 match = false;
538             }
539 
540             break;
541         }
542 
543         case MimeFilter::TIFFiles:
544         {
545             if (info.format() != QLatin1String("TIFF"))
546             {
547                 match = false;
548             }
549 
550             break;
551         }
552 
553         case MimeFilter::DNGFiles:
554         {
555             if (info.format() != QLatin1String("RAW-DNG"))
556             {
557                 match = false;
558             }
559 
560             break;
561         }
562 
563         case MimeFilter::NoRAWFiles:
564         {
565             if (info.format().startsWith(QLatin1String("RAW")))
566             {
567                 match = false;
568             }
569 
570             break;
571         }
572 
573         case MimeFilter::RAWFiles:
574         {
575             if (!info.format().startsWith(QLatin1String("RAW")))
576             {
577                 match = false;
578             }
579 
580             break;
581         }
582 
583         case MimeFilter::MoviesFiles:
584         {
585             if (info.category() != DatabaseItem::Video)
586             {
587                 match = false;
588             }
589 
590             break;
591         }
592 
593         case MimeFilter::AudioFiles:
594         {
595             if (info.category() != DatabaseItem::Audio)
596             {
597                 match = false;
598             }
599 
600             break;
601         }
602 
603         case MimeFilter::RasterGraphics:
604         {
605             if (
606                 info.format() != QLatin1String("PSD") &&         // Adobe Photoshop Document
607                 info.format() != QLatin1String("PSB") &&         // Adobe Photoshop Big
608                 info.format() != QLatin1String("XCF") &&         // Gimp
609                 info.format() != QLatin1String("KRA") &&         // Krita
610                 info.format() != QLatin1String("ORA")            // Open Raster
611                )
612             {
613                 match = false;
614             }
615 
616             break;
617         }
618 
619         default:
620         {
621             // All Files: do nothing...
622             break;
623         }
624     }
625 
626     //-- Filter by geolocation ----------------------------------------------------
627 
628     if (m_geolocationCondition != GeolocationNoFilter)
629     {
630         if (m_geolocationCondition == GeolocationNoCoordinates)
631         {
632             if (info.hasCoordinates())
633             {
634                 match = false;
635             }
636         }
637         else if (m_geolocationCondition == GeolocationHasCoordinates)
638         {
639             if (!info.hasCoordinates())
640             {
641                 match = false;
642             }
643         }
644     }
645 
646     //-- Filter by text -----------------------------------------------------------
647 
648     if (!m_textFilterSettings.text.isEmpty())
649     {
650         bool textMatch = false;
651 
652         QRegExp textRegExp(m_textFilterSettings.text);
653         textRegExp.setPatternSyntax(QRegExp::WildcardUnix);
654         textRegExp.setCaseSensitivity(m_textFilterSettings.caseSensitive);
655 
656         // Image name
657 
658         if ((m_textFilterSettings.textFields & SearchTextFilterSettings::ImageName) &&
659             (textRegExp.exactMatch(info.name())                                     ||
660             info.name().contains(m_textFilterSettings.text, m_textFilterSettings.caseSensitive)))
661         {
662             textMatch = true;
663         }
664 
665         // Image title
666 
667         if ((m_textFilterSettings.textFields & SearchTextFilterSettings::ImageTitle) &&
668             (textRegExp.exactMatch(info.title())                                     ||
669             info.title().contains(m_textFilterSettings.text, m_textFilterSettings.caseSensitive)))
670         {
671             textMatch = true;
672         }
673 
674         // Image comment
675 
676         if ((m_textFilterSettings.textFields & SearchTextFilterSettings::ImageComment) &&
677             (textRegExp.exactMatch(info.comment())                                     ||
678             info.comment().contains(m_textFilterSettings.text, m_textFilterSettings.caseSensitive)))
679         {
680             textMatch = true;
681         }
682 
683         // Tag names
684 
685         foreach (int id, info.tagIds())
686         {
687             if ((m_textFilterSettings.textFields & SearchTextFilterSettings::TagName) &&
688                 (textRegExp.exactMatch(m_tagNameHash.value(id))                       ||
689                 m_tagNameHash.value(id).contains(m_textFilterSettings.text, m_textFilterSettings.caseSensitive)))
690             {
691                 textMatch = true;
692             }
693         }
694 
695         // Album names
696 
697         if ((m_textFilterSettings.textFields & SearchTextFilterSettings::AlbumName) &&
698             (textRegExp.exactMatch(m_albumNameHash.value(info.albumId()))           ||
699             m_albumNameHash.value(info.albumId()).contains(m_textFilterSettings.text, m_textFilterSettings.caseSensitive)))
700         {
701             textMatch = true;
702         }
703 
704         // Image Aspect Ratio
705 
706         if (m_textFilterSettings.textFields & SearchTextFilterSettings::ImageAspectRatio)
707         {
708             QRegExp expRatio (QLatin1String("^\\d+:\\d+$"));
709             QRegExp expFloat (QLatin1String("^\\d+(.\\d+)?$"));
710 
711             if ((expRatio.indexIn(m_textFilterSettings.text) > -1) && m_textFilterSettings.text.contains(QRegExp(QLatin1String(":\\d+"))))
712             {
713                 QString trimmedTextFilterSettingsText = m_textFilterSettings.text;
714                 QStringList numberStringList          = trimmedTextFilterSettingsText.split(QLatin1Char(':'), QString::SkipEmptyParts);
715 
716                 if (numberStringList.length() == 2)
717                 {
718                     QString numString     = (QString)numberStringList.at(0), denomString = (QString)numberStringList.at(1);
719                     bool canConverseNum   = false;
720                     bool canConverseDenom = false;
721                     int num               = numString.toInt(&canConverseNum, 10), denom = denomString.toInt(&canConverseDenom, 10);
722 
723                     if (canConverseNum && canConverseDenom)
724                     {
725                         if (fabs(info.aspectRatio() - (double)num / denom) < 0.1)
726                         {
727                             textMatch = true;
728                         }
729                     }
730                 }
731             }
732             else if (expFloat.indexIn(m_textFilterSettings.text) > -1)
733             {
734                 QString trimmedTextFilterSettingsText = m_textFilterSettings.text;
735                 bool    canConverse                   = false;
736                 double  ratio                         = trimmedTextFilterSettingsText.toDouble(&canConverse);
737 
738                 if (canConverse)
739                 {
740                     if (fabs(info.aspectRatio() - ratio) < 0.1)
741                     {
742                         textMatch = true;
743                     }
744                 }
745             }
746         }
747 
748         // Image Pixel Size
749         // See bug #341053 for details.
750 
751         if (m_textFilterSettings.textFields & SearchTextFilterSettings::ImagePixelSize)
752         {
753             QSize size    = info.dimensions();
754             int pixelSize = size.height()*size.width();
755             QString text  = m_textFilterSettings.text;
756 
757             if      (text.contains(QRegExp(QLatin1String("^>\\d{1,15}$"))) &&
758                 (pixelSize > (text.remove(0, 1)).toInt()))
759             {
760                 textMatch = true;
761             }
762             else if (text.contains(QRegExp(QLatin1String("^<\\d{1,15}$"))) &&
763                      (pixelSize < (text.remove(0, 1)).toInt()))
764             {
765                 textMatch = true;
766             }
767             else if (text.contains(QRegExp(QLatin1String("^\\d+$"))) &&
768                      (pixelSize == text.toInt()))
769             {
770                 textMatch = true;
771             }
772         }
773 
774         match &= textMatch;
775 
776         if (foundText)
777         {
778             *foundText = textMatch;
779         }
780     }
781 
782     // -- filter by URL-whitelists ------------------------------------------------
783     // NOTE: whitelists are always AND for now.
784 
785     if (match)
786     {
787         const QUrl url = info.fileUrl();
788 
789         for (QHash<QString, QList<QUrl>>::const_iterator it = m_urlWhitelists.constBegin() ;
790              it != m_urlWhitelists.constEnd() ; ++it)
791         {
792             match = it->contains(url);
793 
794             if (!match)
795             {
796                 break;
797             }
798         }
799     }
800 
801     if (match)
802     {
803         const qlonglong id = info.id();
804 
805         for (QHash<QString, QList<qlonglong> >::const_iterator it = m_idWhitelists.constBegin() ;
806              it != m_idWhitelists.constEnd() ; ++it)
807         {
808             match = it->contains(id);
809 
810             if (!match)
811             {
812                 break;
813             }
814         }
815     }
816 
817     return match;
818 }
819 
820 // -------------------------------------------------------------------------------------------------
821 
VersionItemFilterSettings()822 VersionItemFilterSettings::VersionItemFilterSettings()
823     : m_includeTagFilter  (0),
824       m_exceptionTagFilter(0)
825 {
826 }
827 
VersionItemFilterSettings(const VersionManagerSettings & settings)828 VersionItemFilterSettings::VersionItemFilterSettings(const VersionManagerSettings& settings)
829 {
830     setVersionManagerSettings(settings);
831 }
832 
operator ==(const VersionItemFilterSettings & other) const833 bool VersionItemFilterSettings::operator==(const VersionItemFilterSettings& other) const
834 {
835     return ((m_excludeTagFilter == other.m_excludeTagFilter) &&
836             (m_exceptionLists   == other.m_exceptionLists));
837 }
838 
matches(const ItemInfo & info) const839 bool VersionItemFilterSettings::matches(const ItemInfo& info) const
840 {
841     if (!isFiltering())
842     {
843         return true;
844     }
845 
846     const qlonglong id = info.id();
847 
848     for (QHash<QString, QList<qlonglong> >::const_iterator it = m_exceptionLists.constBegin() ;
849          it != m_exceptionLists.constEnd() ; ++it)
850     {
851         if (it->contains(id))
852         {
853             return true;
854         }
855     }
856 
857     bool match        = true;
858     QList<int> tagIds = info.tagIds();
859 
860     if (!tagIds.contains(m_includeTagFilter))
861     {
862         for (QList<int>::const_iterator it = m_excludeTagFilter.begin() ;
863              it != m_excludeTagFilter.end() ; ++it)
864         {
865             if (tagIds.contains(*it))
866             {
867                 match = false;
868                 break;
869             }
870         }
871     }
872 
873     if (!match)
874     {
875         if (tagIds.contains(m_exceptionTagFilter))
876         {
877             match = true;
878         }
879     }
880 
881     return match;
882 }
883 
isHiddenBySettings(const ItemInfo & info) const884 bool VersionItemFilterSettings::isHiddenBySettings(const ItemInfo& info) const
885 {
886     QList<int> tagIds = info.tagIds();
887 
888     foreach (int tagId, m_excludeTagFilter)
889     {
890         if (tagIds.contains(tagId))
891         {
892             return true;
893         }
894     }
895 
896     return false;
897 }
898 
isExemptedBySettings(const ItemInfo & info) const899 bool VersionItemFilterSettings::isExemptedBySettings(const ItemInfo& info) const
900 {
901     return info.tagIds().contains(m_exceptionTagFilter);
902 }
903 
setVersionManagerSettings(const VersionManagerSettings & settings)904 void VersionItemFilterSettings::setVersionManagerSettings(const VersionManagerSettings& settings)
905 {
906     m_excludeTagFilter.clear();
907 
908     if (!settings.enabled)
909     {
910         return;
911     }
912 
913     if (!(settings.showInViewFlags & VersionManagerSettings::ShowOriginal))
914     {
915         m_excludeTagFilter << TagsCache::instance()->getOrCreateInternalTag(InternalTagName::originalVersion());
916     }
917 
918     if (!(settings.showInViewFlags & VersionManagerSettings::ShowIntermediates))
919     {
920         m_excludeTagFilter << TagsCache::instance()->getOrCreateInternalTag(InternalTagName::intermediateVersion());
921     }
922 
923     m_includeTagFilter   = TagsCache::instance()->getOrCreateInternalTag(InternalTagName::currentVersion());
924     m_exceptionTagFilter = TagsCache::instance()->getOrCreateInternalTag(InternalTagName::versionAlwaysVisible());
925 }
926 
setExceptionList(const QList<qlonglong> & idList,const QString & id)927 void VersionItemFilterSettings::setExceptionList(const QList<qlonglong>& idList, const QString& id)
928 {
929     if (idList.isEmpty())
930     {
931         m_exceptionLists.remove(id);
932     }
933     else
934     {
935         m_exceptionLists.insert(id, idList);
936     }
937 }
938 
isFiltering() const939 bool VersionItemFilterSettings::isFiltering() const
940 {
941     return !m_excludeTagFilter.isEmpty();
942 }
943 
isFilteringByTags() const944 bool VersionItemFilterSettings::isFilteringByTags() const
945 {
946     return isFiltering();
947 }
948 
949 // -------------------------------------------------------------------------------------------------
950 
GroupItemFilterSettings()951 GroupItemFilterSettings::GroupItemFilterSettings()
952     : m_allOpen(false)
953 {
954 }
955 
operator ==(const GroupItemFilterSettings & other) const956 bool GroupItemFilterSettings::operator==(const GroupItemFilterSettings& other) const
957 {
958     return ((m_allOpen    == other.m_allOpen) &&
959             (m_openGroups == other.m_openGroups));
960 }
961 
matches(const ItemInfo & info) const962 bool GroupItemFilterSettings::matches(const ItemInfo& info) const
963 {
964     if (m_allOpen)
965     {
966         return true;
967     }
968 
969     if (info.isGrouped())
970     {
971         return m_openGroups.contains(info.groupImage().id());
972     }
973 
974     return true;
975 }
976 
setOpen(qlonglong group,bool open)977 void GroupItemFilterSettings::setOpen(qlonglong group, bool open)
978 {
979     if (open)
980     {
981         m_openGroups << group;
982     }
983     else
984     {
985         m_openGroups.remove(group);
986     }
987 }
988 
isOpen(qlonglong group) const989 bool GroupItemFilterSettings::isOpen(qlonglong group) const
990 {
991     return m_openGroups.contains(group);
992 }
993 
setAllOpen(bool open)994 void GroupItemFilterSettings::setAllOpen(bool open)
995 {
996     m_allOpen = open;
997 }
998 
isAllOpen() const999 bool GroupItemFilterSettings::isAllOpen() const
1000 {
1001     return m_allOpen;
1002 }
1003 
isFiltering() const1004 bool GroupItemFilterSettings::isFiltering() const
1005 {
1006     return !m_allOpen;
1007 }
1008 
watchFlags() const1009 DatabaseFields::Set GroupItemFilterSettings::watchFlags() const
1010 {
1011     return DatabaseFields::Set(DatabaseFields::ImageRelations);
1012 }
1013 
1014 } // namespace Digikam
1015