PageRenderTime 363ms CodeModel.GetById 60ms app.highlight 105ms RepoModel.GetById 56ms app.codeStats 136ms

/src/libtomahawk/playlist/trackmodel.cpp

http://github.com/tomahawk-player/tomahawk
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}