File indexing completed on 2025-01-05 03:58:37
0001 /* ============================================================ 0002 * 0003 * This file is a part of digiKam project 0004 * https://www.digikam.org 0005 * 0006 * Date : 2011-01-06 0007 * Description : Helper functions for geolocation interface interaction 0008 * 0009 * SPDX-FileCopyrightText: 2011 by Michael G. Hansen <mike at mghansen dot de> 0010 * SPDX-FileCopyrightText: 2011-2024 by Gilles Caulier <caulier dot gilles at gmail dot com> 0011 * 0012 * SPDX-License-Identifier: GPL-2.0-or-later 0013 * 0014 * ============================================================ */ 0015 0016 #include "gpsiteminfosorter.h" 0017 0018 // Qt includes 0019 0020 #include <QList> 0021 #include <QMenu> 0022 #include <QPointer> 0023 #include <QAction> 0024 #include <QActionGroup> 0025 0026 // KDE includes 0027 0028 #include <klocalizedstring.h> 0029 0030 // Local includes 0031 0032 0033 namespace Digikam 0034 { 0035 0036 class Q_DECL_HIDDEN GPSItemInfoSorter::Private 0037 { 0038 public: 0039 0040 explicit Private() 0041 : mapWidgets (), 0042 sortOrder (GPSItemInfoSorter::SortYoungestFirst), 0043 sortMenu (nullptr), 0044 sortActionOldestFirst (nullptr), 0045 sortActionYoungestFirst (nullptr), 0046 sortActionRating (nullptr) 0047 { 0048 } 0049 0050 QList<QPointer<MapWidget> > mapWidgets; 0051 GPSItemInfoSorter::SortOptions sortOrder; 0052 QPointer<QMenu> sortMenu; 0053 QAction* sortActionOldestFirst; 0054 QAction* sortActionYoungestFirst; 0055 QAction* sortActionRating; 0056 }; 0057 0058 GPSItemInfoSorter::GPSItemInfoSorter(QObject* const parent) 0059 : QObject(parent), 0060 d (new Private()) 0061 { 0062 } 0063 0064 GPSItemInfoSorter::~GPSItemInfoSorter() 0065 { 0066 if (d->sortMenu) 0067 { 0068 delete d->sortMenu; 0069 } 0070 0071 delete d; 0072 } 0073 0074 bool GPSItemInfoSorter::fitsBetter(const GPSItemInfo& oldInfo, 0075 const GeoGroupState oldState, 0076 const GPSItemInfo& newInfo, 0077 const GeoGroupState newState, 0078 const GeoGroupState globalGroupState, 0079 const SortOptions sortOptions) 0080 { 0081 // the best index for a tile is determined like this: 0082 // region selected? -> prefer region selected markers 0083 // positive filtering on? - > prefer positively filtered markers 0084 // next -> depending on sortkey, prefer better rated ones 0085 // next -> depending on sortkey, prefer older or younger ones 0086 // next -> if the image has a URL, prefer the one with the 'lower' URL 0087 // next -> prefer the image with the higher image id 0088 0089 // region selection part 0090 0091 if (globalGroupState & RegionSelectedMask) 0092 { 0093 const bool oldIsRegionSelected = (oldState & RegionSelectedMask); 0094 const bool newIsRegionSelected = (newState & RegionSelectedMask); 0095 0096 if (oldIsRegionSelected != newIsRegionSelected) 0097 { 0098 return newIsRegionSelected; 0099 } 0100 } 0101 0102 // positive filtering part 0103 0104 if (globalGroupState & FilteredPositiveMask) 0105 { 0106 const bool oldIsFilteredPositive = (oldState & FilteredPositiveMask); 0107 const bool newIsFilteredPositive = (newState & FilteredPositiveMask); 0108 0109 if (oldIsFilteredPositive != newIsFilteredPositive) 0110 { 0111 return newIsFilteredPositive; 0112 } 0113 } 0114 0115 // care about rating, if requested 0116 0117 if (sortOptions & SortRating) 0118 { 0119 const bool oldHasRating = (oldInfo.rating > 0); 0120 const bool newHasRating = (newInfo.rating > 0); 0121 0122 if (oldHasRating != newHasRating) 0123 { 0124 return newHasRating; 0125 } 0126 0127 if (oldHasRating && 0128 newHasRating && 0129 (oldInfo.rating != newInfo.rating)) 0130 { 0131 return oldInfo.rating < newInfo.rating; 0132 } 0133 0134 // ratings are equal or both have no rating, therefore fall through to the next level 0135 } 0136 0137 // finally, decide by date 0138 0139 const bool oldHasDate = oldInfo.dateTime.isValid(); 0140 const bool newHasDate = newInfo.dateTime.isValid(); 0141 0142 if (oldHasDate != newHasDate) 0143 { 0144 return newHasDate; 0145 } 0146 0147 if (oldHasDate && newHasDate) 0148 { 0149 if (oldInfo.dateTime != newInfo.dateTime) 0150 { 0151 if (sortOptions & SortOldestFirst) 0152 { 0153 return (oldInfo.dateTime > newInfo.dateTime); 0154 } 0155 else 0156 { 0157 return (oldInfo.dateTime < newInfo.dateTime); 0158 } 0159 } 0160 } 0161 0162 // compare the image URL 0163 0164 if (oldInfo.url.isValid() && newInfo.url.isValid()) 0165 { 0166 return oldInfo.url.url() > newInfo.url.url(); 0167 } 0168 0169 // last resort: use the image id for reproducibility 0170 0171 return (oldInfo.id > newInfo.id); 0172 } 0173 0174 void GPSItemInfoSorter::addToMapWidget(MapWidget* const mapWidget) 0175 { 0176 initializeSortMenu(); 0177 0178 d->mapWidgets << QPointer<MapWidget>(mapWidget); 0179 mapWidget->setSortOptionsMenu(d->sortMenu); 0180 } 0181 0182 void GPSItemInfoSorter::initializeSortMenu() 0183 { 0184 if (d->sortMenu) 0185 { 0186 return; 0187 } 0188 0189 d->sortMenu = new QMenu(); 0190 d->sortMenu->setTitle(i18n("Sorting")); 0191 QActionGroup* const sortOrderExclusive = new QActionGroup(d->sortMenu); 0192 sortOrderExclusive->setExclusive(true); 0193 0194 connect(sortOrderExclusive, SIGNAL(triggered(QAction*)), 0195 this, SLOT(slotSortOptionTriggered())); 0196 0197 d->sortActionOldestFirst = new QAction(i18n("Show oldest first"), sortOrderExclusive); 0198 d->sortActionOldestFirst->setCheckable(true); 0199 d->sortMenu->addAction(d->sortActionOldestFirst); 0200 0201 d->sortActionYoungestFirst = new QAction(i18n("Show youngest first"), sortOrderExclusive); 0202 d->sortActionYoungestFirst->setCheckable(true); 0203 d->sortMenu->addAction(d->sortActionYoungestFirst); 0204 0205 d->sortActionRating = new QAction(i18n("Sort by rating"), this); 0206 d->sortActionRating->setCheckable(true); 0207 d->sortMenu->addAction(d->sortActionRating); 0208 0209 connect(d->sortActionRating, SIGNAL(triggered(bool)), 0210 this, SLOT(slotSortOptionTriggered())); 0211 0212 /// @todo Should we initialize the checked state already or wait for a call to setSortOptions? 0213 } 0214 0215 void GPSItemInfoSorter::setSortOptions(const SortOptions sortOptions) 0216 { 0217 d->sortOrder = sortOptions; 0218 0219 for (int i = 0 ; i < d->mapWidgets.count() ; ++i) 0220 { 0221 if (d->mapWidgets.at(i)) 0222 { 0223 d->mapWidgets.at(i)->setSortKey(d->sortOrder); 0224 } 0225 } 0226 0227 d->sortActionRating->setChecked(d->sortOrder & GPSItemInfoSorter::SortRating); 0228 d->sortActionOldestFirst->setChecked(d->sortOrder & GPSItemInfoSorter::SortOldestFirst); 0229 d->sortActionYoungestFirst->setChecked(!(d->sortOrder & GPSItemInfoSorter::SortOldestFirst)); 0230 } 0231 0232 GPSItemInfoSorter::SortOptions GPSItemInfoSorter::getSortOptions() const 0233 { 0234 return d->sortOrder; 0235 } 0236 0237 void GPSItemInfoSorter::slotSortOptionTriggered() 0238 { 0239 SortOptions newSortKey = SortYoungestFirst; 0240 0241 if (d->sortActionOldestFirst->isChecked()) 0242 { 0243 newSortKey = SortOldestFirst; 0244 } 0245 0246 if (d->sortActionRating->isChecked()) 0247 { 0248 newSortKey |= SortRating; 0249 } 0250 0251 d->sortOrder = newSortKey; 0252 0253 for (int i = 0 ; i < d->mapWidgets.count() ; ++i) 0254 { 0255 if (d->mapWidgets.at(i)) 0256 { 0257 d->mapWidgets.at(i)->setSortKey(d->sortOrder); 0258 } 0259 } 0260 } 0261 0262 } // namespace Digikam 0263 0264 #include "moc_gpsiteminfosorter.cpp"