/src/libtomahawk/playlist/trackmodel.cpp
C++ | 574 lines | 436 code | 120 blank | 18 comment | 66 complexity | 4efdb523ff660b3fe4d24d2643cadeb3 MD5 | raw file
1/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> === 2 * 3 * Copyright 2010-2011, Christian Muehlhaeuser <muesli@tomahawk-player.org> 4 * Copyright 2011 Leo Franchi <lfranchi@kde.org> 5 * 6 * Tomahawk is free software: you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License as published by 8 * the Free Software Foundation, either version 3 of the License, or 9 * (at your option) any later version. 10 * 11 * Tomahawk is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 * 16 * You should have received a copy of the GNU General Public License 17 * along with Tomahawk. If not, see <http://www.gnu.org/licenses/>. 18 */ 19 20#include "trackmodel.h" 21 22#include <QDateTime> 23#include <QMimeData> 24#include <QTreeView> 25 26#include "audio/audioengine.h" 27#include "utils/tomahawkutils.h" 28 29#include "artist.h" 30#include "album.h" 31#include "pipeline.h" 32#include "utils/logger.h" 33 34using namespace Tomahawk; 35 36 37TrackModel::TrackModel( QObject* parent ) 38 : QAbstractItemModel( parent ) 39 , m_rootItem( new TrackModelItem( 0, this ) ) 40 , m_readOnly( true ) 41 , m_style( Detailed ) 42{ 43 connect( AudioEngine::instance(), SIGNAL( started( Tomahawk::result_ptr ) ), SLOT( onPlaybackStarted( Tomahawk::result_ptr ) ), Qt::DirectConnection ); 44 connect( AudioEngine::instance(), SIGNAL( stopped() ), SLOT( onPlaybackStopped() ), Qt::DirectConnection ); 45} 46 47 48TrackModel::~TrackModel() 49{ 50} 51 52 53QModelIndex 54TrackModel::index( int row, int column, const QModelIndex& parent ) const 55{ 56 if ( !m_rootItem || row < 0 || column < 0 ) 57 return QModelIndex(); 58 59 TrackModelItem* parentItem = itemFromIndex( parent ); 60 TrackModelItem* childItem = parentItem->children.value( row ); 61 if ( !childItem ) 62 return QModelIndex(); 63 64 return createIndex( row, column, childItem ); 65} 66 67 68int 69TrackModel::rowCount( const QModelIndex& parent ) const 70{ 71 if ( parent.column() > 0 ) 72 return 0; 73 74 TrackModelItem* parentItem = itemFromIndex( parent ); 75 if ( !parentItem ) 76 return 0; 77 78 return parentItem->children.count(); 79} 80 81 82int 83TrackModel::columnCount( const QModelIndex& parent ) const 84{ 85 Q_UNUSED( parent ); 86 87 switch ( m_style ) 88 { 89 case Short: 90 case ShortWithAvatars: 91 return 1; 92 break; 93 94 case Detailed: 95 default: 96 return 12; 97 break; 98 } 99} 100 101 102QModelIndex 103TrackModel::parent( const QModelIndex& child ) const 104{ 105 TrackModelItem* entry = itemFromIndex( child ); 106 if ( !entry ) 107 return QModelIndex(); 108 109 TrackModelItem* parentEntry = entry->parent; 110 if ( !parentEntry ) 111 return QModelIndex(); 112 113 TrackModelItem* grandparentEntry = parentEntry->parent; 114 if ( !grandparentEntry ) 115 return QModelIndex(); 116 117 int row = grandparentEntry->children.indexOf( parentEntry ); 118 return createIndex( row, 0, parentEntry ); 119} 120 121 122QVariant 123TrackModel::data( const QModelIndex& index, int role ) const 124{ 125 TrackModelItem* entry = itemFromIndex( index ); 126 if ( !entry ) 127 return QVariant(); 128 129 if ( role == Qt::DecorationRole ) 130 { 131 return QVariant(); 132 } 133 134 if ( role == Qt::SizeHintRole ) 135 { 136 return QSize( 0, 18 ); 137 } 138 139 if ( role == Qt::TextAlignmentRole ) 140 { 141 return QVariant( columnAlignment( index.column() ) ); 142 } 143 144 if ( role == StyleRole ) 145 { 146 return m_style; 147 } 148 149 if ( role != Qt::DisplayRole ) // && role != Qt::ToolTipRole ) 150 return QVariant(); 151 152 const query_ptr& query = entry->query(); 153 if ( !query->numResults() ) 154 { 155 switch( index.column() ) 156 { 157 case Artist: 158 return query->artist(); 159 break; 160 161 case Track: 162 return query->track(); 163 break; 164 165 case Album: 166 return query->album(); 167 break; 168 } 169 } 170 else 171 { 172 switch( index.column() ) 173 { 174 case Artist: 175 return query->results().first()->artist()->name(); 176 break; 177 178 case Track: 179 return query->results().first()->track(); 180 break; 181 182 case Album: 183 return query->results().first()->album()->name(); 184 break; 185 186 case Composer: 187 if ( !query->results().first()->composer().isNull() ) 188 return query->results().first()->composer()->name(); 189 break; 190 191 case Duration: 192 return TomahawkUtils::timeToString( query->results().first()->duration() ); 193 break; 194 195 case Bitrate: 196 if ( query->results().first()->bitrate() > 0 ) 197 return query->results().first()->bitrate(); 198 break; 199 200 case Age: 201 return TomahawkUtils::ageToString( QDateTime::fromTime_t( query->results().first()->modificationTime() ) ); 202 break; 203 204 case Year: 205 if ( query->results().first()->year() != 0 ) 206 return query->results().first()->year(); 207 break; 208 209 case Filesize: 210 return TomahawkUtils::filesizeToString( query->results().first()->size() ); 211 break; 212 213 case Origin: 214 return query->results().first()->friendlySource(); 215 break; 216 217 case Score: 218 return query->results().first()->score(); 219 break; 220 221 case AlbumPos: 222 QString tPos; 223 if ( query->results().first()->albumpos() != 0 ) 224 { 225 tPos = QString::number( query->results().first()->albumpos() ); 226 if( query->results().first()->discnumber() == 0 ) 227 return tPos; 228 else 229 return QString( "%1.%2" ).arg( QString::number( query->results().first()->discnumber() ) ) 230 .arg( tPos ); 231 } 232 break; 233 } 234 } 235 236 return QVariant(); 237} 238 239 240QVariant 241TrackModel::headerData( int section, Qt::Orientation orientation, int role ) const 242{ 243 Q_UNUSED( orientation ); 244 245 QStringList headers; 246 headers << tr( "Artist" ) << tr( "Title" ) << tr( "Composer" ) << tr( "Album" ) << tr( "Track" ) << tr( "Duration" ) << tr( "Bitrate" ) << tr( "Age" ) << tr( "Year" ) << tr( "Size" ) << tr( "Origin" ) << tr( "Score" ); 247 if ( role == Qt::DisplayRole && section >= 0 ) 248 { 249 return headers.at( section ); 250 } 251 252 if ( role == Qt::TextAlignmentRole ) 253 { 254 return QVariant( columnAlignment( section ) ); 255 } 256 257 return QVariant(); 258} 259 260 261void 262TrackModel::setCurrentItem( const QModelIndex& index ) 263{ 264 TrackModelItem* oldEntry = itemFromIndex( m_currentIndex ); 265 if ( oldEntry ) 266 { 267 oldEntry->setIsPlaying( false ); 268 } 269 270 TrackModelItem* entry = itemFromIndex( index ); 271 if ( index.isValid() && entry && !entry->query().isNull() ) 272 { 273 m_currentIndex = index; 274 m_currentUuid = entry->query()->id(); 275 entry->setIsPlaying( true ); 276 } 277 else 278 { 279 m_currentIndex = QModelIndex(); 280 m_currentUuid = QString(); 281 } 282} 283 284 285Qt::DropActions 286TrackModel::supportedDropActions() const 287{ 288 return Qt::CopyAction | Qt::MoveAction; 289} 290 291 292Qt::ItemFlags 293TrackModel::flags( const QModelIndex& index ) const 294{ 295 Qt::ItemFlags defaultFlags = QAbstractItemModel::flags( index ); 296 297 if ( index.isValid() && index.column() == 0 ) 298 return Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled | defaultFlags; 299 else 300 return Qt::ItemIsDropEnabled | defaultFlags; 301} 302 303 304QStringList 305TrackModel::mimeTypes() const 306{ 307 QStringList types; 308 types << "application/tomahawk.query.list"; 309 return types; 310} 311 312 313QMimeData* 314TrackModel::mimeData( const QModelIndexList &indexes ) const 315{ 316 qDebug() << Q_FUNC_INFO; 317 318 QByteArray queryData; 319 QDataStream queryStream( &queryData, QIODevice::WriteOnly ); 320 321 foreach ( const QModelIndex& i, indexes ) 322 { 323 if ( i.column() > 0 ) 324 continue; 325 326 QModelIndex idx = index( i.row(), 0, i.parent() ); 327 TrackModelItem* item = itemFromIndex( idx ); 328 if ( item ) 329 { 330 const query_ptr& query = item->query(); 331 queryStream << qlonglong( &query ); 332 } 333 } 334 335 QMimeData* mimeData = new QMimeData(); 336 mimeData->setData( "application/tomahawk.query.list", queryData ); 337 338 return mimeData; 339} 340 341 342void 343TrackModel::clear() 344{ 345 if ( rowCount( QModelIndex() ) ) 346 { 347 emit loadingFinished(); 348 349 emit beginResetModel(); 350 delete m_rootItem; 351 m_rootItem = 0; 352 m_rootItem = new TrackModelItem( 0, this ); 353 emit endResetModel(); 354 } 355} 356 357 358QList< query_ptr > 359TrackModel::queries() const 360{ 361 Q_ASSERT( m_rootItem ); 362 363 QList< query_ptr > tracks; 364 foreach ( TrackModelItem* item, m_rootItem->children ) 365 { 366 tracks << item->query(); 367 } 368 369 return tracks; 370} 371 372 373void 374TrackModel::append( const Tomahawk::query_ptr& query ) 375{ 376 insert( query, rowCount( QModelIndex() ) ); 377} 378 379 380void 381TrackModel::append( const QList< Tomahawk::query_ptr >& queries ) 382{ 383 insert( queries, rowCount( QModelIndex() ) ); 384} 385 386 387void 388TrackModel::insert( const Tomahawk::query_ptr& query, int row ) 389{ 390 if ( query.isNull() ) 391 return; 392 393 QList< Tomahawk::query_ptr > ql; 394 ql << query; 395 396 insert( ql, row ); 397} 398 399 400void 401TrackModel::insert( const QList< Tomahawk::query_ptr >& queries, int row ) 402{ 403 if ( !queries.count() ) 404 { 405 emit trackCountChanged( rowCount( QModelIndex() ) ); 406 return; 407 } 408 409 int c = row; 410 QPair< int, int > crows; 411 crows.first = c; 412 crows.second = c + queries.count() - 1; 413 414 emit beginInsertRows( QModelIndex(), crows.first, crows.second ); 415 416 int i = 0; 417 TrackModelItem* plitem; 418 foreach( const query_ptr& query, queries ) 419 { 420 plitem = new TrackModelItem( query, m_rootItem, row + i ); 421 plitem->index = createIndex( row + i, 0, plitem ); 422 i++; 423 424 if ( query->id() == currentItemUuid() ) 425 setCurrentItem( plitem->index ); 426 427 connect( plitem, SIGNAL( dataChanged() ), SLOT( onDataChanged() ) ); 428 } 429 430 emit endInsertRows(); 431 emit trackCountChanged( rowCount( QModelIndex() ) ); 432} 433 434 435void 436TrackModel::remove( int row, bool moreToCome ) 437{ 438 remove( index( row, 0, QModelIndex() ), moreToCome ); 439} 440 441 442void 443TrackModel::remove( const QModelIndex& index, bool moreToCome ) 444{ 445 if ( QThread::currentThread() != thread() ) 446 { 447 QMetaObject::invokeMethod( this, "remove", 448 Qt::QueuedConnection, 449 Q_ARG(const QModelIndex, index), 450 Q_ARG(bool, moreToCome) ); 451 return; 452 } 453 454 if ( index.column() > 0 ) 455 return; 456 457 TrackModelItem* item = itemFromIndex( index ); 458 if ( item ) 459 { 460 emit beginRemoveRows( index.parent(), index.row(), index.row() ); 461 delete item; 462 emit endRemoveRows(); 463 } 464 465 if ( !moreToCome ) 466 emit trackCountChanged( rowCount( QModelIndex() ) ); 467} 468 469 470void 471TrackModel::remove( const QList<QModelIndex>& indexes ) 472{ 473 QList<QPersistentModelIndex> pil; 474 foreach ( const QModelIndex& idx, indexes ) 475 { 476 pil << idx; 477 } 478 479 remove( pil ); 480} 481 482 483void 484TrackModel::remove( const QList<QPersistentModelIndex>& indexes ) 485{ 486 QList<QPersistentModelIndex> finalIndexes; 487 foreach ( const QPersistentModelIndex index, indexes ) 488 { 489 if ( index.column() > 0 ) 490 continue; 491 finalIndexes << index; 492 } 493 494 for ( int i = 0; i < finalIndexes.count(); i++ ) 495 { 496 remove( finalIndexes.at( i ), i + 1 != finalIndexes.count() ); 497 } 498} 499 500 501TrackModelItem* 502TrackModel::itemFromIndex( const QModelIndex& index ) const 503{ 504 if ( index.isValid() ) 505 { 506 return static_cast<TrackModelItem*>( index.internalPointer() ); 507 } 508 else 509 { 510 return m_rootItem; 511 } 512} 513 514 515void 516TrackModel::onPlaybackStarted( const Tomahawk::result_ptr& result ) 517{ 518 TrackModelItem* oldEntry = itemFromIndex( m_currentIndex ); 519 if ( oldEntry && ( oldEntry->query().isNull() || !oldEntry->query()->numResults() || oldEntry->query()->results().first().data() != result.data() ) ) 520 { 521 oldEntry->setIsPlaying( false ); 522 } 523} 524 525 526void 527TrackModel::onPlaybackStopped() 528{ 529 TrackModelItem* oldEntry = itemFromIndex( m_currentIndex ); 530 if ( oldEntry ) 531 { 532 oldEntry->setIsPlaying( false ); 533 } 534} 535 536 537void 538TrackModel::ensureResolved() 539{ 540 for( int i = 0; i < rowCount( QModelIndex() ); i++ ) 541 { 542 query_ptr query = itemFromIndex( index( i, 0, QModelIndex() ) )->query(); 543 544 if ( !query->resolvingFinished() ) 545 Pipeline::instance()->resolve( query ); 546 } 547} 548 549 550void 551TrackModel::setStyle( TrackModel::TrackItemStyle style ) 552{ 553 m_style = style; 554} 555 556 557Qt::Alignment 558TrackModel::columnAlignment( int column ) const 559{ 560 switch( column ) 561 { 562 case Age: 563 case AlbumPos: 564 case Bitrate: 565 case Duration: 566 case Filesize: 567 case Year: 568 return Qt::AlignHCenter; 569 break; 570 571 default: 572 return Qt::AlignLeft; 573 } 574}