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