File indexing completed on 2023-05-30 11:30:45

0001 /**
0002  * Copyright (C) 2005 Michael Pyne <mpyne@kde.org>
0003  * Copyright (C) 2014 Arnold Dumas <contact@arnolddumas.fr>
0004  *
0005  * This program is free software; you can redistribute it and/or modify it under
0006  * the terms of the GNU General Public License as published by the Free Software
0007  * Foundation; either version 2 of the License, or (at your option) any later
0008  * version.
0009  *
0010  * This program is distributed in the hope that it will be useful, but WITHOUT ANY
0011  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
0012  * PARTICULAR PURPOSE. See the GNU General Public License for more details.
0013  *
0014  * You should have received a copy of the GNU General Public License along with
0015  * this program.  If not, see <http://www.gnu.org/licenses/>.
0016  */
0017 
0018 #include "coverdialog.h"
0019 
0020 #include <KLocalizedString>
0021 #include <kiconloader.h>
0022 
0023 #include <QTimer>
0024 #include <QMenu>
0025 
0026 #include "covericonview.h"
0027 #include "covermanager.h"
0028 #include "collectionlist.h"
0029 #include "juk_debug.h"
0030 
0031 using CoverUtility::CoverIconViewItem;
0032 
0033 class AllArtistsListViewItem : public QListWidgetItem
0034 {
0035 public:
0036     AllArtistsListViewItem(QListWidget *parent) :
0037         QListWidgetItem(i18n("<All Artists>"), parent)
0038     {
0039     }
0040 
0041     bool operator< (const QListWidgetItem& other) const override
0042     {
0043         Q_UNUSED(other);
0044         return true; // Always be at the top.
0045     }
0046 };
0047 
0048 class CaseInsensitiveItem : public QListWidgetItem
0049 {
0050 public:
0051     CaseInsensitiveItem(QListWidget *parent, const QString &text) :
0052         QListWidgetItem(text, parent)
0053     {
0054     }
0055 
0056     bool operator< (const QListWidgetItem& other) const override
0057     {
0058         return text().toLower().localeAwareCompare(other.text().toLower());
0059     }
0060 };
0061 
0062 CoverDialog::CoverDialog(QWidget *parent) :
0063     QWidget(parent, Qt::Dialog)
0064 {
0065     setupUi(this);
0066 
0067     setObjectName(QLatin1String("juk_cover_dialog"));
0068 
0069     m_searchLine->setClearButtonEnabled(true);
0070 
0071     connect(m_artists, SIGNAL(itemClicked(QListWidgetItem*)),
0072             this, SLOT(slotArtistClicked(QListWidgetItem*)));
0073 
0074     connect(m_covers, SIGNAL(customContextMenuRequested(QPoint)),
0075             this, SLOT(slotContextRequested(QPoint)));
0076 
0077     connect(m_searchLine, SIGNAL(textChanged(QString)),
0078             this, SLOT(slotSearchPatternChanged(QString)));
0079 }
0080 
0081 CoverDialog::~CoverDialog()
0082 {
0083 }
0084 
0085 void CoverDialog::show()
0086 {
0087     m_artists->clear();
0088     m_covers->clear();
0089 
0090     QStringList artists = CollectionList::instance()->uniqueSet(CollectionList::Artists);
0091 
0092     new AllArtistsListViewItem(m_artists);
0093     for(QStringList::ConstIterator it = artists.constBegin(); it != artists.constEnd(); ++it)
0094         new CaseInsensitiveItem(m_artists, *it);
0095 
0096     QTimer::singleShot(0, this, SLOT(loadCovers()));
0097     QWidget::show();
0098 }
0099 
0100 // TODO: Make this concurrent on a non-GUI thread
0101 void CoverDialog::loadCovers()
0102 {
0103           auto  it  = CoverManager::begin();
0104     const auto &end = CoverManager::end();
0105 
0106     for(; it != end; ++it) {
0107         (void) new CoverIconViewItem(it->first, m_covers);
0108     }
0109 }
0110 
0111 // TODO: Add a way to show cover art for tracks with no artist.
0112 void CoverDialog::slotArtistClicked(QListWidgetItem *item)
0113 {
0114     m_covers->clear();
0115     if (!item) {
0116         return;
0117     }
0118     if(dynamic_cast<AllArtistsListViewItem *>(item)) {
0119         // All artists.
0120         loadCovers();
0121     }
0122     else {
0123         QString artist = item->text().toLower();
0124 
0125         CoverDataMapIterator it, end;
0126 
0127         it  = CoverManager::begin();
0128         end = CoverManager::end();
0129 
0130         for(; it != end; ++it) {
0131             if(it->second.artist == artist)
0132                 (void) new CoverIconViewItem(it->first, m_covers);
0133         }
0134     }
0135 }
0136 
0137 void CoverDialog::slotContextRequested(const QPoint &pt)
0138 {
0139     static QMenu *menu = nullptr;
0140 
0141     QListWidgetItem* item = m_covers->currentItem();
0142 
0143     if(!item)
0144         return;
0145 
0146     if(!menu) {
0147         menu = new QMenu(this);
0148         menu->addAction(i18n("Remove Cover"), this, SLOT(removeSelectedCover()));
0149     }
0150 
0151     QPoint globalPt = m_covers->mapToGlobal(pt);
0152     menu->popup(globalPt);
0153 }
0154 
0155 void CoverDialog::slotSearchPatternChanged(const QString& pattern)
0156 {
0157     m_covers->clear();
0158 
0159     QListWidgetItem* item = m_artists->currentItem();
0160 
0161     // If the expression is cleared, then use slotArtistClicked.
0162     if (pattern.isEmpty()) {
0163         slotArtistClicked(item);
0164     }
0165 
0166     else {
0167         QRegExp filter(pattern, Qt::CaseInsensitive, QRegExp::Wildcard);
0168         QString artist = item->text().toLower();
0169 
0170               CoverDataMapIterator it  = CoverManager::begin();
0171         const CoverDataMapIterator end = CoverManager::end();
0172 
0173         // Here, only show cover that match the search pattern.
0174         if (dynamic_cast<AllArtistsListViewItem *>(item)) {
0175             for(; it != end; ++it) {
0176                 if (filter.indexIn(it->second.artist) != -1) {
0177                     (void) new CoverIconViewItem(it->first, m_covers);
0178                 }
0179             }
0180         }
0181         // Here, only show the covers that match the search pattern and
0182         // that have the same artist as the currently selected one.
0183         else {
0184             for(; it != end; ++it) {
0185                 if (it->second.artist == artist
0186                         && ((filter.indexIn(it->second.artist) != -1)
0187                         || (filter.indexIn(it->second.album) != -1)))
0188                 {
0189                     (void) new CoverIconViewItem(it->first, m_covers);
0190                 }
0191             }
0192         }
0193     }
0194 }
0195 
0196 void CoverDialog::removeSelectedCover()
0197 {
0198     CoverIconViewItem *coverItem = m_covers->currentItem();
0199 
0200     if(!coverItem || !coverItem->isSelected()) {
0201         qCWarning(JUK_LOG) << "No item selected for removeSelectedCover.\n";
0202         return;
0203     }
0204 
0205     if(!CoverManager::removeCover(coverItem->id()))
0206         qCCritical(JUK_LOG) << "Unable to remove selected cover: " << coverItem->id();
0207     else
0208         delete coverItem;
0209 }
0210 
0211 // vim: set et sw=4 tw=0 sta: