1
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
3
* Copyright 2010-2011, Christian Muehlhaeuser <muesli@tomahawk-player.org>
4
* Copyright 2011 Leo Franchi <lfranchi@kde.org>
5
* Copyright 2010-2011, Jeff Mitchell <jeff@tomahawk-player.org>
7
* Tomahawk is free software: you can redistribute it and/or modify
8
* it under the terms of the GNU General Public License as published by
9
* the Free Software Foundation, either version 3 of the License, or
10
* (at your option) any later version.
12
* Tomahawk is distributed in the hope that it will be useful,
13
* but WITHOUT ANY WARRANTY; without even the implied warranty of
14
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
* GNU General Public License for more details.
17
* You should have received a copy of the GNU General Public License
18
* along with Tomahawk. If not, see <http://www.gnu.org/licenses/>.
21
#include "PlayableModel.h"
30
#include "PlayableItem.h"
31
#include "PlayableProxyModel.h"
34
#include "audio/AudioEngine.h"
35
#include "utils/TomahawkUtils.h"
36
#include "utils/Logger.h"
38
using namespace Tomahawk;
41
PlayableModel::PlayableModel( QObject* parent, bool loading )
42
: QAbstractItemModel( parent )
43
, m_rootItem( new PlayableItem( 0 ) )
45
, m_loading( loading )
47
connect( AudioEngine::instance(), SIGNAL( started( Tomahawk::result_ptr ) ), SLOT( onPlaybackStarted( Tomahawk::result_ptr ) ), Qt::DirectConnection );
48
connect( AudioEngine::instance(), SIGNAL( stopped() ), SLOT( onPlaybackStopped() ), Qt::DirectConnection );
50
m_header << tr( "Artist" ) << tr( "Title" ) << tr( "Composer" ) << tr( "Album" ) << tr( "Track" ) << tr( "Duration" )
51
<< tr( "Bitrate" ) << tr( "Age" ) << tr( "Year" ) << tr( "Size" ) << tr( "Origin" ) << tr( "Accuracy" ) << tr( "Name" );
55
PlayableModel::~PlayableModel()
61
PlayableModel::createIndex( int row, int column, PlayableItem* item ) const
65
connect( item->query().data(), SIGNAL( playableStateChanged( bool ) ), SLOT( onQueryBecamePlayable( bool ) ), Qt::UniqueConnection );
66
connect( item->query().data(), SIGNAL( resolvingFinished( bool ) ), SLOT( onQueryResolved( bool ) ), Qt::UniqueConnection );
69
return QAbstractItemModel::createIndex( row, column, item );
74
PlayableModel::index( int row, int column, const QModelIndex& parent ) const
76
if ( !m_rootItem || row < 0 || column < 0 )
79
PlayableItem* parentItem = itemFromIndex( parent );
80
PlayableItem* childItem = parentItem->children.value( row );
84
return createIndex( row, column, childItem );
89
PlayableModel::rowCount( const QModelIndex& parent ) const
91
if ( parent.column() > 0 )
94
PlayableItem* parentItem = itemFromIndex( parent );
98
return parentItem->children.count();
103
PlayableModel::columnCount( const QModelIndex& parent ) const
112
PlayableModel::hasChildren( const QModelIndex& parent ) const
114
PlayableItem* parentItem = itemFromIndex( parent );
118
if ( parentItem == m_rootItem )
121
return ( !parentItem->artist().isNull() || !parentItem->album().isNull() );
126
PlayableModel::parent( const QModelIndex& child ) const
128
PlayableItem* entry = itemFromIndex( child );
130
return QModelIndex();
132
PlayableItem* parentEntry = entry->parent();
134
return QModelIndex();
136
PlayableItem* grandparentEntry = parentEntry->parent();
137
if ( !grandparentEntry )
138
return QModelIndex();
140
int row = grandparentEntry->children.indexOf( parentEntry );
141
return createIndex( row, 0, parentEntry );
146
PlayableModel::artistData( const artist_ptr& artist, int role ) const
148
if ( role != Qt::DisplayRole ) // && role != Qt::ToolTipRole )
151
return artist->name();
156
PlayableModel::albumData( const album_ptr& album, int role ) const
158
if ( role != Qt::DisplayRole ) // && role != Qt::ToolTipRole )
161
return album->name();
166
PlayableModel::queryData( const query_ptr& query, int column, int role ) const
168
if ( role != Qt::DisplayRole ) // && role != Qt::ToolTipRole )
174
return query->artist();
179
return query->track();
183
return query->album();
187
return query->composer();
191
return TomahawkUtils::timeToString( query->duration() );
197
if ( query->albumpos() != 0 )
199
tPos = QString::number( query->albumpos() );
200
if ( query->discnumber() == 0 )
203
return QString( "%1.%2" ).arg( QString::number( query->discnumber() ) )
212
if ( query->numResults() )
217
if ( query->results().first()->bitrate() > 0 )
218
return query->results().first()->bitrate();
222
return TomahawkUtils::ageToString( QDateTime::fromTime_t( query->results().first()->modificationTime() ) );
226
if ( query->results().first()->year() != 0 )
227
return query->results().first()->year();
231
return TomahawkUtils::filesizeToString( query->results().first()->size() );
235
return query->results().first()->friendlySource();
240
float score = query->results().first()->score();
242
return tr( "Perfect match" );
244
return tr( "Very good match" );
246
return tr( "Good match" );
248
return tr( "Vague match" );
250
return tr( "Bad match" );
252
return tr( "Very bad match" );
254
return tr( "Not available" );
268
PlayableModel::data( const QModelIndex& index, int role ) const
270
PlayableItem* entry = itemFromIndex( index );
274
if ( role == Qt::DecorationRole )
278
else if ( role == Qt::TextAlignmentRole )
280
return QVariant( columnAlignment( index.column() ) );
282
else if ( role == PlayableProxyModel::TypeRole )
284
if ( entry->result() )
286
return Tomahawk::TypeResult;
288
else if ( entry->query() )
290
return Tomahawk::TypeQuery;
292
else if ( entry->artist() )
294
return Tomahawk::TypeArtist;
296
else if ( entry->album() )
298
return Tomahawk::TypeAlbum;
302
if ( !entry->query().isNull() )
304
return queryData( entry->query()->displayQuery(), index.column(), role );
306
else if ( !entry->artist().isNull() )
308
return artistData( entry->artist(), role );
310
else if ( !entry->album().isNull() )
312
return albumData( entry->album(), role );
320
PlayableModel::headerData( int section, Qt::Orientation orientation, int role ) const
322
Q_UNUSED( orientation );
324
if ( role == Qt::DisplayRole && section >= 0 )
326
if ( section < m_header.count() )
327
return m_header.at( section );
332
if ( role == Qt::TextAlignmentRole )
334
return QVariant( columnAlignment( section ) );
342
PlayableModel::setCurrentIndex( const QModelIndex& index )
344
PlayableItem* oldEntry = itemFromIndex( m_currentIndex );
347
oldEntry->setIsPlaying( false );
350
PlayableItem* entry = itemFromIndex( index );
351
if ( index.isValid() && entry && !entry->query().isNull() )
353
m_currentIndex = index;
354
m_currentUuid = entry->query()->id();
355
entry->setIsPlaying( true );
359
m_currentIndex = QModelIndex();
360
m_currentUuid = QString();
363
emit currentIndexChanged();
368
PlayableModel::supportedDropActions() const
370
return Qt::CopyAction | Qt::MoveAction;
375
PlayableModel::flags( const QModelIndex& index ) const
377
Qt::ItemFlags defaultFlags = QAbstractItemModel::flags( index );
379
if ( index.isValid() && index.column() == 0 )
380
return Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled | defaultFlags;
382
return Qt::ItemIsDropEnabled | defaultFlags;
387
PlayableModel::mimeTypes() const
390
types << "application/tomahawk.mixed";
396
PlayableModel::mimeData( const QModelIndexList &indexes ) const
398
qDebug() << Q_FUNC_INFO;
400
QByteArray resultData;
401
QDataStream resultStream( &resultData, QIODevice::WriteOnly );
403
// lets try with artist only
405
foreach ( const QModelIndex& i, indexes )
407
if ( i.column() > 0 || indexes.contains( i.parent() ) )
410
PlayableItem* item = itemFromIndex( i );
414
if ( !item->artist().isNull() )
416
const artist_ptr& artist = item->artist();
417
resultStream << artist->name();
427
QMimeData* mimeData = new QMimeData();
428
mimeData->setData( "application/tomahawk.metadata.artist", resultData );
432
// lets try with album only
435
foreach ( const QModelIndex& i, indexes )
437
if ( i.column() > 0 || indexes.contains( i.parent() ) )
440
PlayableItem* item = itemFromIndex( i );
444
if ( !item->album().isNull() )
446
const album_ptr& album = item->album();
447
resultStream << album->artist()->name();
448
resultStream << album->name();
458
QMimeData* mimeData = new QMimeData();
459
mimeData->setData( "application/tomahawk.metadata.album", resultData );
463
// lets try with tracks only
466
foreach ( const QModelIndex& i, indexes )
468
if ( i.column() > 0 || indexes.contains( i.parent() ) )
471
PlayableItem* item = itemFromIndex( i );
475
if ( !item->result().isNull() )
477
const result_ptr& result = item->result();
478
resultStream << qlonglong( &result );
488
QMimeData* mimeData = new QMimeData();
489
mimeData->setData( "application/tomahawk.result.list", resultData );
493
// Ok... we have to use mixed
495
QDataStream mixedStream( &resultData, QIODevice::WriteOnly );
496
foreach ( const QModelIndex& i, indexes )
498
if ( i.column() > 0 || indexes.contains( i.parent() ) )
501
PlayableItem* item = itemFromIndex( i );
505
if ( !item->artist().isNull() )
507
const artist_ptr& artist = item->artist();
508
mixedStream << QString( "application/tomahawk.metadata.artist" ) << artist->name();
510
else if ( !item->album().isNull() )
512
const album_ptr& album = item->album();
513
mixedStream << QString( "application/tomahawk.metadata.album" ) << album->artist()->name() << album->name();
515
else if ( !item->result().isNull() )
517
const result_ptr& result = item->result();
518
mixedStream << QString( "application/tomahawk.result.list" ) << qlonglong( &result );
520
else if ( !item->query().isNull() )
522
const query_ptr& query = item->query();
523
mixedStream << QString( "application/tomahawk.query.list" ) << qlonglong( &query );
527
QMimeData* mimeData = new QMimeData();
528
mimeData->setData( "application/tomahawk.mixed", resultData );
534
PlayableModel::clear()
536
if ( rowCount( QModelIndex() ) )
540
emit beginResetModel();
543
m_rootItem = new PlayableItem( 0 );
544
emit endResetModel();
550
PlayableModel::queries() const
552
Q_ASSERT( m_rootItem );
554
QList< query_ptr > tracks;
555
foreach ( PlayableItem* item, m_rootItem->children )
557
tracks << item->query();
564
template <typename T>
566
PlayableModel::insertInternal( const QList< T >& items, int row )
568
if ( !items.count() )
570
emit itemCountChanged( rowCount( QModelIndex() ) );
577
QPair< int, int > crows;
579
crows.second = c + items.count() - 1;
581
emit beginInsertRows( QModelIndex(), crows.first, crows.second );
584
PlayableItem* plitem;
585
foreach ( const T& item, items )
587
plitem = new PlayableItem( item, m_rootItem, row + i );
588
plitem->index = createIndex( row + i, 0, plitem );
591
/* if ( item->id() == currentItemUuid() )
592
setCurrentItem( plitem->index );*/
594
connect( plitem, SIGNAL( dataChanged() ), SLOT( onDataChanged() ) );
597
emit endInsertRows();
598
emit itemCountChanged( rowCount( QModelIndex() ) );
604
PlayableModel::remove( int row, bool moreToCome )
606
removeIndex( index( row, 0, QModelIndex() ), moreToCome );
611
PlayableModel::removeIndex( const QModelIndex& index, bool moreToCome )
613
if ( QThread::currentThread() != thread() )
615
QMetaObject::invokeMethod( this, "remove",
616
Qt::QueuedConnection,
617
Q_ARG(const QModelIndex, index),
618
Q_ARG(bool, moreToCome) );
622
if ( index.column() > 0 )
625
PlayableItem* item = itemFromIndex( index );
628
if ( index == m_currentIndex )
629
setCurrentIndex( QModelIndex() );
631
emit beginRemoveRows( index.parent(), index.row(), index.row() );
633
emit endRemoveRows();
637
emit itemCountChanged( rowCount( QModelIndex() ) );
642
PlayableModel::removeIndexes( const QList<QModelIndex>& indexes )
644
QList<QPersistentModelIndex> pil;
645
foreach ( const QModelIndex& idx, indexes )
650
removeIndexes( pil );
655
PlayableModel::removeIndexes( const QList<QPersistentModelIndex>& indexes )
657
QList<QPersistentModelIndex> finalIndexes;
658
foreach ( const QPersistentModelIndex index, indexes )
660
if ( index.column() > 0 )
662
finalIndexes << index;
665
for ( int i = 0; i < finalIndexes.count(); i++ )
667
removeIndex( finalIndexes.at( i ), i + 1 != finalIndexes.count() );
673
PlayableModel::onPlaybackStarted( const Tomahawk::result_ptr& result )
675
PlayableItem* oldEntry = itemFromIndex( m_currentIndex );
676
if ( oldEntry && ( oldEntry->query().isNull() || !oldEntry->query()->numResults() || oldEntry->query()->results().first().data() != result.data() ) )
678
oldEntry->setIsPlaying( false );
684
PlayableModel::onPlaybackStopped()
686
PlayableItem* oldEntry = itemFromIndex( m_currentIndex );
689
oldEntry->setIsPlaying( false );
695
PlayableModel::ensureResolved()
697
for( int i = 0; i < rowCount( QModelIndex() ); i++ )
699
query_ptr query = itemFromIndex( index( i, 0, QModelIndex() ) )->query();
701
if ( !query->resolvingFinished() )
702
Pipeline::instance()->resolve( query );
708
PlayableModel::columnAlignment( int column ) const
719
return Qt::AlignHCenter;
723
return Qt::AlignLeft;
729
PlayableModel::onDataChanged()
731
PlayableItem* p = (PlayableItem*)sender();
732
if ( p && p->index.isValid() )
733
emit dataChanged( p->index, p->index.sibling( p->index.row(), columnCount() - 1 ) );
738
PlayableModel::startLoading()
741
emit loadingStarted();
746
PlayableModel::finishLoading()
749
emit loadingFinished();
754
PlayableModel::itemFromIndex( const QModelIndex& index ) const
756
if ( index.isValid() )
758
return static_cast<PlayableItem*>( index.internalPointer() );
768
PlayableModel::appendArtist( const Tomahawk::artist_ptr& artist )
770
QList< artist_ptr > artists;
773
appendArtists( artists );
778
PlayableModel::appendAlbum( const Tomahawk::album_ptr& album )
780
QList< album_ptr > albums;
783
appendAlbums( albums );
788
PlayableModel::appendQuery( const Tomahawk::query_ptr& query )
790
QList< query_ptr > queries;
793
appendQueries( queries );
798
PlayableModel::appendArtists( const QList< Tomahawk::artist_ptr >& artists )
800
insertArtists( artists, rowCount( QModelIndex() ) );
805
PlayableModel::appendAlbums( const QList< Tomahawk::album_ptr >& albums )
807
insertAlbums( albums, rowCount( QModelIndex() ) );
812
PlayableModel::appendQueries( const QList< Tomahawk::query_ptr >& queries )
814
insertQueries( queries, rowCount( QModelIndex() ) );
819
PlayableModel::insertArtist( const Tomahawk::artist_ptr& artist, int row )
821
QList< artist_ptr > artists;
824
insertArtists( artists, row );
829
PlayableModel::insertAlbum( const Tomahawk::album_ptr& album, int row )
831
QList< album_ptr > albums;
834
insertAlbums( albums, row );
839
PlayableModel::insertQuery( const Tomahawk::query_ptr& query, int row )
841
QList< query_ptr > queries;
844
insertQueries( queries, row );
849
PlayableModel::insertArtists( const QList< Tomahawk::artist_ptr >& artists, int row )
851
insertInternal( artists, row );
856
PlayableModel::insertAlbums( const QList< Tomahawk::album_ptr >& albums, int row )
858
insertInternal( albums, row );
863
PlayableModel::insertQueries( const QList< Tomahawk::query_ptr >& queries, int row )
865
insertInternal( queries, row );
870
PlayableModel::setTitle( const QString& title )
878
PlayableModel::setDescription( const QString& description )
880
m_description = description;
886
PlayableModel::setIcon( const QPixmap& pixmap )
894
PlayableModel::onQueryBecamePlayable( bool playable )
896
Q_UNUSED( playable );
898
Tomahawk::Query* q = qobject_cast< Query* >( sender() );
901
// Track has been removed from the playlist by now
905
Tomahawk::query_ptr query = q->weakRef().toStrongRef();
906
PlayableItem* item = itemFromQuery( query );
910
emit indexPlayable( item->index );
916
PlayableModel::onQueryResolved( bool hasResults )
918
Q_UNUSED( hasResults );
920
Tomahawk::Query* q = qobject_cast< Query* >( sender() );
923
// Track has been removed from the playlist by now
927
Tomahawk::query_ptr query = q->weakRef().toStrongRef();
928
PlayableItem* item = itemFromQuery( query );
932
emit indexResolved( item->index );
938
PlayableModel::itemFromQuery( const Tomahawk::query_ptr& query ) const
943
for ( int i = 0; i < rowCount( QModelIndex() ); i++ )
945
QModelIndex idx = index( i, 0, QModelIndex() );
946
PlayableItem* item = itemFromIndex( idx );
947
if ( item && item->query() == query )
953
tDebug() << "Could not find item for query:" << query->toString();
959
PlayableModel::itemFromResult( const Tomahawk::result_ptr& result ) const
964
for ( int i = 0; i < rowCount( QModelIndex() ); i++ )
966
QModelIndex idx = index( i, 0, QModelIndex() );
967
PlayableItem* item = itemFromIndex( idx );
968
if ( item && item->result() == result )
974
tDebug() << "Could not find item for result:" << result->toString();