File indexing completed on 2025-01-05 04:25:43

0001 /****************************************************************************************
0002  * Copyright (c) 2009-2010 Joffrey Clavel <jclavel@clabert.info>                        *
0003  * Copyright (c) 2010 Alexandre Mendes <alex.mendes1988@gmail.com>                      *
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 #define DEBUG_PREFIX "ArtistWidget"
0019 
0020 #include "ArtistWidget.h"
0021 
0022 //Amarok
0023 #include "PaletteHandler.h"
0024 #include "SvgHandler.h"
0025 #include "amarokurls/AmarokUrl.h"
0026 #include "core/collections/Collection.h"
0027 #include "core/collections/QueryMaker.h"
0028 #include "core/meta/Meta.h"
0029 #include "core/support/Amarok.h"
0030 #include "core/support/Debug.h"
0031 #include "core-impl/collections/support/CollectionManager.h"
0032 #include "playlist/PlaylistController.h"
0033 
0034 //KDE
0035 #include <KColorUtils>
0036 #include <KGlobalSettings>
0037 #include <QIcon>
0038 #include <Plasma/PushButton>
0039 #include <Plasma/Separator>
0040 
0041 //Qt
0042 #include <QDesktopServices>
0043 #include <QFontMetricsF>
0044 #include <QGraphicsGridLayout>
0045 #include <QGraphicsLinearLayout>
0046 #include <QLabel>
0047 #include <QPainter>
0048 #include <QPixmapCache>
0049 #include <QSignalMapper>
0050 #include <QTextDocument>
0051 #include <QTimer>
0052 
0053 #include <cmath>
0054 
0055 ArtistWidget::ArtistWidget( const SimilarArtistPtr &artist,
0056                             QGraphicsWidget *parent, Qt::WindowFlags wFlags )
0057     : QGraphicsWidget( parent, wFlags )
0058     , m_artist( artist )
0059 {
0060     setAttribute( Qt::WA_NoSystemBackground, true );
0061 
0062     m_image = new QLabel;
0063     m_image->setAttribute( Qt::WA_NoSystemBackground, true );
0064     m_image->setFixedSize( 128, 128 );
0065     m_image->setCursor( Qt::PointingHandCursor );
0066     QGraphicsProxyWidget *imageProxy = new QGraphicsProxyWidget( this );
0067     imageProxy->setWidget( m_image );
0068     m_image->installEventFilter( this );
0069 
0070     m_nameLabel = new QLabel;
0071     m_match     = new QLabel;
0072     m_tagsLabel = new QLabel;
0073     m_topTrackLabel = new QLabel;
0074     m_bio = new QGraphicsWidget( this );
0075 
0076     QGraphicsProxyWidget *nameProxy     = new QGraphicsProxyWidget( this );
0077     QGraphicsProxyWidget *matchProxy    = new QGraphicsProxyWidget( this );
0078     QGraphicsProxyWidget *topTrackProxy = new QGraphicsProxyWidget( this );
0079     QGraphicsProxyWidget *tagsProxy     = new QGraphicsProxyWidget( this );
0080     nameProxy->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Preferred );
0081     matchProxy->setSizePolicy( QSizePolicy::Maximum, QSizePolicy::Preferred );
0082     topTrackProxy->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Preferred );
0083     tagsProxy->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Preferred );
0084     imageProxy->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Preferred );
0085 
0086     nameProxy->setWidget( m_nameLabel );
0087     matchProxy->setWidget( m_match );
0088     topTrackProxy->setWidget( m_topTrackLabel );
0089     tagsProxy->setWidget( m_tagsLabel );
0090 
0091     m_nameLabel->setAttribute( Qt::WA_NoSystemBackground );
0092     m_match->setAttribute( Qt::WA_NoSystemBackground );
0093     m_topTrackLabel->setAttribute( Qt::WA_NoSystemBackground );
0094     m_tagsLabel->setAttribute( Qt::WA_NoSystemBackground );
0095 
0096     m_image->setAlignment( Qt::AlignCenter );
0097     m_match->setAlignment( Qt::AlignRight | Qt::AlignVCenter );
0098     m_nameLabel->setAlignment( Qt::AlignLeft | Qt::AlignVCenter );
0099     m_topTrackLabel->setAlignment( Qt::AlignLeft | Qt::AlignVCenter );
0100     m_tagsLabel->setAlignment( Qt::AlignLeft | Qt::AlignVCenter );
0101 
0102     m_nameLabel->setWordWrap( false );
0103     m_match->setWordWrap( false );
0104     m_topTrackLabel->setWordWrap( false );
0105     m_tagsLabel->setWordWrap( false );
0106 
0107     m_match->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Preferred );
0108     m_topTrackLabel->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Preferred );
0109     m_match->setMinimumWidth( 10 );
0110     m_topTrackLabel->setMinimumWidth( 10 );
0111     m_nameLabel->setMinimumWidth( 10 );
0112     m_tagsLabel->setMinimumWidth( 10 );
0113 
0114     QFontMetricsF fm( font() );
0115     m_bio->setMinimumHeight( fm.lineSpacing() * 5 );
0116     m_bio->setMaximumHeight( fm.lineSpacing() * 5 );
0117     m_bio->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Preferred );
0118     m_bioLayout.setCacheEnabled( true );
0119 
0120     QFont artistFont;
0121     artistFont.setPointSize( artistFont.pointSize() + 2 );
0122     artistFont.setBold( true );
0123     m_nameLabel->setFont( artistFont );
0124     m_topTrackLabel->setFont( KGlobalSettings::smallestReadableFont() );
0125     m_tagsLabel->setFont( KGlobalSettings::smallestReadableFont() );
0126     m_match->setFont( KGlobalSettings::smallestReadableFont() );
0127 
0128     m_navigateButton = new Plasma::PushButton( this );
0129     m_navigateButton->setMaximumSize( QSizeF( 22, 22 ) );
0130     m_navigateButton->setIcon( QIcon::fromTheme( "edit-find" ) );
0131     m_navigateButton->setToolTip( i18n( "Show in Media Sources" ) );
0132     connect( m_navigateButton, SIGNAL(clicked()), this, SLOT(navigateToArtist()) );
0133     
0134     m_lastfmStationButton = new Plasma::PushButton( this );
0135     m_lastfmStationButton->setMaximumSize( QSizeF( 22, 22 ) );
0136     m_lastfmStationButton->setIcon( QIcon::fromTheme("view-services-lastfm-amarok") );
0137     m_lastfmStationButton->setToolTip( i18n( "Add Last.fm artist station to the Playlist" ) );
0138     connect( m_lastfmStationButton, SIGNAL(clicked()), this, SLOT(addLastfmArtistStation()) );
0139 
0140     m_topTrackButton = new Plasma::PushButton( this );
0141     m_topTrackButton->setMaximumSize( QSizeF( 22, 22 ) );
0142     m_topTrackButton->setIcon( QIcon::fromTheme( "media-track-add-amarok" ) );
0143     m_topTrackButton->setToolTip( i18n( "Add top track to the Playlist" ) );
0144     m_topTrackButton->hide();
0145     connect( m_topTrackButton, SIGNAL(clicked()), this, SLOT(addTopTrackToPlaylist()) );
0146 
0147     m_similarArtistButton = new Plasma::PushButton( this );
0148     m_similarArtistButton->setMaximumSize( QSizeF( 22, 22 ) );
0149     m_similarArtistButton->setIcon( QIcon::fromTheme( "similarartists-amarok" ) );
0150     m_similarArtistButton->setToolTip( i18n( "Show Similar Artists to %1", m_artist->name() ) );
0151     connect( m_similarArtistButton, SIGNAL(clicked()), this, SIGNAL(showSimilarArtists()) );
0152 
0153     QGraphicsLinearLayout *buttonsLayout = new QGraphicsLinearLayout( Qt::Horizontal );
0154     buttonsLayout->setSizePolicy( QSizePolicy::Maximum, QSizePolicy::Preferred );
0155     buttonsLayout->addItem( m_topTrackButton );
0156     buttonsLayout->addItem( m_navigateButton );
0157     buttonsLayout->addItem( m_lastfmStationButton );
0158 
0159     QString artistUrl = m_artist->url().url();
0160     if( !artistUrl.isEmpty() )
0161     {
0162         m_urlButton = new Plasma::PushButton( this );
0163         m_urlButton->setMaximumSize( QSizeF( 22, 22 ) );
0164         m_urlButton->setIcon( QIcon::fromTheme("internet-services") );
0165         m_urlButton->setToolTip( i18n( "Open Last.fm webpage for this artist" ) );
0166         connect( m_urlButton, SIGNAL(clicked()), this, SLOT(openArtistUrl()) );
0167         buttonsLayout->addItem( m_urlButton );
0168     }
0169 
0170     buttonsLayout->addItem( m_similarArtistButton );
0171 
0172     // the image display is extended on two row
0173     m_layout = new QGraphicsGridLayout( this );
0174     m_layout->addItem( imageProxy, 0, 0, 4, 1 );
0175     m_layout->addItem( nameProxy, 0, 1 );
0176     m_layout->addItem( buttonsLayout, 0, 2, Qt::AlignRight );
0177     m_layout->addItem( topTrackProxy, 1, 1 );
0178     m_layout->addItem( matchProxy, 1, 2, Qt::AlignRight );
0179     m_layout->addItem( tagsProxy, 2, 1, 1, 2 );
0180     m_layout->addItem( m_bio, 3, 1, 1, 2 );
0181 
0182     m_match->setText( i18n( "Match: %1%", QString::number( m_artist->match() ) ) );
0183     m_nameLabel->setText( m_artist->name() );
0184 
0185     QTimer::singleShot( 0, this, SLOT(updateInfo()) );
0186 }
0187 
0188 void
0189 ArtistWidget::updateInfo()
0190 {
0191     fetchPhoto();
0192     fetchInfo();
0193     fetchTopTrack();
0194 }
0195 
0196 ArtistWidget::~ArtistWidget()
0197 {
0198     clear();
0199 }
0200 
0201 bool
0202 ArtistWidget::eventFilter( QObject *obj, QEvent *event )
0203 {
0204     if( obj == m_image )
0205     {
0206         if( event->type() == QEvent::MouseButtonPress )
0207         {
0208             emit showBio();
0209             event->accept();
0210             return true;
0211         }
0212     }
0213     return QGraphicsWidget::eventFilter( obj, event );
0214 }
0215 
0216 void
0217 ArtistWidget::fetchPhoto()
0218 {
0219     // display a message for the user while the fetch of the picture
0220     m_image->clear();
0221 
0222     QPixmap image;
0223     if( QPixmapCache::find( m_artist->urlImage().url(), &image ) )
0224     {
0225         m_image->setPixmap( image );
0226         return;
0227     }
0228     m_image->setPixmap( Amarok::semiTransparentLogo( 120 ) );
0229 
0230     if( m_artist->urlImage().isEmpty() )
0231         return;
0232 
0233     The::networkAccessManager()->getData( m_artist->urlImage(), this,
0234          SLOT(photoFetched(QUrl,QByteArray,NetworkAccessManagerProxy::Error)) );
0235 }
0236 
0237 void
0238 ArtistWidget::fetchInfo()
0239 {
0240     // we generate the url for the demand on the lastFM Api
0241     QUrl url;
0242     url.setScheme( "http" );
0243     url.setHost( "ws.audioscrobbler.com" );
0244     url.setPath( "/2.0/" );
0245     url.addQueryItem( "method", "artist.getInfo" );
0246     url.addQueryItem( "api_key", Amarok::lastfmApiKey() );
0247     url.addQueryItem( "artist", m_artist->name() );
0248 
0249     The::networkAccessManager()->getData( url, this,
0250          SLOT(parseInfo(QUrl,QByteArray,NetworkAccessManagerProxy::Error)) );
0251 }
0252 
0253 void
0254 ArtistWidget::fetchTopTrack()
0255 {
0256     // we generate the url for the demand on the lastFM Api
0257     QUrl url;
0258     url.setScheme( "http" );
0259     url.setHost( "ws.audioscrobbler.com" );
0260     url.setPath( "/2.0/" );
0261     url.addQueryItem( "method", "artist.getTopTracks" );
0262     url.addQueryItem( "api_key", Amarok::lastfmApiKey() );
0263     url.addQueryItem( "artist",  m_artist->name() );
0264 
0265     The::networkAccessManager()->getData( url, this,
0266          SLOT(parseTopTrack(QUrl,QByteArray,NetworkAccessManagerProxy::Error)) );
0267 }
0268 
0269 void
0270 ArtistWidget::photoFetched( const QUrl &url, QByteArray data, NetworkAccessManagerProxy::Error e )
0271 {
0272     if( url != m_artist->urlImage() )
0273         return;
0274 
0275     if( e.code != QNetworkReply::NoError )
0276     {
0277         m_image->clear();
0278         m_image->setText( i18n( "Unable to fetch the picture: %1", e.description ) );
0279         return;
0280     }
0281 
0282     QPixmap image;
0283     if( image.loadFromData( data ) )
0284     {
0285         image = image.scaled( 116, 116, Qt::KeepAspectRatio, Qt::SmoothTransformation );
0286         image = The::svgHandler()->addBordersToPixmap( image, 6, QString(), true );
0287         m_image->setToolTip( i18nc( "@info:tooltip Artist biography", "Show Biography" ) );
0288         m_image->setPixmap( image );
0289         QPixmapCache::insert( url.url(), image );
0290     }
0291 }
0292 
0293 void
0294 ArtistWidget::parseInfo( const QUrl &url, QByteArray data, NetworkAccessManagerProxy::Error e )
0295 {
0296     Q_UNUSED( url )
0297     if( e.code != QNetworkReply::NoError )
0298         return;
0299 
0300     if( data.isEmpty() )
0301         return;
0302 
0303     QXmlStreamReader xml( data );
0304     xml.readNextStartElement(); // lfm
0305     if( xml.attributes().value(QLatin1String("status")) != QLatin1String("ok") )
0306     {
0307         setBioSummary( QString() );
0308         return;
0309     }
0310 
0311     QString summary;
0312     xml.readNextStartElement(); // artist
0313     while( xml.readNextStartElement() )
0314     {
0315         if( xml.name() == QLatin1String("tags") )
0316         {
0317             m_tags.clear();
0318             while( xml.readNextStartElement() )
0319             {
0320                 if( xml.name() != QLatin1String("tag") )
0321                     continue;
0322 
0323                 while( xml.readNextStartElement() )
0324                 {
0325                     if( xml.name() == QLatin1String("name") )
0326                         m_tags << xml.readElementText();
0327                     else
0328                         xml.skipCurrentElement();
0329                 }
0330             }
0331         }
0332         else if( xml.name() == QLatin1String("bio") )
0333         {
0334             while( xml.readNextStartElement() )
0335             {
0336                 if( xml.name() == QLatin1String("published") )
0337                     m_fullBio.first = KDateTime::fromString( xml.readElementText(), "%a, %d %b %Y %H:%M:%S" );
0338                 else if( xml.name() == QLatin1String("summary") )
0339                     summary = xml.readElementText().simplified();
0340                 else if( xml.name() == QLatin1String("content") )
0341                     m_fullBio.second = xml.readElementText().replace( QRegExp("\n+"), QLatin1String("<br>") );
0342                 else
0343                     xml.skipCurrentElement();
0344             }
0345         }
0346         else
0347             xml.skipCurrentElement();
0348     }
0349     setBioSummary( summary );
0350     setTags();
0351 }
0352 
0353 void
0354 ArtistWidget::parseTopTrack( const QUrl &url, QByteArray data, NetworkAccessManagerProxy::Error e )
0355 {
0356     Q_UNUSED( url )
0357     if( e.code != QNetworkReply::NoError )
0358         return;
0359 
0360     if( data.isEmpty() )
0361         return;
0362 
0363     QXmlStreamReader xml( data );
0364     xml.readNextStartElement(); // lfm
0365     if( xml.attributes().value(QLatin1String("status")) != QLatin1String("ok") )
0366     {
0367         setTopTrack( QString() );
0368         return;
0369     }
0370 
0371     QString topTrack;
0372     xml.readNextStartElement(); // toptracks
0373     while( xml.readNextStartElement() )
0374     {
0375         if( xml.name() != QLatin1String("track") )
0376         {
0377             xml.skipCurrentElement();
0378             continue;
0379         }
0380 
0381         while( xml.readNextStartElement() )
0382         {
0383             if( xml.name() != QLatin1String("name") )
0384             {
0385                 xml.skipCurrentElement();
0386                 continue;
0387             }
0388             topTrack = xml.readElementText();
0389             break;
0390         }
0391 
0392         if( !topTrack.isEmpty() )
0393             break;
0394     }
0395     setTopTrack( topTrack );
0396 }
0397 
0398 SimilarArtistPtr
0399 ArtistWidget::artist() const
0400 {
0401     return m_artist;
0402 }
0403 
0404 void
0405 ArtistWidget::clear()
0406 {
0407     m_image->clear();
0408     m_nameLabel->clear();
0409     m_match->clear();
0410     m_topTrackLabel->clear();
0411 }
0412 
0413 void
0414 ArtistWidget::openArtistUrl()
0415 {
0416     // somehow Last.fm decides to supply this url without the scheme
0417     QUrl artistUrl = QString( "http://%1" ).arg( m_artist->url().url() );
0418     if( artistUrl.isValid() )
0419         QDesktopServices::openUrl( artistUrl );
0420 }
0421 
0422 void
0423 ArtistWidget::setBioSummary( const QString &bio )
0424 {
0425     if( bio.isEmpty() )
0426     {
0427         m_bioLayout.clearLayout();
0428         m_bioLayout.setText( i18n( "No description available." ) );
0429     }
0430     else
0431     {
0432         QTextDocument doc;
0433         doc.setHtml( bio );
0434         QString plain = doc.toPlainText();
0435         m_bioLayout.setText( plain );
0436     }
0437     layoutBio();
0438 }
0439 
0440 void
0441 ArtistWidget::setTags()
0442 {
0443     QString tags = m_tags.isEmpty() ? i18n( "none" ) : m_tags.join( QLatin1String(", ") );
0444     QString label = i18nc( "@label:textbox", "Tags: %1", tags );
0445     m_tagsLabel->setText( label );
0446 }
0447 
0448 void
0449 ArtistWidget::setTopTrack( const QString &topTrack )
0450 {
0451     if( topTrack.isEmpty() )
0452     {
0453         m_topTrackLabel->setText( i18n("Top track not found") );
0454         m_topTrackButton->hide();
0455     }
0456     else
0457     {
0458         m_topTrackTitle = topTrack;
0459         m_topTrackLabel->setText( i18n("Top track: %1", topTrack) );
0460 
0461         Collections::QueryMaker *qm = CollectionManager::instance()->queryMaker();
0462 
0463         qm->setQueryType( Collections::QueryMaker::Track );
0464         qm->beginAnd();
0465         qm->addFilter( Meta::valArtist, m_nameLabel->text() );
0466         qm->addFilter( Meta::valTitle, m_topTrackTitle );
0467         qm->endAndOr();
0468         qm->limitMaxResultSize( 1 );
0469         qm->setAutoDelete( true );
0470 
0471         connect( qm, SIGNAL(newResultReady(Meta::TrackList)),
0472                  SLOT(resultReady(Meta::TrackList)) );
0473          
0474         qm->run();
0475     }
0476 }
0477 
0478 void
0479 ArtistWidget::resizeEvent( QGraphicsSceneResizeEvent *event )
0480 {
0481     QGraphicsWidget::resizeEvent( event );
0482     layoutBio();
0483     QFontMetrics fm( m_match->font() );
0484     m_match->setMaximumWidth( fm.horizontalAdvance( m_match->text() ) );
0485 }
0486 
0487 void
0488 ArtistWidget::paint( QPainter *p, const QStyleOptionGraphicsItem *option, QWidget *widget )
0489 {
0490     QGraphicsWidget::paint( p, option, widget );
0491 
0492     p->save();
0493     QFontMetricsF fm( m_bio->font() );
0494     QPointF pos = m_bio->geometry().topLeft();
0495     const int maxLines = floor( m_bio->size().height() / fm.lineSpacing() );
0496     for( int i = 0, lines = m_bioLayout.lineCount(); i < lines; ++i )
0497     {
0498         const QTextLine &line = m_bioLayout.lineAt( i );
0499         if( m_bioCropped && (i == (maxLines - 1)) )
0500         {
0501             // fade out the last bit of text if not all of the bio is shown
0502             QLinearGradient alphaGradient( 0, 0, 1, 0 );
0503             alphaGradient.setCoordinateMode( QGradient::ObjectBoundingMode );
0504             const QColor &textColor = The::paletteHandler()->palette().text().color();
0505             alphaGradient.setColorAt( 0, textColor );
0506             alphaGradient.setColorAt( 0.85, textColor );
0507             alphaGradient.setColorAt( 1, Qt::transparent );
0508             QPen pen = p->pen();
0509             pen.setBrush( alphaGradient );
0510             p->setPen( pen );
0511         }
0512         line.draw( p, pos );
0513         pos.ry() += line.leading();
0514     }
0515     p->restore();
0516 }
0517 
0518 void
0519 ArtistWidget::layoutBio()
0520 {
0521     QFontMetricsF fm( m_bio->font() );
0522     QRectF geom = m_bio->geometry();
0523     int maxLines = floor( m_bio->size().height() / fm.lineSpacing() );
0524     int leading = fm.leading();
0525     qreal height = 0;
0526     m_bioCropped = true;
0527     m_bioLayout.clearLayout();
0528     m_bioLayout.beginLayout();
0529     while( m_bioLayout.lineCount() < maxLines )
0530     {
0531         QTextLine line = m_bioLayout.createLine();
0532         if( !line.isValid() )
0533         {
0534             m_bioCropped = false;
0535             break;
0536         }
0537 
0538         line.setLineWidth( geom.width() );
0539         height += leading;
0540         line.setPosition( QPointF(0, height) );
0541         height += line.height();
0542     }
0543     m_bioLayout.endLayout();
0544     update();
0545 }
0546 
0547 KDateTime
0548 ArtistWidget::bioPublished() const
0549 {
0550     return m_fullBio.first;
0551 }
0552 
0553 QString
0554 ArtistWidget::fullBio() const
0555 {
0556     return m_fullBio.second;
0557 }
0558 
0559 void
0560 ArtistWidget::addTopTrackToPlaylist()
0561 {
0562     The::playlistController()->insertOptioned( m_topTrack, Playlist::OnAppendToPlaylistAction );
0563 }
0564 
0565 void
0566 ArtistWidget::navigateToArtist()
0567 {
0568     AmarokUrl url;
0569     url.setCommand( "navigate" );
0570     url.setPath( "collections" );
0571     url.setArg( "filter", "artist:\"" + AmarokUrl::escape( m_artist->name() ) + '\"' );
0572     url.run();
0573 }
0574 
0575 void
0576 ArtistWidget::addLastfmArtistStation()
0577 {
0578     const QString url = "lastfm://artist/" + m_artist->name() + "/similarartists";
0579     Meta::TrackPtr lastfmtrack = CollectionManager::instance()->trackForUrl( QUrl( url ) );
0580     The::playlistController()->insertOptioned( lastfmtrack, Playlist::OnAppendToPlaylistAction );
0581 }
0582 
0583 ArtistsListWidget::ArtistsListWidget( QGraphicsWidget *parent )
0584     : Plasma::ScrollWidget( parent )
0585     , m_separatorCount( 0 )
0586 {
0587     QGraphicsWidget *content = new QGraphicsWidget( this );
0588     m_layout = new QGraphicsLinearLayout( Qt::Vertical, content );
0589     setWidget( content );
0590 
0591     m_showArtistsSigMapper = new QSignalMapper( this );
0592     connect( m_showArtistsSigMapper, SIGNAL(mapped(QString)), SIGNAL(showSimilarArtists(QString)) );
0593 
0594     m_showBioSigMapper = new QSignalMapper( this );
0595     connect( m_showBioSigMapper, SIGNAL(mapped(QString)), SIGNAL(showBio(QString)) );
0596 }
0597 
0598 ArtistsListWidget::~ArtistsListWidget()
0599 {
0600     clear();
0601 }
0602 
0603 int
0604 ArtistsListWidget::count() const
0605 {
0606     return m_layout->count() - m_separatorCount;
0607 }
0608 
0609 void
0610 ArtistsListWidget::addArtist( const SimilarArtistPtr &artist )
0611 {
0612     if( !m_widgets.isEmpty() )
0613         addSeparator();
0614     ArtistWidget *widget = new ArtistWidget( artist );
0615     const QString &name = artist->name();
0616     connect( widget, SIGNAL(showSimilarArtists()), m_showArtistsSigMapper, SLOT(map()) );
0617     m_showArtistsSigMapper->setMapping( widget, name );
0618     connect( widget, SIGNAL(showBio()), m_showBioSigMapper, SLOT(map()) );
0619     m_showBioSigMapper->setMapping( widget, name );
0620     m_layout->addItem( widget );
0621     m_widgets << widget;
0622 }
0623 
0624 void
0625 ArtistsListWidget::addArtists( const SimilarArtist::List &artists )
0626 {
0627     foreach( const SimilarArtistPtr &artist, artists )
0628         addArtist( artist );
0629     updateGeometry();
0630 }
0631 
0632 void
0633 ArtistsListWidget::addSeparator()
0634 {
0635     m_layout->addItem( new Plasma::Separator );
0636     ++m_separatorCount;
0637 }
0638 
0639 void
0640 ArtistsListWidget::clear()
0641 {
0642     qDeleteAll( m_widgets );
0643     m_widgets.clear();
0644     int count = m_layout->count();
0645     if( count > 0 )
0646     {
0647         while( --count >= 0 )
0648         {
0649             QGraphicsLayoutItem *child = m_layout->itemAt( 0 );
0650             m_layout->removeItem( child );
0651             delete child;
0652         }
0653         m_separatorCount = 0;
0654     }
0655     m_layout->invalidate();
0656     updateGeometry();
0657 }
0658 
0659 QSizeF
0660 ArtistsListWidget::sizeHint( Qt::SizeHint which, const QSizeF &constraint ) const
0661 {
0662     QSizeF sz = Plasma::ScrollWidget::sizeHint( which, constraint );
0663     if( count() == 0 )
0664         sz.rheight() = 0;
0665     return sz;
0666 }
0667 
0668 bool
0669 ArtistsListWidget::isEmpty() const
0670 {
0671     return count() == 0;
0672 }
0673 
0674 QString
0675 ArtistsListWidget::name() const
0676 {
0677     return m_name;
0678 }
0679 
0680 void
0681 ArtistsListWidget::setName( const QString &name )
0682 {
0683     m_name = name;
0684 }
0685 
0686 ArtistWidget *
0687 ArtistsListWidget::widget( const QString &artistName )
0688 {
0689     foreach( ArtistWidget *widget, m_widgets )
0690     {
0691         if( widget->artist()->name() == artistName )
0692             return widget;
0693     }
0694     return 0;
0695 }
0696 
0697 void
0698 ArtistWidget::resultReady( const Meta::TrackList &tracks )
0699 {
0700     if( !tracks.isEmpty() )
0701     {
0702         m_topTrack = tracks.first();
0703         m_topTrackButton->show();
0704     }
0705 }
0706