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"