1 // (c) Pierpaolo Di Panfilo 2004
2 // (c) 2005 Isaiah Damron <xepo@trifault.net>
3 // See COPYING file for licensing information
5 #include "CoverManager.h"
8 #include "amarokconfig.h"
9 #include "collection/BlockingQuery.h"
10 #include "collection/Collection.h"
11 #include "CollectionManager.h"
12 #include "browserToolBar.h"
14 #include "meta/Meta.h"
15 #include "QueryMaker.h"
16 #include "config-amarok.h"
17 #include "PixmapViewer.h"
18 #include "playlist/PlaylistModel.h"
20 #include "TheInstances.h"
22 #include <k3multipledrag.h>
23 #include <k3urldrag.h>
24 #include <KApplication>
27 #include <KFileDialog>
28 #include <KIconLoader>
29 #include <KIO/NetAccess>
32 #include <KMenu> //showCoverMenu()
33 #include <KMessageBox> //showCoverMenu()
34 #include <KPushButton>
35 #include <KSqueezedTextLabel> //status label
36 #include <KStandardDirs> //KGlobal::dirs()
41 #include <KWindowSystem>
43 #include <QDesktopWidget> //ctor: desktop size
45 #include <QFontMetrics> //paintItem()
49 #include <QObject> //used to delete all cover fetchers
50 #include <QPainter> //paintItem()
51 #include <QPalette> //paintItem()
54 #include <QProgressBar>
55 #include <QProgressDialog>
57 #include <QStringList>
59 #include <QTimer> //search filter timer
62 #include <QToolButton>
63 #include <QTreeWidget>
64 #include <QTreeWidgetItem>
66 static QString artistToSelectInInitFunction
;
67 CoverManager
*CoverManager::s_instance
= 0;
69 class ArtistItem
: public QTreeWidgetItem
72 ArtistItem(QTreeWidget
*parent
, Meta::ArtistPtr artist
)
73 : QTreeWidgetItem( parent
)
75 { setText( 0, artist
->prettyName() ); }
76 ArtistItem(const QString
&text
, QTreeWidget
*parent
= 0 )
77 : QTreeWidgetItem( parent
)
78 , m_artist( 0 ) { setText( 0, text
); }
80 Meta::ArtistPtr
artist() const { return m_artist
; }
83 Meta::ArtistPtr m_artist
;
86 CoverManager::CoverManager()
88 , m_timer( new QTimer( this ) ) //search filter timer
89 , m_fetchingCovers( 0 )
90 , m_coversFetched( 0 )
95 setObjectName( "TheCoverManager" );
99 // Sets caption and icon correctly (needed e.g. for GNOME)
100 kapp
->setTopWidget( this );
101 setWindowTitle( KDialog::makeStandardCaption( i18n("Cover Manager") ) );
102 setAttribute( Qt::WA_DeleteOnClose
);
103 setContentsMargins( 4, 4, 4, 4 );
106 m_artistView
= new QTreeWidget( this );
107 m_artistView
->setHeaderLabel( i18n( "Albums By" ) );
108 m_artistView
->setSortingEnabled( false );
109 m_artistView
->setTextElideMode( Qt::ElideRight
);
110 m_artistView
->setMinimumWidth( 140 );
111 m_artistView
->setColumnCount( 1 );
113 setSizes( QList
<int>() << 120 << width() - 120 );
115 ArtistItem
*item
= 0;
116 QList
<QTreeWidgetItem
*> items
;
117 item
= new ArtistItem( i18n( "All Artists" ) );
118 item
->setIcon(0, SmallIcon( Amarok::icon( "album" ) ) );
119 items
.append( item
);
122 foreach( coll
, CollectionManager::instance()->collections() )
123 if( coll
->collectionId() == "localCollection" )
125 QueryMaker
*qm
= coll
->queryMaker();
126 qm
->startArtistQuery();
127 BlockingQuery
bq( qm
);
129 Meta::ArtistList artists
= bq
.artists( coll
->collectionId() );
130 foreach( Meta::ArtistPtr artist
, artists
)
132 item
= new ArtistItem( m_artistView
, artist
);
133 item
->setIcon( 0, SmallIcon( Amarok::icon( "artist" ) ) );
134 items
.append( item
);
136 m_artistView
->insertTopLevelItems( 0, items
);
141 // ArtistItem *last = static_cast<ArtistItem *>(m_artistView->item( m_artistView->count() - 1));
143 // qb.addReturnValue( QueryBuilder::tabAlbum, QueryBuilder::valName );
144 // qb.groupBy( QueryBuilder::tabAlbum, QueryBuilder::valName );
145 // qb.setOptions( QueryBuilder::optOnlyCompilations );
146 // qb.setLimit( 0, 1 );
147 // if ( qb.run().count() ) {
148 // item = new ArtistItem( m_artistView, last, i18n( "Various Artists" ) );
149 // item->setPixmap( 0, SmallIcon("personal") );
152 KVBox
*vbox
= new KVBox( this );
153 KHBox
*hbox
= new KHBox( vbox
);
155 vbox
->setSpacing( 4 );
156 hbox
->setSpacing( 4 );
158 { //<Search LineEdit>
159 // KHBox *searchBox = new KHBox( hbox );
160 m_searchEdit
= new KLineEdit( hbox
);
161 m_searchEdit
->setClickMessage( i18n( "Enter search terms here" ) );
162 m_searchEdit
->setFrame( QFrame::Sunken
);
164 m_searchEdit
->setSizePolicy(QSizePolicy::Expanding
,QSizePolicy::Minimum
);
165 m_searchEdit
->setClearButtonShown( true );
167 m_searchEdit
->setToolTip( i18n( "Enter space-separated terms to search in the albums" ) );
169 hbox
->setStretchFactor( m_searchEdit
, 1 );
170 } //</Search LineEdit>
173 m_viewMenu
= new KMenu( this );
174 m_selectAllAlbums
= m_viewMenu
->addAction( i18n("All Albums"), this, SLOT( slotShowAllAlbums() ) );
175 m_selectAlbumsWithCover
= m_viewMenu
->addAction( i18n("Albums With Cover"), this, SLOT( slotShowAlbumsWithCover() ) );
176 m_selectAlbumsWithoutCover
= m_viewMenu
->addAction( i18n("Albums Without Cover"), this, SLOT( slotShowAlbumsWithoutCover() ) );
178 QActionGroup
*viewGroup
= new QActionGroup( this );
179 viewGroup
->setExclusive( true );
180 viewGroup
->addAction( m_selectAllAlbums
);
181 viewGroup
->addAction( m_selectAlbumsWithCover
);
182 viewGroup
->addAction( m_selectAlbumsWithoutCover
);
183 m_selectAllAlbums
->setChecked( true );
185 // amazon locale menu
186 QString locale
= AmarokConfig::amazonLocale();
187 m_currentLocale
= CoverFetcher::localeStringToID( locale
);
190 QActionGroup
*localeGroup
= new QActionGroup( this );
191 localeGroup
->setExclusive( true );
193 m_amazonLocaleMenu
= new KMenu( this );
195 a
= m_amazonLocaleMenu
->addAction( i18n("International"), this, SLOT( slotSetLocaleIntl() ) );
196 if( m_currentLocale
== CoverFetcher::International
) a
->setChecked( true );
197 localeGroup
->addAction( a
);
199 a
= m_amazonLocaleMenu
->addAction( i18n("Canada"), this, SLOT( slotSetLocaleCa() ) );
200 if( m_currentLocale
== CoverFetcher::Canada
) a
->setChecked( true );
201 localeGroup
->addAction( a
);
203 a
= m_amazonLocaleMenu
->addAction( i18n("France"), this, SLOT( slotSetLocaleFr() ) );
204 if( m_currentLocale
== CoverFetcher::France
) a
->setChecked( true );
205 localeGroup
->addAction( a
);
207 a
= m_amazonLocaleMenu
->addAction( i18n("Germany"), this, SLOT( slotSetLocaleDe() ) );
208 if( m_currentLocale
== CoverFetcher::Germany
) a
->setChecked( true );
209 localeGroup
->addAction( a
);
211 a
= m_amazonLocaleMenu
->addAction( i18n("Japan"), this, SLOT( slotSetLocaleJp() ) );
212 if( m_currentLocale
== CoverFetcher::Japan
) a
->setChecked( true );
213 localeGroup
->addAction( a
);
215 a
= m_amazonLocaleMenu
->addAction( i18n("United Kingdom"), this, SLOT( slotSetLocaleUk() ) );
216 if( m_currentLocale
== CoverFetcher::UK
) a
->setChecked( true );
217 localeGroup
->addAction( a
);
219 KToolBar
* toolBar
= new KToolBar( hbox
);
220 toolBar
->setToolButtonStyle( Qt::ToolButtonTextBesideIcon
);
222 QAction
* viewMenuAction
= new QAction( KIcon( Amarok::icon( "view_choose" ) ), i18nc( "@title buttontext for popup-menu", "View" ), this );
223 viewMenuAction
->setMenu( m_viewMenu
);
224 toolBar
->addAction( viewMenuAction
);
227 QAction
* localeMenuAction
= new QAction( KIcon( Amarok::icon( "babelfish" ) ), i18n( "Amazon Locale" ), this );
228 localeMenuAction
->setMenu( m_amazonLocaleMenu
);
229 toolBar
->addAction( localeMenuAction
);
232 //fetch missing covers button
233 m_fetchButton
= new KPushButton( KGuiItem( i18n("Fetch Missing Covers"), Amarok::icon( "download" ) ), hbox
);
234 connect( m_fetchButton
, SIGNAL(clicked()), SLOT(fetchMissingCovers()) );
238 m_coverView
= new CoverView( vbox
);
241 KStatusBar
*m_statusBar
= new KStatusBar( vbox
);
243 m_statusLabel
= new KSqueezedTextLabel( m_statusBar
);
244 m_statusLabel
->setIndent( 3 );
245 m_progressBox
= new KHBox( m_statusBar
);
247 m_statusBar
->addWidget( m_statusLabel
, 4 );
248 m_statusBar
->addPermanentWidget( m_progressBox
, 1 );
250 KPushButton
*stopButton
= new KPushButton( KGuiItem(i18n("Abort"), "stop"), m_progressBox
);
251 connect( stopButton
, SIGNAL(clicked()), SLOT(stopFetching()) );
252 m_progress
= new QProgressBar( m_progressBox
);
253 m_progress
->setTextVisible( true );
255 const int h
= m_statusLabel
->height() + 3;
256 m_statusLabel
->setFixedHeight( h
);
257 m_progressBox
->setFixedHeight( h
);
258 m_progressBox
->hide();
261 // signals and slots connections
262 connect( m_artistView
, SIGNAL(itemSelectionChanged() ),
263 SLOT( slotArtistSelected() ) );
264 /* connect( m_coverView, SIGNAL(contextMenuRequested( Q3IconViewItem*, const QPoint& )),
265 SLOT(showCoverMenu( Q3IconViewItem*, const QPoint& )) );*/
266 connect( m_coverView
, SIGNAL(itemActivated( QListWidgetItem
* )),
267 SLOT(coverItemExecuted( QListWidgetItem
* )) );
268 connect( m_timer
, SIGNAL(timeout()),
269 SLOT(slotSetFilter()) );
270 connect( m_searchEdit
, SIGNAL(textChanged( const QString
& )),
271 SLOT(slotSetFilterTimeout()) );
273 m_currentView
= AllAlbums
;
275 QSize size
= QApplication::desktop()->screenGeometry( this ).size() / 1.5;
276 QSize sz
= Amarok::config( "Cover Manager" ).readEntry( "Window Size", size
);
277 resize( sz
.width(), sz
.height() );
281 m_fetcher
= The::coverFetcher();
283 QTimer::singleShot( 0, this, SLOT(init()) );
287 CoverManager::~CoverManager()
291 Amarok::config( "Cover Manager" ).writeEntry( "Window Size", size() );
297 void CoverManager::init()
301 QTreeWidgetItem
*item
= 0;
304 if ( !artistToSelectInInitFunction
.isEmpty() )
305 for( item
= m_artistView
->invisibleRootItem()->child( 0 );
306 i
< m_artistView
->invisibleRootItem()->childCount();
307 item
= m_artistView
->invisibleRootItem()->child( i
++ ) )
308 if ( item
->text( 0 ) == artistToSelectInInitFunction
)
312 item
= m_artistView
->invisibleRootItem()->child( 0 );
314 item
->setSelected( true );
318 CoverViewDialog::CoverViewDialog( Meta::AlbumPtr album
, QWidget
*parent
)
319 : QDialog( parent
, Qt::WType_TopLevel
| Qt::WNoAutoErase
)
321 m_pixmap
= album
->image( 0 ); // full sized image
322 setAttribute( Qt::WA_DeleteOnClose
);
325 KWindowSystem::setType( winId(), NET::Utility
);
328 kapp
->setTopWidget( this );
329 setWindowTitle( KDialog::makeStandardCaption( i18n("%1 - %2",
330 album
->albumArtist()->prettyName(), album
->prettyName() ) ) );
332 m_layout
= new QHBoxLayout( this );
333 m_layout
->setSizeConstraint( QLayout::SetFixedSize
);
334 m_pixmapViewer
= new PixmapViewer( this, m_pixmap
);
335 m_layout
->addWidget( m_pixmapViewer
);
339 void CoverManager::viewCover( Meta::AlbumPtr album
, QWidget
*parent
) //static
341 //QDialog means "escape" works as expected
342 QDialog
*dialog
= new CoverViewDialog( album
, parent
);
347 QString
CoverManager::amazonTld() //static
349 if( AmarokConfig::amazonLocale() == "us" )
351 else if( AmarokConfig::amazonLocale()== "jp" )
353 else if( AmarokConfig::amazonLocale() == "uk" )
355 else if( AmarokConfig::amazonLocale() == "ca" )
358 return AmarokConfig::amazonLocale();
362 CoverManager::metadataChanged( Meta::Album
* album
)
366 ArtistItem
*selectedItem
= static_cast<ArtistItem
*>(m_artistView
->selectedItems().first());
367 if( selectedItem
->text( 0 ) != i18n( "All Artists" ) )
369 if ( album
->albumArtist() != selectedItem
->artist() )
374 foreach( CoverViewItem
*item
, m_coverItems
)
376 if( album
->name() == item
->albumPtr()->name() )
379 // Update have/missing count.
384 void CoverManager::fetchMissingCovers() //SLOT
389 for ( QListWidgetItem
*item
= m_coverView
->item( i
);
390 i
< m_coverView
->count();
391 item
= m_coverView
->item( i
++ ) )
393 CoverViewItem
*coverItem
= static_cast<CoverViewItem
*>( item
);
394 if( !coverItem
->hasCover() ) {
395 m_fetchCovers
+= coverItem
->albumPtr();
399 m_fetcher
->queueAlbums( m_fetchCovers
);
402 m_fetchButton
->setEnabled( false );
406 void CoverManager::showOnce( const QString
&artist
)
409 artistToSelectInInitFunction
= artist
;
410 new CoverManager(); //shows itself
413 s_instance
->activateWindow();
418 void CoverManager::slotArtistSelected() //SLOT
420 QTreeWidgetItem
*item
= m_artistView
->selectedItems().first();
421 ArtistItem
*artistItem
= static_cast< ArtistItem
* >(item
);
422 Meta::ArtistPtr artist
= artistItem
->artist();
425 // if( artist->prettyName().endsWith( ", The" ) )
426 // Amarok::manipulateThe( artist->prettyName(), false );
428 m_coverView
->clear();
429 m_coverItems
.clear();
431 // reset current view mode state to "AllAlbum" which is the default on artist change in left panel
432 m_currentView
= AllAlbums
;
433 m_selectAllAlbums
->trigger();
434 m_selectAllAlbums
->setChecked( true );
436 QProgressDialog
progress( this );
437 progress
.setLabelText( i18n("Loading Thumbnails...") );
438 progress
.setWindowModality( Qt::WindowModal
);
440 //this is an extra processEvent call for the sake of init() and aesthetics
442 kapp
->processEvents();
444 //this can be a bit slow
445 QApplication::setOverrideCursor( Qt::WaitCursor
);
447 Meta::AlbumList albums
;
450 foreach( coll
, CollectionManager::instance()->collections() )
451 if( coll
->collectionId() == "localCollection" )
453 QueryMaker
*qm
= coll
->queryMaker();
454 if ( item
!= m_artistView
->invisibleRootItem()->child( 0 ) )
455 qm
->addMatch( artist
);
457 qm
->startAlbumQuery();
458 BlockingQuery
bq( qm
);
460 albums
= bq
.albums( coll
->collectionId() );
464 //also retrieve compilations when we're showing all items (first treenode) or
465 //"Various Artists" (last treenode)
466 // if ( item == m_artistView->firstChild() || item == m_artistView->lastChild() )
471 // qb.addReturnValue( QueryBuilder::tabAlbum, QueryBuilder::valName );
473 // qb.excludeMatch( QueryBuilder::tabAlbum, i18n( "Unknown" ) );
474 // qb.sortBy( QueryBuilder::tabAlbum, QueryBuilder::valName );
475 // qb.setOptions( QueryBuilder::optRemoveDuplicates );
476 // qb.setOptions( QueryBuilder::optOnlyCompilations );
479 // for( int i = 0; i < cl.count(); i++ ) {
480 // albums.append( i18n( "Various Artists" ) );
481 // albums.append( cl[ i ] );
485 QApplication::restoreOverrideCursor();
487 progress
.setMaximum( albums
.count() );
489 //insert the covers first because the list view is soooo paint-happy
490 //doing it in the second loop looks really bad, unfortunately
491 //this is the slowest step in the bit that we can't process events
493 foreach( Meta::AlbumPtr album
, albums
)
495 m_coverItems
.append( new CoverViewItem( m_coverView
, album
) );
497 if ( ++x
% 50 == 0 ) {
498 progress
.setValue( x
);
499 kapp
->processEvents(); // QProgressDialog also calls this, but not always due to Qt bug!
501 //only worth testing for after processEvents() is called
502 if( progress
.wasCanceled() )
507 //now, load the thumbnails
508 QList
<QListWidgetItem
*> items
;
510 for ( QListWidgetItem
*item
= m_coverView
->item( i
);
511 i
< m_coverView
->count();
512 item
= m_coverView
->item( i
++ ) )
514 progress
.setValue( progress
.value() + 1 );
515 kapp
->processEvents();
517 if( progress
.wasCanceled() )
520 static_cast<CoverViewItem
*>(item
)->loadCover();
526 void CoverManager::showCoverMenu( QListWidgetItem
*item
, const QPoint
&p
) //SLOT
532 #define item static_cast<CoverViewItem*>(item)
537 menu
.addTitle( i18n( "Cover Image" ) );
539 QList
<CoverViewItem
*> selected
= selectedItems();
540 const int nSelected
= selected
.count();
542 QAction
* fetchSelectedAction
= new QAction( KIcon( Amarok::icon( "download" ) )
543 , ( nSelected
== 1 ? i18n( "&Fetch From amazon.%1", CoverManager::amazonTld() )
544 : i18n( "&Fetch Selected Covers" ) )
546 connect( fetchSelectedAction
, SIGNAL( triggered() ), this, SLOT( fetchSelectedCovers() ) );
548 QAction
* setCustomAction
= new QAction( KIcon( Amarok::icon( "files" ) )
549 , i18np( "Set &Custom Cover", "Set &Custom Cover for Selected Albums", nSelected
)
551 connect( setCustomAction
, SIGNAL( triggered() ), this, SLOT( setCustomSelectedCovers() ) );
552 QAction
* unsetAction
= new QAction( KIcon( Amarok::icon( "remove" ) ), i18np( "&Unset Cover", "&Unset Selected Covers", nSelected
), &menu
);
553 connect( unsetAction
, SIGNAL( triggered() ), this, SLOT ( deleteSelectedCovers() ) );
555 QAction
* playAlbumAction
= new QAction( KIcon( Amarok::icon( "add_playlist" ) )
556 , i18np( "&Append to Playlist", "&Append Selected Albums to Playlist", nSelected
)
559 connect( playAlbumAction
, SIGNAL( triggered() ), this, SLOT( playSelectedAlbums() ) );
561 if( nSelected
> 1 ) {
562 menu
.addAction( fetchSelectedAction
);
563 menu
.addAction( setCustomAction
);
564 menu
.addAction( playAlbumAction
);
565 menu
.addAction( unsetAction
);
568 QAction
* viewAction
= new QAction( KIcon( Amarok::icon( "zoom" ) ), i18n( "&Show Fullsize" ), &menu
);
569 connect( viewAction
, SIGNAL( triggered() ), this, SLOT( viewSelectedCover() ) );
570 viewAction
->setEnabled( item
->hasCover() );
571 unsetAction
->setEnabled( item
->canRemoveCover() );
572 menu
.addAction( viewAction
);
573 menu
.addAction( fetchSelectedAction
);
574 menu
.addAction( setCustomAction
);
575 menu
.addAction( playAlbumAction
);
578 menu
.addAction( unsetAction
);
587 void CoverManager::viewSelectedCover()
589 CoverViewItem
* item
= selectedItems().first();
590 viewCover( item
->albumPtr(), this );
593 void CoverManager::coverItemExecuted( QListWidgetItem
*item
) //SLOT
595 #define item static_cast<CoverViewItem*>(item)
599 item
->setSelected( true );
600 if ( item
->hasCover() )
601 viewCover( item
->albumPtr(), this );
603 m_fetcher
->manualFetch( item
->albumPtr() );
609 void CoverManager::slotSetFilter() //SLOT
611 m_filter
= m_searchEdit
->text();
613 m_coverView
->clearSelection();
615 QListWidgetItem
*item
= m_coverView
->item( i
);
618 QListWidgetItem
*tmp
= m_coverView
->item( i
+ 1 );
619 m_coverView
->takeItem( i
);
623 // m_coverView->setAutoArrange( false );
624 foreach( QListWidgetItem
*item
, m_coverItems
)
626 CoverViewItem
*coverItem
= static_cast<CoverViewItem
*>(item
);
627 if( coverItem
->album().contains( m_filter
, Qt::CaseInsensitive
) || coverItem
->artist().contains( m_filter
, Qt::CaseInsensitive
) )
628 m_coverView
->insertItem( m_coverView
->count() - 1, item
);
630 // m_coverView->setAutoArrange( true );
632 // m_coverView->arrangeItemsInGrid();
637 void CoverManager::slotSetFilterTimeout() //SLOT
639 if ( m_timer
->isActive() ) m_timer
->stop();
640 m_timer
->setSingleShot( true );
641 m_timer
->start( 180 );
644 void CoverManager::changeView( int id
) //SLOT
646 if( m_currentView
== id
) return;
648 //clear the iconview without deleting items
649 m_coverView
->clearSelection();
650 QListWidgetItem
*item
= m_coverView
->item( 0 );
653 m_coverView
->takeItem( i
);
657 // m_coverView->setAutoArrange(false );
658 foreach( QListWidgetItem
*item
, m_coverItems
)
661 CoverViewItem
*coverItem
= static_cast<CoverViewItem
*>(item
);
662 if( !m_filter
.isEmpty() ) {
663 if( !coverItem
->album().contains( m_filter
, Qt::CaseInsensitive
) && !coverItem
->artist().contains( m_filter
, Qt::CaseInsensitive
) )
667 if( id
== AllAlbums
) //show all albums
669 else if( id
== AlbumsWithCover
&& coverItem
->hasCover() ) //show only albums with cover
671 else if( id
== AlbumsWithoutCover
&& !coverItem
->hasCover() ) //show only albums without cover
674 if( show
) m_coverView
->insertItem( m_coverView
->count() - 1, item
);
676 // m_coverView->setAutoArrange( true );
678 // m_coverView->arrangeItemsInGrid();
682 void CoverManager::changeLocale( int id
) //SLOT
684 QString locale
= CoverFetcher::localeIDToString( id
);
685 AmarokConfig::setAmazonLocale( locale
);
686 m_currentLocale
= id
;
690 void CoverManager::coverFetched( const QString
&artist
, const QString
&album
) //SLOT
692 loadCover( artist
, album
);
698 void CoverManager::coverRemoved( const QString
&artist
, const QString
&album
) //SLOT
700 loadCover( artist
, album
);
706 void CoverManager::coverFetcherError()
715 void CoverManager::stopFetching()
717 Debug::Block
block( __PRETTY_FUNCTION__
);
723 void CoverManager::loadCover( const QString
&artist
, const QString
&album
)
725 foreach( QListWidgetItem
*item
, m_coverItems
)
727 CoverViewItem
*coverItem
= static_cast<CoverViewItem
*>(item
);
728 if ( album
== coverItem
->album() && ( artist
== coverItem
->artist() || ( artist
.isEmpty() && coverItem
->artist().isEmpty() ) ) )
730 coverItem
->loadCover();
736 void CoverManager::setCustomSelectedCovers()
738 //function assumes something is selected
739 CoverViewItem
* first
= selectedItems().first();
741 Meta::TrackPtr track
= first
->albumPtr()->tracks().first();
745 KUrl url
= track
->playableUrl();
746 startPath
= url
.directory();
748 KUrl file
= KFileDialog::getImageOpenUrl( startPath
, this, i18n( "Select Cover Image File" ) );
749 if ( !file
.isEmpty() ) {
750 kapp
->processEvents(); //it may takes a while so process pending events
752 QImage
image( file
.fileName() );
753 foreach( CoverViewItem
*item
, selectedItems() )
755 item
->albumPtr()->setImage( image
);
758 KIO::NetAccess::removeTempFile( tmpFile
);
762 void CoverManager::fetchSelectedCovers()
764 foreach( CoverViewItem
*item
, selectedItems() )
765 m_fetchCovers
+= item
->albumPtr();
767 m_fetchingCovers
+= selectedItems().count();
769 m_fetcher
->queueAlbums( m_fetchCovers
);
775 void CoverManager::deleteSelectedCovers()
777 QList
<CoverViewItem
*> selected
= selectedItems();
779 int button
= KMessageBox::warningContinueCancel( this,
780 i18np( "Are you sure you want to remove this cover from the Collection?",
781 "Are you sure you want to delete these %1 covers from the Collection?",
784 KStandardGuiItem::del() );
786 if ( button
== KMessageBox::Continue
) {
787 foreach( CoverViewItem
*item
, selected
) {
788 kapp
->processEvents();
789 if( item
->albumPtr()->canUpdateImage() )
791 item
->albumPtr()->setImage( QImage() );
792 coverRemoved( item
->artist(), item
->album() );
799 void CoverManager::playSelectedAlbums()
802 foreach( coll
, CollectionManager::instance()->collections() )
803 if( coll
->collectionId() == "localCollection" )
805 QueryMaker
*qm
= coll
->queryMaker();
806 foreach( CoverViewItem
*item
, selectedItems() )
808 qm
->addMatch( item
->albumPtr() );
810 The::playlistModel()->insertOptioned( qm
, Playlist::Append
);
813 QList
<CoverViewItem
*> CoverManager::selectedItems()
815 QList
<CoverViewItem
*> selectedItems
;
816 foreach( QListWidgetItem
*item
, m_coverView
->selectedItems() )
817 selectedItems
.append( static_cast<CoverViewItem
*>(item
) );
819 return selectedItems
;
823 void CoverManager::updateStatusBar()
827 //cover fetching info
828 if( m_fetchingCovers
) {
829 //update the progress bar
830 m_progress
->setMaximum( m_fetchingCovers
);
831 m_progress
->setValue( m_coversFetched
+ m_coverErrors
);
832 if( m_progressBox
->isHidden() )
833 m_progressBox
->show();
835 //update the status text
836 if( m_coversFetched
+ m_coverErrors
>= m_progress
->value() ) {
838 text
= i18n( "Finished." );
840 text
+= i18np( " Cover not found", " <b>%1</b> covers not found", m_coverErrors
);
842 m_fetchingCovers
= 0;
845 QTimer::singleShot( 2000, this, SLOT( updateStatusBar() ) );
848 if( m_fetchingCovers
== 1 ) {
849 foreach( Meta::AlbumPtr album
, m_fetchCovers
)
851 if( album
->albumArtist()->prettyName().isEmpty() )
852 text
= i18n( "Fetching cover for %1..." , album
->prettyName() );
854 text
= i18n( "Fetching cover for %1 - %2...",
855 album
->albumArtist()->prettyName(),
856 album
->prettyName() );
859 else if( m_fetchingCovers
) {
860 text
= i18np( "Fetching 1 cover: ", "Fetching <b>%1</b> covers... : ", m_fetchingCovers
);
861 if( m_coversFetched
)
862 text
+= i18np( "1 fetched", "%1 fetched", m_coversFetched
);
863 if( m_coverErrors
) {
864 if( m_coversFetched
) text
+= i18n(" - ");
865 text
+= i18np( "1 not found", "%1 not found", m_coverErrors
);
867 if( m_coversFetched
+ m_coverErrors
== 0 )
868 text
+= i18n( "Connecting..." );
875 uint totalCounter
= 0, missingCounter
= 0;
877 if( m_progressBox
->isVisible() )
878 m_progressBox
->hide();
882 for( QListWidgetItem
*item
= m_coverView
->item( i
);
883 i
< m_coverView
->count();
884 item
= m_coverView
->item( i
++ ) )
887 if( !static_cast<CoverViewItem
*>( item
)->hasCover() )
889 missingCounter
++; //counter for albums without cover
893 if( !m_filter
.isEmpty() )
894 text
= i18np( "1 result for \"%2\"", "%1 results for \"%2\"", totalCounter
, m_filter
);
895 else if( m_artistView
->selectedItems().count() > 0 ) {
896 text
= i18np( "1 album", "%1 albums", totalCounter
);
897 if( m_artistView
->selectedItems().first() != m_artistView
->invisibleRootItem()->child( 0 ) ) //showing albums by an artist
899 QString artist
= m_artistView
->selectedItems().first()->text(0);
900 if( artist
.endsWith( ", The" ) )
901 Amarok::manipulateThe( artist
, false );
902 text
+= i18n( " by " ) + artist
;
907 text
+= i18n(" - ( <b>%1</b> without cover )", missingCounter
);
909 m_fetchButton
->setEnabled( missingCounter
);
912 m_statusLabel
->setText( text
);
915 void CoverManager::setStatusText( QString text
)
917 m_oldStatusText
= m_statusLabel
->text();
918 m_statusLabel
->setText( text
);
920 //////////////////////////////////////////////////////////////////////
922 /////////////////////////////////////////////////////////////////////
924 CoverView::CoverView( QWidget
*parent
, const char *name
, Qt::WFlags f
)
925 : QListWidget( parent
)
927 Debug::Block
block( __PRETTY_FUNCTION__
);
929 setObjectName( name
);
931 setViewMode( QListView::IconMode
);
932 setMovement( QListView::Static
);
933 setResizeMode( QListView::Adjust
);
934 setSelectionMode( QAbstractItemView::ExtendedSelection
);
938 setIconSize( QSize(100,100) );
939 setTextElideMode( Qt::ElideRight
);
940 setMouseTracking( true );
942 // as long as QIconView only shows tooltips when the cursor is over the
943 // icon (and not the text), we have to create our own tooltips
944 // setShowToolTips( false );
946 connect( this, SIGNAL( itemEntered( QListWidgetItem
* ) ), SLOT( setStatusText( QListWidgetItem
* ) ) );
947 connect( this, SIGNAL( viewportEntered() ), CoverManager::instance(), SLOT( updateStatusBar() ) );
952 // Q3DragObject *CoverView::dragObject()
954 // CoverViewItem *item = static_cast<CoverViewItem*>( currentItem() );
958 // const QString sql = "SELECT tags.url FROM tags, album WHERE album.name %1 AND tags.album = album.id ORDER BY tags.track;";
959 // const QStringList values = CollectionDB::instance()->query( sql.arg( CollectionDB::likeCondition( item->album() ) ) );
962 // for( QStringList::ConstIterator it = values.begin(), end = values.end(); it != end; ++it )
965 // QString imagePath = CollectionDB::instance()->albumImage( item->artist(), item->album(), false, 1 );
966 // K3MultipleDrag *drag = new K3MultipleDrag( this );
967 // drag->setPixmap( item->coverPixmap() );
968 // drag->addDragObject( new Q3IconDrag( this ) );
969 // drag->addDragObject( new Q3ImageDrag( QImage( imagePath ) ) );
970 // drag->addDragObject( new K3URLDrag( urls ) );
975 void CoverView::setStatusText( QListWidgetItem
*item
)
977 #define item static_cast<CoverViewItem *>( item )
981 bool sampler
= false;
982 //compilations have valDummy for artist. see QueryBuilder::addReturnValue(..) for explanation
983 //FIXME: Don't rely on other independent code, use an sql query
984 if( item
->artist().isEmpty() ) sampler
= true;
986 QString tipContent
= i18n( "%1 - %2", sampler
? i18n("Various Artists") : item
->artist() , item
->album() );
988 CoverManager::instance()->setStatusText( tipContent
);
993 //////////////////////////////////////////////////////////////////////
994 // CLASS CoverViewItem
995 /////////////////////////////////////////////////////////////////////
997 CoverViewItem::CoverViewItem( QListWidget
*parent
, Meta::AlbumPtr album
)
998 : QListWidgetItem( parent
)
1000 , m_albumPtr( album
)
1003 m_album
= album
->prettyName();
1004 m_artist
= album
->albumArtist()->prettyName();
1005 setText( album
->prettyName() );
1006 setIcon( album
->image( 100 ) );
1007 album
->subscribe( qobject_cast
<CoverManager
*>(parent
->parent()->parent()) );
1008 // setDragEnabled( true );
1009 // setDropEnabled( true );
1013 CoverViewItem::~CoverViewItem()
1015 m_albumPtr
->unsubscribe( qobject_cast
<CoverManager
*>( m_parent
->parent()->parent()) );
1017 bool CoverViewItem::hasCover() const
1019 return albumPtr()->hasImage();
1022 void CoverViewItem::loadCover()
1024 m_coverPixmap
= m_albumPtr
->image(); //create the scaled cover
1025 setIcon( m_coverPixmap
);
1031 void CoverViewItem::calcRect( const QString
& )
1034 int thumbWidth
= AmarokConfig::coverPreviewSize();
1036 QFontMetrics fm
= listWidget()->fontMetrics();
1037 QRect
itemPixmapRect( 5, 1, thumbWidth
, thumbWidth
);
1038 QRect itemRect
= rect();
1039 itemRect
.setWidth( thumbWidth
+ 10 );
1040 itemRect
.setHeight( thumbWidth
+ fm
.lineSpacing() + 2 );
1041 QRect
itemTextRect( 0, thumbWidth
+2, itemRect
.width(), fm
.lineSpacing() );
1043 setPixmapRect( itemPixmapRect
);
1044 setTextRect( itemTextRect
);
1045 setItemRect( itemRect
);
1051 // void CoverViewItem::dropped( QDropEvent *e, const Q3ValueList<Q3IconDragItem> & )
1053 // if( Q3ImageDrag::canDecode( e ) ) {
1054 // if( hasCover() ) {
1055 // KGuiItem continueButton = KStandardGuiItem::cont();
1056 // continueButton.setText( i18n("&Overwrite") );
1057 // int button = KMessageBox::warningContinueCancel( iconView(),
1058 // i18n( "Are you sure you want to overwrite this cover?"),
1059 // i18n("Overwrite Confirmation"),
1060 // continueButton );
1061 // if( button == KMessageBox::Cancel )
1066 // Q3ImageDrag::decode( e, img );
1067 // m_albumPtr->setImage( img );
1073 void CoverViewItem::dragEntered()
1075 setSelected( true );
1079 void CoverViewItem::dragLeft()
1081 setSelected( false );
1084 #include "CoverManager.moc"