File indexing completed on 2025-01-19 03:53:22

0001 /* ============================================================
0002  *
0003  * This file is a part of digiKam project
0004  * https://www.digikam.org
0005  *
0006  * Date        : 2014-05-17
0007  * Description : Album Labels Tree View.
0008  *
0009  * SPDX-FileCopyrightText: 2014-2015 by Mohamed_Anwer <m_dot_anwer at gmx dot com>
0010  * SPDX-FileCopyrightText: 2014-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 "labelstreeview.h"
0017 
0018 // QT includes
0019 
0020 #include <QApplication>
0021 #include <QPainter>
0022 #include <QUrl>
0023 
0024 // KDE includes
0025 
0026 #include <kconfiggroup.h>
0027 #include <klocalizedstring.h>
0028 
0029 // Local includes
0030 
0031 #include "digikam_debug.h"
0032 #include "digikam_globals.h"
0033 #include "coredbsearchxml.h"
0034 #include "searchtabheader.h"
0035 #include "albummanager.h"
0036 #include "albumtreeview.h"
0037 #include "itemlister.h"
0038 #include "coredbaccess.h"
0039 #include "coredb.h"
0040 #include "colorlabelfilter.h"
0041 #include "picklabelfilter.h"
0042 #include "tagscache.h"
0043 #include "applicationsettings.h"
0044 #include "dnotificationwrapper.h"
0045 #include "digikamapp.h"
0046 #include "ratingwidget.h"
0047 #include "dbjobsmanager.h"
0048 
0049 namespace Digikam
0050 {
0051 
0052 class Q_DECL_HIDDEN LabelsTreeView::Private
0053 {
0054 public:
0055 
0056     explicit Private()
0057       : ratings             (nullptr),
0058         picks               (nullptr),
0059         colors              (nullptr),
0060         isCheckableTreeView (false),
0061         isLoadingState      (false),
0062         iconSizeFromSetting (0)
0063     {
0064     }
0065 
0066     QFont                      regularFont;
0067     QSize                      iconSize;
0068 
0069     QTreeWidgetItem*           ratings;
0070     QTreeWidgetItem*           picks;
0071     QTreeWidgetItem*           colors;
0072 
0073     bool                       isCheckableTreeView;
0074     bool                       isLoadingState;
0075     int                        iconSizeFromSetting;
0076 
0077     QHash<Labels, QList<int> > selectedLabels;
0078 
0079     static const QString       configRatingSelectionEntry;
0080     static const QString       configPickSelectionEntry;
0081     static const QString       configColorSelectionEntry;
0082     static const QString       configExpansionEntry;
0083 };
0084 
0085 const QString LabelsTreeView::Private::configRatingSelectionEntry(QLatin1String("RatingSelection"));
0086 const QString LabelsTreeView::Private::configPickSelectionEntry(QLatin1String("PickSelection"));
0087 const QString LabelsTreeView::Private::configColorSelectionEntry(QLatin1String("ColorSelection"));
0088 const QString LabelsTreeView::Private::configExpansionEntry(QLatin1String("Expansion"));
0089 
0090 LabelsTreeView::LabelsTreeView(QWidget* const parent, bool setCheckable)
0091     : QTreeWidget      (parent),
0092       StateSavingObject(this),
0093       d                (new Private)
0094 {
0095     d->regularFont         = ApplicationSettings::instance()->getTreeViewFont();
0096     d->iconSizeFromSetting = ApplicationSettings::instance()->getTreeViewIconSize();
0097     d->iconSize            = QSize(d->iconSizeFromSetting, d->iconSizeFromSetting);
0098     d->isCheckableTreeView = setCheckable;
0099 
0100     setHeaderLabel(i18nc("@title", "Labels"));
0101     setUniformRowHeights(false);
0102     initTreeView();
0103 
0104     if (d->isCheckableTreeView)
0105     {
0106         QTreeWidgetItemIterator it(this);
0107 
0108         while (*it)
0109         {
0110             if ((*it)->parent())
0111             {
0112                 (*it)->setFlags((*it)->flags()|Qt::ItemIsUserCheckable);
0113                 (*it)->setCheckState(0, Qt::Unchecked);
0114             }
0115 
0116             ++it;
0117         }
0118     }
0119     else
0120     {
0121         setSelectionMode(QAbstractItemView::ExtendedSelection);
0122     }
0123 
0124     connect(ApplicationSettings::instance(), SIGNAL(setupChanged()),
0125             this, SLOT(slotSettingsChanged()));
0126 }
0127 
0128 LabelsTreeView::~LabelsTreeView()
0129 {
0130     delete d;
0131 }
0132 
0133 bool LabelsTreeView::isCheckable() const
0134 {
0135     return d->isCheckableTreeView;
0136 }
0137 
0138 bool LabelsTreeView::isLoadingState() const
0139 {
0140     return d->isLoadingState;
0141 }
0142 
0143 QPixmap LabelsTreeView::goldenStarPixmap(bool fillin) const
0144 {
0145     QPixmap pixmap = QPixmap(60, 60);
0146     pixmap.fill(Qt::transparent);
0147 
0148     QPainter p1(&pixmap);
0149     p1.setRenderHint(QPainter::Antialiasing, true);
0150 
0151     if (fillin)
0152     {
0153         p1.setBrush(qApp->palette().color(QPalette::Link));
0154     }
0155 
0156     QPen pen(palette().color(QPalette::Active, foregroundRole()));
0157     p1.setPen(pen);
0158 
0159     QTransform transform;
0160     transform.scale(4, 4);     // 60px/15px (RatingWidget::starPolygon() size is 15*15px)
0161     p1.setTransform(transform);
0162 
0163     p1.drawPolygon(RatingWidget::starPolygon(), Qt::WindingFill);
0164     p1.end();
0165 
0166     return pixmap;
0167 }
0168 
0169 QPixmap LabelsTreeView::colorRectPixmap(const QColor& color) const
0170 {
0171     QRect rect(8, 8, 48, 48);
0172     QPixmap pixmap = QPixmap(60, 60);
0173     pixmap.fill(Qt::transparent);
0174 
0175     QPainter p1(&pixmap);
0176     p1.setRenderHint(QPainter::Antialiasing, true);
0177     p1.setBrush(color);
0178     p1.setPen(palette().color(QPalette::Active, foregroundRole()));
0179     p1.drawRect(rect);
0180     p1.end();
0181 
0182     return pixmap;
0183 }
0184 
0185 QHash<LabelsTreeView::Labels, QList<int> > LabelsTreeView::selectedLabels()
0186 {
0187     QHash<Labels, QList<int> > selectedLabelsHash;
0188     QList<int> selectedRatings;
0189     QList<int> selectedPicks;
0190     QList<int> selectedColors;
0191 
0192     if (d->isCheckableTreeView)
0193     {
0194         QTreeWidgetItemIterator it(this, QTreeWidgetItemIterator::Checked);
0195 
0196         while (*it)
0197         {
0198             QTreeWidgetItem* const item = (*it);
0199 
0200             if      (item->parent() == d->ratings)
0201             {
0202                 selectedRatings << indexFromItem(item).row();
0203             }
0204             else if (item->parent() == d->picks)
0205             {
0206                 selectedPicks << indexFromItem(item).row();
0207             }
0208             else
0209             {
0210                 selectedColors << indexFromItem(item).row();
0211             }
0212 
0213             ++it;
0214         }
0215     }
0216     else
0217     {
0218         Q_FOREACH (QTreeWidgetItem* const item, selectedItems())
0219         {
0220             if      (item->parent() == d->ratings)
0221             {
0222                 selectedRatings << indexFromItem(item).row();
0223             }
0224             else if (item->parent() == d->picks)
0225             {
0226                 selectedPicks << indexFromItem(item).row();
0227             }
0228             else
0229             {
0230                 selectedColors << indexFromItem(item).row();
0231             }
0232         }
0233     }
0234 
0235     selectedLabelsHash[Ratings] = selectedRatings;
0236     selectedLabelsHash[Picks]   = selectedPicks;
0237     selectedLabelsHash[Colors]  = selectedColors;
0238 
0239     return selectedLabelsHash;
0240 }
0241 
0242 void LabelsTreeView::doLoadState()
0243 {
0244     d->isLoadingState                = true;
0245     KConfigGroup configGroup         = getConfigGroup();
0246     const QList<int> expansion       = configGroup.readEntry(entryName(d->configExpansionEntry),       QList<int>());
0247     const QList<int> selectedRatings = configGroup.readEntry(entryName(d->configRatingSelectionEntry), QList<int>());
0248     const QList<int> selectedPicks   = configGroup.readEntry(entryName(d->configPickSelectionEntry),   QList<int>());
0249     const QList<int> selectedColors  = configGroup.readEntry(entryName(d->configColorSelectionEntry),  QList<int>());
0250 
0251     d->ratings->setExpanded(true);
0252     d->picks->setExpanded(true);
0253     d->colors->setExpanded(true);
0254 
0255     Q_FOREACH (int parent, expansion)
0256     {
0257         switch (parent)
0258         {
0259             case 1:
0260                 d->ratings->setExpanded(false);
0261                 break;
0262 
0263             case 2:
0264                 d->picks->setExpanded(false);
0265                 break;
0266 
0267             case 3:
0268                 d->colors->setExpanded(false);
0269 
0270             default:
0271                 break;
0272         }
0273     }
0274 
0275     Q_FOREACH (int rating, selectedRatings)
0276     {
0277         if (d->isCheckableTreeView)
0278         {
0279             d->ratings->child(rating)->setCheckState(0, Qt::Checked);
0280         }
0281         else
0282         {
0283             d->ratings->child(rating)->setSelected(true);
0284         }
0285     }
0286 
0287     Q_FOREACH (int pick, selectedPicks)
0288     {
0289         if (d->isCheckableTreeView)
0290         {
0291             d->picks->child(pick)->setCheckState(0, Qt::Checked);
0292         }
0293         else
0294         {
0295             d->picks->child(pick)->setSelected(true);
0296         }
0297     }
0298 
0299     Q_FOREACH (int color, selectedColors)
0300     {
0301         if (d->isCheckableTreeView)
0302         {
0303             d->colors->child(color)->setCheckState(0, Qt::Checked);
0304         }
0305         else
0306         {
0307             d->colors->child(color)->setSelected(true);
0308         }
0309     }
0310 
0311     d->isLoadingState = false;
0312 }
0313 
0314 void LabelsTreeView::doSaveState()
0315 {
0316     KConfigGroup configGroup = getConfigGroup();
0317     QList<int> expansion;
0318 
0319     if (!d->ratings->isExpanded())
0320     {
0321         expansion << 1;
0322     }
0323 
0324     if (!d->picks->isExpanded())
0325     {
0326         expansion << 2;
0327     }
0328 
0329     if (!d->colors->isExpanded())
0330     {
0331         expansion << 3;
0332     }
0333 
0334     QHash<Labels, QList<int> > labels = selectedLabels();
0335 
0336     configGroup.writeEntry(entryName(d->configExpansionEntry),       expansion);
0337     configGroup.writeEntry(entryName(d->configRatingSelectionEntry), labels[Ratings]);
0338     configGroup.writeEntry(entryName(d->configPickSelectionEntry),   labels[Picks]);
0339     configGroup.writeEntry(entryName(d->configColorSelectionEntry),  labels[Colors]);
0340 }
0341 
0342 void LabelsTreeView::setCurrentAlbum()
0343 {
0344     Q_EMIT signalSetCurrentAlbum();
0345 }
0346 
0347 void LabelsTreeView::initTreeView()
0348 {
0349     setIconSize(QSize(d->iconSizeFromSetting*5,d->iconSizeFromSetting));
0350     initRatingsTree();
0351     initPicksTree();
0352     initColorsTree();
0353     expandAll();
0354     setRootIsDecorated(false);
0355 }
0356 
0357 void LabelsTreeView::initRatingsTree()
0358 {
0359     d->ratings = new QTreeWidgetItem(this);
0360     d->ratings->setText(0, i18nc("@item: rating tree", "Rating"));
0361     d->ratings->setFont(0, d->regularFont);
0362     d->ratings->setFlags(Qt::ItemIsEnabled);
0363 
0364     QTreeWidgetItem* const noRate = new QTreeWidgetItem(d->ratings);
0365     noRate->setText(0, i18nc("@item: rating tree", "No Rating"));
0366     noRate->setFont(0, d->regularFont);
0367     QPixmap pix2(goldenStarPixmap().size());
0368     pix2.fill(Qt::transparent);
0369     QPainter p2(&pix2);
0370     p2.setRenderHint(QPainter::Antialiasing, true);
0371     p2.setPen(palette().color(QPalette::Active, foregroundRole()));
0372     p2.drawPixmap(0, 0, goldenStarPixmap(false));
0373     noRate->setIcon(0, QIcon(pix2));
0374     noRate->setSizeHint(0, d->iconSize);
0375 
0376     for (int rate = 1 ; rate <= 5 ; ++rate)
0377     {
0378         QTreeWidgetItem* const rateWidget = new QTreeWidgetItem(d->ratings);
0379 
0380         QPixmap pix(goldenStarPixmap().width()*rate, goldenStarPixmap().height());
0381         pix.fill(Qt::transparent);
0382         QPainter p(&pix);
0383         int offset = 0;
0384         p.setRenderHint(QPainter::Antialiasing, true);
0385         p.setPen(palette().color(QPalette::Active, foregroundRole()));
0386 
0387         for (int i = 0 ; i < rate ; ++i)
0388         {
0389             p.drawPixmap(offset, 0, goldenStarPixmap());
0390             offset += goldenStarPixmap().width();
0391         }
0392 
0393         rateWidget->setIcon(0, QIcon(pix));
0394         rateWidget->setSizeHint(0, d->iconSize);
0395     }
0396 }
0397 
0398 void LabelsTreeView::initPicksTree()
0399 {
0400     d->picks = new QTreeWidgetItem(this);
0401     d->picks->setText(0, i18nc("@title: pick tree", "Pick"));
0402     d->picks->setFont(0, d->regularFont);
0403     d->picks->setFlags(Qt::ItemIsEnabled);
0404 
0405     QStringList pickSetNames;
0406     pickSetNames << i18nc("@item: pick tree", "No Pick")
0407                  << i18nc("@item: pick tree", "Rejected Item")
0408                  << i18nc("@item: pick tree", "Pending Item")
0409                  << i18nc("@item: pick tree", "Accepted Item");
0410 
0411     QStringList pickSetIcons;
0412     pickSetIcons << QLatin1String("flag-black")
0413                  << QLatin1String("flag-red")
0414                  << QLatin1String("flag-yellow")
0415                  << QLatin1String("flag-green");
0416 
0417     Q_FOREACH (const QString& pick, pickSetNames)
0418     {
0419         QTreeWidgetItem* const pickWidgetItem = new QTreeWidgetItem(d->picks);
0420         pickWidgetItem->setText(0, pick);
0421         pickWidgetItem->setFont(0, d->regularFont);
0422         pickWidgetItem->setIcon(0, QIcon::fromTheme(pickSetIcons.at(pickSetNames.indexOf(pick))));
0423     }
0424 }
0425 
0426 void LabelsTreeView::initColorsTree()
0427 {
0428     d->colors                      = new QTreeWidgetItem(this);
0429     d->colors->setText(0, i18nc("@item: color tree", "Color"));
0430     d->colors->setFont(0, d->regularFont);
0431     d->colors->setFlags(Qt::ItemIsEnabled);
0432 
0433     QTreeWidgetItem* const noColor = new QTreeWidgetItem(d->colors);
0434     noColor->setText(0, i18nc("@item: color tree", "No Color"));
0435     noColor->setFont(0, d->regularFont);
0436     noColor->setIcon(0, QIcon::fromTheme(QLatin1String("emblem-unmounted")));
0437 
0438     QStringList colorSet;
0439     colorSet << QLatin1String("red")      << QLatin1String("orange")
0440              << QLatin1String("yellow")   << QLatin1String("darkgreen")
0441              << QLatin1String("darkblue") << QLatin1String("magenta")
0442              << QLatin1String("darkgray") << QLatin1String("black")
0443              << QLatin1String("white");
0444 
0445     QStringList colorSetNames;
0446     colorSetNames << i18nc("@item: color tree", "Red")    << i18nc("@item: color tree", "Orange")
0447                   << i18nc("@item: color tree", "Yellow") << i18nc("@item: color tree", "Green")
0448                   << i18nc("@item: color tree", "Blue")   << i18nc("@item: color tree", "Magenta")
0449                   << i18nc("@item: color tree", "Gray")   << i18nc("@item: color tree", "Black")
0450                   << i18nc("@item: color tree", "White");
0451 
0452     Q_FOREACH (const QString& color, colorSet)
0453     {
0454         QTreeWidgetItem* const colorWidgetItem = new QTreeWidgetItem(d->colors);
0455         colorWidgetItem->setText(0, colorSetNames.at(colorSet.indexOf(color)));
0456         colorWidgetItem->setFont(0, d->regularFont);
0457         QPixmap colorIcon                      = colorRectPixmap(QColor(color));
0458         colorWidgetItem->setIcon(0, QIcon(colorIcon));
0459         colorWidgetItem->setSizeHint(0, d->iconSize);
0460     }
0461 }
0462 
0463 void LabelsTreeView::slotSettingsChanged()
0464 {
0465     if (d->iconSizeFromSetting != ApplicationSettings::instance()->getTreeViewIconSize())
0466     {
0467         d->iconSizeFromSetting = ApplicationSettings::instance()->getTreeViewIconSize();
0468         setIconSize(QSize(d->iconSizeFromSetting*5, d->iconSizeFromSetting));
0469         d->iconSize            = QSize(d->iconSizeFromSetting, d->iconSizeFromSetting);
0470         QTreeWidgetItemIterator it(this);
0471 
0472         while (*it)
0473         {
0474             (*it)->setSizeHint(0, d->iconSize);
0475             ++it;
0476         }
0477     }
0478 
0479     if (d->regularFont != ApplicationSettings::instance()->getTreeViewFont())
0480     {
0481         d->regularFont = ApplicationSettings::instance()->getTreeViewFont();
0482         QTreeWidgetItemIterator it(this);
0483 
0484         while (*it)
0485         {
0486             (*it)->setFont(0, d->regularFont);
0487             ++it;
0488         }
0489     }
0490 }
0491 
0492 void LabelsTreeView::restoreSelectionFromHistory(QHash<Labels, QList<int> > neededLabels)
0493 {
0494     QTreeWidgetItemIterator it(this, QTreeWidgetItemIterator::Selected);
0495 
0496     while (*it)
0497     {
0498         (*it)->setSelected(false);
0499         ++it;
0500     }
0501 
0502     Q_FOREACH (int rateItemIndex, neededLabels[Ratings])
0503     {
0504         d->ratings->child(rateItemIndex)->setSelected(true);
0505     }
0506 
0507     Q_FOREACH (int pickItemIndex, neededLabels[Picks])
0508     {
0509         d->picks->child(pickItemIndex)->setSelected(true);
0510     }
0511 
0512     Q_FOREACH (int colorItemIndex, neededLabels[Colors])
0513     {
0514         d->colors->child(colorItemIndex)->setSelected(true);
0515     }
0516 }
0517 
0518 } // namespace Digikam
0519 
0520 #include "moc_labelstreeview.cpp"