Beispiel #1
0
void
DynamicWidget::steeringChanged()
{
    // When steering changes, toss all the tracks that are upcoming, and re-fetch.
    // We have to find the currently playing item
    QModelIndex playing;
    for ( int i = 0; i < m_view->proxyModel()->rowCount( QModelIndex() ); ++i )
    {
        const QModelIndex  cur = m_view->proxyModel()->index( i, 0, QModelIndex() );
        TrackModelItem* item = m_view->proxyModel()->itemFromIndex( m_view->proxyModel()->mapToSource( cur ) );
        if ( item && item->isPlaying() )
        {
            playing = cur;
            break;
        }
    }
    if ( !playing.isValid() )
        return;

    const int upcoming = m_view->proxyModel()->rowCount( QModelIndex() ) - 1 - playing.row();
    tDebug() << "Removing tracks after current in station, found" << upcoming;

    QModelIndexList toRemove;
    for ( int i = playing.row() + 1; i < m_view->proxyModel()->rowCount( QModelIndex() ); i++ )
    {
        toRemove << m_view->proxyModel()->index( i, 0, QModelIndex() );
    }

    m_view->proxyModel()->remove( toRemove );

    m_playlist->generator()->fetchNext();
}
Beispiel #2
0
QMimeData*
TrackModel::mimeData( const QModelIndexList &indexes ) const
{
    qDebug() << Q_FUNC_INFO;

    QByteArray queryData;
    QDataStream queryStream( &queryData, QIODevice::WriteOnly );

    foreach ( const QModelIndex& i, indexes )
    {
        if ( i.column() > 0 )
            continue;

        QModelIndex idx = index( i.row(), 0, i.parent() );
        TrackModelItem* item = itemFromIndex( idx );
        if ( item )
        {
            const query_ptr& query = item->query();
            queryStream << qlonglong( &query );
        }
    }

    QMimeData* mimeData = new QMimeData();
    mimeData->setData( "application/tomahawk.query.list", queryData );

    return mimeData;
}
Beispiel #3
0
void
TrackView::onCustomContextMenu( const QPoint& pos )
{
    m_contextMenu->clear();

    QModelIndex idx = indexAt( pos );
    idx = idx.sibling( idx.row(), 0 );
    setContextMenuIndex( idx );

    if ( !idx.isValid() )
        return;

    if ( model() && !model()->isReadOnly() )
        m_contextMenu->setSupportedActions( m_contextMenu->supportedActions() | ContextMenu::ActionDelete );

    QList<query_ptr> queries;
    foreach ( const QModelIndex& index, selectedIndexes() )
    {
        if ( index.column() )
            continue;

        TrackModelItem* item = proxyModel()->itemFromIndex( proxyModel()->mapToSource( index ) );
        if ( item && !item->query().isNull() )
            queries << item->query();
    }

    m_contextMenu->setQueries( queries );
    m_contextMenu->exec( mapToGlobal( pos ) );
}
QVariant
CollectionModel::data( const QModelIndex& index, int role ) const
{
    if ( role != Qt::DisplayRole )
        return QVariant();

    TrackModelItem* entry = itemFromIndex( index );
    if ( !entry )
        return QVariant();

    const query_ptr& query = entry->query();
    if ( query.isNull() )
    {
        if ( !index.column() )
        {
            return entry->caption.isEmpty() ? "Unknown" : entry->caption;
        }

        if ( index.column() == 1 )
        {
            return entry->childCount;
        }

        return QVariant( "" );
    }

    if ( !query->numResults() )
    {
        switch( index.column() )
        {
            case 0:
                return query->track();
                break;
        }
    }
    else
    {
        switch( index.column() )
        {
            case 0:
                return query->results().first()->track();
                break;

            case 1:
                return QVariant();
                break;

            case 2:
                return TomahawkUtils::timeToString( query->results().first()->duration() );
                break;

            case 3:
                return query->results().first()->collection()->source()->friendlyName();
                break;
        }
    }

    return QVariant();
}
Beispiel #5
0
Tomahawk::result_ptr
TrackProxyModel::currentItem() const
{
    TrackModelItem* item = itemFromIndex( mapToSource( currentIndex() ) );
    if ( item && !item->query().isNull() && item->query()->playable() )
        return item->query()->results().at( 0 );
    return Tomahawk::result_ptr();
}
Tomahawk::result_ptr
TrackProxyModelPlaylistInterface::currentItem() const
{
    if ( m_proxyModel.isNull() )
        return Tomahawk::result_ptr();

    TrackProxyModel* proxyModel = m_proxyModel.data();

    TrackModelItem* item = proxyModel->itemFromIndex( proxyModel->mapToSource( proxyModel->currentIndex() ) );
    if ( item && !item->query().isNull() && item->query()->playable() )
        return item->query()->results().at( 0 );
    return Tomahawk::result_ptr();
}
Beispiel #7
0
void
TrackView::currentChanged( const QModelIndex& current, const QModelIndex& previous )
{
    QTreeView::currentChanged( current, previous );

    if ( !m_updateContextView )
        return;

    TrackModelItem* item = m_model->itemFromIndex( m_proxyModel->mapToSource( current ) );
    if ( item )
    {
        ViewManager::instance()->context()->setQuery( item->query() );
    }
}
QList< Tomahawk::query_ptr >
TrackProxyModel::tracks()
{
    QList<Tomahawk::query_ptr> queries;

    for ( int i = 0; i < rowCount( QModelIndex() ); i++ )
    {
        TrackModelItem* item = itemFromIndex( mapToSource( index( i, 0 ) ) );
        if ( item )
            queries << item->query();
    }

    return queries;
}
Beispiel #9
0
void
TrackView::startAutoPlay( const QModelIndex& index )
{
    if ( tryToPlayItem( index ) )
        return;

    // item isn't playable but still resolving
    TrackModelItem* item = m_model->itemFromIndex( m_proxyModel->mapToSource( index ) );
    if ( item && !item->query().isNull() && !item->query()->resolvingFinished() )
    {
        m_autoPlaying = item->query(); // So we can kill it if user starts autoplaying this playlist again
        NewClosure( item->query().data(), SIGNAL( resolvingFinished( bool ) ), this, SLOT( autoPlayResolveFinished( Tomahawk::query_ptr, int ) ),
                    item->query(), index.row() );
        return;
    }
QList< Tomahawk::query_ptr >
TrackProxyModelPlaylistInterface::tracks()
{
    if ( m_proxyModel.isNull() )
        return QList< Tomahawk::query_ptr >();

    TrackProxyModel* proxyModel = m_proxyModel.data();
    QList<Tomahawk::query_ptr> queries;

    for ( int i = 0; i < proxyModel->rowCount( QModelIndex() ); i++ )
    {
        TrackModelItem* item = proxyModel->itemFromIndex( proxyModel->mapToSource( proxyModel->index( i, 0 ) ) );
        if ( item )
            queries << item->query();
    }

    return queries;
}
bool
TrackProxyModel::filterAcceptsRow( int sourceRow, const QModelIndex& sourceParent ) const
{
    TrackModelItem* pi = itemFromIndex( sourceModel()->index( sourceRow, 0, sourceParent ) );
    if ( !pi )
        return false;

    const Tomahawk::query_ptr& q = pi->query();
    if( q.isNull() ) // uh oh? filter out invalid queries i guess
        return false;

    Tomahawk::result_ptr r;
    if ( q->numResults() )
        r = q->results().first();

    if ( !m_showOfflineResults && !r.isNull() && !r->collection()->source()->isOnline() )
        return false;

    if ( filterRegExp().isEmpty() )
        return true;

    QStringList sl = filterRegExp().pattern().split( " ", QString::SkipEmptyParts );
    foreach( QString s, sl )
    {
        s = s.toLower();
        if ( !r.isNull() )
        {
            if ( !r->artist()->name().toLower().contains( s ) &&
                 !r->album()->name().toLower().contains( s ) &&
                 !r->track().toLower().contains( s ) )
            {
                return false;
            }
        }
        else
        {
            if ( !q->artist().toLower().contains( s ) &&
                 !q->album().toLower().contains( s ) &&
                 !q->track().toLower().contains( s ) )
            {
                return false;
            }
        }
    }
Beispiel #12
0
void
TrackModel::setCurrentItem( const QModelIndex& index )
{
    qDebug() << Q_FUNC_INFO;
    TrackModelItem* oldEntry = itemFromIndex( m_currentIndex );
    if ( oldEntry )
    {
        oldEntry->setIsPlaying( false );
    }

    TrackModelItem* entry = itemFromIndex( index );
    if ( entry )
    {
        m_currentIndex = index;
        entry->setIsPlaying( true );
    }
    else
    {
        m_currentIndex = QModelIndex();
    }
}
Beispiel #13
0
void
WelcomeWidget::onPlaybackFinished( const Tomahawk::query_ptr& query )
{
    int count = m_tracksModel->trackCount();
    unsigned int playtime = query->playedBy().second;

    if ( count )
    {
        TrackModelItem* oldestItem = m_tracksModel->itemFromIndex( m_tracksModel->index( count - 1, 0, QModelIndex() ) );
        if ( oldestItem->query()->playedBy().second >= playtime )
            return;

        TrackModelItem* youngestItem = m_tracksModel->itemFromIndex( m_tracksModel->index( 0, 0, QModelIndex() ) );
        if ( youngestItem->query()->playedBy().second <= playtime )
            m_tracksModel->insert( query, 0 );
        else
        {
            for ( int i = 0; i < count - 1; i++ )
            {
                TrackModelItem* item1 = m_tracksModel->itemFromIndex( m_tracksModel->index( i, 0, QModelIndex() ) );
                TrackModelItem* item2 = m_tracksModel->itemFromIndex( m_tracksModel->index( i + 1, 0, QModelIndex() ) );

                if ( item1->query()->playedBy().second >= playtime && item2->query()->playedBy().second <= playtime )
                {
                    m_tracksModel->insert( query, i + 1 );
                    break;
                }
            }
        }
    }
    else
        m_tracksModel->insert( query, 0 );

    if ( m_tracksModel->trackCount() > HISTORY_TRACK_ITEMS )
        m_tracksModel->remove( HISTORY_TRACK_ITEMS );

    if ( m_timer->isActive() )
        m_timer->stop();
    m_timer->start( HISTORY_RESOLVING_TIMEOUT );
}
void
RecentlyPlayedModel::onPlaybackFinished( const Tomahawk::query_ptr& query )
{
    int count = trackCount();
    unsigned int playtime = query->playedBy().second;

    if ( count )
    {
        TrackModelItem* oldestItem = itemFromIndex( index( count - 1, 0, QModelIndex() ) );
        if ( oldestItem->query()->playedBy().second >= playtime )
            return;

        TrackModelItem* youngestItem = itemFromIndex( index( 0, 0, QModelIndex() ) );
        if ( youngestItem->query()->playedBy().second <= playtime )
            insert( query, 0 );
        else
        {
            for ( int i = 0; i < count - 1; i++ )
            {
                TrackModelItem* item1 = itemFromIndex( index( i, 0, QModelIndex() ) );
                TrackModelItem* item2 = itemFromIndex( index( i + 1, 0, QModelIndex() ) );

                if ( item1->query()->playedBy().second >= playtime && item2->query()->playedBy().second <= playtime )
                {
                    insert( query, i + 1 );
                    break;
                }
            }
        }
    }
    else
        insert( query, 0 );

    if ( trackCount() > (int)m_limit )
        remove( m_limit );

    ensureResolved();
}
Beispiel #15
0
void
TrackView::mousePressEvent( QMouseEvent* event )
{
    QTreeView::mousePressEvent( event );

    if ( !m_model || m_model->style() != TrackModel::Detailed )
        return;

    QModelIndex idx = indexAt( event->pos() );
    if ( event->pos().x() > header()->sectionViewportPosition( idx.column() ) + header()->sectionSize( idx.column() ) - 16 &&
         event->pos().x() < header()->sectionViewportPosition( idx.column() ) + header()->sectionSize( idx.column() ) )
    {
        TrackModelItem* item = proxyModel()->itemFromIndex( proxyModel()->mapToSource( idx ) );
        switch ( idx.column() )
        {
            case TrackModel::Artist:
            {
                if ( item->query()->results().count() )
                {
                    ViewManager::instance()->show( item->query()->results().first()->artist() );
                }
                else
                {
                    ViewManager::instance()->show( Artist::get( item->query()->artist() ) );
                }
                break;
            }

            case TrackModel::Album:
            {
                if ( item->query()->results().count() )
                {
                    ViewManager::instance()->show( item->query()->results().first()->album() );
                }
                else
                {
                    artist_ptr artist = Artist::get( item->query()->artist() );
                    ViewManager::instance()->show( Album::get( artist, item->query()->album() ) );
                }
                break;
            }

            default:
                break;
        }
    }
}
Beispiel #16
0
void
TrackView::onItemActivated( const QModelIndex& index )
{
    if ( !index.isValid() )
        return;

    TrackModelItem* item = m_model->itemFromIndex( m_proxyModel->mapToSource( index ) );
    if ( item && !item->query().isNull() && item->query()->numResults() )
    {
        tDebug() << "Result activated:" << item->query()->toString() << item->query()->results().first()->url();
        m_proxyModel->setCurrentIndex( index );
        AudioEngine::instance()->playItem( m_proxyModel->getPlaylistInterface(), item->query()->results().first() );
    }

    emit itemActivated( index );
}
Beispiel #17
0
void
TrackModel::setCurrentItem( const QModelIndex& index )
{
    TrackModelItem* oldEntry = itemFromIndex( m_currentIndex );
    if ( oldEntry )
    {
        oldEntry->setIsPlaying( false );
    }

    TrackModelItem* entry = itemFromIndex( index );
    if ( index.isValid() && entry && !entry->query().isNull() )
    {
        m_currentIndex = index;
        m_currentUuid = entry->query()->id();
        entry->setIsPlaying( true );
    }
    else
    {
        m_currentIndex = QModelIndex();
        m_currentUuid = QString();
    }
}
void
PlaylistItemDelegate::paintShort( QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index, bool useAvatars ) const
{
    TrackModelItem* item = m_model->itemFromIndex( m_model->mapToSource( index ) );
    Q_ASSERT( item );

    QStyleOptionViewItemV4 opt = option;
    prepareStyleOption( &opt, index, item );
    opt.text.clear();

    qApp->style()->drawControl( QStyle::CE_ItemViewItem, &opt, painter );

    if ( m_view->header()->visualIndex( index.column() ) > 0 )
        return;

    QPixmap pixmap;
    QString artist, track, upperText, lowerText;
    source_ptr source = item->query()->playedBy().first;

    if ( item->query()->results().count() )
    {
        artist = item->query()->results().first()->artist()->name();
        track = item->query()->results().first()->track();
    }
    else
    {
        artist = item->query()->artist();
        track = item->query()->track();
    }

    if ( source.isNull() )
    {
        upperText = artist;
        lowerText = track;
    }
    else
    {
        upperText = QString( "%1 - %2" ).arg( artist ).arg( track );
        QString playtime = TomahawkUtils::ageToString( QDateTime::fromTime_t( item->query()->playedBy().second ) );

        if ( source == SourceList::instance()->getLocal() )
            lowerText = QString( "played %1 ago by you" ).arg( playtime );
        else
            lowerText = QString( "played %1 ago by %2" ).arg( playtime ).arg( source->friendlyName() );

        if ( useAvatars )
            pixmap = source->avatar( Source::FancyStyle );
    }

    if ( pixmap.isNull() && !useAvatars )
        pixmap = QPixmap( RESPATH "images/track-placeholder.png" );
    else if ( pixmap.isNull() && useAvatars )
        pixmap = m_defaultAvatar;

    painter->save();
    {
        QRect r = opt.rect.adjusted( 3, 6, 0, -6 );

        // Paint Now Playing Speaker Icon
        if ( item->isPlaying() )
        {
            r.adjust( 0, 0, 0, 0 );
            QRect npr = r.adjusted( 3, r.height() / 2 - m_nowPlayingIcon.height() / 2, 18 - r.width(), -r.height() / 2 + m_nowPlayingIcon.height() / 2  );
            painter->drawPixmap( npr, m_nowPlayingIcon );
            r.adjust( 22, 0, 0, 0 );
        }

        painter->setPen( opt.palette.text().color() );

        QRect ir = r.adjusted( 4, 0, -option.rect.width() + option.rect.height() - 8 + r.left(), 0 );

        QPixmap scover;
        if ( m_cache.contains( pixmap.cacheKey() ) )
        {
            scover = m_cache.value( pixmap.cacheKey() );
        }
        else
        {
            scover = pixmap.scaled( ir.size(), Qt::KeepAspectRatio, Qt::SmoothTransformation );
            m_cache.insert( pixmap.cacheKey(), scover );
        }
        painter->drawPixmap( ir, scover );

        QFont boldFont = opt.font;
        boldFont.setBold( true );

        r.adjust( ir.width() + 12, 0, -12, 0 );
        painter->setFont( boldFont );
        QString text = painter->fontMetrics().elidedText( upperText, Qt::ElideRight, r.width() );
        painter->drawText( r.adjusted( 0, 1, 0, 0 ), text, m_topOption );


        painter->setFont( opt.font);
        text = painter->fontMetrics().elidedText( lowerText, Qt::ElideRight, r.width() );
        painter->drawText( r.adjusted( 0, 1, 0, 0 ), text, m_bottomOption );
    }
    painter->restore();
}
void
PlaylistItemDelegate::paintDetailed( QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index ) const
{
    TrackModelItem* item = m_model->itemFromIndex( m_model->mapToSource( index ) );
    Q_ASSERT( item );

    QStyleOptionViewItemV4 opt = option;
    prepareStyleOption( &opt, index, item );
    opt.text.clear();
    qApp->style()->drawControl( QStyle::CE_ItemViewItem, &opt, painter );

    if ( m_view->hoveredIndex().row() == index.row() && m_view->hoveredIndex().column() == index.column() &&
       ( index.column() == TrackModel::Artist || index.column() == TrackModel::Album ) )
    {
        opt.rect.setWidth( opt.rect.width() - 16 );
        QRect arrowRect( opt.rect.x() + opt.rect.width(), opt.rect.y() + 1, opt.rect.height() - 2, opt.rect.height() - 2 );

        if ( m_arrowIcon.height() != arrowRect.height() )
            m_arrowIcon = m_arrowIcon.scaled( arrowRect.size(), Qt::KeepAspectRatio, Qt::SmoothTransformation );
        painter->drawPixmap( arrowRect, m_arrowIcon );
    }

    painter->save();

    if ( index.column() == TrackModel::Score )
    {
        QColor barColor( 167, 183, 211 ); // This matches the sidebar (sourcetreeview.cpp:672)
        if ( opt.state & QStyle::State_Selected )
            painter->setPen( opt.palette.brightText().color() );
        else
            painter->setPen( barColor );

        QRect r = opt.rect.adjusted( 3, 3, -6, -4 );
        painter->drawRect( r );

        QRect fillR = r;
        int fillerWidth = (int)( index.data().toFloat() * (float)fillR.width() );
        fillR.adjust( 0, 0, -( fillR.width() - fillerWidth ), 0 );

        if ( opt.state & QStyle::State_Selected )
            painter->setBrush( opt.palette.brightText().color() );
        else
            painter->setBrush( barColor );

        painter->drawRect( fillR );
    }
    else if ( item->isPlaying() )
    {
        {
            QRect r = opt.rect.adjusted( 3, 0, 0, 0 );

            // Paint Now Playing Speaker Icon
            if ( m_view->header()->visualIndex( index.column() ) == 0 )
            {
                r.adjust( 0, 0, 0, -3 );
                painter->drawPixmap( r.adjusted( 3, 1, 18 - r.width(), 1 ), m_nowPlayingIcon );
                r.adjust( 25, 0, 0, 3 );
            }

            painter->setPen( opt.palette.text().color() );
            QString text = painter->fontMetrics().elidedText( index.data().toString(), Qt::ElideRight, r.width() - 3 );
            painter->drawText( r.adjusted( 0, 1, 0, 0 ), text, m_centerOption );
        }
    }
    else
    {
        painter->setPen( opt.palette.text().color() );
        QString text = painter->fontMetrics().elidedText( index.data().toString(), Qt::ElideRight, opt.rect.width() - 3 );
        painter->drawText( opt.rect.adjusted( 3, 1, 0, 0 ), text, m_centerOption );
    }

    painter->restore();
}
Beispiel #20
0
QVariant
TrackModel::data( const QModelIndex& index, int role ) const
{
    TrackModelItem* entry = itemFromIndex( index );
    if ( !entry )
        return QVariant();

    if ( role == Qt::DecorationRole )
    {
        return QVariant();
    }

    if ( role == Qt::SizeHintRole )
    {
        return QSize( 0, 18 );
    }

    if ( role == StyleRole )
    {
        return m_style;
    }

    if ( role != Qt::DisplayRole ) // && role != Qt::ToolTipRole )
        return QVariant();

    const query_ptr& query = entry->query();
    if ( !query->numResults() )
    {
        switch( index.column() )
        {
            case Artist:
                return query->artist();
                break;

            case Track:
                return query->track();
                break;

            case Album:
                return query->album();
                break;
        }
    }
    else
    {
        switch( index.column() )
        {
            case Artist:
                return query->results().first()->artist()->name();
                break;

            case Track:
                return query->results().first()->track();
                break;

            case Album:
                return query->results().first()->album()->name();
                break;

            case Duration:
                return TomahawkUtils::timeToString( query->results().first()->duration() );
                break;

            case Bitrate:
                if ( query->results().first()->bitrate() == 0 )
                    return QString();
                else
                    return query->results().first()->bitrate();
                break;

            case Age:
                return TomahawkUtils::ageToString( QDateTime::fromTime_t( query->results().first()->modificationTime() ) );
                break;

            case Year:
                if ( query->results().first()->year() == 0 )
                    return QString();
                else
                    return query->results().first()->year();
                break;

            case Filesize:
                return TomahawkUtils::filesizeToString( query->results().first()->size() );
                break;

            case Origin:
                return query->results().first()->friendlySource();
                break;

            case Score:
                return query->results().first()->score();
                break;

            case AlbumPos:
                if ( query->results().first()->albumpos() == 0 )
                    return QString();
                return QString::number( query->results().first()->albumpos() );
                break;
        }
    }

    return QVariant();
}
Beispiel #21
0
QVariant
TrackModel::data( const QModelIndex& index, int role ) const
{
    TrackModelItem* entry = itemFromIndex( index );
    if ( !entry )
        return QVariant();

    if ( role == Qt::DecorationRole )
    {
        return QVariant();
    }

    if ( role == Qt::SizeHintRole )
    {
        return QSize( 0, 18 );
    }

    if ( role != Qt::DisplayRole ) // && role != Qt::ToolTipRole )
        return QVariant();

    const query_ptr& query = entry->query();
    if ( query.isNull() )
    {
        if ( !index.column() )
        {
            return entry->caption.isEmpty() ? "Unknown" : entry->caption;
        }

        if ( index.column() == 1 )
        {
            return entry->childCount;
        }

        return QVariant( "" );
    }

    if ( !query->numResults() )
    {
        switch( index.column() )
        {
            case Artist:
                return query->artist();
                break;

            case Track:
                return query->track();
                break;

            case Album:
                return query->album();
                break;
        }
    }
    else
    {
        switch( index.column() )
        {
            case Artist:
                return query->results().first()->artist()->name();
                break;

            case Track:
                return query->results().first()->track();
                break;

            case Album:
                return query->results().first()->album()->name();
                break;

            case Duration:
                return TomahawkUtils::timeToString( query->results().first()->duration() );
                break;

            case Bitrate:
                return query->results().first()->bitrate();
                break;

            case Age:
                return TomahawkUtils::ageToString( QDateTime::fromTime_t( query->results().first()->modificationTime() ) );
                break;

            case Year:
                return query->results().first()->year();
                break;

            case Filesize:
                return TomahawkUtils::filesizeToString( query->results().first()->size() );
                break;

            case Origin:
                return query->results().first()->friendlySource();
                break;
        }
    }

    return QVariant();
}
void
PlaylistChartItemDelegate::paint( QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index ) const
{
    TrackModelItem* item = m_model->itemFromIndex( m_model->mapToSource( index ) );
    Q_ASSERT( item );

    QStyleOptionViewItemV4 opt = option;
    prepareStyleOption( &opt, index, item );
    opt.text.clear();

    qApp->style()->drawControl( QStyle::CE_ItemViewItem, &opt, painter );

    if ( m_view->header()->visualIndex( index.column() ) > 0 )
        return;

    QPixmap pixmap, avatar;
    QString artist, track, upperText, lowerText;
    unsigned int duration = 0;

    if ( item->query()->results().count() )
    {
        artist = item->query()->results().first()->artist()->name();
        track = item->query()->results().first()->track();
        duration = item->query()->results().first()->duration();
    }
    else
    {
        artist = item->query()->artist();
        track = item->query()->track();
    }

    painter->save();
    {
        QRect r = opt.rect.adjusted( 3, 6, 0, -6 );

        // Paint Now Playing Speaker Icon
        if ( item->isPlaying() )
        {
            QPixmap nowPlayingIcon = TomahawkUtils::defaultPixmap( TomahawkUtils::NowPlayingSpeaker );
            QRect npr = r.adjusted( 3, r.height() / 2 - nowPlayingIcon.height() / 2, 18 - r.width(), -r.height() / 2 + nowPlayingIcon.height() / 2 );
            nowPlayingIcon = TomahawkUtils::defaultPixmap( TomahawkUtils::NowPlayingSpeaker, TomahawkUtils::Original, npr.size() );
            painter->drawPixmap( npr, nowPlayingIcon );
            r.adjust( 22, 0, 0, 0 );
        }

        QFont figureFont = opt.font;
        figureFont.setPixelSize( 18 );
        figureFont.setWeight( 99 );

        QFont boldFont = opt.font;
        boldFont.setPixelSize( 15 );
        boldFont.setWeight( 99 );

        QFont smallBoldFont = opt.font;
        smallBoldFont.setPixelSize( 12 );
        smallBoldFont.setWeight( 80 );

        QFont durationFont = opt.font;
        durationFont.setPixelSize( 12 );
        durationFont.setWeight( 80 );

        if ( index.row() == 0 )
        {
            figureFont.setPixelSize( 36 );
            boldFont.setPixelSize( 26 );
            smallBoldFont.setPixelSize( 22 );
        }
        else if ( index.row() == 1 )
        {
            figureFont.setPixelSize( 30 );
            boldFont.setPixelSize( 22 );
            smallBoldFont.setPixelSize( 18 );
        }
        else if ( index.row() == 2 )
        {
            figureFont.setPixelSize( 24 );
            boldFont.setPixelSize( 18 );
            smallBoldFont.setPixelSize( 14 );
        }
        else if ( index.row() >= 10 )
        {
            boldFont.setPixelSize( 12 );
            smallBoldFont.setPixelSize( 11 );
        }

        QRect figureRect = r.adjusted( 0, 0, -option.rect.width() + 60 - 6 + r.left(), 0 );
        painter->setFont( figureFont );
        painter->setPen( option.palette.text().color().lighter( 450 ) );
        painter->drawText( figureRect, QString::number( index.row() + 1 ), m_centerOption );
        painter->setPen( opt.palette.text().color() );

        QRect pixmapRect = r.adjusted( figureRect.width() + 6, 0, -option.rect.width() + figureRect.width() + option.rect.height() - 6 + r.left(), 0 );
        pixmap = item->query()->cover( pixmapRect.size(), false );
        if ( !pixmap )
        {
            pixmap = TomahawkUtils::defaultPixmap( TomahawkUtils::DefaultTrackImage, TomahawkUtils::ScaledCover, pixmapRect.size() );
        }
        painter->drawPixmap( pixmapRect, pixmap );
        
        r.adjust( pixmapRect.width() + figureRect.width() + 18, 1, -28, 0 );
        QRect leftRect = r.adjusted( 0, 0, -48, 0 );
        QRect rightRect = r.adjusted( r.width() - 40, 0, 0, 0 );

        painter->setFont( boldFont );
        QString text = painter->fontMetrics().elidedText( artist, Qt::ElideRight, leftRect.width() );
        painter->drawText( leftRect, text, m_topOption );

        painter->setFont( smallBoldFont );
        text = painter->fontMetrics().elidedText( track, Qt::ElideRight, leftRect.width() );
        painter->drawText( index.row() >= 10 ? leftRect : leftRect.adjusted( 0, painter->fontMetrics().height() + 6, 0, 0 ), text, index.row() >= 10 ? m_bottomOption : m_topOption );

        if ( duration > 0 )
        {
            painter->setFont( durationFont );
            text = painter->fontMetrics().elidedText( TomahawkUtils::timeToString( duration ), Qt::ElideRight, rightRect.width() );
            painter->drawText( rightRect, text, m_centerRightOption );
        }
    }
    painter->restore();
}
Tomahawk::result_ptr
TrackProxyModel::siblingItem( int itemsAway )
{
    qDebug() << Q_FUNC_INFO;

    QModelIndex idx = index( 0, 0 );
    if( rowCount() )
    {
        if ( m_shuffled )
        {
            // random mode is enabled
            // TODO come up with a clever random logic, that keeps track of previously played items
            idx = index( qrand() % rowCount(), 0 );
        }
        else if ( currentItem().isValid() )
        {
            idx = currentItem();

            // random mode is disabled
            if ( m_repeatMode == PlaylistInterface::RepeatOne )
            {
                // repeat one track
                idx = index( idx.row(), 0 );
            }
            else
            {
                // keep progressing through the playlist normally
                idx = index( idx.row() + itemsAway, 0 );
            }
        }
    }

    if ( !idx.isValid() && m_repeatMode == PlaylistInterface::RepeatAll )
    {
        // repeat all tracks
        if ( itemsAway > 0 )
        {
            // reset to first item
            idx = index( 0, 0 );
        }
        else
        {
            // reset to last item
            idx = index( rowCount() - 1, 0 );
        }
    }

    // Try to find the next available PlaylistItem (with results)
    if ( idx.isValid() ) do
    {
        TrackModelItem* item = itemFromIndex( mapToSource( idx ) );
        qDebug() << item->query()->toString();
        if ( item && item->query()->playable() )
        {
            qDebug() << "Next PlaylistItem found:" << item->query()->toString() << item->query()->results().at( 0 )->url();
            setCurrentItem( idx );
            return item->query()->results().at( 0 );
        }

        idx = index( idx.row() + ( itemsAway > 0 ? 1 : -1 ), 0 );
    }
    while ( idx.isValid() );

    setCurrentItem( QModelIndex() );
    return Tomahawk::result_ptr();
}
void
PlaylistLargeItemDelegate::paint( QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index ) const
{
    TrackModelItem* item = m_model->itemFromIndex( m_model->mapToSource( index ) );
    Q_ASSERT( item );

    QStyleOptionViewItemV4 opt = option;
    prepareStyleOption( &opt, index, item );
    opt.text.clear();

    qApp->style()->drawControl( QStyle::CE_ItemViewItem, &opt, painter );

    if ( m_view->header()->visualIndex( index.column() ) > 0 )
        return;

    QPixmap pixmap, avatar;
    QString artist, track, lowerText;
    unsigned int duration = 0;

    if ( item->query()->results().count() )
    {
        artist = item->query()->results().first()->artist()->name();
        track = item->query()->results().first()->track();
        duration = item->query()->results().first()->duration();
    }
    else
    {
        artist = item->query()->artist();
        track = item->query()->track();
    }

    QSize avatarSize( 32, 32 );
    source_ptr source = item->query()->playedBy().first;
    if ( m_mode == RecentlyPlayed && !source.isNull() )
    {
        avatar = source->avatar( Source::FancyStyle, avatarSize );
        QString playtime = TomahawkUtils::ageToString( QDateTime::fromTime_t( item->query()->playedBy().second ), true );

        if ( source == SourceList::instance()->getLocal() )
            lowerText = QString( tr( "played %1 by you" ) ).arg( playtime );
        else
            lowerText = QString( tr( "played %1 by %2" ) ).arg( playtime ).arg( source->friendlyName() );
    }

    if ( m_mode == LatestAdditions && item->query()->numResults() )
    {
        QString playtime = TomahawkUtils::ageToString( QDateTime::fromTime_t( item->query()->results().first()->modificationTime() ), true );

        lowerText = QString( tr( "added %1" ) ).arg( playtime );
    }

    if ( m_mode == LovedTracks )
        lowerText = item->query()->socialActionDescription( "Love", Query::Detailed );

    painter->save();
    {
        QRect r = opt.rect.adjusted( 3, 6, 0, -6 );

        // Paint Now Playing Speaker Icon
        if ( item->isPlaying() )
        {
            QPixmap nowPlayingIcon = TomahawkUtils::defaultPixmap( TomahawkUtils::NowPlayingSpeaker );
            QRect npr = r.adjusted( 3, r.height() / 2 - nowPlayingIcon.height() / 2, 18 - r.width(), -r.height() / 2 + nowPlayingIcon.height() / 2 );
            nowPlayingIcon = TomahawkUtils::defaultPixmap( TomahawkUtils::NowPlayingSpeaker, TomahawkUtils::Original, npr.size() );
            painter->drawPixmap( npr, nowPlayingIcon );
            r.adjust( 22, 0, 0, 0 );
        }

        painter->setPen( opt.palette.text().color() );

        QRect pixmapRect = r.adjusted( 6, 0, -option.rect.width() + option.rect.height() - 6 + r.left(), 0 );
        QRect avatarRect = r.adjusted( option.rect.width() - r.left() - 12 - avatarSize.width(), ( option.rect.height() - avatarSize.height() ) / 2 - 5, 0, 0 );
        avatarRect.setSize( avatarSize );

        pixmap = item->query()->cover( pixmapRect.size(), false );
        if ( !pixmap )
        {
            pixmap = TomahawkUtils::defaultPixmap( TomahawkUtils::DefaultTrackImage, TomahawkUtils::ScaledCover, pixmapRect.size() );
        }

        painter->drawPixmap( pixmapRect, pixmap );

        if ( !avatar.isNull() )
            painter->drawPixmap( avatarRect, avatar );

        QFont boldFont = opt.font;
        boldFont.setPixelSize( 15 );
        boldFont.setWeight( 99 );

        QFont smallBoldFont = opt.font;
        smallBoldFont.setPixelSize( 12 );
        smallBoldFont.setBold( true );
        smallBoldFont.setWeight( 60 );

        QFont smallFont = opt.font;
        smallFont.setPixelSize( 10 );

        r.adjust( pixmapRect.width() + 12, 1, -28 - avatar.width(), 0 );
        QRect leftRect = r.adjusted( 0, 0, -48, 0 );
        QRect rightRect = r.adjusted( r.width() - 40, 0, 0, 0 );

        painter->setFont( boldFont );
        QString text = painter->fontMetrics().elidedText( track, Qt::ElideRight, leftRect.width() );
        painter->drawText( leftRect, text, m_topOption );

        painter->setFont( smallBoldFont );
        text = painter->fontMetrics().elidedText( artist, Qt::ElideRight, leftRect.width() );
        painter->drawText( leftRect.adjusted( 0, 19, 0, 0 ), text, m_topOption );

        painter->setFont( smallFont );
        painter->setPen( Qt::gray );
        QTextDocument textDoc;
        textDoc.setHtml( lowerText );
        textDoc.setDocumentMargin( 0 );
        textDoc.setDefaultFont( painter->font() );
        textDoc.setDefaultTextOption( m_bottomOption );

        if ( textDoc.idealWidth() > leftRect.width() )
            textDoc.setHtml( item->query()->socialActionDescription( "Love", Query::Short ) );

        drawRichText( painter, leftRect, Qt::AlignBottom, textDoc );

        if ( duration > 0 )
        {
            painter->setFont( smallBoldFont );
            text = painter->fontMetrics().elidedText( TomahawkUtils::timeToString( duration ), Qt::ElideRight, rightRect.width() );
            painter->drawText( rightRect, text, m_centerRightOption );
        }
    }
    painter->restore();
}