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        : 2009-05-09
0007  * Description : A combo box for selecting albums
0008  *
0009  * SPDX-FileCopyrightText: 2008-2011 by Marcel Wiesweg <marcel dot wiesweg at gmx dot de>
0010  * SPDX-FileCopyrightText: 2010-2011 by Andi Clemens <andi dot clemens at gmail dot com>
0011  * SPDX-FileCopyrightText: 2012-2024 by Gilles Caulier <caulier dot gilles at gmail dot com>
0012  *
0013  * SPDX-License-Identifier: GPL-2.0-or-later
0014  *
0015  * ============================================================ */
0016 
0017 #include "albumselectcombobox.h"
0018 
0019 // Qt includes
0020 
0021 #include <QSortFilterProxyModel>
0022 #include <QTreeView>
0023 
0024 // KDE includes
0025 
0026 #include <klocalizedstring.h>
0027 
0028 // Local includes
0029 
0030 #include "albummodel.h"
0031 #include "albumfiltermodel.h"
0032 #include "albumtreeview.h"
0033 #include "tagtreeview.h"
0034 #include "contextmenuhelper.h"
0035 
0036 namespace Digikam
0037 {
0038 
0039 class Q_DECL_HIDDEN AlbumSelectComboBox::Private
0040 {
0041 public:
0042 
0043     explicit Private(AlbumSelectComboBox* const q)
0044       : model                   (nullptr),
0045         filterModel             (nullptr),
0046         recursive               (false),
0047         isCheckable             (true),
0048         closeOnActivate         (false),
0049         allSelectedText         (false),
0050         showCheckStateSummary   (true),
0051         q                       (q)
0052     {
0053     }
0054 
0055     void updateCheckable();
0056     void updateCloseOnActivate();
0057 
0058 public:
0059 
0060     AbstractCheckableAlbumModel* model;
0061     AlbumFilterModel*            filterModel;
0062     QString                      noSelectionText;
0063     bool                         recursive;
0064     bool                         isCheckable;
0065     bool                         closeOnActivate;
0066     bool                         allSelectedText;
0067     bool                         showCheckStateSummary;
0068 
0069     AlbumSelectComboBox* const   q;
0070 };
0071 
0072 AlbumSelectComboBox::AlbumSelectComboBox(QWidget* const parent)
0073     : TreeViewLineEditComboBox(parent),
0074       d                       (new Private(this))
0075 {
0076     d->noSelectionText = i18n("No Album Selected");
0077 
0078     // Workaround for QLineEdit text when QComboBox loses focus
0079 
0080     // --- NOTE: use dynamic binding as updateText() is a virtual slot which can be re-implemented in derived classes.
0081     connect(this, &AlbumSelectComboBox::editTextChanged,
0082             this, &AlbumSelectComboBox::updateText);
0083 }
0084 
0085 AlbumSelectComboBox::~AlbumSelectComboBox()
0086 {
0087     delete d;
0088 }
0089 
0090 void AlbumSelectComboBox::setDefaultAlbumModel()
0091 {
0092     d->noSelectionText = i18n("No Album Selected");
0093     setAlbumModels(new AlbumModel(AlbumModel::IgnoreRootAlbum, this));
0094     view()->expandToDepth(0);
0095 }
0096 
0097 void AlbumSelectComboBox::setDefaultTagModel()
0098 {
0099     d->noSelectionText = i18n("No Tag Selected");
0100     setAlbumModels(new TagModel(AlbumModel::IgnoreRootAlbum, this));
0101 }
0102 
0103 void AlbumSelectComboBox::setAlbumModels(AbstractCheckableAlbumModel* model, AlbumFilterModel* filterModel)
0104 {
0105     d->model = model;
0106     d->model->setRecursive(d->recursive);
0107 
0108     if (filterModel)
0109     {
0110         d->filterModel = filterModel;
0111     }
0112     else
0113     {
0114         d->filterModel = new AlbumFilterModel(this);
0115 /*
0116         d->filterModel->setDynamicSortFilter(true);
0117 */
0118         d->filterModel->setSourceAlbumModel(d->model);
0119     }
0120 
0121     d->updateCheckable();
0122 
0123     QComboBox::setModel(d->filterModel);
0124     installView();
0125 
0126     d->updateCloseOnActivate();
0127     updateText();
0128 }
0129 
0130 void AlbumSelectComboBox::installView(QAbstractItemView* v)
0131 {
0132     if (view())
0133     {
0134         return;
0135     }
0136 
0137     TreeViewLineEditComboBox::installView(v);
0138 
0139     // cppcheck-suppress nullPointerRedundantCheck
0140     view()->sortByColumn(0, Qt::AscendingOrder);
0141     // cppcheck-suppress nullPointerRedundantCheck
0142     view()->setSortingEnabled(true);
0143     // cppcheck-suppress nullPointerRedundantCheck
0144     view()->collapseAll();
0145 }
0146 
0147 void AlbumSelectComboBox::setCheckable(bool checkable)
0148 {
0149     if (checkable == d->isCheckable)
0150     {
0151         return;
0152     }
0153 
0154     d->isCheckable = checkable;
0155     d->updateCheckable();
0156 }
0157 
0158 bool AlbumSelectComboBox::isCheckable() const
0159 {
0160     return d->isCheckable;
0161 }
0162 
0163 void AlbumSelectComboBox::Private::updateCheckable()
0164 {
0165     if (!model)
0166     {
0167         return;
0168     }
0169 
0170     model->setCheckable(isCheckable);
0171 
0172     if (isCheckable)
0173     {
0174         connect(model, SIGNAL(checkStateChanged(Album*,Qt::CheckState)),
0175                 q, SLOT(updateText()));
0176     }
0177     else
0178     {
0179         disconnect(model, SIGNAL(checkStateChanged(Album*,Qt::CheckState)),
0180                    q, SLOT(updateText()));
0181     }
0182 }
0183 
0184 void AlbumSelectComboBox::setCloseOnActivate(bool close)
0185 {
0186     if (d->closeOnActivate == close)
0187     {
0188         return;
0189     }
0190 
0191     d->closeOnActivate = close;
0192     d->updateCloseOnActivate();
0193 }
0194 
0195 void AlbumSelectComboBox::Private::updateCloseOnActivate()
0196 {
0197     if (!q->view())
0198     {
0199         return;
0200     }
0201 
0202     if (closeOnActivate)
0203     {
0204         connect(q->view(), SIGNAL(activated(QModelIndex)),
0205                 q, SLOT(hidePopup()));
0206     }
0207     else
0208     {
0209         disconnect(q->view(), SIGNAL(activated(QModelIndex)),
0210                    q, SLOT(hidePopup()));
0211     }
0212 }
0213 
0214 void AlbumSelectComboBox::setRecursive(bool recursive)
0215 {
0216     d->recursive = recursive; // used if a new model will be set
0217 
0218     if (d->model)
0219     {
0220         d->model->setRecursive(recursive);
0221     }
0222 }
0223 
0224 void AlbumSelectComboBox::setNoSelectionText(const QString& text)
0225 {
0226     d->noSelectionText = text;
0227     updateText();
0228 }
0229 
0230 void AlbumSelectComboBox::setAllSelectedText(bool all)
0231 {
0232     d->allSelectedText = all;
0233     updateText();
0234 }
0235 
0236 void AlbumSelectComboBox::setShowCheckStateSummary(bool show)
0237 {
0238     d->showCheckStateSummary = show;
0239     updateText();
0240 }
0241 
0242 AbstractCheckableAlbumModel* AlbumSelectComboBox::model() const
0243 {
0244     return d->model;
0245 }
0246 
0247 QSortFilterProxyModel* AlbumSelectComboBox::filterModel() const
0248 {
0249     return d->filterModel;
0250 }
0251 
0252 void AlbumSelectComboBox::hidePopup()
0253 {
0254     // just make this a slot
0255 
0256     TreeViewLineEditComboBox::hidePopup();
0257     updateText();
0258 }
0259 
0260 void AlbumSelectComboBox::updateText()
0261 {
0262     if (!d->isCheckable || !d->showCheckStateSummary)
0263     {
0264         return;
0265     }
0266 
0267     QList<Album*> checkedAlbums;
0268 
0269     if (!d->allSelectedText)
0270     {
0271         checkedAlbums = d->model->checkedAlbums();
0272     }
0273     else
0274     {
0275         if (d->model->albumType() == Album::TAG)
0276         {
0277             checkedAlbums = AlbumManager::instance()->allTAlbums();
0278         }
0279         else
0280         {
0281             checkedAlbums = AlbumManager::instance()->allPAlbums();
0282         }
0283     }
0284 
0285     QList<Album*> partiallyCheckedAlbums = d->model->partiallyCheckedAlbums();
0286     QString newIncludeText;
0287     QString newExcludeText;
0288 
0289     if (!checkedAlbums.isEmpty())
0290     {
0291         if (checkedAlbums.count() == 1)
0292         {
0293             newIncludeText = checkedAlbums.first()->title();
0294         }
0295         else
0296         {
0297             if (d->model->albumType() == Album::TAG)
0298             {
0299                 newIncludeText = i18np("1 Tag selected", "%1 Tags selected",
0300                                        checkedAlbums.count());
0301             }
0302             else
0303             {
0304                 newIncludeText = i18np("1 Album selected", "%1 Albums selected",
0305                                        checkedAlbums.count());
0306             }
0307         }
0308     }
0309 
0310     if (!partiallyCheckedAlbums.isEmpty() && !d->allSelectedText)
0311     {
0312         if (d->model->albumType() == Album::TAG)
0313         {
0314             newExcludeText = i18np("1 Tag excluded", "%1 Tags excluded",
0315                                    partiallyCheckedAlbums.count());
0316         }
0317         else
0318         {
0319             newExcludeText = i18np("1 Album excluded", "%1 Albums excluded",
0320                                    partiallyCheckedAlbums.count());
0321         }
0322     }
0323 
0324     blockSignals(true);
0325 
0326     if      (newIncludeText.isEmpty() && newExcludeText.isEmpty())
0327     {
0328         setLineEditText(d->noSelectionText);
0329     }
0330     else if (newIncludeText.isEmpty() || newExcludeText.isEmpty())
0331     {
0332         setLineEditText(newIncludeText + newExcludeText);
0333     }
0334     else
0335     {
0336         setLineEditText(newIncludeText + QLatin1String(", ") + newExcludeText);
0337     }
0338 
0339     blockSignals(false);
0340 }
0341 
0342 // ---------------------------------------------------------------------------------------------------
0343 
0344 AbstractAlbumTreeViewSelectComboBox::AbstractAlbumTreeViewSelectComboBox(QWidget* const parent)
0345     : AlbumSelectComboBox(parent),
0346       m_treeView         (nullptr)
0347 {
0348 }
0349 
0350 void AbstractAlbumTreeViewSelectComboBox::installView(QAbstractItemView* view)
0351 {
0352     if (!view)
0353     {
0354         view = m_treeView;
0355     }
0356 
0357     AlbumSelectComboBox::installView(view);
0358 }
0359 
0360 void AbstractAlbumTreeViewSelectComboBox::sendViewportEventToView(QEvent* e)
0361 {
0362     // needed for StayPoppedUpComboBox
0363 
0364     m_treeView->viewportEvent(e);
0365 }
0366 
0367 void AbstractAlbumTreeViewSelectComboBox::setTreeView(AbstractAlbumTreeView* const treeView)
0368 {
0369     // this is independent from the installView mechanism, just to override
0370     // the tree view created below without the need to subclass
0371 
0372     if (!m_treeView)
0373     {
0374         m_treeView = treeView;
0375     }
0376 }
0377 
0378 // -------------------------------------------------------------------------------------------------------------------
0379 
0380 class Q_DECL_HIDDEN CheckUncheckContextMenuElement : public QObject,
0381                                                      public AbstractAlbumTreeView::ContextMenuElement
0382 {
0383     Q_OBJECT
0384 
0385 public:
0386 
0387     explicit CheckUncheckContextMenuElement(QObject* const parent)
0388         : QObject(parent)
0389     {
0390     }
0391 
0392     void addActions(AbstractAlbumTreeView* view, ContextMenuHelper& cmh, Album* album) override
0393     {
0394         AbstractCheckableAlbumModel* const checkable = qobject_cast<AbstractCheckableAlbumModel*>(view->albumModel());
0395 
0396         if (checkable)
0397         {
0398             cmh.setAlbumModel(checkable);
0399             cmh.addAlbumCheckUncheckActions(album);
0400         }
0401     }
0402 };
0403 
0404 void AbstractAlbumTreeViewSelectComboBox::addCheckUncheckContextMenuActions()
0405 {
0406     if (m_treeView)
0407     {
0408         m_treeView->setEnableContextMenu(true);
0409         m_treeView->addContextMenuElement(new CheckUncheckContextMenuElement(this));
0410     }
0411 }
0412 
0413 // ---------------------------------------------------------------------------------
0414 
0415 AlbumTreeViewSelectComboBox::AlbumTreeViewSelectComboBox(QWidget* const parent)
0416     : AbstractAlbumTreeViewSelectComboBox(parent)
0417 {
0418 }
0419 
0420 AlbumTreeView* AlbumTreeViewSelectComboBox::view() const
0421 {
0422     return static_cast<AlbumTreeView*>(m_treeView);
0423 }
0424 
0425 void AlbumTreeViewSelectComboBox::setDefaultModel()
0426 {
0427     setAlbumModels(nullptr, nullptr);
0428 }
0429 
0430 void AlbumTreeViewSelectComboBox::setAlbumModels(AlbumModel* model, CheckableAlbumFilterModel* filterModel)
0431 {
0432     if (!m_treeView)
0433     {
0434         AlbumTreeView::Flags flags;
0435         m_treeView = new AlbumTreeView(this, flags);
0436     }
0437 
0438     if (!model)
0439     {
0440         model = new AlbumModel(AlbumModel::IgnoreRootAlbum, this);
0441     }
0442 
0443     if (!filterModel)
0444     {
0445         filterModel = new CheckableAlbumFilterModel(this);
0446     }
0447 
0448     view()->setAlbumModel(model);
0449     view()->setAlbumFilterModel(filterModel);
0450 
0451     AlbumSelectComboBox::setAlbumModels(view()->albumModel(), view()->albumFilterModel());
0452 
0453     view()->expandToDepth(0);
0454 }
0455 
0456 // ---------------------------------------------------------------------------------------------------
0457 
0458 TagTreeViewSelectComboBox::TagTreeViewSelectComboBox(QWidget* const parent)
0459     : AbstractAlbumTreeViewSelectComboBox(parent)
0460 {
0461 }
0462 
0463 TagTreeView* TagTreeViewSelectComboBox::view() const
0464 {
0465     return static_cast<TagTreeView*>(m_treeView);
0466 }
0467 
0468 void TagTreeViewSelectComboBox::setDefaultModel()
0469 {
0470     setAlbumModels(nullptr, nullptr);
0471 }
0472 
0473 void TagTreeViewSelectComboBox::setAlbumModels(TagModel* model,
0474                                                TagPropertiesFilterModel* filteredModel,
0475                                                CheckableAlbumFilterModel* filterModel)
0476 {
0477     if (!m_treeView)
0478     {
0479         TagTreeView::Flags flags;
0480         m_treeView = new TagTreeView(this, flags);
0481     }
0482 
0483     if (!model)
0484     {
0485         model = new TagModel(AlbumModel::IgnoreRootAlbum, this);
0486     }
0487 
0488     if (!filteredModel)
0489     {
0490         filteredModel = new TagPropertiesFilterModel(this);
0491     }
0492 
0493     if (!filterModel)
0494     {
0495         filterModel = new CheckableAlbumFilterModel(this);
0496     }
0497 
0498     view()->setAlbumModel(model);
0499     view()->setAlbumFilterModel(filteredModel, filterModel);
0500 
0501     AlbumSelectComboBox::setAlbumModels(view()->albumModel(), view()->albumFilterModel());
0502 }
0503 
0504 } // namespace Digikam
0505 
0506 #include "albumselectcombobox.moc"
0507 
0508 #include "moc_albumselectcombobox.cpp"