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