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

0001 /* ============================================================
0002  *
0003  * This file is a part of digiKam project
0004  * https://www.digikam.org
0005  *
0006  * Date        : 2010-10-09
0007  * Description : A widget to select Physical or virtual albums with combo-box
0008  *
0009  * SPDX-FileCopyrightText: 2010-2012 by Marcel Wiesweg <marcel dot wiesweg at gmx dot de>
0010  * SPDX-FileCopyrightText: 2012-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 "albumselectors.h"
0017 
0018 // Qt includes
0019 
0020 #include <QApplication>
0021 #include <QLayout>
0022 #include <QLabel>
0023 #include <QCheckBox>
0024 #include <QRadioButton>
0025 #include <QButtonGroup>
0026 #include <QIcon>
0027 #include <QMessageBox>
0028 
0029 // KDE includes
0030 
0031 #include <kconfiggroup.h>
0032 #include <ksharedconfig.h>
0033 #include <klocalizedstring.h>
0034 
0035 // Local includes
0036 
0037 #include "digikam_debug.h"
0038 #include "albummodel.h"
0039 #include "albummanager.h"
0040 #include "albumselectcombobox.h"
0041 #include "albumtreeview.h"
0042 #include "tagtreeview.h"
0043 #include "searchutilities.h"
0044 
0045 namespace Digikam
0046 {
0047 
0048 class Q_DECL_HIDDEN ModelClearButton : public AnimatedClearButton       // clazy:exclude=ctor-missing-parent-argument
0049 {
0050     Q_OBJECT
0051 
0052 public:
0053 
0054     explicit ModelClearButton(AbstractCheckableAlbumModel* const model)
0055     {
0056         setPixmap(QIcon::fromTheme(qApp->isLeftToRight() ? QLatin1String("edit-clear-locationbar-rtl")
0057                                                          : QLatin1String("edit-clear-locationbar-ltr")).pixmap(style()->pixelMetric(QStyle::PM_SmallIconSize)));
0058         stayVisibleWhenAnimatedOut(true);
0059 
0060         connect(this, SIGNAL(clicked()),
0061                 model, SLOT(resetAllCheckedAlbums()));
0062     }
0063 };
0064 
0065 // ------------------------------------------------------------------------------------------
0066 
0067 class Q_DECL_HIDDEN AlbumSelectors::Private
0068 {
0069 public:
0070 
0071     explicit Private()
0072       : albumSelectCB          (nullptr),
0073         tagSelectCB            (nullptr),
0074         albumClearButton       (nullptr),
0075         tagClearButton         (nullptr),
0076         recursiveSelectionAlbum(nullptr),
0077         wholeAlbums            (nullptr),
0078         recursiveSelectionTags (nullptr),
0079         wholeTags              (nullptr),
0080         tabWidget              (nullptr),
0081         albumWidget            (nullptr),
0082         tagWidget              (nullptr),
0083         selectionMode          (All),
0084         allowRecursive         (false)
0085     {
0086     }
0087 
0088     static const QString         configUseWholeAlbumsEntry;
0089     static const QString         configUseWholeTagsEntry;
0090     static const QString         configAlbumTypeEntry;
0091 
0092     QString                      configName;
0093 
0094     AlbumTreeViewSelectComboBox* albumSelectCB;
0095     TagTreeViewSelectComboBox*   tagSelectCB;
0096     ModelClearButton*            albumClearButton;
0097     ModelClearButton*            tagClearButton;
0098 
0099     QCheckBox*                   recursiveSelectionAlbum;
0100     QCheckBox*                   wholeAlbums;
0101     QCheckBox*                   recursiveSelectionTags;
0102     QCheckBox*                   wholeTags;
0103 
0104     QTabWidget*                  tabWidget;
0105     QWidget*                     albumWidget;
0106     QWidget*                     tagWidget;
0107 
0108     AlbumType                    selectionMode;
0109 
0110     bool                         allowRecursive;
0111 };
0112 
0113 const QString AlbumSelectors::Private::configUseWholeAlbumsEntry(QLatin1String("UseWholeAlbumsEntry"));
0114 const QString AlbumSelectors::Private::configUseWholeTagsEntry(QLatin1String("UseWholeTagsEntry"));
0115 const QString AlbumSelectors::Private::configAlbumTypeEntry(QLatin1String("AlbumTypeEntry"));
0116 
0117 AlbumSelectors::AlbumSelectors(const QString& label,
0118                                const QString& configName,
0119                                QWidget* const parent,
0120                                AlbumType albumType,
0121                                bool allowRecursive)
0122     : QWidget(parent),
0123       d      (new Private)
0124 {
0125     d->allowRecursive             = allowRecursive;
0126     d->configName                 = configName;
0127     setObjectName(d->configName);
0128 
0129     d->selectionMode              = albumType;
0130 
0131     QVBoxLayout* const mainLayout = new QVBoxLayout(this);
0132 
0133     if (!label.isEmpty())
0134     {
0135         mainLayout->addWidget(new QLabel(label));
0136     }
0137 
0138     switch (d->selectionMode)
0139     {
0140         case All:
0141         {
0142             d->tabWidget = new QTabWidget(this);
0143 
0144             initAlbumWidget();
0145             d->tabWidget->insertTab(PhysAlbum, d->albumWidget, i18nc("@title", "Albums (All)"));
0146 
0147             initTagWidget();
0148             d->tabWidget->insertTab(TagsAlbum, d->tagWidget,   i18nc("@title", "Tags (0)"));
0149 
0150             mainLayout->addWidget(d->tabWidget);
0151             break;
0152         }
0153 
0154         case PhysAlbum:
0155         {
0156             initAlbumWidget();
0157             mainLayout->addWidget(d->albumWidget);
0158             break;
0159         }
0160 
0161         case TagsAlbum:
0162         {
0163             initTagWidget();
0164             mainLayout->addWidget(d->tagWidget);
0165             break;
0166         }
0167     }
0168 
0169     mainLayout->setContentsMargins(QMargins());
0170 }
0171 
0172 AlbumSelectors::~AlbumSelectors()
0173 {
0174     delete d;
0175 }
0176 
0177 void AlbumSelectors::initAlbumWidget()
0178 {
0179     d->albumWidget             = new QWidget(this);
0180     d->wholeAlbums             = new QCheckBox(i18nc("@option", "All albums"), d->albumWidget);
0181     d->recursiveSelectionAlbum = new QCheckBox(i18nc("@option", "Recursive"), d->albumWidget);
0182     d->recursiveSelectionAlbum->setToolTip(i18nc("@info:tooltip", "Recursive selection."));
0183     d->recursiveSelectionAlbum->setChecked(true);
0184 
0185     if (!d->allowRecursive)
0186     {
0187         d->recursiveSelectionAlbum->setVisible(false);
0188         d->recursiveSelectionAlbum->setChecked(false);
0189     }
0190 
0191     d->albumSelectCB    = new AlbumTreeViewSelectComboBox(d->albumWidget);
0192     d->albumSelectCB->setToolTip(i18nc("@info:tooltip", "Select all albums that should be processed."));
0193     d->albumSelectCB->setDefaultModel();
0194     d->albumSelectCB->setRecursive(d->allowRecursive ? d->recursiveSelectionAlbum->isChecked() : false);
0195     d->albumSelectCB->setNoSelectionText(i18nc("@info", "No Album Selected"));
0196     //d->albumSelectCB->setAddExcludeTristate(true);
0197     d->albumSelectCB->addCheckUncheckContextMenuActions();
0198 
0199     d->albumClearButton = new ModelClearButton(d->albumSelectCB->view()->albumModel());
0200     d->albumClearButton->setToolTip(i18nc("@info:tooltip", "Reset selected albums"));
0201 
0202     QHBoxLayout* l      = new QHBoxLayout;
0203     l->addWidget(d->wholeAlbums);
0204     l->addWidget(d->recursiveSelectionAlbum);
0205     l->addItem(new QSpacerItem(0, 0, QSizePolicy::Policy::Expanding));
0206 
0207     QGridLayout* const pAlbumsGrid = new QGridLayout(d->albumWidget);
0208     pAlbumsGrid->addLayout(l,                   0, 0, 1, 2);
0209     pAlbumsGrid->addWidget(d->albumSelectCB,    1, 0, 1, 1);
0210     pAlbumsGrid->addWidget(d->albumClearButton, 1, 1, 1, 1);
0211     pAlbumsGrid->setSpacing(0);
0212 
0213     connect(d->wholeAlbums, SIGNAL(toggled(bool)),
0214             this, SLOT(slotWholeAlbums(bool)));
0215 
0216     connect(d->wholeAlbums, SIGNAL(toggled(bool)),
0217             this, SIGNAL(signalSelectionChanged()));
0218 
0219     connect(d->recursiveSelectionAlbum, &QCheckBox::clicked,
0220             [this] (bool checked)
0221         {
0222             d->albumSelectCB->setRecursive(checked);
0223         }
0224     );
0225 
0226     connect(d->albumSelectCB->view()->albumModel(), SIGNAL(checkStateChanged(Album*,Qt::CheckState)),
0227             this, SLOT(slotUpdateClearButtons()));
0228 
0229     d->albumSelectCB->view()->setObjectName(d->configName);
0230     d->albumSelectCB->view()->setEntryPrefix(QLatin1String("AlbumComboBox-"));
0231     d->albumSelectCB->view()->setRestoreCheckState(true);
0232 }
0233 
0234 void AlbumSelectors::initTagWidget()
0235 {
0236     d->tagWidget              = new QWidget(this);
0237     d->wholeTags              = new QCheckBox(i18nc("@option", "All tags"), d->tagWidget);
0238     d->recursiveSelectionTags = new QCheckBox(i18nc("@option", "Recursive"), d->albumWidget);
0239     d->recursiveSelectionTags->setToolTip(i18nc("@info:tooltip", "Recursive selection."));
0240     d->recursiveSelectionTags->setChecked(true);
0241 
0242     if (!d->allowRecursive)
0243     {
0244         d->recursiveSelectionTags->setVisible(false);
0245         d->recursiveSelectionTags->setChecked(false);
0246     }
0247 
0248     d->tagSelectCB    = new TagTreeViewSelectComboBox(d->tagWidget);
0249     d->tagSelectCB->setToolTip(i18nc("@info:tooltip", "Select all tags that should be processed."));
0250     d->tagSelectCB->setDefaultModel();
0251     d->tagSelectCB->setRecursive(d->allowRecursive ? d->recursiveSelectionTags->isChecked() : false);
0252     d->tagSelectCB->setNoSelectionText(i18nc("@info", "No Tag Selected"));
0253     d->tagSelectCB->addCheckUncheckContextMenuActions();
0254 
0255     d->tagClearButton = new ModelClearButton(d->tagSelectCB->view()->albumModel());
0256     d->tagClearButton->setToolTip(i18nc("@info:tooltip", "Reset selected tags"));
0257 
0258     QHBoxLayout* l    = new QHBoxLayout;
0259     l->addWidget(d->wholeTags);
0260     l->addWidget(d->recursiveSelectionTags);
0261     l->addItem(new QSpacerItem(0, 0, QSizePolicy::Policy::Expanding));
0262 
0263     QGridLayout* const tAlbumsGrid = new QGridLayout(d->tagWidget);
0264     tAlbumsGrid->addLayout(l,                 0, 0, 1, 2);
0265     tAlbumsGrid->addWidget(d->tagSelectCB,    1, 0, 1, 1);
0266     tAlbumsGrid->addWidget(d->tagClearButton, 1, 1, 1, 1);
0267     tAlbumsGrid->setSpacing(0);
0268 
0269     connect(d->wholeTags, SIGNAL(toggled(bool)),
0270             this, SIGNAL(signalSelectionChanged()));
0271 
0272     connect(d->wholeTags, SIGNAL(toggled(bool)),
0273             this, SLOT(slotWholeTags(bool)));
0274 
0275     connect(d->tagSelectCB->view()->albumModel(), SIGNAL(checkStateChanged(Album*,Qt::CheckState)),
0276             this, SLOT(slotUpdateClearButtons()));
0277 
0278     connect(d->recursiveSelectionTags, &QCheckBox::clicked,
0279             [this] (bool checked)
0280         {
0281             d->tagSelectCB->setRecursive(checked);
0282         }
0283     );
0284 
0285     d->tagSelectCB->view()->setObjectName(d->configName);
0286     d->tagSelectCB->view()->setEntryPrefix(QLatin1String("TagComboBox-"));
0287     d->tagSelectCB->view()->setRestoreCheckState(true);
0288 
0289 }
0290 
0291 void AlbumSelectors::slotWholeAlbums(bool b)
0292 {
0293     if ((d->selectionMode == PhysAlbum) || (d->selectionMode == All))
0294     {
0295         d->albumSelectCB->setEnabled(!b);
0296         d->albumClearButton->setEnabled(!b);
0297         d->albumSelectCB->setAllSelectedText(b);
0298     }
0299 
0300     updateTabText();
0301 }
0302 
0303 void AlbumSelectors::slotWholeTags(bool b)
0304 {
0305     if ((d->selectionMode == TagsAlbum) || (d->selectionMode == All))
0306     {
0307         d->tagSelectCB->setEnabled(!b);
0308         d->tagClearButton->setEnabled(!b);
0309         d->tagSelectCB->setAllSelectedText(b);
0310     }
0311 
0312     updateTabText();
0313 }
0314 
0315 void AlbumSelectors::slotUpdateClearButtons()
0316 {
0317     bool selectionChanged = false;
0318 
0319     if ((d->selectionMode == PhysAlbum) || (d->selectionMode == All))
0320     {
0321         d->albumClearButton->animateVisible(!d->albumSelectCB->model()->checkedAlbums().isEmpty());
0322         selectionChanged = true;
0323     }
0324 
0325     if ((d->selectionMode == TagsAlbum) || (d->selectionMode == All))
0326     {
0327         d->tagClearButton->animateVisible(!d->tagSelectCB->model()->checkedAlbums().isEmpty());
0328         selectionChanged = true;
0329     }
0330 
0331     if (selectionChanged)
0332     {
0333         Q_EMIT signalSelectionChanged();
0334     }
0335 
0336     updateTabText();
0337 }
0338 
0339 bool AlbumSelectors::wholeAlbumsChecked() const
0340 {
0341     return (d->wholeAlbums && d->wholeAlbums->isChecked());
0342 }
0343 
0344 AlbumList AlbumSelectors::selectedAlbums() const
0345 {
0346     AlbumList albums;
0347 
0348     if      (wholeAlbumsChecked())
0349     {
0350         albums << AlbumManager::instance()->allPAlbums();
0351     }
0352     else if (d->albumSelectCB)
0353     {
0354         albums << d->albumSelectCB->model()->checkedAlbums();
0355     }
0356 
0357     return albums;
0358 }
0359 
0360 QList<int> AlbumSelectors::selectedAlbumIds() const
0361 {
0362     QList<int> albumIds;
0363     AlbumList  albums = selectedAlbums();
0364 
0365     Q_FOREACH (Album* const album, albums)
0366     {
0367         albumIds << album->id();
0368     }
0369 
0370     return albumIds;
0371 }
0372 
0373 bool AlbumSelectors::wholeTagsChecked() const
0374 {
0375     return d->wholeTags && d->wholeTags->isChecked();
0376 }
0377 
0378 AlbumList AlbumSelectors::selectedTags() const
0379 {
0380     AlbumList albums;
0381 
0382     if      (wholeTagsChecked())
0383     {
0384         albums << AlbumManager::instance()->allTAlbums();
0385     }
0386     else if (d->tagSelectCB)
0387     {
0388         albums << d->tagSelectCB->model()->checkedAlbums();
0389     }
0390 
0391     return albums;
0392 }
0393 
0394 QList<int> AlbumSelectors::selectedTagIds() const
0395 {
0396     QList<int> tagIds;
0397     AlbumList  tags = selectedTags();
0398 
0399     Q_FOREACH (Album* const tag, tags)
0400     {
0401         tagIds << tag->id();
0402     }
0403 
0404     return tagIds;
0405 }
0406 
0407 AlbumList AlbumSelectors::selectedAlbumsAndTags() const
0408 {
0409     AlbumList albums;
0410     albums << selectedAlbums();
0411     albums << selectedTags();
0412 
0413     return albums;
0414 }
0415 
0416 void AlbumSelectors::setAlbumSelected(Album* const album, SelectionType type)
0417 {
0418     if (d->albumWidget && album)
0419     {
0420         if (type == SingleSelection)
0421         {
0422             d->albumSelectCB->model()->resetCheckedAlbums();
0423         }
0424 
0425         d->albumSelectCB->model()->setChecked(album, true);
0426         d->wholeAlbums->setChecked(false);
0427     }
0428 }
0429 
0430 void AlbumSelectors::setTagSelected(Album* const album, SelectionType type)
0431 {
0432     if (d->tagWidget && album)
0433     {
0434         if (type == SingleSelection)
0435         {
0436             d->tagSelectCB->model()->resetCheckedAlbums();
0437         }
0438 
0439         d->tagSelectCB->model()->setChecked(album, true);
0440         d->wholeTags->setChecked(false);
0441     }
0442 }
0443 
0444 void AlbumSelectors::setTypeSelection(int albumType)
0445 {
0446     if (d->selectionMode == All)
0447     {
0448         d->tabWidget->setCurrentIndex(albumType);
0449     }
0450 }
0451 
0452 int AlbumSelectors::typeSelection() const
0453 {
0454     if (d->selectionMode == All)
0455     {
0456         return d->tabWidget->currentIndex();
0457     }
0458     else
0459     {
0460         return d->selectionMode;
0461     }
0462 }
0463 
0464 void AlbumSelectors::resetPAlbumSelection()
0465 {
0466     d->albumSelectCB->model()->resetAllCheckedAlbums();
0467     d->wholeAlbums->setChecked(false);
0468     slotWholeAlbums(wholeAlbumsChecked());
0469 }
0470 
0471 void AlbumSelectors::resetTAlbumSelection()
0472 {
0473     d->tagSelectCB->model()->resetAllCheckedAlbums();
0474     d->wholeTags->setChecked(false);
0475     slotWholeTags(wholeTagsChecked());
0476 }
0477 
0478 void AlbumSelectors::resetSelection()
0479 {
0480     if (d->albumWidget)
0481     {
0482         resetPAlbumSelection();
0483     }
0484 
0485     if (d->tagWidget)
0486     {
0487         resetTAlbumSelection();
0488     }
0489 }
0490 
0491 void AlbumSelectors::loadState()
0492 {
0493     KSharedConfig::Ptr config = KSharedConfig::openConfig();
0494     KConfigGroup group        = config->group(d->configName);
0495 
0496     if (d->albumWidget)
0497     {
0498         d->wholeAlbums->setChecked(group.readEntry(d->configUseWholeAlbumsEntry, true));
0499         d->albumSelectCB->view()->loadState();
0500         d->albumClearButton->animateVisible(!d->albumSelectCB->model()->checkedAlbums().isEmpty());
0501 
0502         slotWholeAlbums(wholeAlbumsChecked());
0503         d->albumSelectCB->updateText();
0504     }
0505 
0506     if (d->tagWidget)
0507     {
0508         d->wholeTags->setChecked(group.readEntry(d->configUseWholeTagsEntry, false));
0509         d->tagSelectCB->view()->loadState();
0510         d->tagClearButton->animateVisible(!d->tagSelectCB->model()->checkedAlbums().isEmpty());
0511 
0512         slotWholeTags(wholeTagsChecked());
0513         d->tagSelectCB->updateText();
0514     }
0515 
0516     if (d->selectionMode == All)
0517     {
0518         d->tabWidget->setCurrentIndex(group.readEntry(d->configAlbumTypeEntry, (int)PhysAlbum));
0519     }
0520 }
0521 
0522 void AlbumSelectors::saveState()
0523 {
0524     KSharedConfig::Ptr config = KSharedConfig::openConfig();
0525     KConfigGroup group        = config->group(d->configName);
0526 
0527     if (d->albumWidget)
0528     {
0529         group.writeEntry(d->configUseWholeAlbumsEntry, wholeAlbumsChecked());
0530         d->albumSelectCB->view()->saveState();
0531     }
0532 
0533     if (d->tagWidget)
0534     {
0535         group.writeEntry(d->configUseWholeTagsEntry, wholeTagsChecked());
0536         d->tagSelectCB->view()->saveState();
0537     }
0538 
0539     if (d->selectionMode == All)
0540     {
0541         group.writeEntry(d->configAlbumTypeEntry, typeSelection());
0542     }
0543 }
0544 
0545 void AlbumSelectors::updateTabText()
0546 {
0547     if (d->selectionMode == All)
0548     {
0549         d->tabWidget->tabBar()->setTabText(PhysAlbum,
0550                                            wholeAlbumsChecked() ? i18nc("@title", "Albums (All)")
0551                                                                 : i18nc("@title", "Albums (%1)",
0552                                                  selectedAlbums().count()));
0553         d->tabWidget->tabBar()->setTabText(TagsAlbum,
0554                                            wholeTagsChecked() ? i18nc("@title", "Tags (All)")
0555                                                               : i18nc("@title", "Tags (%1)",
0556                                                  selectedTags().count()));
0557     }
0558 }
0559 
0560 } // namespace Digikam
0561 
0562 #include "albumselectors.moc"
0563 
0564 #include "moc_albumselectors.cpp"