1 /****************************************************************************************
2  * Copyright (c) 2009-2010 Bart Cerneels <bart.cerneels@kde.org>                        *
3  *                                                                                      *
4  * This program is free software; you can redistribute it and/or modify it under        *
5  * the terms of the GNU General Public License as published by the Free Software        *
6  * Foundation; either version 2 of the License, or (at your option) any later           *
7  * version.                                                                             *
8  *                                                                                      *
9  * This program is distributed in the hope that it will be useful, but WITHOUT ANY      *
10  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A      *
11  * PARTICULAR PURPOSE. See the GNU General Public License for more details.             *
12  *                                                                                      *
13  * You should have received a copy of the GNU General Public License along with         *
14  * this program.  If not, see <http://www.gnu.org/licenses/>.                           *
15  ****************************************************************************************/
16 
17 #define DEBUG_PREFIX "PlaylistBrowserModel"
18 
19 #include "PlaylistBrowserModel.h"
20 
21 #include "AmarokMimeData.h"
22 #include "playlistmanager/PlaylistManager.h"
23 #include "playlist/PlaylistController.h"
24 #include "playlist/PlaylistModel.h"
25 #include "core/support/Debug.h"
26 #include "widgets/PrettyTreeRoles.h"
27 
28 #include <QIcon>
29 
30 #include <QAction>
31 
32 using namespace PlaylistBrowserNS;
33 
34 // to be used with qSort.
35 static bool
lessThanPlaylistTitles(const Playlists::PlaylistPtr & lhs,const Playlists::PlaylistPtr & rhs)36 lessThanPlaylistTitles( const Playlists::PlaylistPtr &lhs, const Playlists::PlaylistPtr &rhs )
37 {
38     return lhs->prettyName().toLower() < rhs->prettyName().toLower();
39 }
40 
PlaylistBrowserModel(int playlistCategory)41 PlaylistBrowserModel::PlaylistBrowserModel( int playlistCategory )
42     : m_playlistCategory( playlistCategory )
43 {
44     connect( The::playlistManager(), &PlaylistManager::updated, this, &PlaylistBrowserModel::slotUpdate );
45 
46     connect( The::playlistManager(), &PlaylistManager::playlistAdded,
47              this, &PlaylistBrowserModel::slotPlaylistAdded );
48     connect( The::playlistManager(), &PlaylistManager::playlistRemoved,
49              this, &PlaylistBrowserModel::slotPlaylistRemoved );
50     connect( The::playlistManager(), &PlaylistManager::playlistUpdated,
51              this, &PlaylistBrowserModel::slotPlaylistUpdated );
52 
53     connect( The::playlistManager(), &PlaylistManager::renamePlaylist,
54              this, &PlaylistBrowserModel::slotRenamePlaylist );
55 
56     m_playlists = loadPlaylists();
57 }
58 
59 QVariant
data(const QModelIndex & index,int role) const60 PlaylistBrowserModel::data( const QModelIndex &index, int role ) const
61 {
62     int row = REMOVE_TRACK_MASK(index.internalId());
63     Playlists::PlaylistPtr playlist = m_playlists.value( row );
64 
65     QString name;
66     QIcon icon;
67     int playlistCount = 0;
68     QList<QAction *> providerActions;
69     QList<Playlists::PlaylistProvider *> providers =
70         The::playlistManager()->getProvidersForPlaylist( playlist );
71     Playlists::PlaylistProvider *provider = providers.count() == 1 ? providers.first() : 0;
72     Meta::TrackPtr track;
73 
74     switch( index.column() )
75     {
76         case PlaylistBrowserModel::PlaylistItemColumn: //playlist or track data
77         {
78             if( IS_TRACK(index) )
79             {
80                 track = playlist->tracks()[index.row()];
81                 name = track->prettyName();
82                 icon = QIcon::fromTheme( QStringLiteral("amarok_track") );
83             }
84             else
85             {
86                 name = playlist->prettyName();
87                 icon = QIcon::fromTheme( QStringLiteral("amarok_playlist") );
88             }
89             break;
90         }
91         case PlaylistBrowserModel::LabelColumn: //group
92         {
93             if( !playlist->groups().isEmpty() )
94             {
95                 name = playlist->groups().first();
96                 icon = QIcon::fromTheme( QStringLiteral("folder") );
97             }
98             break;
99         }
100 
101         case PlaylistBrowserModel::ProviderColumn: //source
102         {
103             if( providers.count() > 1 )
104             {
105                 QVariantList nameData;
106                 QVariantList iconData;
107                 QVariantList playlistCountData;
108                 QVariantList providerActionsData;
109                 foreach( Playlists::PlaylistProvider *provider, providers )
110                 {
111                     name = provider->prettyName();
112                     nameData << name;
113                     icon = provider->icon();
114                     iconData << QVariant( icon );
115                     playlistCount = provider->playlists().count();
116                     if( playlistCount >= 0 )
117                         playlistCountData << i18ncp(
118                                 "number of playlists from one source",
119                                 "One Playlist", "%1 playlists",
120                                 playlistCount );
121                     else
122                         playlistCountData << i18nc(
123                                 "normally number of playlists, but they are still loading",
124                                 "Loading..." );
125                     providerActions << provider->providerActions();
126                     providerActionsData << QVariant::fromValue( providerActions );
127                 }
128                 switch( role )
129                 {
130                 case Qt::DisplayRole:
131                 case Qt::EditRole:
132                 case Qt::ToolTipRole:
133                     return nameData;
134                 case Qt::DecorationRole:
135                     return iconData;
136                 case PrettyTreeRoles::ByLineRole:
137                     return playlistCountData;
138                 case PrettyTreeRoles::DecoratorRoleCount:
139                     return providerActions.count();
140                 case PrettyTreeRoles::DecoratorRole:
141                     return providerActionsData;
142                 }
143             }
144             else if( provider )
145             {
146                 name = provider->prettyName();
147                 icon = provider->icon();
148                 playlistCount = provider->playlistCount();
149                 providerActions << provider->providerActions();
150             }
151 
152             break;
153         }
154 
155         default: break;
156     }
157 
158 
159     switch( role )
160     {
161         case Qt::DisplayRole:
162         case Qt::EditRole:
163         case Qt::ToolTipRole:
164             return name;
165         case Qt::DecorationRole:
166             return QVariant( icon );
167         case PrettyTreeRoles::ByLineRole:
168             if( IS_TRACK(index) )
169                 return QVariant();
170             else
171                 return i18ncp( "number of playlists from one source", "One playlist",
172                                "%1 playlists", playlistCount );
173         case PlaylistBrowserModel::ProviderRole:
174             return provider ? QVariant::fromValue( provider ) : QVariant();
175         case PlaylistBrowserModel::PlaylistRole:
176             return playlist ? QVariant::fromValue( playlist ) : QVariant();
177         case PlaylistBrowserModel::TrackRole:
178             return track ? QVariant::fromValue( track ) : QVariant();
179 
180         default:
181             return QVariant();
182     }
183 }
184 
185 bool
setData(const QModelIndex & idx,const QVariant & value,int role)186 PlaylistBrowserModel::setData( const QModelIndex &idx, const QVariant &value, int role )
187 {
188 
189     if( !idx.isValid() )
190         return false;
191 
192     switch( idx.column() )
193     {
194         case ProviderColumn:
195         {
196             if( role == Qt::DisplayRole || role == Qt::EditRole )
197             {
198                 Playlists::PlaylistProvider *provider = getProviderByName( value.toString() );
199                 if( !provider )
200                     return false;
201 
202                 if( IS_TRACK( idx ) )
203                 {
204                     Meta::TrackPtr track = trackFromIndex( idx );
205                     if( !track )
206                         return false;
207                     debug() << QStringLiteral( "Copy track \"%1\" to \"%2\"." )
208                             .arg( track->prettyName(),  provider->prettyName() );
209     //                return !provider->addTrack( track ).isNull();
210                     provider->addTrack( track ); //ignore result since UmsPodcastProvider returns NULL
211                     return true;
212                 }
213                 else
214                 {
215                     Playlists::PlaylistPtr playlist = playlistFromIndex( idx );
216                     if( !playlist || ( playlist->provider() == provider ) )
217                         return false;
218 
219                     foreach( Playlists::PlaylistPtr tempPlaylist , provider->playlists() )
220                     {
221                         if ( tempPlaylist->name() == playlist->name() )
222                             return false;
223                     }
224 
225                     debug() << QStringLiteral( "Copy playlist \"%1\" to \"%2\"." )
226                             .arg( playlist->prettyName(), provider->prettyName() );
227 
228                     return !provider->addPlaylist( playlist ).isNull();
229                 }
230             }
231 
232             //return true even for the data we didn't handle to get QAbstractItemModel::setItemData to work
233             //TODO: implement setItemData()
234             return true;
235         }
236         case LabelColumn:
237         {
238             debug() << "changing group of item " << idx.internalId() << " to " << value.toString();
239             Playlists::PlaylistPtr item = m_playlists.value( idx.internalId() );
240             item->setGroups( value.toStringList() );
241 
242             return true;
243         }
244     }
245 
246     return false;
247 }
248 
249 QModelIndex
index(int row,int column,const QModelIndex & parent) const250 PlaylistBrowserModel::index( int row, int column, const QModelIndex &parent) const
251 {
252     if( !parent.isValid() )
253     {
254         if( row >= 0 && row < m_playlists.count() )
255             return createIndex( row, column, row );
256     }
257     else //if it has a parent it is a track
258     {
259         //but check if the playlist indeed has that track
260         Playlists::PlaylistPtr playlist = m_playlists.value( parent.row() );
261         if( row < playlist->tracks().count() )
262             return createIndex( row, column, SET_TRACK_MASK(parent.row()) );
263     }
264 
265     return QModelIndex();
266 }
267 
268 QModelIndex
parent(const QModelIndex & index) const269 PlaylistBrowserModel::parent( const QModelIndex &index ) const
270 {
271     if( IS_TRACK(index) )
272     {
273         int row = REMOVE_TRACK_MASK(index.internalId());
274         return this->index( row, index.column(), QModelIndex() );
275     }
276 
277     return QModelIndex();
278 }
279 
280 bool
hasChildren(const QModelIndex & parent) const281 PlaylistBrowserModel::hasChildren( const QModelIndex &parent ) const
282 {
283     if( parent.column() > 0 )
284         return false;
285     if( !parent.isValid() )
286     {
287         return !m_playlists.isEmpty();
288     }
289     else if( !IS_TRACK(parent) )
290     {
291         Playlists::PlaylistPtr playlist = m_playlists.value( parent.internalId() );
292         return playlist->trackCount() != 0; //-1 might mean there are tracks, but not yet loaded.
293     }
294 
295     return false;
296 }
297 
298 int
rowCount(const QModelIndex & parent) const299 PlaylistBrowserModel::rowCount( const QModelIndex &parent ) const
300 {
301     if( parent.column() > 0 )
302         return 0;
303 
304     if( !parent.isValid() )
305     {
306         return m_playlists.count();
307     }
308     else if( !IS_TRACK(parent) )
309     {
310         Playlists::PlaylistPtr playlist = m_playlists.value( parent.internalId() );
311         return playlist->trackCount();
312     }
313 
314     return 0;
315 }
316 
317 int
columnCount(const QModelIndex & parent) const318 PlaylistBrowserModel::columnCount( const QModelIndex &parent ) const
319 {
320     if( !parent.isValid() ) //for playlists (children of root)
321         return 3; //name, group and provider
322 
323     //for tracks
324     return 1; //only name
325 }
326 
327 bool
canFetchMore(const QModelIndex & parent) const328 PlaylistBrowserModel::canFetchMore( const QModelIndex &parent ) const
329 {
330     if( parent.column() > 0 )
331         return false;
332 
333     if( !parent.isValid() )
334     {
335         return false;
336     }
337     else if( !IS_TRACK(parent) )
338     {
339         Playlists::PlaylistPtr playlist = m_playlists.value( parent.internalId() );
340         //TODO: implement incremental loading of tracks by checking for ==
341         if( playlist->trackCount() != playlist->tracks().count() )
342             return true; //tracks still need to be loaded.
343     }
344 
345     return false;
346 }
347 
348 void
fetchMore(const QModelIndex & parent)349 PlaylistBrowserModel::fetchMore ( const QModelIndex &parent )
350 {
351     if( parent.column() > 0 )
352         return;
353 
354     //TODO: load playlists dynamically from provider
355     if( !parent.isValid() )
356         return;
357 
358     if( !IS_TRACK(parent) )
359     {
360         Playlists::PlaylistPtr playlist = m_playlists.value( parent.internalId() );
361          // TODO: following doesn't seem to be needed, PlaylistBrowserModel seems to be able to cope with async track loading fine
362         playlist->makeLoadingSync();
363         playlist->triggerTrackLoad();
364     }
365 }
366 
367 Qt::ItemFlags
flags(const QModelIndex & idx) const368 PlaylistBrowserModel::flags( const QModelIndex &idx ) const
369 {
370     //Both providers and groups can be empty. QtGroupingProxy makes empty groups from the data in
371     //the rootnode (here an invalid QModelIndex).
372     //TODO: editable only if provider is writable.
373     if( idx.column() == PlaylistBrowserModel::ProviderColumn )
374         return Qt::ItemIsEnabled | Qt::ItemIsEditable;
375 
376     if( idx.column() == PlaylistBrowserModel::LabelColumn )
377         return Qt::ItemIsEnabled | Qt::ItemIsEditable | Qt::ItemIsDropEnabled;
378 
379     if( !idx.isValid() )
380         return Qt::ItemIsDropEnabled;
381 
382     if( IS_TRACK(idx) )
383             return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled;
384 
385     //item is a playlist
386     return Qt::ItemIsEnabled | Qt::ItemIsEditable | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled |
387            Qt::ItemIsDropEnabled;
388 }
389 
390 QVariant
headerData(int section,Qt::Orientation orientation,int role) const391 PlaylistBrowserModel::headerData( int section, Qt::Orientation orientation, int role ) const
392 {
393     if( orientation == Qt::Horizontal && role == Qt::DisplayRole )
394     {
395         switch( section )
396         {
397             case PlaylistBrowserModel::PlaylistItemColumn: return i18n("Name");
398             case PlaylistBrowserModel::LabelColumn: return i18n("Group");
399             case PlaylistBrowserModel::ProviderColumn: return i18n("Source");
400             default: return QVariant();
401         }
402     }
403 
404     return QVariant();
405 }
406 
407 QStringList
mimeTypes() const408 PlaylistBrowserModel::mimeTypes() const
409 {
410     QStringList ret;
411     ret << AmarokMimeData::PLAYLIST_MIME;
412     ret << AmarokMimeData::TRACK_MIME;
413     return ret;
414 }
415 
416 QMimeData*
mimeData(const QModelIndexList & indices) const417 PlaylistBrowserModel::mimeData( const QModelIndexList &indices ) const
418 {
419     AmarokMimeData* mime = new AmarokMimeData();
420 
421     Playlists::PlaylistList playlists;
422     Meta::TrackList tracks;
423 
424     foreach( const QModelIndex &index, indices )
425     {
426         if( IS_TRACK(index) )
427             tracks << trackFromIndex( index );
428         else
429             playlists << m_playlists.value( index.internalId() );
430     }
431 
432     mime->setPlaylists( playlists );
433     mime->setTracks( tracks );
434 
435     return mime;
436 }
437 
438 bool
dropMimeData(const QMimeData * data,Qt::DropAction action,int row,int column,const QModelIndex & parent)439 PlaylistBrowserModel::dropMimeData( const QMimeData *data, Qt::DropAction action, int row, int column,
440                                  const QModelIndex &parent )
441 {
442     DEBUG_BLOCK
443     debug() << "Dropped on" << parent << "row" << row << "column" << column << "action" << action;
444     if( action == Qt::IgnoreAction )
445         return true;
446 
447     //drop on track is not possible
448     if( IS_TRACK(parent) )
449         return false;
450 
451     const AmarokMimeData* amarokMime = dynamic_cast<const AmarokMimeData*>( data );
452     if( !amarokMime )
453         return false;
454 
455     if( data->hasFormat( AmarokMimeData::PLAYLIST_MIME ) )
456     {
457         // TODO: is this ever called????
458         Playlists::PlaylistList playlists = amarokMime->playlists();
459 
460         foreach( Playlists::PlaylistPtr playlist, playlists )
461         {
462             if( !m_playlists.contains( playlist ) )
463                 debug() << "Unknown playlist dragged in: " << playlist->prettyName();
464         }
465 
466         return true;
467     }
468     else if( data->hasFormat( AmarokMimeData::TRACK_MIME ) )
469     {
470         Meta::TrackList tracks = amarokMime->tracks();
471         if( !parent.isValid() && row == -1 && column == -1 )
472         {
473             debug() << "Dropped tracks on empty area: create new playlist in a default provider";
474             The::playlistManager()->save( tracks, Amarok::generatePlaylistName( tracks ) );
475             return true;
476         }
477         else if( !parent.isValid() )
478         {
479             warning() << "Dropped tracks between root items, this is not supported!";
480             return false;
481         }
482         else
483         {
484             debug() << "Dropped tracks on " << parent << " at row: " << row;
485 
486             Playlists::PlaylistPtr playlist = playlistFromIndex( parent );
487             if( !playlist )
488                 return false;
489 
490             foreach( Meta::TrackPtr track, tracks )
491                 playlist->addTrack( track, ( row >= 0 ) ? row++ : -1 ); // increment only if positive
492 
493             return true;
494         }
495     }
496 
497     return false;
498 }
499 
500 void
metadataChanged(const Playlists::PlaylistPtr & playlist)501 PlaylistBrowserModel::metadataChanged( const Playlists::PlaylistPtr &playlist )
502 {
503     int indexNumber = m_playlists.indexOf( playlist );
504     if( indexNumber == -1 )
505     {
506         error() << "This playlist is not in the list of this model.";
507         return;
508     }
509     QModelIndex playlistIdx = index( indexNumber, 0 );
510     Q_EMIT dataChanged( playlistIdx, playlistIdx );
511 }
512 
513 void
trackAdded(const Playlists::PlaylistPtr & playlist,const Meta::TrackPtr & track,int position)514 PlaylistBrowserModel::trackAdded(const Playlists::PlaylistPtr &playlist, const Meta::TrackPtr &track,
515                                           int position )
516 {
517     Q_UNUSED( track );
518     int indexNumber = m_playlists.indexOf( playlist );
519     if( indexNumber == -1 )
520     {
521         error() << "This playlist is not in the list of this model.";
522         return;
523     }
524     QModelIndex playlistIdx = index( indexNumber, 0, QModelIndex() );
525     beginInsertRows( playlistIdx, position, position );
526     endInsertRows();
527 }
528 
529 void
trackRemoved(const Playlists::PlaylistPtr & playlist,int position)530 PlaylistBrowserModel::trackRemoved(const Playlists::PlaylistPtr &playlist, int position )
531 {
532     int indexNumber = m_playlists.indexOf( playlist );
533     if( indexNumber == -1 )
534     {
535         error() << "This playlist is not in the list of this model.";
536         return;
537     }
538     QModelIndex playlistIdx = index( indexNumber, 0, QModelIndex() );
539     beginRemoveRows( playlistIdx, position, position );
540     endRemoveRows();
541 }
542 
543 void
slotRenamePlaylist(Playlists::PlaylistPtr playlist)544 PlaylistBrowserModel::slotRenamePlaylist( Playlists::PlaylistPtr playlist )
545 {
546     if( !playlist->provider() || playlist->provider()->category() != m_playlistCategory )
547         return;
548 
549     int row = 0;
550     foreach( Playlists::PlaylistPtr p, m_playlists )
551     {
552         if( p == playlist )
553         {
554             Q_EMIT renameIndex( index( row, 0 ) );
555             break;
556         }
557         row++;
558     }
559 }
560 
561 void
slotUpdate(int category)562 PlaylistBrowserModel::slotUpdate( int category )
563 {
564     if( category != m_playlistCategory )
565         return;
566 
567     beginResetModel();
568 
569     foreach( Playlists::PlaylistPtr playlist, m_playlists )
570         unsubscribeFrom( playlist );
571 
572     m_playlists.clear();
573     m_playlists = loadPlaylists();
574 
575     endResetModel();
576 }
577 
578 Playlists::PlaylistList
loadPlaylists()579 PlaylistBrowserModel::loadPlaylists()
580 {
581     Playlists::PlaylistList playlists =
582             The::playlistManager()->playlistsOfCategory( m_playlistCategory );
583     QListIterator<Playlists::PlaylistPtr> i( playlists );
584 
585     debug() << playlists.count() << " playlists for category " << m_playlistCategory;
586 
587     while( i.hasNext() )
588     {
589         Playlists::PlaylistPtr playlist = i.next();
590         subscribeTo( playlist );
591     }
592 
593     qSort( playlists.begin(), playlists.end(), lessThanPlaylistTitles );
594 
595     return playlists;
596 }
597 
598 void
slotPlaylistAdded(Playlists::PlaylistPtr playlist,int category)599 PlaylistBrowserModel::slotPlaylistAdded( Playlists::PlaylistPtr playlist, int category )
600 {
601     //ignore playlists of a different category
602     if( category != m_playlistCategory )
603         return;
604 
605     subscribeTo( playlist );
606     int i;
607     for( i = 0; i < m_playlists.count(); i++ )
608     {
609         if( lessThanPlaylistTitles( playlist, m_playlists[i] ) )
610             break;
611     }
612 
613     beginInsertRows( QModelIndex(), i, i );
614     m_playlists.insert( i, playlist );
615     endInsertRows();
616 }
617 
618 void
slotPlaylistRemoved(Playlists::PlaylistPtr playlist,int category)619 PlaylistBrowserModel::slotPlaylistRemoved( Playlists::PlaylistPtr playlist, int category )
620 {
621     if( category != m_playlistCategory )
622         return;
623 
624     int position = m_playlists.indexOf( playlist );
625     if( position == -1 )
626     {
627         error() << "signal received for removed playlist not in m_playlists";
628         return;
629     }
630 
631     beginRemoveRows( QModelIndex(), position, position );
632     m_playlists.removeAt( position );
633     endRemoveRows();
634 }
635 
636 void
slotPlaylistUpdated(Playlists::PlaylistPtr playlist,int category)637 PlaylistBrowserModel::slotPlaylistUpdated( Playlists::PlaylistPtr playlist, int category )
638 {
639     if( category != m_playlistCategory )
640         return;
641 
642     int position = m_playlists.indexOf( playlist );
643     if( position == -1 )
644     {
645         error() << "signal received for updated playlist not in m_playlists";
646         return;
647     }
648 
649     //TODO: this should work by signaling a change in the model data, but QtGroupingProxy doesn't
650     //work like that ATM
651 //    const QModelIndex &idx = index( position, 0 );
652 //    Q_EMIT dataChanged( idx, idx );
653 
654     //HACK: remove and readd so QtGroupingProxy can put it in the correct groups.
655     beginRemoveRows( QModelIndex(), position, position );
656     endRemoveRows();
657 
658     beginInsertRows( QModelIndex(), position, position );
659     endInsertRows();
660 }
661 
662 Meta::TrackList
tracksFromIndexes(const QModelIndexList & list) const663 PlaylistBrowserModel::tracksFromIndexes( const QModelIndexList &list ) const
664 {
665     Meta::TrackList tracks;
666     foreach( const QModelIndex &index, list )
667     {
668         if( IS_TRACK(index) )
669             tracks << trackFromIndex( index );
670         else if( Playlists::PlaylistPtr playlist = playlistFromIndex( index ) )
671         {
672             playlist->makeLoadingSync();
673             //first trigger a load of the tracks or we'll end up with an empty list
674             playlist->triggerTrackLoad();
675             tracks << playlist->tracks();
676         }
677     }
678     return tracks;
679 }
680 
681 Meta::TrackPtr
trackFromIndex(const QModelIndex & idx) const682 PlaylistBrowserModel::trackFromIndex( const QModelIndex &idx ) const
683 {
684     if( !idx.isValid() || !IS_TRACK(idx) )
685         return Meta::TrackPtr();
686 
687     int playlistRow = REMOVE_TRACK_MASK(idx.internalId());
688     if( playlistRow >= m_playlists.count() )
689         return Meta::TrackPtr();
690 
691     Playlists::PlaylistPtr playlist = m_playlists.value( playlistRow );
692     if( playlist.isNull() || playlist->tracks().count() <= idx.row() )
693         return Meta::TrackPtr();
694 
695     return playlist->tracks()[idx.row()];
696 }
697 
698 Playlists::PlaylistPtr
playlistFromIndex(const QModelIndex & index) const699 PlaylistBrowserModel::playlistFromIndex( const QModelIndex &index ) const
700 {
701     if( !index.isValid() )
702         return Playlists::PlaylistPtr();
703 
704     return m_playlists.value( index.internalId() );
705 }
706 
707 Playlists::PlaylistProvider *
providerForIndex(const QModelIndex & idx) const708 PlaylistBrowserModel::providerForIndex( const QModelIndex &idx ) const
709 {
710     if( !idx.isValid() )
711         return 0;
712 
713     int playlistRow;
714     if( IS_TRACK( idx ) )
715         playlistRow = REMOVE_TRACK_MASK( idx.internalId() );
716     else
717         playlistRow = idx.row();
718 
719     if( playlistRow >= m_playlists.count() )
720         return 0;
721 
722     return m_playlists.at( playlistRow )->provider();
723 }
724 
725 Playlists::PlaylistProvider *
getProviderByName(const QString & name)726 PlaylistBrowserModel::getProviderByName( const QString &name )
727 {
728     QList<Playlists::PlaylistProvider *> providers =
729             The::playlistManager()->providersForCategory( m_playlistCategory );
730     foreach( Playlists::PlaylistProvider *provider, providers )
731     {
732         if( provider->prettyName() == name )
733             return provider;
734     }
735     return 0;
736 }
737