File indexing completed on 2025-01-05 03:53:47
0001 /* ============================================================ 0002 * 0003 * This file is a part of digiKam project 0004 * https://www.digikam.org 0005 * 0006 * Date : 2004-06-15 0007 * Description : digiKam album types 0008 * 0009 * SPDX-FileCopyrightText: 2004-2005 by Renchi Raju <renchi dot raju at gmail dot com> 0010 * SPDX-FileCopyrightText: 2006-2024 by Gilles Caulier <caulier dot gilles at gmail dot com> 0011 * SPDX-FileCopyrightText: 2014-2015 by Mohamed_Anwer <m_dot_anwer at gmx dot com> 0012 * 0013 * SPDX-License-Identifier: GPL-2.0-or-later 0014 * 0015 * ============================================================ */ 0016 0017 #include "album.h" 0018 0019 // KDE includes 0020 0021 #include <klocalizedstring.h> 0022 0023 // Local includes 0024 0025 #include "digikam_debug.h" 0026 #include "dtrash.h" 0027 #include "coredb.h" 0028 #include "coredburl.h" 0029 #include "coredbaccess.h" 0030 #include "albummanager.h" 0031 #include "collectionmanager.h" 0032 #include "tagscache.h" 0033 0034 namespace Digikam 0035 { 0036 0037 Album::Album(Album::Type type, int id, bool root) 0038 : m_root (root), 0039 m_usedByLabelsTree(false), 0040 m_albumInDeletion (false), 0041 m_id (id), 0042 m_type (type), 0043 m_parent (nullptr) 0044 { 0045 } 0046 0047 Album::~Album() 0048 { 0049 m_albumInDeletion = true; 0050 0051 if (m_parent) 0052 { 0053 m_parent->removeChild(this); 0054 } 0055 0056 clear(); 0057 AlbumManager::internalInstance->notifyAlbumDeletion(this); 0058 } 0059 0060 void Album::setParent(Album* const parent) 0061 { 0062 if (parent) 0063 { 0064 m_parent = parent; 0065 parent->insertChild(this); 0066 } 0067 } 0068 0069 Album* Album::parent() const 0070 { 0071 return m_parent; 0072 } 0073 0074 Album* Album::firstChild() const 0075 { 0076 QReadLocker locker(&m_cacheLock); 0077 0078 if (m_childCache.isEmpty()) 0079 { 0080 return nullptr; 0081 } 0082 0083 return m_childCache.constFirst(); 0084 } 0085 0086 Album* Album::lastChild() const 0087 { 0088 QReadLocker locker(&m_cacheLock); 0089 0090 if (m_childCache.isEmpty()) 0091 { 0092 return nullptr; 0093 } 0094 0095 return m_childCache.constLast(); 0096 } 0097 0098 Album* Album::next() const 0099 { 0100 if (!m_parent) 0101 { 0102 return nullptr; 0103 } 0104 0105 QReadLocker parentLocker(&m_parent->m_cacheLock); 0106 0107 int row = m_parent->m_childCache.indexOf(const_cast<Album*>(this)); 0108 0109 if ((row < 0) || ((row + 1) >= m_parent->m_childCache.size())) 0110 { 0111 return nullptr; 0112 } 0113 0114 return m_parent->m_childCache.at(row + 1); 0115 } 0116 0117 Album* Album::prev() const 0118 { 0119 if (!m_parent) 0120 { 0121 return nullptr; 0122 } 0123 0124 QReadLocker parentLocker(&m_parent->m_cacheLock); 0125 0126 int row = m_parent->m_childCache.indexOf(const_cast<Album*>(this)); 0127 0128 if (row < 1) 0129 { 0130 return nullptr; 0131 } 0132 0133 return m_parent->m_childCache.at(row - 1); 0134 } 0135 0136 Album* Album::childAtRow(int row) const 0137 { 0138 if (m_albumInDeletion) 0139 { 0140 return nullptr; 0141 } 0142 0143 QReadLocker locker(&m_cacheLock); 0144 0145 if (m_albumInDeletion || (row < 0) || (row >= m_childCache.size())) 0146 { 0147 return nullptr; 0148 } 0149 0150 return m_childCache.at(row); 0151 } 0152 0153 AlbumList Album::childAlbums(bool recursive) 0154 { 0155 AlbumList childList; 0156 0157 QReadLocker locker(&m_cacheLock); 0158 0159 QList<Album*>::const_iterator it = m_childCache.constBegin(); 0160 0161 while (it != m_childCache.constEnd()) 0162 { 0163 childList << (*it); 0164 0165 if (recursive) 0166 { 0167 childList << (*it)->childAlbums(recursive); 0168 } 0169 0170 ++it; 0171 } 0172 0173 return childList; 0174 } 0175 0176 QList<int> Album::childAlbumIds(bool recursive) 0177 { 0178 QList<int> ids; 0179 0180 AlbumList childList = childAlbums(recursive); 0181 0182 QListIterator<Album*> it(childList); 0183 0184 while (it.hasNext()) 0185 { 0186 ids << it.next()->id(); 0187 } 0188 0189 return ids; 0190 } 0191 0192 void Album::insertChild(Album* const child) 0193 { 0194 if (!child) 0195 { 0196 return; 0197 } 0198 0199 QWriteLocker locker(&m_cacheLock); 0200 0201 m_childCache.append(child); 0202 } 0203 0204 void Album::removeChild(Album* const child) 0205 { 0206 if (!child) 0207 { 0208 return; 0209 } 0210 0211 QWriteLocker locker(&m_cacheLock); 0212 0213 m_childCache.removeOne(child); 0214 } 0215 0216 void Album::clear() 0217 { 0218 QWriteLocker locker(&m_cacheLock); 0219 0220 while (!m_childCache.isEmpty()) 0221 { 0222 Album* const child = m_childCache.takeLast(); 0223 child->m_parent = nullptr; 0224 0225 delete child; 0226 } 0227 } 0228 0229 int Album::globalID() const 0230 { 0231 return globalID(m_type, m_id); 0232 } 0233 0234 int Album::globalID(Type type, int id) 0235 { 0236 switch (type) 0237 { 0238 // Use the upper bits to create unique ids. 0239 case (PHYSICAL): 0240 return id; 0241 0242 case (TAG): 0243 return (id | (1 << 27)); 0244 0245 case (DATE): 0246 return (id | (1 << 28)); 0247 0248 case (SEARCH): 0249 return (id | (1 << 29)); 0250 0251 case (FACE): 0252 return (id | (1 << 30)); 0253 0254 default: 0255 qCDebug(DIGIKAM_GENERAL_LOG) << "Unknown album type"; 0256 return -1; 0257 } 0258 } 0259 0260 int Album::id() const 0261 { 0262 return m_id; 0263 } 0264 0265 int Album::childCount() const 0266 { 0267 QReadLocker locker(&m_cacheLock); 0268 0269 return m_childCache.count(); 0270 } 0271 0272 int Album::rowFromAlbum() const 0273 { 0274 if (!m_parent) 0275 { 0276 return 0; 0277 } 0278 0279 QReadLocker parentLocker(&m_parent->m_cacheLock); 0280 0281 int row = m_parent->m_childCache.indexOf(const_cast<Album*>(this)); 0282 0283 return ((row != -1) ? row : 0); 0284 } 0285 0286 void Album::setTitle(const QString& title) 0287 { 0288 m_title = title; 0289 } 0290 0291 QString Album::title() const 0292 { 0293 return m_title; 0294 } 0295 0296 Album::Type Album::type() const 0297 { 0298 return m_type; 0299 } 0300 0301 void Album::setExtraData(const void* const key, void* const value) 0302 { 0303 m_extraMap.insert(key, value); 0304 } 0305 0306 void Album::removeExtraData(const void* const key) 0307 { 0308 m_extraMap.remove(key); 0309 } 0310 0311 void* Album::extraData(const void* const key) const 0312 { 0313 return m_extraMap.value(key, nullptr); 0314 } 0315 0316 bool Album::isRoot() const 0317 { 0318 return m_root; 0319 } 0320 0321 bool Album::isAncestorOf(Album* const album) const 0322 { 0323 bool val = false; 0324 Album* a = album; 0325 0326 while (a && !a->isRoot()) 0327 { 0328 if (a == this) 0329 { 0330 val = true; 0331 break; 0332 } 0333 0334 a = a->parent(); 0335 } 0336 0337 return val; 0338 } 0339 0340 bool Album::isUsedByLabelsTree() const 0341 { 0342 return m_usedByLabelsTree; 0343 } 0344 0345 bool Album::isTrashAlbum() const 0346 { 0347 if ((m_id < -1) && (m_type == PHYSICAL)) 0348 { 0349 return true; 0350 } 0351 0352 return false; 0353 } 0354 0355 void Album::setUsedByLabelsTree(bool isUsed) 0356 { 0357 m_usedByLabelsTree = isUsed; 0358 } 0359 0360 // ------------------------------------------------------------------------------ 0361 0362 int PAlbum::m_uniqueTrashId = -2; 0363 0364 PAlbum::PAlbum(const QString& title) 0365 : Album (Album::PHYSICAL, 0, true), 0366 m_isAlbumRootAlbum(false), 0367 m_albumRootId (-1), 0368 m_parentPath (QLatin1Char('/')), 0369 m_iconId (0) 0370 { 0371 setTitle(title); 0372 m_path.clear(); 0373 } 0374 0375 PAlbum::PAlbum(int albumRoot, const QString& label) 0376 : Album (Album::PHYSICAL, -1, false), 0377 m_isAlbumRootAlbum(true), 0378 m_albumRootId (albumRoot), 0379 m_parentPath (QLatin1Char('/')), 0380 m_iconId (0) 0381 { 0382 // set the id to -1 (line above). AlbumManager may change that later. 0383 setTitle(label); 0384 m_path.clear(); 0385 } 0386 0387 PAlbum::PAlbum(int albumRoot, const QString& parentPath, const QString& title, int id) 0388 : Album (Album::PHYSICAL, id, false), 0389 m_isAlbumRootAlbum(false), 0390 m_albumRootId (albumRoot), 0391 m_path (title), 0392 m_parentPath (parentPath + QLatin1Char('/')), 0393 m_iconId (0), 0394 m_date (QDate::currentDate()) 0395 { 0396 // If path is /holidays/2007, title is only "2007", path is "/holidays" 0397 setTitle(title); 0398 } 0399 0400 PAlbum::PAlbum(const QString& parentPath, int albumRoot) 0401 : Album (Album::PHYSICAL, m_uniqueTrashId--, false), 0402 m_isAlbumRootAlbum(false), 0403 m_albumRootId (albumRoot), 0404 m_path (DTrash::TRASH_FOLDER), 0405 m_parentPath (parentPath + QLatin1Char('/')), 0406 m_iconId (0) 0407 { 0408 setTitle(i18n("Trash")); 0409 } 0410 0411 PAlbum::~PAlbum() 0412 { 0413 } 0414 0415 bool PAlbum::isAlbumRoot() const 0416 { 0417 return m_isAlbumRootAlbum; 0418 } 0419 0420 void PAlbum::setCaption(const QString& caption) 0421 { 0422 m_caption = caption; 0423 0424 CoreDbAccess access; 0425 access.db()->setAlbumCaption(id(), m_caption); 0426 } 0427 0428 void PAlbum::setCategory(const QString& category) 0429 { 0430 m_category = category; 0431 0432 CoreDbAccess access; 0433 access.db()->setAlbumCategory(id(), m_category); 0434 } 0435 0436 void PAlbum::setDate(const QDate& date) 0437 { 0438 m_date = date; 0439 0440 CoreDbAccess access; 0441 access.db()->setAlbumDate(id(), m_date); 0442 } 0443 0444 QString PAlbum::albumRootPath() const 0445 { 0446 return CollectionManager::instance()->albumRootPath(m_albumRootId); 0447 } 0448 0449 QString PAlbum::albumRootLabel() const 0450 { 0451 return CollectionManager::instance()->albumRootLabel(m_albumRootId); 0452 } 0453 0454 int PAlbum::albumRootId() const 0455 { 0456 return m_albumRootId; 0457 } 0458 0459 QString PAlbum::caption() const 0460 { 0461 return m_caption; 0462 } 0463 0464 QString PAlbum::category() const 0465 { 0466 return m_category; 0467 } 0468 0469 QDate PAlbum::date() const 0470 { 0471 return m_date; 0472 } 0473 0474 QString PAlbum::albumPath() const 0475 { 0476 return (m_parentPath + m_path); 0477 } 0478 0479 CoreDbUrl PAlbum::databaseUrl() const 0480 { 0481 return CoreDbUrl::fromAlbumAndName(QString(), albumPath(), 0482 QUrl::fromLocalFile(albumRootPath()), m_albumRootId); 0483 } 0484 0485 QString PAlbum::prettyUrl() const 0486 { 0487 QString u = i18n("Albums") + QLatin1Char('/') + 0488 albumRootLabel() + 0489 albumPath(); 0490 0491 if (u.endsWith(QLatin1Char('/'))) 0492 { 0493 u.chop(1); 0494 } 0495 0496 return u; 0497 } 0498 0499 qlonglong PAlbum::iconId() const 0500 { 0501 return m_iconId; 0502 } 0503 0504 QUrl PAlbum::fileUrl() const 0505 { 0506 return databaseUrl().fileUrl(); 0507 } 0508 0509 QString PAlbum::folderPath() const 0510 { 0511 return databaseUrl().fileUrl().toLocalFile(); 0512 } 0513 0514 // -------------------------------------------------------------------------- 0515 0516 TAlbum::TAlbum(const QString& title, int id, bool root) 0517 : Album (Album::TAG, id, root), 0518 m_pid (0), 0519 m_iconId(0) 0520 { 0521 setTitle(title); 0522 } 0523 0524 TAlbum::~TAlbum() 0525 { 0526 } 0527 0528 QString TAlbum::tagPath(bool leadingSlash) const 0529 { 0530 if (isRoot()) 0531 { 0532 return (leadingSlash ? QLatin1String("/") : QLatin1String("")); 0533 } 0534 0535 QString u; 0536 0537 if (parent()) 0538 { 0539 u = (static_cast<TAlbum*>(parent()))->tagPath(leadingSlash); 0540 0541 if (!parent()->isRoot()) 0542 { 0543 u += QLatin1Char('/'); 0544 } 0545 } 0546 0547 u += title(); 0548 0549 return u; 0550 } 0551 0552 QString TAlbum::standardIconName() const 0553 { 0554 return (hasProperty(TagPropertyName::person()) ? QLatin1String("smiley") 0555 : QLatin1String("tag")); 0556 } 0557 0558 QString TAlbum::prettyUrl() const 0559 { 0560 return (i18n("Tags") + tagPath(true)); 0561 } 0562 0563 CoreDbUrl TAlbum::databaseUrl() const 0564 { 0565 return CoreDbUrl::fromTagIds(tagIDs()); 0566 } 0567 0568 QList<int> TAlbum::tagIDs() const 0569 { 0570 if (isRoot()) 0571 { 0572 return QList<int>(); 0573 } 0574 else if (parent()) 0575 { 0576 return (static_cast<TAlbum*>(parent())->tagIDs() << id()); 0577 } 0578 else 0579 { 0580 return (QList<int>() << id()); 0581 } 0582 } 0583 0584 QString TAlbum::icon() const 0585 { 0586 return m_icon; 0587 } 0588 0589 bool TAlbum::isInternalTag() const 0590 { 0591 return TagsCache::instance()->isInternalTag(id()); 0592 } 0593 0594 qlonglong TAlbum::iconId() const 0595 { 0596 return m_iconId; 0597 } 0598 0599 bool TAlbum::hasProperty(const QString& key) const 0600 { 0601 return TagsCache::instance()->hasProperty(id(), key); 0602 } 0603 0604 QString TAlbum::property(const QString& key) const 0605 { 0606 return TagsCache::instance()->propertyValue(id(), key); 0607 } 0608 0609 QMap<QString, QString> TAlbum::properties() const 0610 { 0611 return TagsCache::instance()->properties(id()); 0612 } 0613 0614 // -------------------------------------------------------------------------- 0615 0616 int DAlbum::m_uniqueID = 0; 0617 0618 DAlbum::DAlbum(const QDate& date, bool root, Range range) 0619 : Album (Album::DATE, root ? 0 : ++m_uniqueID, root), 0620 m_date (date), 0621 m_range(range) 0622 { 0623 // Set the name of the date album 0624 0625 QString dateTitle; 0626 0627 if (m_range == Month) 0628 { 0629 dateTitle = m_date.toString(QLatin1String("MMMM yyyy")); 0630 } 0631 else 0632 { 0633 dateTitle = m_date.toString(QLatin1String("yyyy")); 0634 } 0635 0636 setTitle(dateTitle); 0637 } 0638 0639 DAlbum::~DAlbum() 0640 { 0641 } 0642 0643 QDate DAlbum::date() const 0644 { 0645 return m_date; 0646 } 0647 0648 DAlbum::Range DAlbum::range() const 0649 { 0650 return m_range; 0651 } 0652 0653 CoreDbUrl DAlbum::databaseUrl() const 0654 { 0655 if (m_range == Month) 0656 { 0657 return CoreDbUrl::fromDateForMonth(m_date); 0658 } 0659 0660 return CoreDbUrl::fromDateForYear(m_date); 0661 } 0662 0663 // -------------------------------------------------------------------------- 0664 0665 SAlbum::SAlbum(const QString& title, int id, bool root) 0666 : Album (Album::SEARCH, id, root), 0667 m_searchType(DatabaseSearch::UndefinedType) 0668 { 0669 setTitle(title); 0670 } 0671 0672 SAlbum::~SAlbum() 0673 { 0674 } 0675 0676 void SAlbum::setSearch(DatabaseSearch::Type type, const QString& query) 0677 { 0678 m_searchType = type; 0679 m_query = query; 0680 } 0681 0682 CoreDbUrl SAlbum::databaseUrl() const 0683 { 0684 return CoreDbUrl::searchUrl(id()); 0685 } 0686 0687 QString SAlbum::query() const 0688 { 0689 return m_query; 0690 } 0691 0692 DatabaseSearch::Type SAlbum::searchType() const 0693 { 0694 return m_searchType; 0695 } 0696 0697 bool SAlbum::isNormalSearch() const 0698 { 0699 switch (m_searchType) 0700 { 0701 case DatabaseSearch::KeywordSearch: 0702 case DatabaseSearch::AdvancedSearch: 0703 case DatabaseSearch::LegacyUrlSearch: 0704 return true; 0705 0706 default: 0707 return false; 0708 } 0709 } 0710 0711 bool SAlbum::isAdvancedSearch() const 0712 { 0713 return (m_searchType == DatabaseSearch::AdvancedSearch); 0714 } 0715 0716 bool SAlbum::isKeywordSearch() const 0717 { 0718 return (m_searchType == DatabaseSearch::KeywordSearch); 0719 } 0720 0721 bool SAlbum::isTimelineSearch() const 0722 { 0723 return (m_searchType == DatabaseSearch::TimeLineSearch); 0724 } 0725 0726 bool SAlbum::isHaarSearch() const 0727 { 0728 return (m_searchType == DatabaseSearch::HaarSearch); 0729 } 0730 0731 bool SAlbum::isMapSearch() const 0732 { 0733 return (m_searchType == DatabaseSearch::MapSearch); 0734 } 0735 0736 bool SAlbum::isDuplicatesSearch() const 0737 { 0738 return (m_searchType == DatabaseSearch::DuplicatesSearch); 0739 } 0740 0741 bool SAlbum::isTemporarySearch() const 0742 { 0743 if (isHaarSearch()) 0744 { 0745 return ((title() == getTemporaryHaarTitle(DatabaseSearch::HaarImageSearch))) || 0746 (title() == getTemporaryHaarTitle(DatabaseSearch::HaarSketchSearch)); 0747 } 0748 0749 return (title() == getTemporaryTitle(m_searchType)); 0750 } 0751 0752 QString SAlbum::displayTitle() const 0753 { 0754 if (isTemporarySearch()) 0755 { 0756 switch (m_searchType) 0757 { 0758 case DatabaseSearch::TimeLineSearch: 0759 return i18n("Current Timeline Search"); 0760 0761 case DatabaseSearch::HaarSearch: 0762 { 0763 if (title() == getTemporaryHaarTitle(DatabaseSearch::HaarImageSearch)) 0764 { 0765 return i18n("Current Fuzzy Image Search"); 0766 } 0767 else if (title() == getTemporaryHaarTitle(DatabaseSearch::HaarSketchSearch)) 0768 { 0769 return i18n("Current Fuzzy Sketch Search"); 0770 } 0771 0772 break; 0773 } 0774 0775 case DatabaseSearch::MapSearch: 0776 return i18n("Current Map Search"); 0777 0778 case DatabaseSearch::KeywordSearch: 0779 case DatabaseSearch::AdvancedSearch: 0780 case DatabaseSearch::LegacyUrlSearch: 0781 return i18n("Current Search"); 0782 0783 case DatabaseSearch::DuplicatesSearch: 0784 return i18n("Current Duplicates Search"); 0785 0786 case DatabaseSearch::UndefinedType: 0787 break; 0788 } 0789 } 0790 0791 return title(); 0792 } 0793 0794 QString SAlbum::getTemporaryTitle(DatabaseSearch::Type type, DatabaseSearch::HaarSearchType haarType) 0795 { 0796 switch (type) 0797 { 0798 case DatabaseSearch::TimeLineSearch: 0799 return QLatin1String("_Current_Time_Line_Search_"); 0800 0801 case DatabaseSearch::HaarSearch: 0802 return getTemporaryHaarTitle(haarType); 0803 0804 case DatabaseSearch::MapSearch: 0805 return QLatin1String("_Current_Map_Search_"); 0806 0807 case DatabaseSearch::KeywordSearch: 0808 case DatabaseSearch::AdvancedSearch: 0809 case DatabaseSearch::LegacyUrlSearch: 0810 return QLatin1String("_Current_Search_View_Search_"); 0811 0812 case DatabaseSearch::DuplicatesSearch: 0813 return QLatin1String("_Current_Duplicates_Search_"); 0814 0815 default: 0816 qCDebug(DIGIKAM_GENERAL_LOG) << "Untreated temporary search type " << type; 0817 return QLatin1String("_Current_Unknown_Search_"); 0818 } 0819 } 0820 0821 QString SAlbum::getTemporaryHaarTitle(DatabaseSearch::HaarSearchType haarType) 0822 { 0823 switch (haarType) 0824 { 0825 case DatabaseSearch::HaarImageSearch: 0826 return QLatin1String("_Current_Fuzzy_Image_Search_"); 0827 0828 case DatabaseSearch::HaarSketchSearch: 0829 return QLatin1String("_Current_Fuzzy_Sketch_Search_"); 0830 0831 default: 0832 qCDebug(DIGIKAM_GENERAL_LOG) << "Untreated temporary haar search type " << haarType; 0833 return QLatin1String("_Current_Unknown_Haar_Search_"); 0834 } 0835 } 0836 0837 // -------------------------------------------------------------------------- 0838 0839 AlbumIterator::AlbumIterator(Album* const album) 0840 : m_current(album ? album->firstChild() : nullptr), 0841 m_root (album) 0842 { 0843 } 0844 0845 AlbumIterator::~AlbumIterator() 0846 { 0847 } 0848 0849 AlbumIterator& AlbumIterator::operator++() 0850 { 0851 if (!m_current) 0852 { 0853 return *this; 0854 } 0855 0856 Album* album = m_current->firstChild(); 0857 0858 if (!album) 0859 { 0860 while ((album = m_current->next()) == nullptr) 0861 { 0862 m_current = m_current->parent(); 0863 0864 if (m_current == m_root) 0865 { 0866 // we have reached the root. 0867 // that means no more children 0868 0869 m_current = nullptr; 0870 break; 0871 } 0872 0873 if (m_current == nullptr) 0874 { 0875 break; 0876 } 0877 } 0878 } 0879 0880 m_current = album; 0881 0882 return *this; 0883 } 0884 0885 Album* AlbumIterator::operator*() 0886 { 0887 return m_current; 0888 } 0889 0890 Album* AlbumIterator::current() const 0891 { 0892 return m_current; 0893 } 0894 0895 } // namespace Digikam