File indexing completed on 2024-05-05 04:48:22
0001 /**************************************************************************************** 0002 * Copyright (c) 2004 Pierpaolo Di Panfilo <pippo_dp@libero.it> * 0003 * Copyright (c) 2005 Isaiah Damron <xepo@trifault.net> * 0004 * Copyright (c) 2007 Dan Meltzer <parallelgrapefruit@gmail.com> * 0005 * Copyright (c) 2008 Seb Ruiz <ruiz@kde.org> * 0006 * * 0007 * This program is free software; you can redistribute it and/or modify it under * 0008 * the terms of the GNU General Public License as published by the Free Software * 0009 * Foundation; either version 2 of the License, or (at your option) any later * 0010 * version. * 0011 * * 0012 * This program is distributed in the hope that it will be useful, but WITHOUT ANY * 0013 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * 0014 * PARTICULAR PURPOSE. See the GNU General Public License for more details. * 0015 * * 0016 * You should have received a copy of the GNU General Public License along with * 0017 * this program. If not, see <http://www.gnu.org/licenses/>. * 0018 ****************************************************************************************/ 0019 0020 #define DEBUG_PREFIX "CoverManager" 0021 0022 #include "CoverManager.h" 0023 0024 #include "amarokconfig.h" 0025 #include <config.h> 0026 #include "core/capabilities/ActionsCapability.h" 0027 #include "core/collections/Collection.h" 0028 #include "core/collections/QueryMaker.h" 0029 #include "core/meta/Meta.h" 0030 #include "core/support/Amarok.h" 0031 #include "core/support/Debug.h" 0032 #include "core-impl/collections/support/CollectionManager.h" 0033 #include "covermanager/CoverFetchingActions.h" 0034 #include "covermanager/CoverViewDialog.h" 0035 #include "playlist/PlaylistController.h" 0036 #include "statusbar/CompoundProgressBar.h" 0037 #include "widgets/LineEdit.h" 0038 #include "widgets/PixmapViewer.h" 0039 0040 #include <QAction> 0041 #include <QApplication> 0042 #include <QDesktopWidget> 0043 #include <QDialogButtonBox> 0044 #include <QMenu> //showCoverMenu() 0045 #include <QProgressBar> 0046 #include <QPushButton> 0047 #include <QSplitter> 0048 #include <QStatusBar> 0049 #include <QStringList> 0050 #include <QTimer> //search filter timer 0051 #include <QToolButton> 0052 #include <QTreeWidget> 0053 #include <QTreeWidgetItem> 0054 #include <QVBoxLayout> 0055 0056 #include <KConfigGroup> 0057 #include <KIconLoader> 0058 #include <KLocalizedString> 0059 #include <KSqueezedTextLabel> //status label 0060 #include <KToolBar> 0061 0062 static QString artistToSelectInInitFunction; 0063 CoverManager *CoverManager::s_instance = nullptr; 0064 0065 class ArtistItem : public QTreeWidgetItem 0066 { 0067 public: 0068 ArtistItem( QTreeWidget *parent, Meta::ArtistPtr artist ) 0069 : QTreeWidgetItem( parent ) 0070 , m_artist( artist ) 0071 { 0072 setText( 0, artist->prettyName() ); 0073 } 0074 0075 ArtistItem( const QString &text, QTreeWidget *parent = nullptr ) 0076 : QTreeWidgetItem( parent ) 0077 , m_artist( nullptr ) 0078 { 0079 setText( 0, text ); 0080 } 0081 0082 Meta::ArtistPtr artist() const { return m_artist; } 0083 0084 private: 0085 Meta::ArtistPtr m_artist; 0086 }; 0087 0088 0089 CoverManager::CoverManager( QWidget *parent ) 0090 : QDialog( parent ) 0091 , m_currentView( AllAlbums ) 0092 , m_timer( new QTimer( this ) ) //search filter timer 0093 , m_fetchingCovers( false ) 0094 , m_coversFetched( 0 ) 0095 , m_coverErrors( 0 ) 0096 , m_isLoadingCancelled( false ) 0097 { 0098 DEBUG_BLOCK 0099 0100 setObjectName( "TheCoverManager" ); 0101 0102 s_instance = this; 0103 0104 // Sets caption and icon correctly (needed e.g. for GNOME) 0105 //kapp->setTopWidget( this ); 0106 0107 QDialogButtonBox *buttonBox = new QDialogButtonBox( QDialogButtonBox::Close, this ); 0108 QVBoxLayout *mainLayout = new QVBoxLayout(this); 0109 connect(buttonBox, &QDialogButtonBox::accepted, this, &CoverManager::accept); 0110 connect(buttonBox, &QDialogButtonBox::rejected, this, &CoverManager::reject); 0111 setWindowTitle( i18n("Cover Manager") ); 0112 setAttribute( Qt::WA_DeleteOnClose ); 0113 0114 // TODO: There is no hidden signal in QDialog. Needs porting to QT5. 0115 // connect( this, &CoverManager::hidden, this, &CoverManager::delayedDestruct ); 0116 connect( buttonBox->button(QDialogButtonBox::Close), &QAbstractButton::clicked, this, &CoverManager::delayedDestruct ); 0117 0118 m_splitter = new QSplitter( this ); 0119 mainLayout->addWidget(m_splitter); 0120 mainLayout->addWidget(buttonBox); 0121 0122 //artist listview 0123 m_artistView = new QTreeWidget( m_splitter ); 0124 m_artistView->setHeaderLabel( i18n( "Albums By" ) ); 0125 m_artistView->setSortingEnabled( false ); 0126 m_artistView->setTextElideMode( Qt::ElideRight ); 0127 m_artistView->setMinimumWidth( 200 ); 0128 m_artistView->setColumnCount( 1 ); 0129 m_artistView->setAlternatingRowColors( true ); 0130 m_artistView->setUniformRowHeights( true ); 0131 m_artistView->setSelectionMode( QAbstractItemView::ExtendedSelection ); 0132 0133 ArtistItem *item = nullptr; 0134 item = new ArtistItem( i18n( "All Artists" ) ); 0135 item->setIcon(0, QIcon::fromTheme( "media-optical-audio-amarok" ) ); 0136 m_items.append( item ); 0137 0138 Collections::Collection *coll = CollectionManager::instance()->primaryCollection(); 0139 Collections::QueryMaker *qm = coll->queryMaker(); 0140 qm->setAutoDelete( true ); 0141 qm->setQueryType( Collections::QueryMaker::Artist ); 0142 qm->setAlbumQueryMode( Collections::QueryMaker::OnlyNormalAlbums ); 0143 qm->orderBy( Meta::valArtist ); 0144 0145 connect( qm, &Collections::QueryMaker::newArtistsReady, 0146 this, &CoverManager::slotArtistQueryResult ); 0147 0148 connect( qm, &Collections::QueryMaker::queryDone, this, &CoverManager::slotContinueConstruction ); 0149 0150 qm->run(); 0151 } 0152 0153 void 0154 CoverManager::slotArtistQueryResult( Meta::ArtistList artists ) //SLOT 0155 { 0156 DEBUG_BLOCK 0157 foreach( Meta::ArtistPtr artist, artists ) 0158 m_artistList << artist; 0159 } 0160 0161 void 0162 CoverManager::slotContinueConstruction() //SLOT 0163 { 0164 DEBUG_BLOCK 0165 foreach( Meta::ArtistPtr artist, m_artistList ) 0166 { 0167 ArtistItem* item = new ArtistItem( m_artistView, artist ); 0168 item->setIcon( 0, QIcon::fromTheme( "view-media-artist-amarok" ) ); 0169 m_items.append( item ); 0170 } 0171 m_artistView->insertTopLevelItems( 0, m_items ); 0172 0173 BoxWidget *vbox = new BoxWidget( true, m_splitter ); 0174 BoxWidget *hbox = new BoxWidget( false, vbox ); 0175 vbox->layout()->setSpacing( 4 ); 0176 hbox->layout()->setSpacing( 4 ); 0177 0178 { //<Search LineEdit> 0179 m_searchEdit = new Amarok::LineEdit( hbox ); 0180 m_searchEdit->setPlaceholderText( i18n( "Enter search terms here" ) ); 0181 m_searchEdit->setFrame( true ); 0182 0183 m_searchEdit->setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Minimum); 0184 m_searchEdit->setClearButtonEnabled( true ); 0185 0186 static_cast<QHBoxLayout*>( hbox->layout() )->setStretchFactor( m_searchEdit, 1 ); 0187 } //</Search LineEdit> 0188 0189 // view menu 0190 m_viewButton = new QPushButton( hbox ); 0191 0192 m_viewMenu = new QMenu( m_viewButton ); 0193 m_selectAllAlbums = m_viewMenu->addAction( i18n("All Albums"), this, &CoverManager::slotShowAllAlbums ); 0194 m_selectAlbumsWithCover = m_viewMenu->addAction( i18n("Albums With Cover"), this, &CoverManager::slotShowAlbumsWithCover ); 0195 m_selectAlbumsWithoutCover = m_viewMenu->addAction( i18n("Albums Without Cover"), this, &CoverManager::slotShowAlbumsWithoutCover ); 0196 0197 QActionGroup *viewGroup = new QActionGroup( m_viewButton ); 0198 viewGroup->setExclusive( true ); 0199 viewGroup->addAction( m_selectAllAlbums ); 0200 viewGroup->addAction( m_selectAlbumsWithCover ); 0201 viewGroup->addAction( m_selectAlbumsWithoutCover ); 0202 0203 m_viewButton->setMenu( m_viewMenu ); 0204 m_viewButton->setIcon( QIcon::fromTheme( QStringLiteral("filename-album-amarok") ) ); 0205 connect( m_viewMenu, &QMenu::triggered, this, &CoverManager::slotAlbumFilterTriggered ); 0206 0207 //fetch missing covers button 0208 m_fetchButton = new QPushButton( QIcon( "get-hot-new-stuff-amarok" ), i18n("Fetch Missing Covers"), hbox ); 0209 connect( m_fetchButton, &QAbstractButton::clicked, this, &CoverManager::fetchMissingCovers ); 0210 0211 m_selectAllAlbums->setChecked( true ); 0212 m_selectAllAlbums->trigger(); 0213 0214 //cover view 0215 m_coverView = new CoverView( vbox ); 0216 m_coverViewSpacer = new CoverView( vbox ); 0217 m_coverViewSpacer->hide(); 0218 0219 //status bar 0220 QStatusBar *statusBar = new QStatusBar( vbox ); 0221 0222 m_statusLabel = new KSqueezedTextLabel( statusBar ); 0223 m_statusLabel->setIndent( 3 ); 0224 m_progress = new CompoundProgressBar( statusBar ); 0225 0226 statusBar->addWidget( m_statusLabel, 4 ); 0227 statusBar->addPermanentWidget( m_progress, 1 ); 0228 0229 connect( m_progress, &CompoundProgressBar::allDone, this, &CoverManager::progressAllDone ); 0230 0231 QSize size = QApplication::desktop()->screenGeometry( this ).size() / 1.5; 0232 QSize sz = Amarok::config( "Cover Manager" ).readEntry( "Window Size", size ); 0233 resize( sz.width(), sz.height() ); 0234 0235 m_splitter->setStretchFactor( m_splitter->indexOf( m_artistView ), 1 ); 0236 m_splitter->setStretchFactor( m_splitter->indexOf( vbox ), 4 ); 0237 0238 m_fetcher = The::coverFetcher(); 0239 0240 QTreeWidgetItem *item = nullptr; 0241 int i = 0; 0242 if ( !artistToSelectInInitFunction.isEmpty() ) 0243 { 0244 for( item = m_artistView->invisibleRootItem()->child( 0 ); 0245 i < m_artistView->invisibleRootItem()->childCount(); 0246 item = m_artistView->invisibleRootItem()->child( i++ ) ) 0247 { 0248 if ( item->text( 0 ) == artistToSelectInInitFunction ) 0249 break; 0250 } 0251 } 0252 0253 // signals and slots connections 0254 connect( m_artistView, &QTreeWidget::itemSelectionChanged, 0255 this, &CoverManager::slotArtistSelected ); 0256 connect( m_coverView, &CoverView::itemActivated, 0257 this, &CoverManager::coverItemClicked ); 0258 connect( m_timer, &QTimer::timeout, 0259 this, &CoverManager::slotSetFilter ); 0260 connect( m_searchEdit, &Amarok::LineEdit::textChanged, 0261 this, &CoverManager::slotSetFilterTimeout ); 0262 0263 if( item == nullptr ) 0264 item = m_artistView->invisibleRootItem()->child( 0 ); 0265 0266 item->setSelected( true ); 0267 show(); 0268 } 0269 0270 CoverManager::~CoverManager() 0271 { 0272 Amarok::config( "Cover Manager" ).writeEntry( "Window Size", size() ); 0273 qDeleteAll( m_coverItems ); 0274 delete m_coverView; 0275 m_coverView = nullptr; 0276 s_instance = nullptr; 0277 } 0278 0279 void 0280 CoverManager::viewCover( const Meta::AlbumPtr &album, QWidget *parent ) //static 0281 { 0282 //QDialog means "escape" works as expected 0283 QDialog *dialog = new CoverViewDialog( album, parent ); 0284 dialog->show(); 0285 } 0286 0287 void 0288 CoverManager::metadataChanged(const Meta::AlbumPtr &album ) 0289 { 0290 const QString albumName = album->name(); 0291 foreach( CoverViewItem *item, m_coverItems ) 0292 { 0293 if( albumName == item->albumPtr()->name() ) 0294 item->loadCover(); 0295 } 0296 updateStatusBar(); 0297 } 0298 0299 void 0300 CoverManager::fetchMissingCovers() //SLOT 0301 { 0302 m_fetchCovers.clear(); 0303 for( int i = 0, coverCount = m_coverView->count(); i < coverCount; ++i ) 0304 { 0305 QListWidgetItem *item = m_coverView->item( i ); 0306 CoverViewItem *coverItem = static_cast<CoverViewItem*>( item ); 0307 if( !coverItem->hasCover() ) 0308 m_fetchCovers += coverItem->albumPtr(); 0309 } 0310 0311 debug() << QString( "Fetching %1 missing covers" ).arg( m_fetchCovers.size() ); 0312 0313 ProgressBar *fetchProgressBar = new ProgressBar( this ); 0314 fetchProgressBar->setDescription( i18n( "Fetching" ) ); 0315 fetchProgressBar->setMaximum( m_fetchCovers.size() ); 0316 m_progress->addProgressBar( fetchProgressBar, m_fetcher ); 0317 m_progress->show(); 0318 0319 m_fetcher->queueAlbums( m_fetchCovers ); 0320 m_fetchingCovers = true; 0321 0322 updateStatusBar(); 0323 m_fetchButton->setEnabled( false ); 0324 connect( m_fetcher, &CoverFetcher::finishedSingle, this, &CoverManager::updateFetchingProgress ); 0325 } 0326 0327 void 0328 CoverManager::showOnce( const QString &artist, QWidget* parent ) 0329 { 0330 if( !s_instance ) 0331 { 0332 artistToSelectInInitFunction = artist; 0333 new CoverManager( parent ); 0334 } 0335 else 0336 { 0337 s_instance->activateWindow(); 0338 s_instance->raise(); 0339 } 0340 } 0341 0342 void 0343 CoverManager::slotArtistSelected() //SLOT 0344 { 0345 DEBUG_BLOCK 0346 0347 // delete cover items before clearing cover view 0348 qDeleteAll( m_coverItems ); 0349 m_coverItems.clear(); 0350 m_coverView->clear(); 0351 0352 //this can be a bit slow 0353 QApplication::setOverrideCursor( Qt::WaitCursor ); 0354 0355 Collections::Collection *coll = CollectionManager::instance()->primaryCollection(); 0356 0357 Collections::QueryMaker *qm = coll->queryMaker(); 0358 qm->setAutoDelete( true ); 0359 qm->setQueryType( Collections::QueryMaker::Album ); 0360 qm->orderBy( Meta::valAlbum ); 0361 0362 qm->beginOr(); 0363 const QList< QTreeWidgetItem* > items = m_artistView->selectedItems(); 0364 foreach( const QTreeWidgetItem *item, items ) 0365 { 0366 const ArtistItem *artistItem = static_cast< const ArtistItem* >( item ); 0367 if( artistItem != m_artistView->invisibleRootItem()->child( 0 ) ) 0368 qm->addFilter( Meta::valArtist, artistItem->artist()->name(), true, true ); 0369 else 0370 qm->excludeFilter( Meta::valAlbum, QString(), true, true ); 0371 } 0372 qm->endAndOr(); 0373 0374 // do not show albums with no name, i.e. tracks not belonging to any album 0375 qm->beginAnd(); 0376 qm->excludeFilter( Meta::valAlbum, QString(), true, true ); 0377 qm->endAndOr(); 0378 0379 connect( qm, &Collections::QueryMaker::newAlbumsReady, 0380 this, &CoverManager::slotAlbumQueryResult ); 0381 0382 connect( qm, &Collections::QueryMaker::queryDone, this, &CoverManager::slotArtistQueryDone ); 0383 0384 qm->run(); 0385 } 0386 0387 void 0388 CoverManager::slotAlbumQueryResult( const Meta::AlbumList &albums ) //SLOT 0389 { 0390 m_albumList = albums; 0391 } 0392 0393 void 0394 CoverManager::slotAlbumFilterTriggered( QAction *action ) //SLOT 0395 { 0396 m_viewButton->setText( action->text() ); 0397 } 0398 0399 void 0400 CoverManager::slotArtistQueryDone() //SLOT 0401 { 0402 DEBUG_BLOCK 0403 0404 QApplication::restoreOverrideCursor(); 0405 0406 const int albumCount = m_albumList.count(); 0407 0408 ProgressBar *coverLoadProgressBar = new ProgressBar( this ); 0409 coverLoadProgressBar->setDescription( i18n( "Loading" ) ); 0410 coverLoadProgressBar->setMaximum( albumCount ); 0411 connect( coverLoadProgressBar, &ProgressBar::cancelled, 0412 this, &CoverManager::cancelCoverViewLoading ); 0413 0414 m_progress->addProgressBar( coverLoadProgressBar, m_coverView ); 0415 m_progress->show(); 0416 0417 uint x = 0; 0418 debug() << "Loading covers for selected artist(s)"; 0419 0420 //the process events calls below causes massive flickering in the m_albumList 0421 //so we hide this view and only show it when all items has been inserted. This 0422 //also provides quite a massive speed improvement when loading covers. 0423 m_coverView->hide(); 0424 m_coverViewSpacer->show(); 0425 foreach( const Meta::AlbumPtr &album, m_albumList ) 0426 { 0427 qApp->processEvents( QEventLoop::ExcludeSocketNotifiers ); 0428 if( isHidden() ) 0429 { 0430 m_progress->endProgressOperation( m_coverView ); 0431 return; 0432 } 0433 /* 0434 * Loading is stopped if cancelled by the user, or the number of albums 0435 * has changed. The latter occurs when the artist selection changes. 0436 */ 0437 if( m_isLoadingCancelled || albumCount != m_albumList.count() ) 0438 { 0439 m_isLoadingCancelled = false; 0440 break; 0441 } 0442 0443 CoverViewItem *item = new CoverViewItem( m_coverView, album ); 0444 m_coverItems.append( item ); 0445 0446 if( ++x % 10 == 0 ) 0447 { 0448 m_progress->setProgress( m_coverView, x ); 0449 } 0450 } 0451 m_progress->endProgressOperation( m_coverView ); 0452 0453 // makes sure View is retained when artist selection changes 0454 changeView( m_currentView, true ); 0455 0456 m_coverViewSpacer->hide(); 0457 m_coverView->show(); 0458 updateStatusBar(); 0459 } 0460 0461 void 0462 CoverManager::cancelCoverViewLoading() 0463 { 0464 m_isLoadingCancelled = true; 0465 } 0466 0467 // called when a cover item is clicked 0468 void 0469 CoverManager::coverItemClicked( QListWidgetItem *item ) //SLOT 0470 { 0471 #define item static_cast<CoverViewItem*>(item) 0472 0473 if( !item ) return; 0474 0475 item->setSelected( true ); 0476 if ( item->hasCover() ) 0477 viewCover( item->albumPtr(), this ); 0478 else 0479 m_fetcher->manualFetch( item->albumPtr() ); 0480 0481 #undef item 0482 } 0483 0484 0485 void 0486 CoverManager::slotSetFilter() //SLOT 0487 { 0488 m_filter = m_searchEdit->text(); 0489 0490 m_coverView->clearSelection(); 0491 uint i = 0; 0492 QListWidgetItem *item = m_coverView->item( i ); 0493 while ( item ) 0494 { 0495 QListWidgetItem *tmp = m_coverView->item( i + 1 ); 0496 m_coverView->takeItem( i ); 0497 item = tmp; 0498 } 0499 0500 foreach( QListWidgetItem *item, m_coverItems ) 0501 { 0502 CoverViewItem *coverItem = static_cast<CoverViewItem*>(item); 0503 if( coverItem->album().contains( m_filter, Qt::CaseInsensitive ) || coverItem->artist().contains( m_filter, Qt::CaseInsensitive ) ) 0504 m_coverView->insertItem( m_coverView->count() - 1, item ); 0505 } 0506 0507 // makes sure View is retained when filter text has changed 0508 changeView( m_currentView, true ); 0509 updateStatusBar(); 0510 } 0511 0512 0513 void 0514 CoverManager::slotSetFilterTimeout() //SLOT 0515 { 0516 if ( m_timer->isActive() ) m_timer->stop(); 0517 m_timer->setSingleShot( true ); 0518 m_timer->start( 180 ); 0519 } 0520 0521 void 0522 CoverManager::changeView( CoverManager::View id, bool force ) //SLOT 0523 { 0524 if( !force && m_currentView == id ) 0525 return; 0526 0527 //clear the iconview without deleting items 0528 m_coverView->clearSelection(); 0529 int itemsCount = m_coverView->count(); 0530 while( itemsCount-- > 0 ) 0531 m_coverView->takeItem( 0 ); 0532 0533 foreach( QListWidgetItem *item, m_coverItems ) 0534 { 0535 bool show = false; 0536 CoverViewItem *coverItem = static_cast<CoverViewItem*>(item); 0537 if( !m_filter.isEmpty() ) 0538 { 0539 if( !coverItem->album().contains( m_filter, Qt::CaseInsensitive ) && 0540 !coverItem->artist().contains( m_filter, Qt::CaseInsensitive ) ) 0541 continue; 0542 } 0543 0544 if( id == AllAlbums ) //show all albums 0545 show = true; 0546 else if( id == AlbumsWithCover && coverItem->hasCover() ) //show only albums with cover 0547 show = true; 0548 else if( id == AlbumsWithoutCover && !coverItem->hasCover() )//show only albums without cover 0549 show = true; 0550 0551 if( show ) 0552 m_coverView->insertItem( m_coverView->count() - 1, item ); 0553 } 0554 m_currentView = id; 0555 } 0556 0557 void 0558 CoverManager::updateFetchingProgress( int state ) 0559 { 0560 switch( static_cast< CoverFetcher::FinishState >( state ) ) 0561 { 0562 case CoverFetcher::Success: 0563 m_coversFetched++; 0564 break; 0565 0566 case CoverFetcher::Cancelled: 0567 case CoverFetcher::Error: 0568 case CoverFetcher::NotFound: 0569 default: 0570 m_coverErrors++; 0571 break; 0572 } 0573 m_progress->incrementProgress( m_fetcher ); 0574 updateStatusBar(); 0575 } 0576 0577 void 0578 CoverManager::stopFetching() 0579 { 0580 DEBUG_FUNC_INFO 0581 0582 m_fetchCovers.clear(); 0583 m_fetchingCovers = false; 0584 m_progress->endProgressOperation( m_fetcher ); 0585 updateStatusBar(); 0586 } 0587 0588 void 0589 CoverManager::loadCover( const QString &artist, const QString &album ) 0590 { 0591 foreach( QListWidgetItem *item, m_coverItems ) 0592 { 0593 CoverViewItem *coverItem = static_cast<CoverViewItem*>(item); 0594 if ( album == coverItem->album() && ( artist == coverItem->artist() || ( artist.isEmpty() && coverItem->artist().isEmpty() ) ) ) 0595 { 0596 coverItem->loadCover(); 0597 return; 0598 } 0599 } 0600 } 0601 0602 void 0603 CoverManager::progressAllDone() 0604 { 0605 m_progress->hide(); 0606 } 0607 0608 void 0609 CoverManager::updateStatusBar() 0610 { 0611 QString text; 0612 0613 //cover fetching info 0614 if( m_fetchingCovers ) 0615 { 0616 //update the status text 0617 if( m_coversFetched + m_coverErrors >= m_fetchCovers.size() ) 0618 { 0619 //fetching finished 0620 text = i18nc( "The fetching is done.", "Finished." ); 0621 if( m_coverErrors ) 0622 text += i18np( " Cover not found", " <b>%1</b> covers not found", m_coverErrors ); 0623 //reset counters 0624 m_coversFetched = 0; 0625 m_coverErrors = 0; 0626 m_fetchCovers.clear(); 0627 m_fetchingCovers = false; 0628 m_progress->endProgressOperation( m_fetcher ); 0629 0630 disconnect( m_fetcher, &CoverFetcher::finishedSingle, this, &CoverManager::updateFetchingProgress ); 0631 QTimer::singleShot( 2000, this, &CoverManager::updateStatusBar ); 0632 } 0633 0634 if( m_fetchCovers.size() == 1 ) 0635 { 0636 foreach( Meta::AlbumPtr album, m_fetchCovers ) 0637 { 0638 if( album->hasAlbumArtist() && !album->albumArtist()->prettyName().isEmpty() ) 0639 { 0640 text = i18n( "Fetching cover for %1 - %2...", 0641 album->albumArtist()->prettyName(), 0642 album->prettyName() ); 0643 } 0644 else 0645 { 0646 text = i18n( "Fetching cover for %1..." , album->prettyName() ); 0647 } 0648 } 0649 } 0650 else 0651 { 0652 text = i18np( "Fetching 1 cover: ", "Fetching <b>%1</b> covers... : ", m_fetchCovers.size() ); 0653 if( m_coversFetched ) 0654 text += i18np( "1 fetched", "%1 fetched", m_coversFetched ); 0655 if( m_coverErrors ) 0656 { 0657 if( m_coversFetched ) 0658 text += i18n(" - "); 0659 text += i18np( "1 not found", "%1 not found", m_coverErrors ); 0660 } 0661 if( m_coversFetched + m_coverErrors == 0 ) 0662 text += i18n( "Connecting..." ); 0663 } 0664 } 0665 else 0666 { 0667 m_coversFetched = 0; 0668 m_coverErrors = 0; 0669 0670 uint totalCounter = 0, missingCounter = 0; 0671 0672 //album info 0673 for( int i = 0, coverCount = m_coverView->count(); i < coverCount; ++i ) 0674 { 0675 totalCounter++; 0676 QListWidgetItem *item = m_coverView->item( i ); 0677 if( !static_cast<CoverViewItem*>( item )->hasCover() ) 0678 missingCounter++; //counter for albums without cover 0679 } 0680 0681 const QList< QTreeWidgetItem* > selected = m_artistView->selectedItems(); 0682 0683 if( !m_filter.isEmpty() ) 0684 { 0685 text = i18np( "1 result for \"%2\"", "%1 results for \"%2\"", totalCounter, m_filter ); 0686 } 0687 else if( selected.count() > 0 ) 0688 { 0689 text = i18np( "1 album", "%1 albums", totalCounter ); 0690 0691 // showing albums by selected artist(s) 0692 if( selected.first() != m_artistView->invisibleRootItem()->child( 0 ) ) 0693 { 0694 QStringList artists; 0695 foreach( const QTreeWidgetItem *item, selected ) 0696 { 0697 QString artist = item->text( 0 ); 0698 Amarok::manipulateThe( artist, false ); 0699 artists.append( artist ); 0700 } 0701 text = i18n( "%1 by %2", text, artists.join( i18nc("Separator for artists", ", ")) ); 0702 } 0703 } 0704 0705 if( missingCounter ) 0706 text = i18np("%2 - ( <b>1</b> without cover )", "%2 - ( <b>%1</b> without cover )", 0707 missingCounter, text ); 0708 0709 m_fetchButton->setEnabled( missingCounter ); 0710 } 0711 0712 m_statusLabel->setText( text ); 0713 } 0714 0715 void 0716 CoverManager::delayedDestruct() 0717 { 0718 if ( isVisible() ) 0719 hide(); 0720 0721 deleteLater(); 0722 } 0723 0724 void 0725 CoverManager::setStatusText( const QString &text ) 0726 { 0727 m_oldStatusText = m_statusLabel->text(); 0728 m_statusLabel->setText( text ); 0729 } 0730 0731 ////////////////////////////////////////////////////////////////////// 0732 // CLASS CoverView 0733 ///////////////////////////////////////////////////////////////////// 0734 0735 CoverView::CoverView( QWidget *parent, const char *name, Qt::WindowFlags f ) 0736 : QListWidget( parent ) 0737 { 0738 DEBUG_BLOCK 0739 0740 setObjectName( name ); 0741 setWindowFlags( f ); 0742 setViewMode( QListView::IconMode ); 0743 setMovement( QListView::Static ); 0744 setResizeMode( QListView::Adjust ); 0745 setSelectionMode( QAbstractItemView::ExtendedSelection ); 0746 setWrapping( true ); 0747 setWordWrap( true ); 0748 setIconSize( QSize(100, 100) ); 0749 setGridSize( QSize(120, 160) ); 0750 setTextElideMode( Qt::ElideRight ); 0751 setContextMenuPolicy( Qt::DefaultContextMenu ); 0752 setMouseTracking( true ); // required for setting status text when itemEntered signal is emitted 0753 0754 connect( this, &CoverView::itemEntered, this, &CoverView::setStatusText ); 0755 connect( this, &CoverView::viewportEntered, CoverManager::instance(), &CoverManager::updateStatusBar ); 0756 } 0757 0758 void 0759 CoverView::contextMenuEvent( QContextMenuEvent *event ) 0760 { 0761 QList<QListWidgetItem*> items = selectedItems(); 0762 const int itemsCount = items.count(); 0763 0764 QMenu menu; 0765 menu.addSection( i18n( "Cover Image" ) ); 0766 0767 if( itemsCount == 1 ) 0768 { 0769 // only one item selected: get all custom actions this album is capable of. 0770 CoverViewItem *item = dynamic_cast<CoverViewItem*>( items.first() ); 0771 QList<QAction *> actions; 0772 Meta::AlbumPtr album = item->albumPtr(); 0773 if( album ) 0774 { 0775 QScopedPointer<Capabilities::ActionsCapability> ac( album->create<Capabilities::ActionsCapability>() ); 0776 if( ac ) 0777 { 0778 actions = ac->actions(); 0779 foreach( QAction *action, actions ) 0780 menu.addAction( action ); 0781 } 0782 } 0783 menu.exec( event->globalPos() ); 0784 } 0785 else if( itemsCount > 1 ) 0786 { 0787 // multiple albums selected: only unset cover and fetch cover actions 0788 // make sense here, and perhaps (un)setting compilation flag (TODO). 0789 0790 Meta::AlbumList unsetAlbums; 0791 Meta::AlbumList fetchAlbums; 0792 0793 foreach( QListWidgetItem *item, items ) 0794 { 0795 CoverViewItem *cvItem = dynamic_cast<CoverViewItem*>(item); 0796 Meta::AlbumPtr album = cvItem->albumPtr(); 0797 if( album ) 0798 { 0799 QScopedPointer<Capabilities::ActionsCapability> ac( album->create<Capabilities::ActionsCapability>() ); 0800 if( ac ) 0801 { 0802 QList<QAction *> actions = ac->actions(); 0803 foreach( QAction *action, actions ) 0804 { 0805 if( qobject_cast<FetchCoverAction*>(action) ) 0806 fetchAlbums << album; 0807 else if( qobject_cast<UnsetCoverAction*>(action) ) 0808 unsetAlbums << album; 0809 } 0810 } 0811 } 0812 } 0813 0814 if( itemsCount == fetchAlbums.count() ) 0815 { 0816 FetchCoverAction *fetchAction = new FetchCoverAction( this, fetchAlbums ); 0817 menu.addAction( fetchAction ); 0818 } 0819 if( itemsCount == unsetAlbums.count() ) 0820 { 0821 UnsetCoverAction *unsetAction = new UnsetCoverAction( this, unsetAlbums ); 0822 menu.addAction( unsetAction ); 0823 } 0824 menu.exec( event->globalPos() ); 0825 } 0826 else 0827 QListView::contextMenuEvent( event ); 0828 0829 // TODO: Play, Load and Append to playlist actions 0830 } 0831 0832 void 0833 CoverView::setStatusText( QListWidgetItem *item ) 0834 { 0835 #define itemmacro static_cast<CoverViewItem *>( item ) 0836 if ( !itemmacro ) 0837 return; 0838 0839 const QString artist = itemmacro->albumPtr()->isCompilation() ? i18n( "Various Artists" ) : itemmacro->artist(); 0840 const QString tipContent = i18n( "%1 - %2", artist , itemmacro->album() ); 0841 CoverManager::instance()->setStatusText( tipContent ); 0842 #undef item 0843 } 0844 0845 ////////////////////////////////////////////////////////////////////// 0846 // CLASS CoverViewItem 0847 ///////////////////////////////////////////////////////////////////// 0848 0849 CoverViewItem::CoverViewItem( QListWidget *parent, Meta::AlbumPtr album ) 0850 : QListWidgetItem( parent ) 0851 , m_albumPtr( album) 0852 { 0853 m_album = album->prettyName(); 0854 if( album->hasAlbumArtist() ) 0855 m_artist = album->albumArtist()->prettyName(); 0856 else 0857 m_artist = i18n( "No Artist" ); 0858 setText( album->prettyName() ); 0859 0860 loadCover(); 0861 0862 CoverManager::instance()->subscribeTo( album ); 0863 } 0864 0865 CoverViewItem::~CoverViewItem() 0866 {} 0867 0868 bool 0869 CoverViewItem::hasCover() const 0870 { 0871 return albumPtr()->hasImage(); 0872 } 0873 0874 void 0875 CoverViewItem::loadCover() 0876 { 0877 const bool isSuppressing = m_albumPtr->suppressImageAutoFetch(); 0878 m_albumPtr->setSuppressImageAutoFetch( true ); 0879 setIcon( QPixmap::fromImage( m_albumPtr->image( 100 ) ) ); 0880 m_albumPtr->setSuppressImageAutoFetch( isSuppressing ); 0881 } 0882 0883 void 0884 CoverViewItem::dragEntered() 0885 { 0886 setSelected( true ); 0887 } 0888 0889 0890 void 0891 CoverViewItem::dragLeft() 0892 { 0893 setSelected( false ); 0894 } 0895 0896