File indexing completed on 2025-01-05 03:54:12

0001 /* ============================================================
0002  *
0003  * This file is a part of digiKam project
0004  * https://www.digikam.org
0005  *
0006  * Date        : 2009-03-05
0007  * Description : Filter values for use with ItemFilterModel
0008  *
0009  * SPDX-FileCopyrightText: 2009 by Marcel Wiesweg <marcel dot wiesweg at gmx dot de>
0010  * SPDX-FileCopyrightText: 2014 by Mohamed_Anwer <m_dot_anwer at gmx dot com>
0011  *
0012  * SPDX-License-Identifier: GPL-2.0-or-later
0013  *
0014  * ============================================================ */
0015 
0016 #include "itemsortsettings.h"
0017 
0018 // Qt includes
0019 
0020 #include <QRectF>
0021 #include <QDateTime>
0022 
0023 // Local includes
0024 
0025 #include "iteminfo.h"
0026 #include "coredbfields.h"
0027 #include "facetagseditor.h"
0028 
0029 namespace Digikam
0030 {
0031 
0032 ItemSortSettings::ItemSortSettings()
0033     : categorizationMode            (NoCategories),
0034       categorizationSortOrder       (DefaultOrder),
0035       currentCategorizationSortOrder(Qt::AscendingOrder),
0036       categorizationCaseSensitivity (Qt::CaseSensitive),
0037       sortRole                      (SortByFileName),
0038       sortOrder                     (DefaultOrder),
0039       strTypeNatural                (true),
0040       currentSortOrder              (Qt::AscendingOrder),
0041       sortCaseSensitivity           (Qt::CaseSensitive)
0042 {
0043 }
0044 
0045 bool ItemSortSettings::operator==(const ItemSortSettings& other) const
0046 {
0047     return (
0048             (categorizationMode            == other.categorizationMode)            &&
0049             (categorizationSortOrder       == other.categorizationSortOrder)       &&
0050             (categorizationCaseSensitivity == other.categorizationCaseSensitivity) &&
0051             (sortRole                      == other.sortRole)                      &&
0052             (sortOrder                     == other.sortOrder)                     &&
0053             (sortCaseSensitivity           == other.sortCaseSensitivity)
0054            );
0055 }
0056 
0057 void ItemSortSettings::setCategorizationMode(CategorizationMode mode)
0058 {
0059     categorizationMode = mode;
0060 
0061     if (categorizationSortOrder == DefaultOrder)
0062     {
0063         currentCategorizationSortOrder = defaultSortOrderForCategorizationMode(categorizationMode);
0064     }
0065 }
0066 
0067 void ItemSortSettings::setCategorizationSortOrder(SortOrder order)
0068 {
0069     categorizationSortOrder = order;
0070 
0071     if (categorizationSortOrder == DefaultOrder)
0072     {
0073         currentCategorizationSortOrder = defaultSortOrderForCategorizationMode(categorizationMode);
0074     }
0075     else
0076     {
0077         currentCategorizationSortOrder = (Qt::SortOrder)categorizationSortOrder;
0078     }
0079 }
0080 
0081 void ItemSortSettings::setSortRole(SortRole role)
0082 {
0083     sortRole = role;
0084 
0085     if (sortOrder == DefaultOrder)
0086     {
0087         currentSortOrder = defaultSortOrderForSortRole(sortRole);
0088     }
0089 }
0090 
0091 void ItemSortSettings::setSortOrder(SortOrder order)
0092 {
0093     sortOrder = order;
0094 
0095     if (sortOrder == DefaultOrder)
0096     {
0097         currentSortOrder = defaultSortOrderForSortRole(sortRole);
0098     }
0099     else
0100     {
0101         currentSortOrder = (Qt::SortOrder)order;
0102     }
0103 }
0104 
0105 void ItemSortSettings::setStringTypeNatural(bool natural)
0106 {
0107     strTypeNatural = natural;
0108 }
0109 
0110 Qt::SortOrder ItemSortSettings::defaultSortOrderForCategorizationMode(CategorizationMode mode)
0111 {
0112     switch (mode)
0113     {
0114         case OneCategory:
0115         case NoCategories:
0116         case CategoryByAlbum:
0117         case CategoryByMonth:
0118         case CategoryByFormat:
0119         default:
0120         {
0121             return Qt::AscendingOrder;
0122         }
0123     }
0124 }
0125 
0126 Qt::SortOrder ItemSortSettings::defaultSortOrderForSortRole(SortRole role)
0127 {
0128     switch (role)
0129     {
0130         case SortByFilePath:
0131         case SortByFileName:
0132         case SortByCreationDate:
0133         case SortByModificationDate:
0134         case SortByManualOrderAndName:
0135         case SortByManualOrderAndDate:
0136         case SortByFaces:
0137         {
0138             return Qt::AscendingOrder;
0139         }
0140 
0141         case SortByRating:
0142         case SortByFileSize:
0143         case SortByImageSize:
0144         case SortBySimilarity:
0145         case SortByAspectRatio:
0146         {
0147             return Qt::DescendingOrder;
0148         }
0149 
0150         default:
0151         {
0152             return Qt::AscendingOrder;
0153         }
0154     }
0155 }
0156 
0157 // Feel free to optimize. QString::number is 3x slower.
0158 static inline QString fastNumberToString(int id)
0159 {
0160     const int size = sizeof(int) * 2;
0161     int number     = id;
0162     char c[size + 1];
0163     c[size]        = '\0';
0164 
0165     for (int i = 0 ; i < size ; ++i)
0166     {
0167         c[i]     = 'a' + (number & 0xF);
0168         number >>= 4;
0169     }
0170 
0171     return QLatin1String(c);
0172 }
0173 
0174 int ItemSortSettings::compareCategories(const ItemInfo& left, const ItemInfo& right,
0175                                         const FaceTagsIface& leftFace, const FaceTagsIface& rightFace) const
0176 {
0177     switch (categorizationMode)
0178     {
0179         case NoCategories:
0180         case OneCategory:
0181         {
0182             return 0;
0183         }
0184 
0185         case CategoryByAlbum:
0186         {
0187             int leftAlbum  = left.albumId();
0188             int rightAlbum = right.albumId();
0189 
0190             // return comparison result
0191 
0192             if      (leftAlbum == rightAlbum)
0193             {
0194                 return 0;
0195             }
0196             else if (lessThanByOrder(leftAlbum, rightAlbum,
0197                                      currentCategorizationSortOrder))
0198             {
0199                 return -1;
0200             }
0201             else
0202             {
0203                 return 1;
0204             }
0205         }
0206 
0207         case CategoryByFormat:
0208         {
0209             return naturalCompare(left.format(), right.format(),
0210                                   currentCategorizationSortOrder,
0211                                   categorizationCaseSensitivity, strTypeNatural);
0212         }
0213 
0214         case CategoryByMonth:
0215         {
0216             int leftMonth  = left.dateTime().date().year()  * 100 +
0217                              left.dateTime().date().month();
0218             int rightMonth = right.dateTime().date().year() * 100 +
0219                              right.dateTime().date().month();
0220 
0221             return compareByOrder(leftMonth, rightMonth,
0222                                   currentCategorizationSortOrder);
0223         }
0224 
0225         case CategoryByFaces:
0226         {
0227             if (leftFace.isNull() && rightFace.isNull())
0228             {
0229                 return compareByOrder(left.faceCount(),
0230                                       right.faceCount(),
0231                                       currentCategorizationSortOrder);
0232             }
0233 
0234             bool isLeftIgnored = (leftFace.type() == FaceTagsIface::IgnoredName);
0235             bool isLeftUnknown = (leftFace.type() == FaceTagsIface::UnknownName);
0236 
0237             if (isLeftIgnored != (rightFace.type() == FaceTagsIface::IgnoredName))
0238             {
0239                 if (currentCategorizationSortOrder == Qt::AscendingOrder)
0240                 {
0241                     return isLeftIgnored ? 1 : -1;
0242                 }
0243                 else
0244                 {
0245                     return isLeftIgnored ? -1 : 1;
0246                 }
0247             }
0248 
0249             if (isLeftUnknown != (rightFace.type() == FaceTagsIface::UnknownName))
0250             {
0251                 if (currentCategorizationSortOrder == Qt::AscendingOrder)
0252                 {
0253                     return isLeftUnknown ? 1 : -1;
0254                 }
0255                 else
0256                 {
0257                     return isLeftUnknown ? -1 : 1;
0258                 }
0259             }
0260 
0261             QString leftValue;
0262             QString rightValue;
0263 
0264             if      (leftFace.type() == FaceTagsIface::ConfirmedName)
0265             {
0266                 leftValue = FaceTags::faceNameForTag(leftFace.tagId());
0267             }
0268             else if (leftFace.type() == FaceTagsIface::UnconfirmedName)
0269             {
0270                 leftValue = left.getSuggestedNames().value(leftFace.region().toXml());
0271             }
0272             else
0273             {
0274                 leftValue = TagPropertyName::unknownPerson();
0275             }
0276 
0277             if      (rightFace.type() == FaceTagsIface::ConfirmedName)
0278             {
0279                 rightValue = FaceTags::faceNameForTag(rightFace.tagId());
0280             }
0281             else if (rightFace.type() == FaceTagsIface::UnconfirmedName)
0282             {
0283                  rightValue = right.getSuggestedNames().value(rightFace.region().toXml());
0284             }
0285             else
0286             {
0287                 rightValue = TagPropertyName::unknownPerson();
0288             }
0289 
0290             leftValue  += fastNumberToString(leftFace.type()) +
0291                           fastNumberToString(leftFace.tagId());
0292 
0293             rightValue += fastNumberToString(rightFace.type()) +
0294                           fastNumberToString(rightFace.tagId());
0295 
0296             // Compare alphabetically based on the face name
0297 
0298             return naturalCompare(leftValue, rightValue,
0299                                   currentCategorizationSortOrder,
0300                                   categorizationCaseSensitivity, strTypeNatural);
0301         }
0302 
0303         default:
0304         {
0305             return 0;
0306         }
0307     }
0308 }
0309 
0310 bool ItemSortSettings::lessThan(const ItemInfo& left, const ItemInfo& right) const
0311 {
0312     int result = compare(left, right, sortRole);
0313 
0314     if (result != 0)
0315     {
0316         return (result < 0);
0317     }
0318 
0319     // are they identical?
0320 
0321     if (left == right)
0322     {
0323         return false;
0324     }
0325 
0326     // If left and right equal for first sort order, use a hierarchy of all sort orders
0327 
0328     if ((result = compare(left, right, SortByFileName)) != 0)
0329     {
0330         return (result < 0);
0331     }
0332 
0333     if ((result = compare(left, right, SortByCreationDate)) != 0)
0334     {
0335         return (result < 0);
0336     }
0337 
0338     if ((result = compare(left, right, SortByModificationDate)) != 0)
0339     {
0340         return (result < 0);
0341     }
0342 
0343     if ((result = compare(left, right, SortByFilePath)) != 0)
0344     {
0345         return (result < 0);
0346     }
0347 
0348     if ((result = compare(left, right, SortByFileSize)) != 0)
0349     {
0350         return (result < 0);
0351     }
0352 
0353     if ((result = compare(left, right, SortBySimilarity)) != 0)
0354     {
0355         return (result < 0);
0356     }
0357 
0358     if ((result = compare(left, right, SortByManualOrderAndName)) != 0)
0359     {
0360         return (result < 0);
0361     }
0362 
0363     if ((result = compare(left, right, SortByManualOrderAndDate)) != 0)
0364     {
0365         return (result < 0);
0366     }
0367 
0368     return false;
0369 }
0370 
0371 int ItemSortSettings::compare(const ItemInfo& left, const ItemInfo& right) const
0372 {
0373     return compare(left, right, sortRole);
0374 }
0375 
0376 int ItemSortSettings::compare(const ItemInfo& left, const ItemInfo& right, SortRole role) const
0377 {
0378     switch (role)
0379     {
0380         case SortByFileName:
0381         {
0382             return naturalCompare(left.name(), right.name(),
0383                                   currentSortOrder, sortCaseSensitivity, strTypeNatural);
0384         }
0385 
0386         case SortByFilePath:
0387         {
0388             return naturalCompare(left.filePath(), right.filePath(),
0389                                   currentSortOrder, sortCaseSensitivity, strTypeNatural);
0390         }
0391 
0392         case SortByFileSize:
0393         {
0394             return compareByOrder(left.fileSize(), right.fileSize(), currentSortOrder);
0395         }
0396 
0397         case SortByCreationDate:
0398         {
0399             return compareByOrder(left.dateTime(), right.dateTime(), currentSortOrder);
0400         }
0401 
0402         case SortByModificationDate:
0403         {
0404             return compareByOrder(left.modDateTime(), right.modDateTime(), currentSortOrder);
0405         }
0406 
0407         case SortByRating:
0408         {
0409             // I have the feeling that inverting the sort order for rating is the natural order
0410 
0411             return - compareByOrder(left.rating(), right.rating(), currentSortOrder);
0412         }
0413 
0414         case SortByImageSize:
0415         {
0416             QSize leftSize  = left.dimensions();
0417             QSize rightSize = right.dimensions();
0418             int leftPixels  = leftSize.width()  * leftSize.height();
0419             int rightPixels = rightSize.width() * rightSize.height();
0420             return compareByOrder(leftPixels, rightPixels, currentSortOrder);
0421         }
0422 
0423         case SortByAspectRatio:
0424         {
0425             QSize leftSize  = left.dimensions();
0426             QSize rightSize = right.dimensions();
0427             int leftAR      = (double(leftSize.width())  / double(leftSize.height()))  * 1000000;
0428             int rightAR     = (double(rightSize.width()) / double(rightSize.height())) * 1000000;
0429             return compareByOrder(leftAR, rightAR, currentSortOrder);
0430         }
0431 
0432         case SortBySimilarity:
0433         {
0434             qlonglong leftReferenceImageId  = left.currentReferenceImage();
0435             qlonglong rightReferenceImageId = right.currentReferenceImage();
0436 
0437             // make sure that the original image has always the highest similarity.
0438 
0439             double leftSimilarity           = left.id()  == leftReferenceImageId  ? 1.1 : left.currentSimilarity();
0440             double rightSimilarity          = right.id() == rightReferenceImageId ? 1.1 : right.currentSimilarity();
0441             return compareByOrder(leftSimilarity, rightSimilarity, currentSortOrder);
0442         }
0443 
0444         // Implementation to make Unconfirmed Faces of a tag appear before Confirmed faces.
0445 
0446         case SortByFaces:
0447         {
0448             return compareByOrder(right.unconfirmedFaceCount(), left.unconfirmedFaceCount(), currentSortOrder);
0449         }
0450 
0451         case SortByManualOrderAndName:
0452         case SortByManualOrderAndDate:
0453         {
0454             int result;
0455 
0456             if ((result = compareByOrder(left.manualOrder(), right.manualOrder(), currentSortOrder)) != 0)
0457             {
0458                 return result;
0459             }
0460 
0461             if (role == SortByManualOrderAndDate)
0462             {
0463                 return compareByOrder(left.dateTime(), right.dateTime(), currentSortOrder);
0464             }
0465 
0466             return naturalCompare(left.name(), right.name(),
0467                                   currentSortOrder, sortCaseSensitivity, strTypeNatural);
0468         }
0469 
0470         default:
0471         {
0472             return 1;
0473         }
0474     }
0475 }
0476 
0477 bool ItemSortSettings::lessThan(const QVariant& left, const QVariant& right) const
0478 {
0479 
0480 #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
0481 
0482     if (left.typeId() != right.typeId())
0483 
0484 #else
0485 
0486     if (left.type() != right.type())
0487 
0488 #endif
0489 
0490     {
0491         return false;
0492     }
0493 
0494 #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
0495 
0496     switch (left.typeId())
0497 
0498 #else
0499 
0500     switch (left.type())
0501 
0502 #endif
0503 
0504     {
0505         case QVariant::Int:
0506         {
0507             return compareByOrder(left.toInt(), right.toInt(), currentSortOrder);
0508         }
0509 
0510         case QVariant::UInt:
0511         {
0512             return compareByOrder(left.toUInt(), right.toUInt(), currentSortOrder);
0513         }
0514 
0515         case QVariant::LongLong:
0516         {
0517             return compareByOrder(left.toLongLong(), right.toLongLong(), currentSortOrder);
0518         }
0519 
0520         case QVariant::ULongLong:
0521         {
0522             return compareByOrder(left.toULongLong(), right.toULongLong(), currentSortOrder);
0523         }
0524 
0525         case QVariant::Double:
0526         {
0527             return compareByOrder(left.toDouble(), right.toDouble(), currentSortOrder);
0528         }
0529 
0530         case QVariant::Date:
0531         {
0532             return compareByOrder(left.toDate(), right.toDate(), currentSortOrder);
0533         }
0534 
0535         case QVariant::DateTime:
0536         {
0537             return compareByOrder(left.toDateTime(), right.toDateTime(), currentSortOrder);
0538         }
0539 
0540         case QVariant::Time:
0541         {
0542             return compareByOrder(left.toTime(), right.toTime(), currentSortOrder);
0543         }
0544 
0545         case QVariant::Rect:
0546         case QVariant::RectF:
0547         {
0548             QRectF rectLeft  = left.toRectF();
0549             QRectF rectRight = right.toRectF();
0550             int result;
0551 
0552             if ((result = compareByOrder(rectLeft.top(), rectRight.top(), currentSortOrder)) != 0)
0553             {
0554                 return (result < 0);
0555             }
0556 
0557             if ((result = compareByOrder(rectLeft.left(), rectRight.left(), currentSortOrder)) != 0)
0558             {
0559                 return (result < 0);
0560             }
0561 
0562             QSizeF sizeLeft  = rectLeft.size();
0563             QSizeF sizeRight = rectRight.size();
0564 
0565             if ((result = compareByOrder(sizeLeft.width()*sizeLeft.height(), sizeRight.width()*sizeRight.height(), currentSortOrder)) != 0)
0566             {
0567                 return (result < 0);
0568             }
0569 
0570 #if __GNUC__ >= 7       // krazy:exclude=cpp
0571             [[fallthrough]];
0572 #endif
0573         }
0574 
0575         default:
0576         {
0577             return naturalCompare(left.toString(), right.toString(),
0578                                   currentSortOrder, sortCaseSensitivity, strTypeNatural);
0579         }
0580     }
0581 }
0582 
0583 DatabaseFields::Set ItemSortSettings::watchFlags() const
0584 {
0585     DatabaseFields::Set set;
0586 
0587     switch (sortRole)
0588     {
0589         case SortByFileName:
0590         {
0591             set |= DatabaseFields::Name;
0592             break;
0593         }
0594 
0595         case SortByFilePath:
0596         {
0597             set |= DatabaseFields::Name;
0598             break;
0599         }
0600 
0601         case SortByFileSize:
0602         {
0603             set |= DatabaseFields::FileSize;
0604             break;
0605         }
0606 
0607         case SortByModificationDate:
0608         {
0609             set |= DatabaseFields::ModificationDate;
0610             break;
0611         }
0612 
0613         case SortByCreationDate:
0614         {
0615             set |= DatabaseFields::CreationDate;
0616             break;
0617         }
0618 
0619         case SortByRating:
0620         {
0621             set |= DatabaseFields::Rating;
0622             break;
0623         }
0624 
0625         case SortByImageSize:
0626         {
0627             set |= DatabaseFields::Width | DatabaseFields::Height;
0628             break;
0629         }
0630 
0631         case SortByAspectRatio:
0632         {
0633             set |= DatabaseFields::Width | DatabaseFields::Height;
0634             break;
0635         }
0636 
0637         case SortBySimilarity:
0638         {
0639             // TODO: Not sure what to do here....
0640 
0641             set |= DatabaseFields::Name;
0642             break;
0643         }
0644 
0645         case SortByFaces:
0646         {
0647             // Nothing needed for this.
0648 
0649             break;
0650         }
0651 
0652         case SortByManualOrderAndName:
0653         case SortByManualOrderAndDate:
0654         {
0655             set |= DatabaseFields::ManualOrder;
0656             break;
0657         }
0658     }
0659 
0660     switch (categorizationMode)
0661     {
0662         case OneCategory:
0663         case NoCategories:
0664         {
0665             break;
0666         }
0667 
0668         case CategoryByAlbum:
0669         {
0670             set |= DatabaseFields::Album;
0671             break;
0672         }
0673 
0674         case CategoryByFormat:
0675         {
0676             set |= DatabaseFields::Format;
0677             break;
0678         }
0679 
0680         case CategoryByMonth:
0681         {
0682             set |= DatabaseFields::CreationDate;
0683             break;
0684         }
0685 
0686         case CategoryByFaces:
0687         {
0688             // nothing needed here.
0689 
0690             break;
0691         }
0692     }
0693 
0694     return set;
0695 }
0696 
0697 } // namespace Digikam