/src/libtomahawk/playlist/trackmodel.cpp

http://github.com/tomahawk-player/tomahawk · C++ · 574 lines · 436 code · 120 blank · 18 comment · 69 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. #include "trackmodel.h"
  20. #include <QDateTime>
  21. #include <QMimeData>
  22. #include <QTreeView>
  23. #include "audio/audioengine.h"
  24. #include "utils/tomahawkutils.h"
  25. #include "artist.h"
  26. #include "album.h"
  27. #include "pipeline.h"
  28. #include "utils/logger.h"
  29. using namespace Tomahawk;
  30. TrackModel::TrackModel( QObject* parent )
  31. : QAbstractItemModel( parent )
  32. , m_rootItem( new TrackModelItem( 0, this ) )
  33. , m_readOnly( true )
  34. , m_style( Detailed )
  35. {
  36. connect( AudioEngine::instance(), SIGNAL( started( Tomahawk::result_ptr ) ), SLOT( onPlaybackStarted( Tomahawk::result_ptr ) ), Qt::DirectConnection );
  37. connect( AudioEngine::instance(), SIGNAL( stopped() ), SLOT( onPlaybackStopped() ), Qt::DirectConnection );
  38. }
  39. TrackModel::~TrackModel()
  40. {
  41. }
  42. QModelIndex
  43. TrackModel::index( int row, int column, const QModelIndex& parent ) const
  44. {
  45. if ( !m_rootItem || row < 0 || column < 0 )
  46. return QModelIndex();
  47. TrackModelItem* parentItem = itemFromIndex( parent );
  48. TrackModelItem* childItem = parentItem->children.value( row );
  49. if ( !childItem )
  50. return QModelIndex();
  51. return createIndex( row, column, childItem );
  52. }
  53. int
  54. TrackModel::rowCount( const QModelIndex& parent ) const
  55. {
  56. if ( parent.column() > 0 )
  57. return 0;
  58. TrackModelItem* parentItem = itemFromIndex( parent );
  59. if ( !parentItem )
  60. return 0;
  61. return parentItem->children.count();
  62. }
  63. int
  64. TrackModel::columnCount( const QModelIndex& parent ) const
  65. {
  66. Q_UNUSED( parent );
  67. switch ( m_style )
  68. {
  69. case Short:
  70. case ShortWithAvatars:
  71. return 1;
  72. break;
  73. case Detailed:
  74. default:
  75. return 12;
  76. break;
  77. }
  78. }
  79. QModelIndex
  80. TrackModel::parent( const QModelIndex& child ) const
  81. {
  82. TrackModelItem* entry = itemFromIndex( child );
  83. if ( !entry )
  84. return QModelIndex();
  85. TrackModelItem* parentEntry = entry->parent;
  86. if ( !parentEntry )
  87. return QModelIndex();
  88. TrackModelItem* grandparentEntry = parentEntry->parent;
  89. if ( !grandparentEntry )
  90. return QModelIndex();
  91. int row = grandparentEntry->children.indexOf( parentEntry );
  92. return createIndex( row, 0, parentEntry );
  93. }
  94. QVariant
  95. TrackModel::data( const QModelIndex& index, int role ) const
  96. {
  97. TrackModelItem* entry = itemFromIndex( index );
  98. if ( !entry )
  99. return QVariant();
  100. if ( role == Qt::DecorationRole )
  101. {
  102. return QVariant();
  103. }
  104. if ( role == Qt::SizeHintRole )
  105. {
  106. return QSize( 0, 18 );
  107. }
  108. if ( role == Qt::TextAlignmentRole )
  109. {
  110. return QVariant( columnAlignment( index.column() ) );
  111. }
  112. if ( role == StyleRole )
  113. {
  114. return m_style;
  115. }
  116. if ( role != Qt::DisplayRole ) // && role != Qt::ToolTipRole )
  117. return QVariant();
  118. const query_ptr& query = entry->query();
  119. if ( !query->numResults() )
  120. {
  121. switch( index.column() )
  122. {
  123. case Artist:
  124. return query->artist();
  125. break;
  126. case Track:
  127. return query->track();
  128. break;
  129. case Album:
  130. return query->album();
  131. break;
  132. }
  133. }
  134. else
  135. {
  136. switch( index.column() )
  137. {
  138. case Artist:
  139. return query->results().first()->artist()->name();
  140. break;
  141. case Track:
  142. return query->results().first()->track();
  143. break;
  144. case Album:
  145. return query->results().first()->album()->name();
  146. break;
  147. case Composer:
  148. if ( !query->results().first()->composer().isNull() )
  149. return query->results().first()->composer()->name();
  150. break;
  151. case Duration:
  152. return TomahawkUtils::timeToString( query->results().first()->duration() );
  153. break;
  154. case Bitrate:
  155. if ( query->results().first()->bitrate() > 0 )
  156. return query->results().first()->bitrate();
  157. break;
  158. case Age:
  159. return TomahawkUtils::ageToString( QDateTime::fromTime_t( query->results().first()->modificationTime() ) );
  160. break;
  161. case Year:
  162. if ( query->results().first()->year() != 0 )
  163. return query->results().first()->year();
  164. break;
  165. case Filesize:
  166. return TomahawkUtils::filesizeToString( query->results().first()->size() );
  167. break;
  168. case Origin:
  169. return query->results().first()->friendlySource();
  170. break;
  171. case Score:
  172. return query->results().first()->score();
  173. break;
  174. case AlbumPos:
  175. QString tPos;
  176. if ( query->results().first()->albumpos() != 0 )
  177. {
  178. tPos = QString::number( query->results().first()->albumpos() );
  179. if( query->results().first()->discnumber() == 0 )
  180. return tPos;
  181. else
  182. return QString( "%1.%2" ).arg( QString::number( query->results().first()->discnumber() ) )
  183. .arg( tPos );
  184. }
  185. break;
  186. }
  187. }
  188. return QVariant();
  189. }
  190. QVariant
  191. TrackModel::headerData( int section, Qt::Orientation orientation, int role ) const
  192. {
  193. Q_UNUSED( orientation );
  194. QStringList headers;
  195. 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" );
  196. if ( role == Qt::DisplayRole && section >= 0 )
  197. {
  198. return headers.at( section );
  199. }
  200. if ( role == Qt::TextAlignmentRole )
  201. {
  202. return QVariant( columnAlignment( section ) );
  203. }
  204. return QVariant();
  205. }
  206. void
  207. TrackModel::setCurrentItem( const QModelIndex& index )
  208. {
  209. TrackModelItem* oldEntry = itemFromIndex( m_currentIndex );
  210. if ( oldEntry )
  211. {
  212. oldEntry->setIsPlaying( false );
  213. }
  214. TrackModelItem* entry = itemFromIndex( index );
  215. if ( index.isValid() && entry && !entry->query().isNull() )
  216. {
  217. m_currentIndex = index;
  218. m_currentUuid = entry->query()->id();
  219. entry->setIsPlaying( true );
  220. }
  221. else
  222. {
  223. m_currentIndex = QModelIndex();
  224. m_currentUuid = QString();
  225. }
  226. }
  227. Qt::DropActions
  228. TrackModel::supportedDropActions() const
  229. {
  230. return Qt::CopyAction | Qt::MoveAction;
  231. }
  232. Qt::ItemFlags
  233. TrackModel::flags( const QModelIndex& index ) const
  234. {
  235. Qt::ItemFlags defaultFlags = QAbstractItemModel::flags( index );
  236. if ( index.isValid() && index.column() == 0 )
  237. return Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled | defaultFlags;
  238. else
  239. return Qt::ItemIsDropEnabled | defaultFlags;
  240. }
  241. QStringList
  242. TrackModel::mimeTypes() const
  243. {
  244. QStringList types;
  245. types << "application/tomahawk.query.list";
  246. return types;
  247. }
  248. QMimeData*
  249. TrackModel::mimeData( const QModelIndexList &indexes ) const
  250. {
  251. qDebug() << Q_FUNC_INFO;
  252. QByteArray queryData;
  253. QDataStream queryStream( &queryData, QIODevice::WriteOnly );
  254. foreach ( const QModelIndex& i, indexes )
  255. {
  256. if ( i.column() > 0 )
  257. continue;
  258. QModelIndex idx = index( i.row(), 0, i.parent() );
  259. TrackModelItem* item = itemFromIndex( idx );
  260. if ( item )
  261. {
  262. const query_ptr& query = item->query();
  263. queryStream << qlonglong( &query );
  264. }
  265. }
  266. QMimeData* mimeData = new QMimeData();
  267. mimeData->setData( "application/tomahawk.query.list", queryData );
  268. return mimeData;
  269. }
  270. void
  271. TrackModel::clear()
  272. {
  273. if ( rowCount( QModelIndex() ) )
  274. {
  275. emit loadingFinished();
  276. emit beginResetModel();
  277. delete m_rootItem;
  278. m_rootItem = 0;
  279. m_rootItem = new TrackModelItem( 0, this );
  280. emit endResetModel();
  281. }
  282. }
  283. QList< query_ptr >
  284. TrackModel::queries() const
  285. {
  286. Q_ASSERT( m_rootItem );
  287. QList< query_ptr > tracks;
  288. foreach ( TrackModelItem* item, m_rootItem->children )
  289. {
  290. tracks << item->query();
  291. }
  292. return tracks;
  293. }
  294. void
  295. TrackModel::append( const Tomahawk::query_ptr& query )
  296. {
  297. insert( query, rowCount( QModelIndex() ) );
  298. }
  299. void
  300. TrackModel::append( const QList< Tomahawk::query_ptr >& queries )
  301. {
  302. insert( queries, rowCount( QModelIndex() ) );
  303. }
  304. void
  305. TrackModel::insert( const Tomahawk::query_ptr& query, int row )
  306. {
  307. if ( query.isNull() )
  308. return;
  309. QList< Tomahawk::query_ptr > ql;
  310. ql << query;
  311. insert( ql, row );
  312. }
  313. void
  314. TrackModel::insert( const QList< Tomahawk::query_ptr >& queries, int row )
  315. {
  316. if ( !queries.count() )
  317. {
  318. emit trackCountChanged( rowCount( QModelIndex() ) );
  319. return;
  320. }
  321. int c = row;
  322. QPair< int, int > crows;
  323. crows.first = c;
  324. crows.second = c + queries.count() - 1;
  325. emit beginInsertRows( QModelIndex(), crows.first, crows.second );
  326. int i = 0;
  327. TrackModelItem* plitem;
  328. foreach( const query_ptr& query, queries )
  329. {
  330. plitem = new TrackModelItem( query, m_rootItem, row + i );
  331. plitem->index = createIndex( row + i, 0, plitem );
  332. i++;
  333. if ( query->id() == currentItemUuid() )
  334. setCurrentItem( plitem->index );
  335. connect( plitem, SIGNAL( dataChanged() ), SLOT( onDataChanged() ) );
  336. }
  337. emit endInsertRows();
  338. emit trackCountChanged( rowCount( QModelIndex() ) );
  339. }
  340. void
  341. TrackModel::remove( int row, bool moreToCome )
  342. {
  343. remove( index( row, 0, QModelIndex() ), moreToCome );
  344. }
  345. void
  346. TrackModel::remove( const QModelIndex& index, bool moreToCome )
  347. {
  348. if ( QThread::currentThread() != thread() )
  349. {
  350. QMetaObject::invokeMethod( this, "remove",
  351. Qt::QueuedConnection,
  352. Q_ARG(const QModelIndex, index),
  353. Q_ARG(bool, moreToCome) );
  354. return;
  355. }
  356. if ( index.column() > 0 )
  357. return;
  358. TrackModelItem* item = itemFromIndex( index );
  359. if ( item )
  360. {
  361. emit beginRemoveRows( index.parent(), index.row(), index.row() );
  362. delete item;
  363. emit endRemoveRows();
  364. }
  365. if ( !moreToCome )
  366. emit trackCountChanged( rowCount( QModelIndex() ) );
  367. }
  368. void
  369. TrackModel::remove( const QList<QModelIndex>& indexes )
  370. {
  371. QList<QPersistentModelIndex> pil;
  372. foreach ( const QModelIndex& idx, indexes )
  373. {
  374. pil << idx;
  375. }
  376. remove( pil );
  377. }
  378. void
  379. TrackModel::remove( const QList<QPersistentModelIndex>& indexes )
  380. {
  381. QList<QPersistentModelIndex> finalIndexes;
  382. foreach ( const QPersistentModelIndex index, indexes )
  383. {
  384. if ( index.column() > 0 )
  385. continue;
  386. finalIndexes << index;
  387. }
  388. for ( int i = 0; i < finalIndexes.count(); i++ )
  389. {
  390. remove( finalIndexes.at( i ), i + 1 != finalIndexes.count() );
  391. }
  392. }
  393. TrackModelItem*
  394. TrackModel::itemFromIndex( const QModelIndex& index ) const
  395. {
  396. if ( index.isValid() )
  397. {
  398. return static_cast<TrackModelItem*>( index.internalPointer() );
  399. }
  400. else
  401. {
  402. return m_rootItem;
  403. }
  404. }
  405. void
  406. TrackModel::onPlaybackStarted( const Tomahawk::result_ptr& result )
  407. {
  408. TrackModelItem* oldEntry = itemFromIndex( m_currentIndex );
  409. if ( oldEntry && ( oldEntry->query().isNull() || !oldEntry->query()->numResults() || oldEntry->query()->results().first().data() != result.data() ) )
  410. {
  411. oldEntry->setIsPlaying( false );
  412. }
  413. }
  414. void
  415. TrackModel::onPlaybackStopped()
  416. {
  417. TrackModelItem* oldEntry = itemFromIndex( m_currentIndex );
  418. if ( oldEntry )
  419. {
  420. oldEntry->setIsPlaying( false );
  421. }
  422. }
  423. void
  424. TrackModel::ensureResolved()
  425. {
  426. for( int i = 0; i < rowCount( QModelIndex() ); i++ )
  427. {
  428. query_ptr query = itemFromIndex( index( i, 0, QModelIndex() ) )->query();
  429. if ( !query->resolvingFinished() )
  430. Pipeline::instance()->resolve( query );
  431. }
  432. }
  433. void
  434. TrackModel::setStyle( TrackModel::TrackItemStyle style )
  435. {
  436. m_style = style;
  437. }
  438. Qt::Alignment
  439. TrackModel::columnAlignment( int column ) const
  440. {
  441. switch( column )
  442. {
  443. case Age:
  444. case AlbumPos:
  445. case Bitrate:
  446. case Duration:
  447. case Filesize:
  448. case Year:
  449. return Qt::AlignHCenter;
  450. break;
  451. default:
  452. return Qt::AlignLeft;
  453. }
  454. }