File indexing completed on 2024-05-12 15:55:37
0001 // SPDX-FileCopyrightText: 2003 - 2020 The KPhotoAlbum Development Team 0002 // SPDX-FileCopyrightText: 2021 - 2022 Johannes Zarl-Zierl <johannes@zarl-zierl.at> 0003 // SPDX-FileCopyrightText: 2023 Alexander Lohnau <alexander.lohnau@gmx.de> 0004 // 0005 // SPDX-License-Identifier: GPL-2.0-or-later 0006 0007 #include "SearchInfo.h" 0008 0009 #include "Database.h" 0010 0011 #include <kpabase/FileName.h> 0012 0013 #include <KLocalizedString> 0014 0015 /** 0016 * \class Exif::SearchInfo 0017 * This class represents a search for Exif information. It is similar in functionality for category searches which is in the 0018 * class \ref DB::ImageSearchInfo. 0019 * 0020 * The search is build, from \ref Exif::SearchDialog, using the functions addRangeKey(), addSearchKey(), and addCamara(). 0021 * The search is stored in an instance of \ref DB::ImageSearchInfo, and may later be executed using search(). 0022 * Once a search has been executed, the application may ask if a given image is in the search result using matches() 0023 */ 0024 Exif::SearchInfo::SearchInfo() 0025 : m_exifDB(nullptr) 0026 { 0027 } 0028 0029 Exif::SearchInfo::SearchInfo(const Database *db) 0030 : m_exifDB(db) 0031 { 0032 } 0033 0034 void Exif::SearchInfo::addSearchKey(const QString &key, const IntList &values) 0035 { 0036 m_intKeys.append(qMakePair(key, values)); 0037 } 0038 0039 QStringList Exif::SearchInfo::buildIntKeyQuery() const 0040 { 0041 QStringList andArgs; 0042 for (IntKeyList::ConstIterator intIt = m_intKeys.begin(); intIt != m_intKeys.end(); ++intIt) { 0043 QStringList orArgs; 0044 const QString key = (*intIt).first; 0045 const IntList values = (*intIt).second; 0046 0047 for (int value : values) { 0048 orArgs << QString::fromLatin1("(%1 == %2)").arg(key).arg(value); 0049 } 0050 if (orArgs.count() != 0) 0051 andArgs << QString::fromLatin1("(%1)").arg(orArgs.join(QString::fromLatin1(" or "))); 0052 } 0053 0054 return andArgs; 0055 } 0056 0057 void Exif::SearchInfo::addRangeKey(const Range &range) 0058 { 0059 m_rangeKeys.append(range); 0060 } 0061 0062 Exif::SearchInfo::Range::Range(const QString &key) 0063 : key(key) 0064 { 0065 } 0066 0067 QString Exif::SearchInfo::buildQuery() const 0068 { 0069 QStringList subQueries; 0070 subQueries += buildIntKeyQuery(); 0071 subQueries += buildRangeQuery(); 0072 QString cameraQuery = buildCameraSearchQuery(); 0073 if (!cameraQuery.isEmpty()) 0074 subQueries.append(cameraQuery); 0075 QString lensQuery = buildLensSearchQuery(); 0076 if (!lensQuery.isEmpty()) 0077 subQueries.append(lensQuery); 0078 0079 if (subQueries.empty()) 0080 return QString(); 0081 else 0082 return QString::fromLatin1("SELECT filename from exif WHERE %1") 0083 .arg(subQueries.join(QString::fromLatin1(" and "))); 0084 } 0085 0086 QStringList Exif::SearchInfo::buildRangeQuery() const 0087 { 0088 QStringList result; 0089 for (QList<Range>::ConstIterator it = m_rangeKeys.begin(); it != m_rangeKeys.end(); ++it) { 0090 QString str = sqlForOneRangeItem(*it); 0091 if (!str.isEmpty()) 0092 result.append(str); 0093 } 0094 return result; 0095 } 0096 0097 QString Exif::SearchInfo::sqlForOneRangeItem(const Range &range) const 0098 { 0099 // Notice I multiplied factors on each value to ensure that we do not fail due to rounding errors for say 1/3 0100 0101 if (range.isLowerMin) { 0102 // Min to Min means < x 0103 if (range.isUpperMin) 0104 return QString::fromLatin1("%1 < %2 and %3 > 0").arg(range.key).arg(range.min * 1.01).arg(range.key); 0105 0106 // Min to Max means all images 0107 if (range.isUpperMax) 0108 return QString(); 0109 0110 // Min to y means <= y 0111 return QString::fromLatin1("%1 <= %2 and %3 > 0").arg(range.key).arg(range.max * 1.01).arg(range.key); 0112 } 0113 0114 // MAX to MAX means >= y 0115 if (range.isLowerMax) 0116 return QString::fromLatin1("%1 > %2").arg(range.key).arg(range.max * 0.99); 0117 0118 // x to Max means >= x 0119 if (range.isUpperMax) 0120 return QString::fromLatin1("%1 >= %2").arg(range.key).arg(range.min * 0.99); 0121 0122 // x to y means >=x and <=y 0123 return QString::fromLatin1("(%1 <= %2 and %2 <= %4)") 0124 .arg(range.min * 0.99) 0125 .arg(range.key) 0126 .arg(range.max * 1.01); 0127 } 0128 0129 void Exif::SearchInfo::search() const 0130 { 0131 QString queryStr = buildQuery(); 0132 m_emptyQuery = queryStr.isEmpty(); 0133 0134 // ensure to do SQL queries as little as possible. 0135 static QString lastQuery; 0136 if (queryStr == lastQuery) 0137 return; 0138 lastQuery = queryStr; 0139 0140 m_matches.clear(); 0141 if (m_emptyQuery) 0142 return; 0143 m_matches = m_exifDB->filesMatchingQuery(queryStr); 0144 } 0145 0146 bool Exif::SearchInfo::matches(const DB::FileName &fileName) const 0147 { 0148 if (m_emptyQuery) 0149 return true; 0150 0151 return m_matches.contains(fileName); 0152 } 0153 0154 bool Exif::SearchInfo::isNull() const 0155 { 0156 return m_exifDB == nullptr; 0157 } 0158 0159 bool Exif::SearchInfo::isEmpty() const 0160 { 0161 return isNull() || buildQuery().isEmpty(); 0162 } 0163 0164 void Exif::SearchInfo::addCamera(const CameraList &list) 0165 { 0166 m_cameras = list; 0167 } 0168 0169 void Exif::SearchInfo::addLens(const LensList &list) 0170 { 0171 m_lenses = list; 0172 } 0173 0174 QString Exif::SearchInfo::buildCameraSearchQuery() const 0175 { 0176 QStringList subResults; 0177 for (CameraList::ConstIterator cameraIt = m_cameras.begin(); cameraIt != m_cameras.end(); ++cameraIt) { 0178 subResults.append(QString::fromUtf8("(Exif_Image_Make='%1' and Exif_Image_Model='%2')") 0179 .arg((*cameraIt).first, (*cameraIt).second)); 0180 } 0181 if (subResults.count() != 0) 0182 return QString::fromUtf8("(%1)").arg(subResults.join(QString::fromLatin1(" or "))); 0183 else 0184 return QString(); 0185 } 0186 0187 QString Exif::SearchInfo::buildLensSearchQuery() const 0188 { 0189 QStringList subResults; 0190 for (LensList::ConstIterator lensIt = m_lenses.begin(); lensIt != m_lenses.end(); ++lensIt) { 0191 if (*lensIt == i18nc("As in No persons, no locations etc.", "None")) 0192 // compare to null (=entry from old db schema) and empty string (=entry w/o exif lens info) 0193 subResults.append(QString::fromUtf8("(nullif(Exif_Photo_LensModel,'') is null)")); 0194 else 0195 subResults.append(QString::fromUtf8("(Exif_Photo_LensModel='%1')") 0196 .arg(*lensIt)); 0197 } 0198 if (subResults.count() != 0) 0199 return QString::fromUtf8("(%1)").arg(subResults.join(QString::fromLatin1(" or "))); 0200 else 0201 return QString(); 0202 } 0203 0204 // vi:expandtab:tabstop=4 shiftwidth=4: