Git commit d3982dc6c2fa2953c37d4864bca61e73aff19a52 by Mat=C4=9Bj Laitl. Committed on 06/05/2013 at 22:48. Pushed by laitl into branch 'master'. Fix Saved Playlists and Podcasts actions After 2 failed attempts to solve this non-intrusively, I realized the obvious: the actions belong to the view, not the model! (Qt has no Model/View/Controller, it has Model/View+Controller) After this it was "easy", just a fair bit of work to move relevant actions to PlaylistBrowserModel and ensure they work correctly. Some playlist (and podcast) actions weren't really appropriate for PlaylistBrowserModel. Methods to get actions from these needed to be changed, because the old way simply didn't allow reliable action execution. When I was at it, I cleaned up a fair bit of things in podcasts (and playlist providers) - I haven't seen such code bloat in a while - 100-line-long unused function isn't anything surprising there. I'm pointing at you, Bart. :-) This regression exists since Ralf's cleanups, although Ralf certainly didn't introduce it - he just broke the delicate equilibrium of hacks that kept it working. (and I still think it was working just by chance) This doesn't go into ChangeLog as it was introduced post-2.7. The code is not perfect yet (we have lost "Create Empty Playlist" if you clink into empty space), but the bug this fixes is much more critical. BUG: 318782 FIXED-IN: 2.8 CCMAIL: amarok-devel@kde.org M +12 -103 src/browsers/playlistbrowser/PlaylistBrowserModel.cpp M +6 -14 src/browsers/playlistbrowser/PlaylistBrowserModel.h M +351 -69 src/browsers/playlistbrowser/PlaylistBrowserView.cpp M +36 -21 src/browsers/playlistbrowser/PlaylistBrowserView.h M +13 -68 src/browsers/playlistbrowser/PodcastModel.cpp M +2 -9 src/browsers/playlistbrowser/PodcastModel.h M +13 -30 src/core-impl/collections/ipodcollection/IpodPlaylistProvide= r.cpp M +4 -5 src/core-impl/collections/ipodcollection/IpodPlaylistProvide= r.h M +2 -62 src/core-impl/collections/mediadevicecollection/playlist/Med= iaDeviceUserPlaylistProvider.cpp M +3 -18 src/core-impl/collections/mediadevicecollection/playlist/Med= iaDeviceUserPlaylistProvider.h M +48 -46 src/core-impl/collections/umscollection/podcasts/UmsPodcastP= rovider.cpp M +4 -6 src/core-impl/collections/umscollection/podcasts/UmsPodcastP= rovider.h M +1 -218 src/core-impl/playlists/providers/user/UserPlaylistProvider.= cpp M +17 -64 src/core-impl/playlists/providers/user/UserPlaylistProvider.h M +1 -1 src/core-impl/playlists/types/file/PlaylistFile.h M +83 -283 src/core-impl/podcasts/sql/SqlPodcastProvider.cpp M +3 -6 src/core-impl/podcasts/sql/SqlPodcastProvider.h M +0 -17 src/core/playlists/Playlist.cpp M +0 -12 src/core/playlists/Playlist.h M +43 -4 src/core/playlists/PlaylistProvider.cpp M +50 -19 src/core/playlists/PlaylistProvider.h M +1 -26 src/core/podcasts/PodcastProvider.h M +1 -1 src/playlistmanager/PlaylistManager.cpp M +0 -18 src/playlistmanager/SyncedPlaylist.cpp M +0 -3 src/playlistmanager/SyncedPlaylist.h M +7 -2 src/playlistmanager/file/PlaylistFileProvider.cpp M +3 -9 src/playlistmanager/file/PlaylistFileProvider.h M +2 -34 src/playlistmanager/sql/SqlUserPlaylistProvider.cpp M +2 -8 src/playlistmanager/sql/SqlUserPlaylistProvider.h M +17 -29 src/services/gpodder/GpodderProvider.cpp M +3 -2 src/services/gpodder/GpodderProvider.h M +1 -1 tests/playlistmanager/file/TestPlaylistFileProvider.cpp M +1 -1 tests/playlistmanager/sql/TestSqlUserPlaylistProvider.cpp http://commits.kde.org/amarok/d3982dc6c2fa2953c37d4864bca61e73aff19a52 diff --git a/src/browsers/playlistbrowser/PlaylistBrowserModel.cpp b/src/br= owsers/playlistbrowser/PlaylistBrowserModel.cpp index 6ee3db3..d2b55ff 100644 --- a/src/browsers/playlistbrowser/PlaylistBrowserModel.cpp +++ b/src/browsers/playlistbrowser/PlaylistBrowserModel.cpp @@ -41,29 +41,6 @@ lessThanPlaylistTitles( const Playlists::PlaylistPtr &lh= s, const Playlists::Play PlaylistBrowserModel::PlaylistBrowserModel( int playlistCategory ) : m_playlistCategory( playlistCategory ) { - m_createEmptyPlaylistAction =3D new QAction( KIcon( "media-track-add-a= marok" ), - i18n( "Create empty playlis= t" ), - this ); - connect( m_createEmptyPlaylistAction, SIGNAL(triggered()), SLOT(slotCr= eateEmptyPlaylist()) ); - - //common, unconditional actions - m_appendAction =3D new QAction( KIcon( "media-track-add-amarok" ), i18= n( "&Add to Playlist" ), - this ); - // object name must match one in PlaylistBrowserNS::PlaylistBrowserView - m_appendAction->setObjectName( "appendAction" ); - // key shortcut is only for display purposes here, actual one is deter= mined by View in Model/View classes - m_appendAction->setShortcut( Qt::Key_Enter ); - m_appendAction->setProperty( "popupdropper_svg_id", "append" ); - connect( m_appendAction, SIGNAL(triggered()), this, SLOT(slotAppend())= ); - - m_loadAction =3D new QAction( KIcon( "folder-open" ), - i18nc( "Replace the currently loaded track= s with these", - "&Replace Playlist" ), - this ); - m_loadAction->setObjectName( "loadAction" ); - m_loadAction->setProperty( "popupdropper_svg_id", "load" ); - connect( m_loadAction, SIGNAL(triggered()), this, SLOT(slotLoad()) ); - connect( The::playlistManager(), SIGNAL(updated(int)), SLOT(slotUpdate= (int)) ); = connect( The::playlistManager(), SIGNAL(playlistAdded(Playlists::Playl= istPtr,int)), @@ -93,6 +70,7 @@ PlaylistBrowserModel::data( const QModelIndex &index, int= role ) const QList providers =3D The::playlistManager()->getProvidersForPlaylist( playlist ); Playlists::PlaylistProvider *provider =3D providers.count() =3D=3D 1 ?= providers.first() : 0; + Meta::TrackPtr track; = switch( index.column() ) { @@ -100,7 +78,7 @@ PlaylistBrowserModel::data( const QModelIndex &index, in= t role ) const { if( IS_TRACK(index) ) { - Meta::TrackPtr track =3D playlist->tracks()[index.row()]; + track =3D playlist->tracks()[index.row()]; name =3D track->prettyName(); icon =3D KIcon( "amarok_track" ); } @@ -190,17 +168,17 @@ PlaylistBrowserModel::data( const QModelIndex &index,= int role ) const if( IS_TRACK(index) ) return QVariant(); else - return i18ncp( "number of playlists from one source", - "One playlist", "%1 playlists", - playlistCount ); - case PrettyTreeRoles::DecoratorRole: - return QVariant::fromValue( actionsFor( index ) ); - case PrettyTreeRoles::DecoratorRoleCount: - return actionsFor( index ).count(); + return i18ncp( "number of playlists from one source", "One= playlist", + "%1 playlists", playlistCount ); case PlaylistBrowserModel::ProviderRole: - return provider ? QVariant::fromValue( provider ) : QVariant(); - - default: return QVariant(); + return provider ? QVariant::fromValue( provider ) : QVariant(); + case PlaylistBrowserModel::PlaylistRole: + return playlist ? QVariant::fromValue( playlist ) : QVariant(); + case PlaylistBrowserModel::TrackRole: + return track ? QVariant::fromValue( track ) : QVariant(); + + default: + return QVariant(); } } = @@ -618,34 +596,6 @@ PlaylistBrowserModel::loadPlaylists() } = void -PlaylistBrowserModel::slotLoad() -{ - QAction *action =3D qobject_cast( QObject::sender() ); - if( action =3D=3D 0 ) - return; - - QModelIndexList indexes =3D action->data().value(); - - Meta::TrackList tracks =3D tracksFromIndexes( indexes ); - if( !tracks.isEmpty() ) - The::playlistController()->insertOptioned( tracks, Playlist::LoadA= ndPlay ); -} - -void -PlaylistBrowserModel::slotAppend() -{ - QAction *action =3D qobject_cast( QObject::sender() ); - if( action =3D=3D 0 ) - return; - - QModelIndexList indexes =3D action->data().value(); - - Meta::TrackList tracks =3D tracksFromIndexes( indexes ); - if( !tracks.isEmpty() ) - The::playlistController()->insertOptioned( tracks, Playlist::Appen= dAndPlay ); -} - -void PlaylistBrowserModel::slotPlaylistAdded( Playlists::PlaylistPtr playlist, = int category ) { //ignore playlists of a different category @@ -709,13 +659,6 @@ PlaylistBrowserModel::slotPlaylistUpdated( Playlists::= PlaylistPtr playlist, int endInsertRows(); } = -void -PlaylistBrowserModel::slotCreateEmptyPlaylist() -{ - The::playlistManager()->save( Meta::TrackList(), - Amarok::generatePlaylistName( Meta::Trac= kList() ) ); -} - Meta::TrackList PlaylistBrowserModel::tracksFromIndexes( const QModelIndexList &list ) con= st { @@ -779,40 +722,6 @@ PlaylistBrowserModel::providerForIndex( const QModelIn= dex &idx ) const return m_playlists.at( playlistRow )->provider(); } = -QActionList -PlaylistBrowserModel::actionsFor( const QModelIndex &idx ) const -{ - if( !idx.isValid() ) - { - QActionList emptyActions; - emptyActions << m_createEmptyPlaylistAction; - return emptyActions; - } - //whether we use the list from m_appendAction of m_loadAction does not= matter they are the same - QModelIndexList actionList =3D m_appendAction->data().value(); - - actionList << idx; - QVariant value =3D QVariant::fromValue( actionList ); - m_appendAction->setData( value ); - m_loadAction->setData( value ); - - QActionList actions; - actions << m_appendAction << m_loadAction; - - if( !IS_TRACK(idx) ) - { - Playlists::PlaylistPtr playlist =3D m_playlists.value( idx.interna= lId() ); - actions << playlist->actions(); - } - else - { - Playlists::PlaylistPtr playlist =3D m_playlists.value( idx.parent(= ).internalId() ); - actions << playlist->trackActions( idx.row() ); - } - - return actions; -} - Playlists::PlaylistProvider * PlaylistBrowserModel::getProviderByName( const QString &name ) { diff --git a/src/browsers/playlistbrowser/PlaylistBrowserModel.h b/src/brow= sers/playlistbrowser/PlaylistBrowserModel.h index d507793..6f3d584 100644 --- a/src/browsers/playlistbrowser/PlaylistBrowserModel.h +++ b/src/browsers/playlistbrowser/PlaylistBrowserModel.h @@ -39,8 +39,7 @@ namespace PlaylistBrowserNS { /** @author Bart Cerneels */ -class PlaylistBrowserModel : public QAbstractItemModel, - public Playlists::PlaylistObserver +class PlaylistBrowserModel : public QAbstractItemModel, public Playlists::= PlaylistObserver { Q_OBJECT public: @@ -53,8 +52,11 @@ class PlaylistBrowserModel : public QAbstractItemModel, = enum { - ProviderRole =3D Qt::UserRole + 21, // pointer to associated = PlaylistProvider - CustomRoleOffset =3D Qt::UserRole + 22 //first role that can b= e used by sublasses for their own data + ProviderRole =3D Qt::UserRole + 21, // pointer to associated P= laylistProvider + PlaylistRole =3D Qt::UserRole + 22, // PlaylistPtr for associa= ted playlist or null + TrackRole =3D Qt::UserRole + 23, // TrackPtr for associated tr= ack or null + EpisodeIsNewRole =3D Qt::UserRole + 24, // for podcast episode= s, supports setting, type: bool + CustomRoleOffset =3D Qt::UserRole + 25 //first role that can b= e used by sublasses for their own data }; = PlaylistBrowserModel( int PlaylistCategory ); @@ -97,7 +99,6 @@ class PlaylistBrowserModel : public QAbstractItemModel, = protected: virtual Playlists::PlaylistList loadPlaylists(); - virtual QActionList actionsFor( const QModelIndex &idx ) const; = Meta::TrackList tracksFromIndexes( const QModelIndexList &list ) c= onst; Meta::TrackPtr trackFromIndex( const QModelIndex &index ) const; @@ -110,23 +111,14 @@ class PlaylistBrowserModel : public QAbstractItemMode= l, Playlists::PlaylistProvider *getProviderByName( const QString &nam= e ); = private slots: - void slotLoad(); - void slotAppend(); void slotPlaylistAdded( Playlists::PlaylistPtr playlist, int categ= ory ); void slotPlaylistRemoved( Playlists::PlaylistPtr playlist, int cat= egory ); void slotPlaylistUpdated( Playlists::PlaylistPtr playlist, int cat= egory ); - void slotCreateEmptyPlaylist(); = private: int m_playlistCategory; - QAction *m_appendAction; - QAction *m_loadAction; - QAction *m_createEmptyPlaylistAction; }; = } = -//we store these in a QVariant for the load and append actions -Q_DECLARE_METATYPE( QModelIndexList ) - #endif //AMAROK_PLAYLISTBROWSERMODEL_H diff --git a/src/browsers/playlistbrowser/PlaylistBrowserView.cpp b/src/bro= wsers/playlistbrowser/PlaylistBrowserView.cpp index c0e1d1d..4418a06 100644 --- a/src/browsers/playlistbrowser/PlaylistBrowserView.cpp +++ b/src/browsers/playlistbrowser/PlaylistBrowserView.cpp @@ -17,11 +17,10 @@ = #include "PlaylistBrowserView.h" = -#define DEBUG_PREFIX "PlaylistBrowserView" - #include "PaletteHandler.h" #include "PopupDropperFactory.h" #include "SvgHandler.h" +#include "amarokconfig.h" #include "browsers/playlistbrowser/PlaylistBrowserModel.h" #include "browsers/playlistbrowser/PlaylistsByProviderProxy.h" #include "browsers/playlistbrowser/PlaylistsInFoldersProxy.h" @@ -29,22 +28,31 @@ #include "context/popupdropper/libpud/PopupDropperItem.h" #include "context/popupdropper/libpud/PopupDropper.h" #include "core/support/Debug.h" +#include "core-impl/playlists/types/file/PlaylistFileSupport.h" #include "playlist/PlaylistModel.h" #include "playlist/PlaylistController.h" +#include "playlistmanager/PlaylistManager.h" #include "widgets/PrettyTreeRoles.h" = -#include +#include #include #include = #include #include +#include +#include + +#define DEBUG_PREFIX "PlaylistBrowserView" + +Q_DECLARE_METATYPE( QModelIndexList ) + +using namespace PlaylistBrowserNS; = PlaylistBrowserNS::PlaylistBrowserView::PlaylistBrowserView( QAbstractItem= Model *model, QWidget *pare= nt ) : Amarok::PrettyTreeView( parent ) , m_pd( 0 ) - , m_addFolderAction( 0 ) , m_ongoingDrag( false ) { DEBUG_BLOCK @@ -55,10 +63,56 @@ PlaylistBrowserNS::PlaylistBrowserView::PlaylistBrowser= View( QAbstractItemModel setAcceptDrops( true ); setEditTriggers( QAbstractItemView::EditKeyPressed ); setMouseTracking( true ); // needed for highlighting provider action i= cons -} = -PlaylistBrowserNS::PlaylistBrowserView::~PlaylistBrowserView() -{ + m_createEmptyPlaylistAction =3D new QAction( KIcon( "media-track-add-a= marok" ), + i18n( "Create an Empty Play= list" ), this ); + connect( m_createEmptyPlaylistAction, SIGNAL(triggered()), SLOT(slotCr= eateEmptyPlaylist()) ); + + m_appendAction =3D new QAction( KIcon( "media-track-add-amarok" ), + i18n( "&Add to Playlist" ), this ); + // key shortcut is only for display purposes here, actual one is deter= mined by View in Model/View classes + m_appendAction->setShortcut( Qt::Key_Enter ); + m_appendAction->setProperty( "popupdropper_svg_id", "append" ); + connect( m_appendAction, SIGNAL(triggered()), this, SLOT(slotAppend())= ); + + m_loadAction =3D new QAction( KIcon( "folder-open" ), i18nc( "Replace = the currently " + "loaded tracks with these", "&Replace Playlist" ), this ); + m_loadAction->setProperty( "popupdropper_svg_id", "load" ); + connect( m_loadAction, SIGNAL(triggered()), this, SLOT(slotLoad()) ); + + m_setNewAction =3D new QAction( KIcon( "rating" ), i18nc( "toggle the = \"new\" status " + " of this podcast episode", "&New" ), this ); + m_setNewAction->setProperty( "popupdropper_svg_id", "new" ); + m_setNewAction->setCheckable( true ); + connect( m_setNewAction, SIGNAL(triggered(bool)), SLOT(slotSetNew(bool= )) ); + + m_renamePlaylistAction =3D new QAction( KIcon( "media-track-edit-amaro= k" ), + i18n( "&Rename..." ), this ); + m_renamePlaylistAction->setProperty( "popupdropper_svg_id", "edit" ); + // key shortcut is only for display purposes here, actual one is deter= mined by View in Model/View classes + m_renamePlaylistAction->setShortcut( Qt::Key_F2 ); + connect( m_renamePlaylistAction, SIGNAL(triggered()), this, SLOT(slotR= ename()) ); + + m_deletePlaylistAction =3D new QAction( KIcon( "media-track-remove-ama= rok" ), + i18n( "&Delete..." ), this ); + m_deletePlaylistAction->setProperty( "popupdropper_svg_id", "delete" ); + // key shortcut is only for display purposes here, actual one is deter= mined by View in Model/View classes + m_deletePlaylistAction->setShortcut( Qt::Key_Delete ); + connect( m_deletePlaylistAction, SIGNAL(triggered()), SLOT(slotDelete(= )) ); + + m_removeTracksAction =3D new QAction( KIcon( "media-track-remove-amaro= k" ), + QString( "" ), this ); + m_removeTracksAction->setProperty( "popupdropper_svg_id", "delete" ); + // key shortcut is only for display purposes here, actual one is deter= mined by View in Model/View classes + m_removeTracksAction->setShortcut( Qt::Key_Delete ); + connect( m_removeTracksAction, SIGNAL(triggered()), SLOT(slotRemoveTra= cks()) ); + + m_exportAction =3D new QAction( KIcon( "document-export-amarok" ), + i18n( "&Export As..." ), this ); + connect( m_exportAction, SIGNAL(triggered()), this, SLOT(slotExport())= ); + + m_separatorAction =3D new QAction( this ); + m_separatorAction->setSeparator( true ); } = void @@ -100,7 +154,7 @@ PlaylistBrowserNS::PlaylistBrowserView::mouseReleaseEve= nt( QMouseEvent *event ) = void PlaylistBrowserNS::PlaylistBrowserView::startDrag( Qt::DropActions su= pportedActions ) { - //Waah? when a parent item is dragged, startDrag is called a bunch of = times + // Waah? when a parent item is dragged, startDrag is called a bunch of= times if( m_ongoingDrag ) return; m_ongoingDrag =3D true; @@ -108,12 +162,9 @@ void PlaylistBrowserNS::PlaylistBrowserView::startDrag= ( Qt::DropActions supporte if( !m_pd ) m_pd =3D The::popupDropperFactory()->createPopupDropper( Context::= ContextView::self() ); = - QList actions; - if( m_pd && m_pd->isHidden() ) { - actions =3D actionsFor( selectedIndexes() ); - + QActionList actions =3D actionsFor( selectedIndexes() ); foreach( QAction *action, actions ) m_pd->addItem( The::popupDropperFactory()->createItem( action = ) ); = @@ -121,16 +172,13 @@ void PlaylistBrowserNS::PlaylistBrowserView::startDra= g( Qt::DropActions supporte } = QTreeView::startDrag( supportedActions ); - debug() << "After the drag!"; = - //We keep the items that the actions need to be applied to in the acti= ons private data. - //Clear the data from all actions now that the PUD has executed. - foreach( QAction *action, actions ) - action->setData( QVariant() ); + // We keep the items that the actions need to be applied to. + // Clear the data from all actions now that the PUD has executed. + resetActionTargets(); = if( m_pd ) { - debug() << "clearing PUD"; connect( m_pd, SIGNAL(fadeHideFinished()), m_pd, SLOT(clear()) ); m_pd->hide(); } @@ -141,7 +189,8 @@ void PlaylistBrowserNS::PlaylistBrowserView::keyPressEvent( QKeyEvent *event ) { QModelIndexList indices =3D selectedIndexes(); - if( indices.isEmpty() ) + // mind bug 305203 + if( indices.isEmpty() || state() !=3D QAbstractItemView::NoState ) { Amarok::PrettyTreeView::keyPressEvent( event ); return; @@ -152,20 +201,21 @@ PlaylistBrowserNS::PlaylistBrowserView::keyPressEvent= ( QKeyEvent *event ) //activated() only works for current index, not all selected case Qt::Key_Enter: case Qt::Key_Return: - if( state() !=3D EditingState ) - { - //Why do we even get in this state? Shouldn't the editor c= onsume the - //keypress? The delete works. see bug 305203 - appendAndPlay( indices ); - return; - } - break; + appendAndPlay( indices ); + return; case Qt::Key_Delete: - deletePlaylistsTracks( indices ); + { + QActionList actions =3D actionsFor( indices ); // sets action = targets + if( actions.contains( m_removeTracksAction ) ) + m_removeTracksAction->trigger(); + else if( actions.contains( m_deletePlaylistAction ) ) + m_deletePlaylistAction->trigger(); + resetActionTargets(); return; + } default: break; - } + } Amarok::PrettyTreeView::keyPressEvent( event ); } = @@ -207,56 +257,146 @@ void PlaylistBrowserNS::PlaylistBrowserView::context= MenuEvent( QContextMenuEvent QModelIndex clickedIdx =3D indexAt( event->pos() ); = QModelIndexList indices; - if( selectedIndexes().contains( clickedIdx ) ) + if( clickedIdx.isValid() && selectedIndexes().contains( clickedIdx ) ) indices << selectedIndexes(); - else + else if( clickedIdx.isValid() ) indices << clickedIdx; = QActionList actions =3D actionsFor( indices ); - if( actions.isEmpty() ) + { + resetActionTargets(); return; + } = KMenu menu; foreach( QAction *action, actions ) - { - if( action ) - menu.addAction( action ); - } - - if( indices.count() =3D=3D 0 ) - menu.addAction( m_addFolderAction ); - + menu.addAction( action ); menu.exec( mapToGlobal( event->pos() ) ); = - //We keep the items that the action need to be applied to in the actio= n's private data. - //Clear the data from all actions now that the context menu has execut= ed. - foreach( QAction *action, actions ) - action->setData( QVariant() ); + // We keep the items that the action need to be applied to. + // Clear the data from all actions now that the context menu has execu= ted. + resetActionTargets(); } = QList -PlaylistBrowserNS::PlaylistBrowserView::actionsFor( QModelIndexList indexe= s ) +PlaylistBrowserNS::PlaylistBrowserView::actionsFor( const QModelIndexList = &indexes ) { + resetActionTargets(); + if( indexes.isEmpty() ) + return QActionList(); + + using namespace Playlists; + QSet providers, writableProviders; QActionList actions; - foreach( QModelIndex idx, indexes ) + QModelIndexList newPodcastEpisodes, oldPodcastEpisodes; + foreach( const QModelIndex &idx, indexes ) { - QActionList idxActions =3D model()->data( idx, - PrettyTreeRoles::DecoratorRole ).value(); - //only add unique actions model is responsible for making them uni= que - foreach( QAction *action, idxActions ) + // direct provider actions: + actions << idx.data( PrettyTreeRoles::DecoratorRole ).value(); + + PlaylistProvider *provider =3D idx.data( PlaylistBrowserModel::Pro= viderRole ).value(); + if( provider ) + providers << provider; + bool isWritable =3D provider ? provider->isWritable() : false; + if( isWritable ) + writableProviders |=3D provider; + Meta::TrackPtr track =3D idx.data( PlaylistBrowserModel::TrackRole= ).value(); + PlaylistPtr playlist =3D idx.data( PlaylistBrowserModel::PlaylistR= ole ).value(); + if( !track && playlist ) // a playlist (must check it is not a tra= ck) { - if( !actions.contains( action ) ) - actions << action; + m_actionPlaylists << playlist; + if( isWritable ) + m_writableActionPlaylists << playlist; + } + if( track ) + { + m_actionTracks.insert( playlist, idx.row() ); + if( isWritable ) + m_writableActionTracks.insert( playlist, idx.row() ); + } + + QVariant episodeIsNew =3D idx.data( PlaylistBrowserModel::EpisodeI= sNewRole ); + if( episodeIsNew.type() =3D=3D QVariant::Bool ) + { + if( episodeIsNew.toBool() ) + newPodcastEpisodes << idx; + else + oldPodcastEpisodes << idx; } } - return actions; + // all actions taking provider have only sense with one provider + if( writableProviders.count() =3D=3D 1 ) + m_writableActionProvider =3D writableProviders.toList().first(); + + // process per-provider actions + foreach( PlaylistProvider *provider, providers ) + { + // prepare arguments and get relevant actions + PlaylistList providerPlaylists; + foreach( const PlaylistPtr &playlist, m_actionPlaylists ) + { + if( playlist->provider() =3D=3D provider ) + providerPlaylists << playlist; + } + actions << provider->playlistActions( providerPlaylists ); + + QMultiHash playlistTracks; + QHashIterator it( m_actionTracks ); + while( it.hasNext() ) + { + it.next(); + if( it.key()->provider() =3D=3D provider ) + playlistTracks.insert( it.key(), it.value() ); + } + actions << provider->trackActions( playlistTracks ); + } + + // separate model actions from standard actions we provide (at the top) + QActionList standardActions; + if( m_actionPlaylists.isEmpty() && m_actionTracks.isEmpty() && m_writa= bleActionProvider ) + standardActions << m_createEmptyPlaylistAction; + if( !m_actionPlaylists.isEmpty() || !m_actionTracks.isEmpty() ) + standardActions << m_appendAction << m_loadAction; + if( !newPodcastEpisodes.isEmpty() || !oldPodcastEpisodes.isEmpty() ) + { + m_setNewAction->setChecked( oldPodcastEpisodes.isEmpty() ); + m_setNewAction->setData( QVariant::fromValue( newPodcastEpisodes += oldPodcastEpisodes ) ); + standardActions << m_setNewAction; + } + if( m_writableActionPlaylists.count() =3D=3D 1 && m_actionTracks.isEmp= ty() ) + standardActions << m_renamePlaylistAction; + if( !m_writableActionPlaylists.isEmpty() && m_actionTracks.isEmpty() ) + standardActions << m_deletePlaylistAction; + if( m_actionPlaylists.isEmpty() && !m_writableActionTracks.isEmpty() ) + { + const int actionTrackCount =3D m_writableActionTracks.count(); + const int playlistCount =3D m_writableActionTracks.uniqueKeys().co= unt(); + if( playlistCount > 1 ) + m_removeTracksAction->setText( i18ncp( "Number of playlists is= >=3D 2", + "Remove a Track From %2 Playlists", "Remove %1 Tracks From= %2 Playlists", + actionTrackCount, playlistCount ) ); + else + m_removeTracksAction->setText( i18ncp( "%2 is saved playlist n= ame", + "Remove a Track From %2", "Remove %1 Tracks From %2", acti= onTrackCount, + m_writableActionTracks.uniqueKeys().first()->prettyName() = ) ); + standardActions << m_removeTracksAction; + } + if( m_actionPlaylists.count() =3D=3D 1 && m_actionTracks.isEmpty() ) + standardActions << m_exportAction; + standardActions << m_separatorAction; + + return standardActions + actions; } = void -PlaylistBrowserNS::PlaylistBrowserView::setNewFolderAction( KAction *actio= n ) +PlaylistBrowserView::resetActionTargets() { - m_addFolderAction =3D action; + m_writableActionProvider =3D 0; + m_actionPlaylists.clear(); + m_writableActionPlaylists.clear(); + m_actionTracks.clear(); + m_writableActionTracks.clear(); } = void @@ -269,35 +409,177 @@ PlaylistBrowserNS::PlaylistBrowserView::currentChang= ed( const QModelIndex &curre } = void -PlaylistBrowserNS::PlaylistBrowserView::appendAndPlay( const QModelIndex &= index ) +PlaylistBrowserView::slotCreateEmptyPlaylist() { - appendAndPlay( QModelIndexList() << index ); + // m_actionProvider may be null, which is fine + The::playlistManager()->save( Meta::TrackList(), Amarok::generatePlayl= istName( + Meta::TrackList() ), m_writableActionProvider ); } = void -PlaylistBrowserNS::PlaylistBrowserView::appendAndPlay( const QModelIndexLi= st &list ) +PlaylistBrowserView::slotAppend() { - performActionNamed( "appendAction", list ); + insertToPlayQueue( Playlist::AppendAndPlay ); } = void -PlaylistBrowserNS::PlaylistBrowserView::deletePlaylistsTracks( const QMode= lIndexList &list ) +PlaylistBrowserView::slotLoad() { - performActionNamed( "deleteAction", list ); + insertToPlayQueue( Playlist::LoadAndPlay ); } = void -PlaylistBrowserNS::PlaylistBrowserView::performActionNamed( const QString = &name, - const QModelIn= dexList &list ) +PlaylistBrowserView::slotSetNew( bool newState ) { - QActionList actions =3D actionsFor( list ); + QModelIndexList indices =3D m_setNewAction->data().value(); + foreach( const QModelIndex &idx, indices ) + model()->setData( idx, newState, PlaylistBrowserModel::EpisodeIsNe= wRole ); +} = - foreach( QAction *action, actions ) +void +PlaylistBrowserView::slotRename() +{ + if( m_writableActionPlaylists.count() !=3D 1 ) { - if( action->objectName() =3D=3D name ) - action->trigger(); - action->setData( QVariant() ); // reset data of all actions + warning() << __PRETTY_FUNCTION__ << "m_writableActionPlaylists.cou= nt() is not 1"; + return; + } + Playlists::PlaylistPtr playlist =3D m_writableActionPlaylists.at( 0 ); + + // TODO: this makes a rather complicated round-trip and ends up in edi= t(QModelIndex) + // here -- simplify that + The::playlistManager()->rename( playlist ); +} + +void +PlaylistBrowserView::slotDelete() +{ + if( m_writableActionPlaylists.isEmpty() ) + return; + + using namespace Playlists; + QHash providerPlaylists; + foreach( const PlaylistPtr &playlist, m_writableActionPlaylists ) + { + if( playlist->provider() ) + providerPlaylists[ playlist->provider() ] << playlist; + } + QStringList providerNames; + foreach( const PlaylistProvider *provider, providerPlaylists.keys() ) + providerNames << provider->prettyName(); + + KDialog dialog; + dialog.setCaption( i18n( "Confirm Playlist Deletion" ) ); + dialog.setButtons( KDialog::Ok | KDialog::Cancel ); + QLabel *label =3D new QLabel( i18np( "Are you sure you want to delete = this playlist?", + "Are you sure you want to delete these %1 playlists?", + m_writableActionPlaylists.count() ), &dialog ); + // TODO: include a text area with all the names of the playlists + dialog.setButtonText( KDialog::Ok, i18nc( "%1 is playlist provider pre= tty name", + "Yes, delete from %1.", providerNames.join( ", " ) ) ); + dialog.setMainWidget( label ); + if( dialog.exec() =3D=3D QDialog::Accepted ) + { + foreach( PlaylistProvider *provider, providerPlaylists.keys() ) + provider->deletePlaylists( providerPlaylists.value( provider )= ); + } +} + +void +PlaylistBrowserView::slotRemoveTracks() +{ + foreach( Playlists::PlaylistPtr playlist, m_writableActionTracks.uniqu= eKeys() ) + { + QList trackIndices =3D m_writableActionTracks.values( playlis= t ); + qSort( trackIndices ); + int removed =3D 0; + foreach( int trackIndex, trackIndices ) + { + playlist->removeTrack( trackIndex - removed /* account for alr= eady removed */ ); + removed++; + } } } = -#include "PlaylistBrowserView.moc" +void +PlaylistBrowserView::slotExport() +{ + if( m_actionPlaylists.count() !=3D 1 ) + { + warning() << __PRETTY_FUNCTION__ << "m_actionPlaylists.count() is = not 1"; + return; + } + Playlists::PlaylistPtr playlist =3D m_actionPlaylists.at( 0 ); + + // --- display save location dialog + // compare with MainWindow::exportPlaylist + // TODO: have this code only once + QCheckBox *saveRelativeCheck =3D new QCheckBox( i18n("Use relative pat= h for &saving") ); + saveRelativeCheck->setChecked( AmarokConfig::relativePlaylist() ); + KFileDialog fileDialog( KUrl( "kfiledialog:///amarok-playlist-export" = ), QString(), 0, saveRelativeCheck ); + + QStringList supportedMimeTypes; + supportedMimeTypes << "video/x-ms-asf"; // ASX + supportedMimeTypes << "audio/x-mpegurl"; // M3U + supportedMimeTypes << "audio/x-scpls"; // PLS + supportedMimeTypes << "application/xspf+xml"; // XSPF + + fileDialog.setSelection( playlist->name() ); + fileDialog.setMimeFilter( supportedMimeTypes, supportedMimeTypes.value= ( 1 ) ); + fileDialog.setOperationMode( KFileDialog::Saving ); + fileDialog.setMode( KFile::File ); + fileDialog.setCaption( i18n("Save As") ); + fileDialog.setObjectName( "PlaylistExport" ); + + fileDialog.exec(); + QString playlistPath =3D fileDialog.selectedFile(); + + // --- actually save the playlist + if( !playlistPath.isEmpty() ) + Playlists::exportPlaylistFile( playlist->tracks(), playlistPath, s= aveRelativeCheck->isChecked() ); +} + +void +PlaylistBrowserNS::PlaylistBrowserView::appendAndPlay( const QModelIndex &= index ) +{ + appendAndPlay( QModelIndexList() << index ); +} + +void +PlaylistBrowserNS::PlaylistBrowserView::appendAndPlay( const QModelIndexLi= st &list ) +{ + actionsFor( list ); // sets action targets + insertToPlayQueue( Playlist::AppendAndPlay ); + resetActionTargets(); +} + +void +PlaylistBrowserView::insertToPlayQueue( int options ) +{ + Meta::TrackList tracks; + + // add tracks for fully-selected playlists: + foreach( Playlists::PlaylistPtr playlist, m_actionPlaylists ) + { + tracks << playlist->tracks(); + } + + // filter-out tracks from playlists that are selected, add lone tracks: + foreach( Playlists::PlaylistPtr playlist, m_actionTracks.uniqueKeys() ) + { + if( m_actionPlaylists.contains( playlist ) ) + continue; + + Meta::TrackList playlistTracks =3D playlist->tracks(); + QList positions =3D m_actionTracks.values( playlist ); + qSort( positions ); + foreach( int position, positions ) + { + if( position >=3D 0 && position < playlistTracks.count() ) + tracks << playlistTracks.at( position ); + } + } + + if( !tracks.isEmpty() ) + The::playlistController()->insertOptioned( tracks, options ); +} diff --git a/src/browsers/playlistbrowser/PlaylistBrowserView.h b/src/brows= ers/playlistbrowser/PlaylistBrowserView.h index b0d72bd..e2bb3b2 100644 --- a/src/browsers/playlistbrowser/PlaylistBrowserView.h +++ b/src/browsers/playlistbrowser/PlaylistBrowserView.h @@ -18,12 +18,12 @@ #ifndef PLAYLISTBROWSERVIEW_H #define PLAYLISTBROWSERVIEW_H = +#include "core/playlists/Playlist.h" #include "widgets/PrettyTreeView.h" = #include = class PopupDropper; -class KAction; class QKeyEvent; class QMouseEvent; class QContextMenuEvent; @@ -35,54 +35,69 @@ class PlaylistBrowserView : public Amarok::PrettyTreeVi= ew Q_OBJECT public: explicit PlaylistBrowserView( QAbstractItemModel *model, QWidget *pare= nt =3D 0 ); - ~PlaylistBrowserView(); = virtual void setModel( QAbstractItemModel *model ); = - void setNewFolderAction( KAction *action ); - signals: void currentItemChanged( const QModelIndex ¤t ); = protected: - //TODO:re-implement QWidget::dragEnterEvent() to show drop-not-allowed= indicator + // TODO: re-implement QWidget::dragEnterEvent() to show drop-not-allow= ed indicator = virtual void keyPressEvent( QKeyEvent *event ); virtual void mouseDoubleClickEvent( QMouseEvent *event ); virtual void mouseReleaseEvent( QMouseEvent *event ); virtual void startDrag( Qt::DropActions supportedActions ); = - virtual void contextMenuEvent( QContextMenuEvent* event ); + virtual void contextMenuEvent( QContextMenuEvent *event ); = protected slots: /** reimplemented to emit a signal */ void currentChanged( const QModelIndex ¤t, const QModelIndex &pr= evious ); = +private slots: + // these are connected to m_*Actions: + void slotCreateEmptyPlaylist(); + void slotAppend(); + void slotLoad(); + void slotSetNew( bool newState ); + void slotRename(); + void slotDelete(); + void slotRemoveTracks(); + void slotExport(); + private: - /** - * Execute all actions that are named appendAction - */ void appendAndPlay( const QModelIndex &index ); void appendAndPlay( const QModelIndexList &list ); + void insertToPlayQueue( int options ); = /** - * Execute all actions that are named deleteAction - */ - void deletePlaylistsTracks( const QModelIndexList &list ); - - /** - * Executes all actions that are named @param name + * Gets action for a list of indices and sets internal action targets = to these. + * + * After you have processed/triggered the actions, you should call + * resetActionTargets() to prevent stale targets laying around. */ - void performActionNamed( const QString &name, const QModelIndexList &l= ist ); - - QAction *decoratorActionAt( const QModelIndex &idx, const QPoint posit= ion ); - QList actionsFor( QModelIndexList indexes ); + QList actionsFor( const QModelIndexList &indexes ); + void resetActionTargets(); = PopupDropper* m_pd; = - KAction *m_addFolderAction; - + QAction *m_createEmptyPlaylistAction; + QAction *m_appendAction; + QAction *m_loadAction; + QAction *m_setNewAction; // for podcasts + QAction *m_renamePlaylistAction; + QAction *m_deletePlaylistAction; + QAction *m_removeTracksAction; + QAction *m_exportAction; + QAction *m_separatorAction; bool m_ongoingDrag; + + Playlists::PlaylistProvider *m_writableActionProvider; + Playlists::PlaylistList m_actionPlaylists; + Playlists::PlaylistList m_writableActionPlaylists; + QMultiHash m_actionTracks; // maps playli= sts to track positions + QMultiHash m_writableActionTracks; }; = } // namespace PlaylistBrowserNS diff --git a/src/browsers/playlistbrowser/PodcastModel.cpp b/src/browsers/p= laylistbrowser/PodcastModel.cpp index 5f277aa..6f9b935 100644 --- a/src/browsers/playlistbrowser/PodcastModel.cpp +++ b/src/browsers/playlistbrowser/PodcastModel.cpp @@ -66,17 +66,8 @@ PlaylistBrowserNS::PodcastModel::destroy() = PlaylistBrowserNS::PodcastModel::PodcastModel() : PlaylistBrowserModel( PlaylistManager::PodcastChannel ) - , m_setNewAction( 0 ) { s_instance =3D this; - m_setNewAction =3D new QAction( KIcon( "rating" ), - i18nc( "toggle the \"new\" status of thi= s podcast episode", - "&New" ), - this - ); - m_setNewAction->setProperty( "popupdropper_svg_id", "new" ); - m_setNewAction->setCheckable( true ); - connect( m_setNewAction, SIGNAL(triggered(bool)), SLOT(slotSetNew(bool= )) ); } = bool @@ -267,6 +258,8 @@ PlaylistBrowserNS::PodcastModel::episodeData( const Pod= castEpisodePtr &episode, if( idx.column() =3D=3D PlaylistBrowserModel::PlaylistItemColu= mn ) return icon( episode ); break; + case EpisodeIsNewRole: + return episode->isNew(); } = return PlaylistBrowserModel::data( idx, role ); @@ -275,10 +268,18 @@ PlaylistBrowserNS::PodcastModel::episodeData( const P= odcastEpisodePtr &episode, bool PlaylistBrowserNS::PodcastModel::setData( const QModelIndex &idx, const QV= ariant &value, int role ) { - DEBUG_BLOCK + PodcastEpisodePtr episode =3D episodeForIndex( idx ); + if( !episode || !value.canConvert() || role !=3D EpisodeIsNewRol= e ) + { + return PlaylistBrowserModel::setData( idx, value, role ); + } = - //TODO: implement setNew. - return PlaylistBrowserModel::setData( idx, value, role ); + bool checked =3D value.toBool(); + episode->setNew( checked ); + if( checked ) + emit episodeMarkedAsNew( episode ); + emit dataChanged( idx, idx ); + return true; } = int @@ -353,62 +354,6 @@ PlaylistBrowserNS::PodcastModel::refreshPodcasts() } } = -QActionList -PlaylistBrowserNS::PodcastModel::actionsFor( const QModelIndex &idx ) const -{ - if( !idx.isValid() ) - { - //TODO: add podcast action - return QActionList(); - } - - QActionList actions =3D PlaylistBrowserModel::actionsFor( idx ); - - /* by default a list of podcast episodes can only be changed to isNew = =3D false or - isKeep =3D false, except when all selected episodes are the same st= ate */ - m_setNewAction->setChecked( false ); - - Podcasts::PodcastEpisodeList episodes =3D m_setNewAction->data().value= (); - if( IS_TRACK(idx) ) - episodes << episodeForIndex( idx ); - else - episodes << channelForIndex( idx )->episodes(); - - foreach( const Podcasts::PodcastEpisodePtr episode, episodes ) - { - if( episode->isNew() ) - m_setNewAction->setChecked( true ); - } - - m_setNewAction->setData( QVariant::fromValue( episodes ) ); - - actions << m_setNewAction; - - return actions; -} - -void -PlaylistBrowserNS::PodcastModel::slotSetNew( bool newState ) -{ - Q_UNUSED( newState ); - QAction *action =3D qobject_cast( QObject::sender() ); - if( action =3D=3D 0 ) - return; - - Podcasts::PodcastEpisodeList episodes =3D action->data().value(); - - foreach( Podcasts::PodcastEpisodePtr episode, episodes ) - { - if( !episode.isNull() ) - { - episode->setNew( action->isChecked() ); - - if( action->isChecked() ) - emit episodeMarkedAsNew( episode ); - } - } -} - Podcasts::PodcastChannelPtr PlaylistBrowserNS::PodcastModel::channelForIndex( const QModelIndex &idx )= const { diff --git a/src/browsers/playlistbrowser/PodcastModel.h b/src/browsers/pla= ylistbrowser/PodcastModel.h index 0f0b3b5..4d8eb41 100644 --- a/src/browsers/playlistbrowser/PodcastModel.h +++ b/src/browsers/playlistbrowser/PodcastModel.h @@ -50,6 +50,7 @@ enum class PodcastModel : public PlaylistBrowserModel { Q_OBJECT + public: static PodcastModel *instance(); static void destroy(); @@ -77,14 +78,8 @@ class PodcastModel : public PlaylistBrowserModel void addPodcast(); void refreshPodcasts(); = - private slots: - void slotSetNew( bool newState ); - - protected: - virtual QActionList actionsFor( const QModelIndex &idx ) const; - private: - static PodcastModel* s_instance; + static PodcastModel *s_instance; PodcastModel(); = QVariant channelData( const Podcasts::PodcastChannelPtr &channel, @@ -97,8 +92,6 @@ class PodcastModel : public PlaylistBrowserModel = Q_DISABLE_COPY( PodcastModel ) = - QAction *m_setNewAction; - /** * A convenience function to convert a PodcastEpisodeList into a T= rackList. */ diff --git a/src/core-impl/collections/ipodcollection/IpodPlaylistProvider.= cpp b/src/core-impl/collections/ipodcollection/IpodPlaylistProvider.cpp index abf0fae..ff9e4ad 100644 --- a/src/core-impl/collections/ipodcollection/IpodPlaylistProvider.cpp +++ b/src/core-impl/collections/ipodcollection/IpodPlaylistProvider.cpp @@ -101,45 +101,28 @@ IpodPlaylistProvider::providerActions() } = QActionList -IpodPlaylistProvider::playlistActions( Playlists::PlaylistPtr playlist ) +IpodPlaylistProvider::playlistActions( const Playlists::PlaylistList &play= lists ) { - QList actions; - if( !m_playlists.contains( playlist ) ) // make following static cast= safe - return actions; - KSharedPtr ipodPlaylist =3D KSharedPtr::st= aticCast( playlist ); - switch( ipodPlaylist->type() ) + QActionList actions; + foreach( const Playlists::PlaylistPtr &playlist, playlists ) { - case IpodPlaylist::Normal: - actions << Playlists::UserPlaylistProvider::playlistActions( p= laylist ); - break; - case IpodPlaylist::Stale: - case IpodPlaylist::Orphaned: + if( !m_playlists.contains( playlist ) ) // make following static = cast safe + continue; + IpodPlaylist::Type type =3D KSharedPtr::staticCast( = playlist )->type(); + if( type =3D=3D IpodPlaylist::Stale || type =3D=3D IpodPlaylist::O= rphaned ) + { actions << m_coll->m_consolidateAction; break; + } } = return actions; } = QActionList -IpodPlaylistProvider::trackActions( Playlists::PlaylistPtr playlist, int t= rackIndex ) +IpodPlaylistProvider::trackActions( const QMultiHash &playlistTracks ) { - QList actions; - if( !m_playlists.contains( playlist ) ) // make following static cast= safe - return actions; - KSharedPtr ipodPlaylist =3D KSharedPtr::st= aticCast( playlist ); - switch( ipodPlaylist->type() ) - { - case IpodPlaylist::Normal: - actions << Playlists::UserPlaylistProvider::trackActions( play= list, trackIndex ); - break; - case IpodPlaylist::Stale: - case IpodPlaylist::Orphaned: - actions << m_coll->m_consolidateAction; - break; - } - - return actions; + return playlistActions( playlistTracks.uniqueKeys() ); } = bool @@ -149,7 +132,7 @@ IpodPlaylistProvider::isWritable() } = void -IpodPlaylistProvider::rename( Playlists::PlaylistPtr playlist, const QStri= ng &newName ) +IpodPlaylistProvider::renamePlaylist( Playlists::PlaylistPtr playlist, con= st QString &newName ) { if( !m_playlists.contains( playlist ) ) // make following static cast= safe return; @@ -163,7 +146,7 @@ IpodPlaylistProvider::rename( Playlists::PlaylistPtr pl= aylist, const QString &ne } = bool -IpodPlaylistProvider::deletePlaylists( Playlists::PlaylistList playlistlis= t ) +IpodPlaylistProvider::deletePlaylists( const Playlists::PlaylistList &play= listlist ) { if( !isWritable() ) return false; diff --git a/src/core-impl/collections/ipodcollection/IpodPlaylistProvider.= h b/src/core-impl/collections/ipodcollection/IpodPlaylistProvider.h index 990d423..0ba2d15 100644 --- a/src/core-impl/collections/ipodcollection/IpodPlaylistProvider.h +++ b/src/core-impl/collections/ipodcollection/IpodPlaylistProvider.h @@ -49,13 +49,12 @@ class IpodPlaylistProvider : public Playlists::UserPlay= listProvider, private Pla const QString& name =3D QStri= ng() ); = virtual QActionList providerActions(); - virtual QActionList playlistActions( Playlists::PlaylistPtr playli= st ); - virtual QActionList trackActions( Playlists::PlaylistPtr playlist, - int trackIndex ); + virtual QActionList playlistActions( const Playlists::PlaylistList= &playlists ); + virtual QActionList trackActions( const QMultiHash &playlistTracks ); = virtual bool isWritable(); - virtual void rename( Playlists::PlaylistPtr playlist, const QStrin= g &newName ); - virtual bool deletePlaylists( Playlists::PlaylistList playlistlist= ); + virtual void renamePlaylist( Playlists::PlaylistPtr playlist, cons= t QString &newName ); + virtual bool deletePlaylists( const Playlists::PlaylistList &playl= istlist ); = // PlaylistObserver methods: virtual void metadataChanged( Playlists::PlaylistPtr playlist ); diff --git a/src/core-impl/collections/mediadevicecollection/playlist/Media= DeviceUserPlaylistProvider.cpp b/src/core-impl/collections/mediadevicecolle= ction/playlist/MediaDeviceUserPlaylistProvider.cpp index f49a9f6..e6d52e1 100644 --- a/src/core-impl/collections/mediadevicecollection/playlist/MediaDeviceU= serPlaylistProvider.cpp +++ b/src/core-impl/collections/mediadevicecollection/playlist/MediaDeviceU= serPlaylistProvider.cpp @@ -33,7 +33,6 @@ #include #include = -#include #include = static const int USERPLAYLIST_DB_VERSION =3D 2; @@ -43,7 +42,6 @@ namespace Playlists { = MediaDeviceUserPlaylistProvider::MediaDeviceUserPlaylistProvider( Collecti= ons::MediaDeviceCollection *collection ) : Playlists::UserPlaylistProvider() - , m_renameAction( 0 ) , m_collection( collection ) { DEBUG_BLOCK @@ -79,65 +77,7 @@ MediaDeviceUserPlaylistProvider::playlists() = return playlists; } -#if 0 -void -SqlPlaylists::UserPlaylistProvider::slotDelete() -{ - DEBUG_BLOCK - - //TODO FIXME Confirmation of delete - foreach( Playlists::PlaylistPtr playlist, The::userPlaylistModel()->se= lectedPlaylists() ) - { - Meta::SqlPlaylistPtr sqlPlaylist =3D - Meta::SqlPlaylistPtr::dynamicCast( playlist ); - if( sqlPlaylist ) - { - debug() << "deleting " << sqlPlaylist->name(); - sqlPlaylist->removeFromDb(); - } - } - reloadFromDb(); -} -#endif - -#if 0 -void -MediaDeviceUserPlaylistProvider::slotRename() -{ - DEBUG_BLOCK - //only one playlist can be selected at this point - Playlists::MediaDevicePlaylistPtr playlist =3D selectedPlaylists().fir= st(); - if( playlist.isNull() ) - return; - - bool ok; - const QString newName =3D KInputDialog::getText( i18n("Change playlist= "), - i18n("Enter new name for playlist:"), playlist->name(), - &ok ); - if ( ok ) - { - playlist->setName( newName.trimmed() ); - emit( updated() ); - } -} -#endif -#if 0 -void -SqlPlaylists::UserPlaylistProvider::slotRemove() -{ - QAction *action =3D qobject_cast( QObject::sender() ); - if( action =3D=3D 0 ) - return; = - PlaylistTrackMap playlistMap =3D action->data().value(); - foreach( Playlists::PlaylistPtr playlist, playlistMap.keys() ) - foreach( Meta::TrackPtr track, playlistMap.values( playlist ) ) - playlist->removeTrack( playlist->tracks().indexOf( track ) ); - - //clear the data - action->setData( QVariant() ); -} -#endif Playlists::PlaylistPtr MediaDeviceUserPlaylistProvider::save( const Meta::TrackList &tracks ) { @@ -169,7 +109,7 @@ MediaDeviceUserPlaylistProvider::save( const Meta::Trac= kList &tracks, const QStr } = void -MediaDeviceUserPlaylistProvider::rename( Playlists::PlaylistPtr playlist, = const QString &newName ) +MediaDeviceUserPlaylistProvider::renamePlaylist( Playlists::PlaylistPtr pl= aylist, const QString &newName ) { DEBUG_BLOCK Playlists::MediaDevicePlaylistPtr pl =3D Playlists::MediaDevicePlaylis= tPtr::staticCast( playlist ); @@ -183,7 +123,7 @@ MediaDeviceUserPlaylistProvider::rename( Playlists::Pla= ylistPtr playlist, const } = bool -MediaDeviceUserPlaylistProvider::deletePlaylists( Playlists::PlaylistList = playlistlist ) +MediaDeviceUserPlaylistProvider::deletePlaylists( const Playlists::Playlis= tList &playlistlist ) { Playlists::MediaDevicePlaylistList pllist; foreach( Playlists::PlaylistPtr playlist, playlistlist ) diff --git a/src/core-impl/collections/mediadevicecollection/playlist/Media= DeviceUserPlaylistProvider.h b/src/core-impl/collections/mediadevicecollect= ion/playlist/MediaDeviceUserPlaylistProvider.h index 3a1572f..d86decc 100644 --- a/src/core-impl/collections/mediadevicecollection/playlist/MediaDeviceU= serPlaylistProvider.h +++ b/src/core-impl/collections/mediadevicecollection/playlist/MediaDeviceU= serPlaylistProvider.h @@ -23,8 +23,6 @@ #include #include = -class QAction; - namespace Collections { class MediaDeviceCollection; } @@ -39,7 +37,7 @@ class AMAROK_EXPORT MediaDeviceUserPlaylistProvider : pub= lic Playlists::UserPlay ~MediaDeviceUserPlaylistProvider(); = /* PlaylistProvider functions */ - virtual QString prettyName() const { return i18n("Media Device pla= ylists"); }; + virtual QString prettyName() const { return i18n( "Media Device pl= aylists" ); }; virtual KIcon icon() const { return KIcon( "multimedia-player" ); } = /* Playlists::UserPlaylistProvider functions */ @@ -48,15 +46,9 @@ class AMAROK_EXPORT MediaDeviceUserPlaylistProvider : pu= blic Playlists::UserPlay virtual Playlists::PlaylistPtr save( const Meta::TrackList &tracks= ); virtual Playlists::PlaylistPtr save( const Meta::TrackList &tracks= , const QString& name ); = - // virtual bool supportsEmptyGroups() { return true; } - - //virtual QList playlistActions( Meta::PlaylistList lis= t ); - virtual bool isWritable() { return true; } - - virtual void rename( Playlists::PlaylistPtr playlist, const QStrin= g &newName ); - - virtual bool deletePlaylists( Playlists::PlaylistList playlistlist= ); + virtual void renamePlaylist( Playlists::PlaylistPtr playlist, cons= t QString &newName ); + virtual bool deletePlaylists( const Playlists::PlaylistList &playl= istlist ); = /// MediaDevice-specific Functions = @@ -71,16 +63,9 @@ class AMAROK_EXPORT MediaDeviceUserPlaylistProvider : pu= blic Playlists::UserPlay void playlistRenamed( const Playlists::MediaDevicePlaylistPtr = &playlist ); void playlistsDeleted( const Playlists::MediaDevicePlaylistLis= t &playlistlist ); = - private slots: - //void slotDelete(); - //void slotRename(); - //void slotRemove(); - private: - MediaDevicePlaylistList m_playlists; = - QAction *m_renameAction; Collections::MediaDeviceCollection *m_collection; }; = diff --git a/src/core-impl/collections/umscollection/podcasts/UmsPodcastPro= vider.cpp b/src/core-impl/collections/umscollection/podcasts/UmsPodcastProv= ider.cpp index e8af676..7579ba2 100644 --- a/src/core-impl/collections/umscollection/podcasts/UmsPodcastProvider.c= pp +++ b/src/core-impl/collections/umscollection/podcasts/UmsPodcastProvider.c= pp @@ -174,28 +174,23 @@ UmsPodcastProvider::playlists() return playlists; } = -QList +QActionList UmsPodcastProvider::episodeActions( PodcastEpisodeList episodes ) { - QList actions; + QActionList actions; + if( episodes.isEmpty() ) + return actions; + if( m_deleteEpisodeAction =3D=3D 0 ) { - m_deleteEpisodeAction =3D new QAction( - KIcon( "edit-delete" ), - i18n( "&Delete Episode" ), - this - ); + m_deleteEpisodeAction =3D new QAction( KIcon( "edit-delete" ), i18= n( "&Delete Episode" ), this ); m_deleteEpisodeAction->setProperty( "popupdropper_svg_id", "delete= " ); - connect( m_deleteEpisodeAction, SIGNAL(triggered()), - SLOT(slotDeleteEpisodes()) ); + connect( m_deleteEpisodeAction, SIGNAL(triggered()), SLOT(slotDele= teEpisodes()) ); } - //set the episode list as data that we'll retrieve in the slot - PodcastEpisodeList actionList =3D - m_deleteEpisodeAction->data().value(); - - actionList << episodes; - m_deleteEpisodeAction->setData( QVariant::fromValue( actionList ) ); + // set the episode list as data that we'll retrieve in the slot + m_deleteEpisodeAction->setData( QVariant::fromValue( episodes ) ); actions << m_deleteEpisodeAction; + return actions; } = @@ -318,29 +313,24 @@ UmsPodcastProvider::deleteJobComplete( KJob *job ) } } = -QList +QActionList UmsPodcastProvider::channelActions( PodcastChannelList channels ) { - QList actions; + QActionList actions; + if( channels.isEmpty() ) + return actions; + if( m_deleteChannelAction =3D=3D 0 ) { - m_deleteChannelAction =3D new QAction( - KIcon( "edit-delete" ), - i18n( "&Delete Channel and Episodes" ), - this - ); + m_deleteChannelAction =3D new QAction( KIcon( "edit-delete" ), i18= n( "&Delete " + "Channel and Episodes" ), this ); m_deleteChannelAction->setProperty( "popupdropper_svg_id", "delete= " ); - connect( m_deleteChannelAction, SIGNAL(triggered()), - SLOT(slotDeleteChannels()) ); + connect( m_deleteChannelAction, SIGNAL(triggered()), SLOT(slotDele= teChannels()) ); } - //set the episode list as data that we'll retrieve in the slot - PodcastChannelList actionList =3D - m_deleteChannelAction->data().value(); - - actionList << channels; - m_deleteChannelAction->setData( QVariant::fromValue( actionList ) ); - + // set the episode list as data that we'll retrieve in the slot + m_deleteChannelAction->setData( QVariant::fromValue( channels ) ); actions << m_deleteChannelAction; + return actions; } = @@ -372,29 +362,41 @@ UmsPodcastProvider::slotDeleteChannels() } } = -QList -UmsPodcastProvider::playlistActions( Playlists::PlaylistPtr playlist ) +QActionList +UmsPodcastProvider::playlistActions( const Playlists::PlaylistList &playli= sts ) { PodcastChannelList channels; - PodcastChannelPtr channel =3D PodcastChannelPtr::dynamicCast( playlist= ); - if( channel.isNull() ) - return QList(); + foreach( const Playlists::PlaylistPtr &playlist, playlists ) + { + PodcastChannelPtr channel =3D PodcastChannelPtr::dynamicCast( play= list ); + if( channel ) + channels << channel; + } = - return channelActions( channels << channel ); + return channelActions( channels ); } = -QList -UmsPodcastProvider::trackActions( Playlists::PlaylistPtr playlist, int tra= ckIndex ) +QActionList +UmsPodcastProvider::trackActions( const QMultiHash &playlistTracks ) { - if( playlist->tracks().count() > trackIndex ) + PodcastEpisodeList episodes; + foreach( const Playlists::PlaylistPtr &playlist, playlistTracks.unique= Keys() ) { - PodcastEpisodeList episodes; - episodes << UmsPodcastEpisode::toPodcastEpisodePtr( - UmsPodcastEpisode::fromTrackPtr( playlist->tracks()[tr= ackIndex] ) - ); - return episodeActions( episodes ); + PodcastChannelPtr channel =3D PodcastChannelPtr::dynamicCast( play= list ); + if( !channel ) + continue; + + PodcastEpisodeList channelEpisodes =3D channel->episodes(); + QList trackPositions =3D playlistTracks.values( playlist ); + qSort( trackPositions ); + foreach( int trackPosition, trackPositions ) + { + if( trackPosition >=3D 0 && trackPosition < channelEpisodes.co= unt() ) + episodes << channelEpisodes.at( trackPosition ); + } } - return QList(); + + return episodeActions( episodes ); } = void diff --git a/src/core-impl/collections/umscollection/podcasts/UmsPodcastPro= vider.h b/src/core-impl/collections/umscollection/podcasts/UmsPodcastProvid= er.h index d46f63d..18f29f6 100644 --- a/src/core-impl/collections/umscollection/podcasts/UmsPodcastProvider.h +++ b/src/core-impl/collections/umscollection/podcasts/UmsPodcastProvider.h @@ -57,12 +57,8 @@ class UmsPodcastProvider : public PodcastProvider = virtual Playlists::PlaylistList playlists(); = - virtual QList episodeActions( Podcasts::PodcastEpisodeL= ist ); - virtual QList channelActions( Podcasts::PodcastChannelL= ist ); - - virtual QList playlistActions( Playlists::PlaylistPtr p= laylist ); - virtual QList trackActions( Playlists::PlaylistPtr play= list, - int trackIndex ); + virtual QActionList playlistActions( const Playlists::PlaylistList= &playlists ); + virtual QActionList trackActions( const QMultiHash &playlistTracks ); = virtual void completePodcastDownloads(); = @@ -84,6 +80,8 @@ class UmsPodcastProvider : public PodcastProvider void slotCopyComplete( KJob *job ); = private: + QList episodeActions( Podcasts::PodcastEpisodeList ); + QList channelActions( Podcasts::PodcastChannelList ); void deleteEpisodes( UmsPodcastEpisodeList umsEpisodes ); = KUrl m_scanDirectory; diff --git a/src/core-impl/playlists/providers/user/UserPlaylistProvider.cp= p b/src/core-impl/playlists/providers/user/UserPlaylistProvider.cpp index d9d066c..abcd1d4 100644 --- a/src/core-impl/playlists/providers/user/UserPlaylistProvider.cpp +++ b/src/core-impl/playlists/providers/user/UserPlaylistProvider.cpp @@ -15,56 +15,11 @@ * this program. If not, see . = * *************************************************************************= ***************/ = -#include "core-impl/playlists/providers/user/UserPlaylistProvider.h" -#include "core-impl/playlists/types/file/PlaylistFileSupport.h" - -#include "playlistmanager/PlaylistManager.h" -#include "amarokconfig.h" - -#include -#include -#include // KFileDialog needs this. It's a scandal. -#include - -#include -#include -#include - -// For removing multiple tracks from different playlists with one QAction -typedef QMultiMap PlaylistTrackMap; -Q_DECLARE_METATYPE( PlaylistTrackMap ) +#include "UserPlaylistProvider.h" = Playlists::UserPlaylistProvider::UserPlaylistProvider( QObject *parent ) : PlaylistProvider( parent ) { - m_deleteAction =3D new QAction( KIcon( "media-track-remove-amarok" ), = i18n( "&Delete..." ), this ); - m_deleteAction->setProperty( "popupdropper_svg_id", "delete" ); - // object name must match one in PlaylistBrowserNS::PlaylistBrowserView - m_deleteAction->setObjectName( "deleteAction" ); - // key shortcut is only for display purposes here, actual one is deter= mined by View in Model/View classes - m_deleteAction->setShortcut( Qt::Key_Delete ); - connect( m_deleteAction, SIGNAL(triggered()), SLOT(slotDelete()) ); - - m_renameAction =3D new QAction( KIcon( "media-track-edit-amarok" ), i= 18n( "&Rename..." ), this ); - m_renameAction->setProperty( "popupdropper_svg_id", "edit" ); - // key shortcut is only for display purposes here, actual one is deter= mined by View in Model/View classes - m_renameAction->setShortcut( Qt::Key_F2 ); - connect( m_renameAction, SIGNAL(triggered()), this, SLOT(slotRename())= ); - - m_exportAction =3D new QAction( KIcon( "document-export-amarok" ), i18= n("&Export Playlist As..."), this ); - connect( m_exportAction, SIGNAL(triggered()), this, SLOT(slotExport())= ); - - m_removeTrackAction =3D new QAction( KIcon( "media-track-remove-amarok= " ), QString( "Placeholder" ), this ); - m_removeTrackAction->setProperty( "popupdropper_svg_id", "delete" ); - // object name must match one in PlaylistBrowserNS::PlaylistBrowserView - m_removeTrackAction->setObjectName( "deleteAction" ); - // key shortcut is only for display purposes here, actual one is deter= mined by View in Model/View classes - m_removeTrackAction->setShortcut( Qt::Key_Delete ); - connect( m_removeTrackAction, SIGNAL(triggered()), SLOT(slotRemoveTrac= k()) ); -} - -Playlists::UserPlaylistProvider::~UserPlaylistProvider() -{ } = int @@ -72,175 +27,3 @@ Playlists::UserPlaylistProvider::category() const { return Playlists::UserPlaylist; } - -QList -Playlists::UserPlaylistProvider::playlistActions( Playlists::PlaylistPtr p= laylist ) -{ - QList actions; - - Playlists::PlaylistList actionList =3D m_deleteAction->data().value(); - actionList << playlist; - m_deleteAction->setData( QVariant::fromValue( actionList ) ); - actions << m_deleteAction; - - m_renameAction->setData( QVariant::fromValue( playlist ) ); - actions << m_renameAction; - - m_exportAction->setData( QVariant::fromValue( playlist ) ); - actions << m_exportAction; - - return actions; -} - -QList -Playlists::UserPlaylistProvider::trackActions( Playlists::PlaylistPtr play= list, int trackIndex ) -{ - Q_UNUSED( trackIndex ); - QList actions; - - if( trackIndex < 0 ) - return actions; - int trackCount =3D playlist->trackCount(); - if( trackCount =3D=3D -1 ) - trackCount =3D playlist->tracks().size(); - if( trackIndex >=3D trackCount ) - return actions; - - // Add the playlist/track combination to a QMultiMap that is stored in= the action. - // In the slot we use this data to remove that track from the playlist. - PlaylistTrackMap playlistMap =3D m_removeTrackAction->data().value(); - - //only add action to map if playlist/track combo is not in there yet. - if( !playlistMap.keys().contains( playlist ) || - !playlistMap.values( playlist ).contains( trackIndex ) ) - { - playlistMap.insert( playlist, trackIndex ); - } - m_removeTrackAction->setData( QVariant::fromValue( playlistMap ) ); - - const int actionTrackCount =3D playlistMap.count(); - const int playlistCount =3D playlistMap.uniqueKeys().count(); - if( playlistCount > 1 ) - { - m_removeTrackAction->setText( i18ncp( "Number of playlists is >=3D= 2", - "Remove a track from %2 playlists", "Remove %1 tracks from %2 = playlists", - actionTrackCount, playlistCount ) ); - } - else - { - m_removeTrackAction->setText( i18ncp( "%2 is saved playlist name", - "Remove a track from %2", "Remove %1 tracks from %2", actionTr= ackCount, - playlist->name() ) ); - } - actions << m_removeTrackAction; - - return actions; -} - -void -Playlists::UserPlaylistProvider::slotDelete() -{ - QAction *action =3D qobject_cast( QObject::sender() ); - if( action =3D=3D 0 ) - return; - - Playlists::PlaylistList playlists =3D action->data().value(); - if( playlists.count() =3D=3D 0 ) - return; - - KDialog dialog; - dialog.setCaption( i18n( "Confirm Delete" ) ); - dialog.setButtons( KDialog::Ok | KDialog::Cancel ); - QLabel label( i18np( "Are you sure you want to delete this playlist?", - "Are you sure you want to delete these %1 playlis= ts?", - playlists.count() ) - , &dialog - ); - //TODO:include a text area with all the names of the playlists - dialog.setButtonText( KDialog::Ok, i18nc( "%1 is playlist provider pre= tty name", - "Yes, delete from %1.", pret= tyName() ) ); - dialog.setMainWidget( &label ); - if( dialog.exec() =3D=3D QDialog::Accepted ) - deletePlaylists( playlists ); -} - -void -Playlists::UserPlaylistProvider::slotRename() -{ - QAction *action =3D qobject_cast( QObject::sender() ); - if( action =3D=3D 0 ) - return; - - Playlists::PlaylistPtr playlist =3D action->data().value(); - if( playlist.isNull() ) - return; - - The::playlistManager()->rename( playlist ); // initiates inline renam= e of the playlist -} - -void -Playlists::UserPlaylistProvider::slotExport() -{ - QAction *action =3D qobject_cast( QObject::sender() ); - if( action =3D=3D 0 ) - return; - - Playlists::PlaylistPtr playlist =3D action->data().value(); - if( playlist.isNull() ) - return; - - // --- display save location dialog - // compare with MainWindow::exportPlaylist - // TODO: have this code only once - KFileDialog fileDialog( KUrl("kfiledialog:///amarok-playlist-export"),= QString(), 0 ); - QCheckBox *saveRelativeCheck =3D new QCheckBox( i18n("Use relative pat= h for &saving") ); - saveRelativeCheck->setChecked( AmarokConfig::relativePlaylist() ); - - QStringList supportedMimeTypes; - - supportedMimeTypes << "video/x-ms-asf"; //ASX - supportedMimeTypes << "audio/x-mpegurl"; //M3U - supportedMimeTypes << "audio/x-scpls"; //PLS - supportedMimeTypes << "application/xspf+xml"; //XSPF - - fileDialog.setMimeFilter( supportedMimeTypes, supportedMimeTypes.first= () ); - fileDialog.fileWidget()->setCustomWidget( saveRelativeCheck ); - fileDialog.setOperationMode( KFileDialog::Saving ); - fileDialog.setMode( KFile::File ); - fileDialog.setCaption( i18n("Save As") ); - fileDialog.setObjectName( "PlaylistExport" ); - - fileDialog.exec(); - - QString playlistPath =3D fileDialog.selectedFile(); - - // --- actually save the playlist - if( !playlistPath.isEmpty() ) - exportPlaylistFile( playlist->tracks(), playlistPath, saveRelative= Check->isChecked() ); -} - -void -Playlists::UserPlaylistProvider::slotRemoveTrack() -{ - QAction *action =3D qobject_cast( QObject::sender() ); - if( action =3D=3D 0 ) - return; - - PlaylistTrackMap playlistMap =3D action->data().value(); - foreach( Playlists::PlaylistPtr playlist, playlistMap.uniqueKeys() ) - { - QList trackIndices =3D playlistMap.values( playlist ); - qSort( trackIndices ); - int removed =3D 0; - foreach( int trackIndex, trackIndices ) - { - playlist->removeTrack( trackIndex - removed /* account for alr= eady removed */ ); - removed++; - } - } - - //clear the data - action->setData( QVariant() ); -} - -#include "UserPlaylistProvider.moc" diff --git a/src/core-impl/playlists/providers/user/UserPlaylistProvider.h = b/src/core-impl/playlists/providers/user/UserPlaylistProvider.h index 38a3cd9..b72ead5 100644 --- a/src/core-impl/playlists/providers/user/UserPlaylistProvider.h +++ b/src/core-impl/playlists/providers/user/UserPlaylistProvider.h @@ -20,73 +20,26 @@ #define USERPLAYLISTPROVIDER_H = #include "amarok_export.h" -#include "core/playlists/Playlist.h" #include "core/playlists/PlaylistProvider.h" = -class QAction; - namespace Playlists { - -/** - @author Bart Cerneels -*/ -class AMAROK_EXPORT UserPlaylistProvider : public PlaylistProvider -{ - Q_OBJECT - public: - explicit UserPlaylistProvider( QObject *parent =3D 0 ); - virtual ~UserPlaylistProvider(); - - /* PlaylistProvider functions */ - virtual int category() const; - - /* UserPlaylistProvider functions */ - virtual Playlists::PlaylistPtr save( const Meta::TrackList &tracks, - const QString& name =3D QStri= ng() ) =3D 0; - - virtual QList playlistActions( Playlists::PlaylistPtr p= laylist ); - virtual QList trackActions( Playlists::PlaylistPtr play= list, - int trackIndex ); - - // UserPlaylistProvider-specific - virtual bool isWritable() { return false; } - virtual void rename( Playlists::PlaylistPtr playlist, const QStrin= g &newName ) - { Q_UNUSED( playlist ) Q_UNUSED(newName) } - virtual bool deletePlaylists( Playlists::PlaylistList playlistlist= ) - { Q_UNUSED( playlistlist ) return false; } - - protected slots: - /** - * Delete selected playlists. - * Will only work if the sender is a QAction with a PlaylistPtr as= data. - */ - virtual void slotDelete(); - - /** - * Rename selected playlist. - * Will only work if the sender is a QAction with a PlaylistPtr as= data. - */ - virtual void slotRename(); - - /** - * Export the selected playlist. - * Will only work if the sender is a QAction with a PlaylistPtr as= data. - */ - virtual void slotExport(); - - /** - * Remove a track (or tracks) from a playlist. - * Will only work if the sender is a QAction with a PlaylistTrackM= ap as data. - */ - virtual void slotRemoveTrack(); - - protected: - QAction *m_deleteAction; - QAction *m_renameAction; - QAction *m_exportAction; - QAction *m_removeTrackAction; -}; - + /** + * @author Bart Cerneels + */ + class AMAROK_EXPORT UserPlaylistProvider : public PlaylistProvider + { + Q_OBJECT + + public: + explicit UserPlaylistProvider( QObject *parent =3D 0 ); + + /* PlaylistProvider functions */ + virtual int category() const; + + /* UserPlaylistProvider functions */ + virtual PlaylistPtr save( const Meta::TrackList &tracks, + const QString &name =3D QString() ) = =3D 0; + }; } //namespace Playlists = #endif diff --git a/src/core-impl/playlists/types/file/PlaylistFile.h b/src/core-i= mpl/playlists/types/file/PlaylistFile.h index de71057..bd88199 100644 --- a/src/core-impl/playlists/types/file/PlaylistFile.h +++ b/src/core-impl/playlists/types/file/PlaylistFile.h @@ -25,11 +25,11 @@ #include #include = -class PlaylistProvider; class QFile; = namespace Playlists { + class PlaylistProvider; class PlaylistFile; class PlaylistFileLoaderJob; = diff --git a/src/core-impl/podcasts/sql/SqlPodcastProvider.cpp b/src/core-i= mpl/podcasts/sql/SqlPodcastProvider.cpp index ad60e2b..ba7387b 100644 --- a/src/core-impl/podcasts/sql/SqlPodcastProvider.cpp +++ b/src/core-impl/podcasts/sql/SqlPodcastProvider.cpp @@ -280,31 +280,25 @@ SqlPodcastProvider::playlists() return playlistList; } = -QList +QActionList SqlPodcastProvider::providerActions() { if( m_providerActions.isEmpty() ) { QAction *updateAllAction =3D new QAction( KIcon( "view-refresh-ama= rok" ), - i18n( "&Update All Channels" ), - this - ); + i18n( "&Update All Channels" ), this ); updateAllAction->setProperty( "popupdropper_svg_id", "update" ); connect( updateAllAction, SIGNAL(triggered()), this, SLOT(updateAl= l()) ); m_providerActions << updateAllAction; = QAction *configureAction =3D new QAction( KIcon( "configure" ), - i18n( "&Configure General Settings" ), - this - ); + i18n( "&Configure General Settings" ), this ); configureAction->setProperty( "popupdropper_svg_id", "configure" ); connect( configureAction, SIGNAL(triggered()), this, SLOT(slotConf= igureProvider()) ); m_providerActions << configureAction; = QAction *exportOpmlAction =3D new QAction( KIcon( "document-export= " ), - i18n( "&Export subscripti= ons to OPML file" ), - this - ); + i18n( "&Export subscriptions to OPML file" ), this ); connect( exportOpmlAction, SIGNAL(triggered()), SLOT(slotExportOpm= l()) ); m_providerActions << exportOpmlAction; } @@ -312,114 +306,100 @@ SqlPodcastProvider::providerActions() return m_providerActions; } = -QList -SqlPodcastProvider::playlistActions( Playlists::PlaylistPtr playlist ) +QActionList +SqlPodcastProvider::playlistActions( const Playlists::PlaylistList &playli= sts ) { - QList actions; + QActionList actions; + SqlPodcastChannelList sqlChannels; + foreach( const Playlists::PlaylistPtr &playlist, playlists ) + { + SqlPodcastChannelPtr sqlChannel =3D SqlPodcastChannel::fromPlaylis= tPtr( playlist ); + if( sqlChannel ) + sqlChannels << sqlChannel; + } = - Podcasts::SqlPodcastChannelPtr sqlChannel =3D Podcasts::SqlPodcastChan= nel::fromPlaylistPtr( playlist ); - if( sqlChannel.isNull() ) + if( sqlChannels.isEmpty() ) return actions; = //TODO: add export OPML action for selected playlists only. Use the QA= ction::data() trick. if( m_configureChannelAction =3D=3D 0 ) { - m_configureChannelAction =3D new QAction( - KIcon( "configure" ), - i18n( "&Configure" ), - this - ); + m_configureChannelAction =3D new QAction( KIcon( "configure" ), i1= 8n( "&Configure" ), this ); m_configureChannelAction->setProperty( "popupdropper_svg_id", "con= figure" ); connect( m_configureChannelAction, SIGNAL(triggered()), SLOT(slotC= onfigureChannel()) ); } - //only one channel can be configured at a time. - if( m_configureChannelAction->data().isNull() ) - m_configureChannelAction->setData( QVariant::fromValue( sqlChannel= ) ); - - actions << m_configureChannelAction; + if( sqlChannels.count() =3D=3D 1 ) + { + m_configureChannelAction->setData( QVariant::fromValue( sqlChannel= s.first() ) ); + actions << m_configureChannelAction; + } = - Podcasts::SqlPodcastChannelList actionChannels; if( m_removeAction =3D=3D 0 ) { - m_removeAction =3D new QAction( - KIcon( "news-unsubscribe" ), - i18n( "&Remove Subscription" ), - this - ); + m_removeAction =3D new QAction( KIcon( "news-unsubscribe" ), i18n(= "&Remove Subscription" ), this ); m_removeAction->setProperty( "popupdropper_svg_id", "remove" ); connect( m_removeAction, SIGNAL(triggered()), SLOT(slotRemoveChann= els()) ); } - else - { - actionChannels =3D m_removeAction->data().value(); - } - m_removeAction->setObjectName( "deleteAction" ); - - actionChannels << sqlChannel; - m_removeAction->setData( QVariant::fromValue( actionChannels ) ); - + m_removeAction->setData( QVariant::fromValue( sqlChannels ) ); actions << m_removeAction; = - actionChannels.clear(); if( m_updateAction =3D=3D 0 ) { - m_updateAction =3D new QAction( - KIcon( "view-refresh-amarok" ), - i18n( "&Update Channel" ), - this - ); + m_updateAction =3D new QAction( KIcon( "view-refresh-amarok" ), i1= 8n( "&Update Channel" ), this ); m_updateAction->setProperty( "popupdropper_svg_id", "update" ); connect( m_updateAction, SIGNAL(triggered()), SLOT(slotUpdateChann= els()) ); } - else - { - actionChannels =3D m_updateAction->data().value(); - } - - actionChannels << sqlChannel; - m_updateAction->setData( QVariant::fromValue( actionChannels ) ); - + m_updateAction->setData( QVariant::fromValue( sqlChannels ) ); actions << m_updateAction; = return actions; } = -QList -SqlPodcastProvider::trackActions( Playlists::PlaylistPtr playlist, int tra= ckIndex ) +QActionList +SqlPodcastProvider::trackActions( const QMultiHash &playlistTracks ) { - QList actions; - Podcasts::SqlPodcastChannelPtr sqlChannel =3D Podcasts::SqlPodcastChan= nel::fromPlaylistPtr( playlist ); - if( sqlChannel.isNull() ) - return actions; + SqlPodcastEpisodeList episodes; + foreach( const Playlists::PlaylistPtr &playlist, playlistTracks.unique= Keys() ) + { + SqlPodcastChannelPtr sqlChannel =3D SqlPodcastChannel::fromPlaylis= tPtr( playlist ); + if( !sqlChannel ) + continue; = - Podcasts::SqlPodcastEpisodeList sqlEpisodes =3D sqlChannel->sqlEpisode= s(); - if( trackIndex >=3D sqlEpisodes.count() ) - return actions; + SqlPodcastEpisodeList channelEpisodes =3D sqlChannel->sqlEpisodes(= ); + QList trackPositions =3D playlistTracks.values( playlist ); + qSort( trackPositions ); + foreach( int trackPosition, trackPositions ) + { + if( trackPosition >=3D 0 && trackPosition < channelEpisodes.co= unt() ) + episodes << channelEpisodes.at( trackPosition ); + } + } = - Podcasts::SqlPodcastEpisodePtr sqlEpisode =3D sqlEpisodes.at( trackInd= ex ); - if( sqlEpisode.isNull() ) + QActionList actions; + if( episodes.isEmpty() ) return actions; = + if( m_downloadAction =3D=3D 0 ) + { + m_downloadAction =3D new QAction( KIcon( "go-down" ), i18n( "&Down= load Episode" ), this ); + m_downloadAction->setProperty( "popupdropper_svg_id", "download" ); + connect( m_downloadAction, SIGNAL(triggered()), SLOT(slotDownloadE= pisodes()) ); + } + if( m_deleteAction =3D=3D 0 ) { - m_deleteAction =3D new QAction( - KIcon( "edit-delete" ), - i18n( "&Delete Downloaded Episode" ), - this - ); + m_deleteAction =3D new QAction( KIcon( "edit-delete" ), + i18n( "&Delete Downloaded Episode" ), this ); m_deleteAction->setProperty( "popupdropper_svg_id", "delete" ); + m_deleteAction->setObjectName( "deleteAction" ); connect( m_deleteAction, SIGNAL(triggered()), SLOT(slotDeleteDownl= oadedEpisodes()) ); } - m_deleteAction->setObjectName( "deleteAction" ); = if( m_writeTagsAction =3D=3D 0 ) { - m_writeTagsAction =3D new QAction( - KIcon( "media-track-edit-amarok" ), - i18n( "&Write Feed Information to File" ), - this - ); + m_writeTagsAction =3D new QAction( KIcon( "media-track-edit-amarok= " ), + i18n( "&Write Feed Information to File" ), this ); m_writeTagsAction->setProperty( "popupdropper_svg_id", "edit" ); connect( m_writeTagsAction, SIGNAL(triggered()), SLOT(slotWriteTag= sToFiles()) ); } @@ -427,51 +407,44 @@ SqlPodcastProvider::trackActions( Playlists::Playlist= Ptr playlist, int trackInde if( m_keepAction =3D=3D 0 ) { m_keepAction =3D new QAction( KIcon( "podcast-amarok" ), - i18nc( "toggle the \"keep\" downloa= ded file status of this podcast episode. " - "Notice that downloaded file= s with this status wouldn't be deleted if we apply a purge.", - "&Keep downloaded file" ), - this - ); + i18n( "&Keep downloaded file" ), this ); + m_keepAction->setToolTip( i18n( "Toggle the \"keep\" downloaded fi= le status of " + "this podcast episode. Downloaded files with this status w= ouldn't be " + "deleted even if we apply a purge." ) ); m_keepAction->setProperty( "popupdropper_svg_id", "keep" ); m_keepAction->setCheckable( true ); - connect( m_keepAction, SIGNAL(triggered(bool)), SLOT(slotSetKeep()= ) ); } = - Podcasts::SqlPodcastEpisodeList actionEpisodes; - if( !sqlEpisode->localUrl().isEmpty() ) - { - actionEpisodes =3D m_deleteAction->data().value(); - actionEpisodes << sqlEpisode; - m_keepAction->setChecked( sqlEpisode->isKeep() ); - m_keepAction->setData( QVariant::fromValue( actionEpisodes ) ); - m_deleteAction->setData( QVariant::fromValue( actionEpisodes ) ); - //these lists are the same anyway - m_writeTagsAction->setData( QVariant::fromValue( actionEpisodes ) = ); - actions << m_keepAction; - actions << m_deleteAction; - actions << m_writeTagsAction; - } - else + SqlPodcastEpisodeList remoteEpisodes; + SqlPodcastEpisodeList keptDownloadedEpisodes, unkeptDownloadedEpisodes; + foreach( const SqlPodcastEpisodePtr &episode, episodes ) { - if( m_downloadAction =3D=3D 0 ) - { - m_downloadAction =3D new QAction( - KIcon( "go-down" ), - i18n( "&Download Episode" ), - this - ); - m_downloadAction->setProperty( "popupdropper_svg_id", "downloa= d" ); - connect( m_downloadAction, SIGNAL(triggered()), SLOT(slotDownl= oadEpisodes()) ); - } + if( episode->localUrl().isEmpty() ) + remoteEpisodes << episode; else { - actionEpisodes =3D m_downloadAction->data().value(); + if( episode->isKeep() ) + keptDownloadedEpisodes << episode; + else + unkeptDownloadedEpisodes << episode; } - actionEpisodes << sqlEpisode; - m_downloadAction->setData( QVariant::fromValue( actionEpisodes ) ); + } + + if( !remoteEpisodes.isEmpty() ) + { + m_downloadAction->setData( QVariant::fromValue( remoteEpisodes ) ); actions << m_downloadAction; } + if( !( keptDownloadedEpisodes + unkeptDownloadedEpisodes ).isEmpty() ) + { + m_deleteAction->setData( QVariant::fromValue( keptDownloadedEpisod= es + unkeptDownloadedEpisodes ) ); + actions << m_deleteAction; + + m_keepAction->setChecked( unkeptDownloadedEpisodes.isEmpty() ); + m_keepAction->setData( QVariant::fromValue( keptDownloadedEpisodes= + unkeptDownloadedEpisodes ) ); + actions << m_keepAction; + } = return actions; } @@ -806,179 +779,6 @@ SqlPodcastProvider::configureChannel( Podcasts::SqlPo= dcastChannelPtr sqlChannel startTimer(); } = -QList -SqlPodcastProvider::episodeActions( Podcasts::PodcastEpisodeList episodes ) -{ - QList< QAction * > actions; - if( episodes.isEmpty() ) - return actions; - - Podcasts::PodcastEpisodeList actionEpisodes; - if( m_deleteAction =3D=3D 0 ) - { - m_deleteAction =3D new QAction( - KIcon( "edit-delete" ), - i18n( "&Delete Downloaded Episode" ), - this - ); - m_deleteAction->setProperty( "popupdropper_svg_id", "delete" ); - connect( m_deleteAction, SIGNAL(triggered()), SLOT(slotDeleteDownl= oadedEpisodes()) ); - } - - actionEpisodes.clear(); - if( m_writeTagsAction =3D=3D 0 ) - { - m_writeTagsAction =3D new QAction( - KIcon( "media-track-edit-amarok" ), - i18n( "&Write Feed Information to File" ), - this - ); - m_writeTagsAction->setProperty( "popupdropper_svg_id", "edit" ); - connect( m_writeTagsAction, SIGNAL(triggered()), SLOT(slotWriteTag= sToFiles()) ); - } - - if( m_keepAction =3D=3D 0 ) - { - m_keepAction =3D new QAction( KIcon( "podcast-amarok" ), - i18nc( "toggle the \"keep\" downloa= ded file status of this podcast episode. " - "Notice that downloaded file= s with this status wouldn't be deleted if we apply a purge.", - "&Keep downloaded file" ), - this - ); - m_keepAction->setProperty( "popupdropper_svg_id", "keep" ); - m_keepAction->setCheckable( true ); - - connect( m_keepAction, SIGNAL(triggered(bool)), SLOT(slotSetKeep()= ) ); - } - - bool hasDownloaded =3D false; - bool hasKeep =3D false; - foreach( Podcasts::PodcastEpisodePtr episode, episodes ) - { - Podcasts::SqlPodcastEpisodePtr sqlEpisode - =3D Podcasts::SqlPodcastEpisodePtr::dynamicCast( episode ); - if( sqlEpisode.isNull() ) - break; - - if( !sqlEpisode->localUrl().isEmpty() ) - { - hasDownloaded =3D true; - - if ( sqlEpisode->isKeep() ) - hasKeep =3D true; - - break; - } - } - if( hasDownloaded ) - { - Podcasts::PodcastEpisodeList actionEpisodes =3D m_deleteAction->da= ta().value(); - actionEpisodes << episodes; - //these lists are the same anyway - m_keepAction->setData( QVariant::fromValue( actionEpisodes ) ); - m_deleteAction->setData( QVariant::fromValue( actionEpisodes ) ); - m_writeTagsAction->setData( QVariant::fromValue( actionEpisodes ) = ); - - m_keepAction->setChecked( hasKeep ); - - actions << m_keepAction; - actions << m_deleteAction; - actions << m_writeTagsAction; - } - else - { - if( m_downloadAction =3D=3D 0 ) - { - m_downloadAction =3D new QAction( - KIcon( "go-down" ), - i18n( "&Download Episode" ), - this - ); - m_downloadAction->setProperty( "popupdropper_svg_id", "downloa= d" ); - connect( m_downloadAction, SIGNAL(triggered()), SLOT(slotDownl= oadEpisodes()) ); - } - else - { - actionEpisodes =3D m_downloadAction->data().value(); - } - actionEpisodes << episodes; - m_downloadAction->setData( QVariant::fromValue( actionEpisodes ) ); - actions << m_downloadAction; - } - - return actions; -} - -QList -SqlPodcastProvider::channelActions( Podcasts::PodcastChannelList channels ) -{ - QList< QAction * > actions; - - if( channels.isEmpty() ) - return actions; - - if( m_configureChannelAction =3D=3D 0 ) - { - m_configureChannelAction =3D new QAction( - KIcon( "configure" ), - i18n( "&Configure" ), - this - ); - m_configureChannelAction->setProperty( "popupdropper_svg_id", "con= figure" ); - connect( m_configureChannelAction, SIGNAL(triggered()), SLOT(slotC= onfigureChannel()) ); - } - - //only one playlist can be renamed at a time. - if( m_configureChannelAction->data().isNull() ) - m_configureChannelAction->setData( QVariant::fromValue( channels.f= irst() ) ); - - actions << m_configureChannelAction; - - Podcasts::PodcastChannelList actionChannels; - if( m_removeAction =3D=3D 0 ) - { - m_removeAction =3D new QAction( - KIcon( "news-unsubscribe" ), - i18n( "&Remove Subscription" ), - this - ); - m_removeAction->setProperty( "popupdropper_svg_id", "remove" ); - connect( m_removeAction, SIGNAL(triggered()), SLOT(slotRemoveChann= els()) ); - } - else - { - actionChannels =3D m_removeAction->data().value(); - } - - actionChannels << channels; - m_removeAction->setData( QVariant::fromValue( actionChannels ) ); - - actions << m_removeAction; - - actionChannels.clear(); - if( m_updateAction =3D=3D 0 ) - { - m_updateAction =3D new QAction( - KIcon( "view-refresh-amarok" ), - i18n( "&Update Channel" ), - this - ); - m_updateAction->setProperty( "popupdropper_svg_id", "update" ); - connect( m_updateAction, SIGNAL(triggered()), SLOT(slotUpdateChann= els()) ); - } - else - { - actionChannels =3D m_updateAction->data().value(); - } - - actionChannels << channels; - m_updateAction->setData( QVariant::fromValue( actionChannels ) ); - - actions << m_updateAction; - - return actions; -} - void SqlPodcastProvider::deleteDownloadedEpisodes( Podcasts::SqlPodcastEpisodeL= ist &episodes ) { diff --git a/src/core-impl/podcasts/sql/SqlPodcastProvider.h b/src/core-imp= l/podcasts/sql/SqlPodcastProvider.h index 6d341ca..2e590b4 100644 --- a/src/core-impl/podcasts/sql/SqlPodcastProvider.h +++ b/src/core-impl/podcasts/sql/SqlPodcastProvider.h @@ -59,9 +59,9 @@ class AMAROK_EXPORT SqlPodcastProvider : public Podcasts:= :PodcastProvider virtual Playlists::PlaylistList playlists(); = //PlaylistProvider methods - virtual QList providerActions(); - virtual QList playlistActions( Playlists::PlaylistPtr p= laylist ); - virtual QList trackActions( Playlists::PlaylistPtr play= list, int trackIndex ); + virtual QActionList providerActions(); + virtual QActionList playlistActions( const Playlists::PlaylistList= &playlists ); + virtual QActionList trackActions( const QMultiHash &playlistTracks ); = //PodcastProvider methods virtual Podcasts::PodcastEpisodePtr episodeForGuid( const QString = &guid ); @@ -73,9 +73,6 @@ class AMAROK_EXPORT SqlPodcastProvider : public Podcasts:= :PodcastProvider = virtual Podcasts::PodcastChannelList channels(); = - virtual QList episodeActions( Podcasts::PodcastEpisodeL= ist ); - virtual QList channelActions( Podcasts::PodcastChannelL= ist ); - virtual void completePodcastDownloads(); = //SqlPodcastProvider specific methods diff --git a/src/core/playlists/Playlist.cpp b/src/core/playlists/Playlist.= cpp index 2677c78..401d728 100644 --- a/src/core/playlists/Playlist.cpp +++ b/src/core/playlists/Playlist.cpp @@ -96,23 +96,6 @@ Playlist::syncTrackStatus( int, Meta::TrackPtr ) { } = -QActionList -Playlist::actions() -{ - if( provider() ) - return provider()->playlistActions( PlaylistPtr( this ) ); - - return QActionList(); -} - -QActionList -Playlist::trackActions( int trackIndex ) -{ - if( provider() ) - return provider()->trackActions( PlaylistPtr( this ), trackIndex ); - return QActionList(); -} - QStringList Playlist::groups() { diff --git a/src/core/playlists/Playlist.h b/src/core/playlists/Playlist.h index 9fa854f..8543b9c 100644 --- a/src/core/playlists/Playlist.h +++ b/src/core/playlists/Playlist.h @@ -206,18 +206,6 @@ namespace Playlists virtual void syncTrackStatus( int position, Meta::TrackPtr oth= erTrack ); = /** - * Return user-actionable actions for this playlist. Default i= mplementation - * just returns provider actions for this playlist. - */ - virtual QActionList actions(); - - /** - * Return actions for track at position @trackIndex for this p= laylist. Default - * implementation returns provider()'s trackActions(). - */ - virtual QActionList trackActions( int trackIndex ); - - /** * A list of groups or labels this playlist belongs to. * * Can be used for grouping in folders (use ex. '/' as separat= or) or for diff --git a/src/core/playlists/PlaylistProvider.cpp b/src/core/playlists/P= laylistProvider.cpp index 545a11e..b42061f 100644 --- a/src/core/playlists/PlaylistProvider.cpp +++ b/src/core/playlists/PlaylistProvider.cpp @@ -19,16 +19,55 @@ = using namespace Playlists; = +PlaylistProvider::PlaylistProvider( QObject *parent) + : QObject( parent ) +{ +} + +QActionList +PlaylistProvider::providerActions() +{ + return QActionList(); +} + +QActionList +PlaylistProvider::playlistActions( const PlaylistList & ) +{ + return QActionList(); + +} + +QActionList +PlaylistProvider::trackActions( const QMultiHash & ) +{ + return QActionList(); +} + +bool +PlaylistProvider::isWritable() +{ + return false; +} + PlaylistPtr -PlaylistProvider::addPlaylist( Playlists::PlaylistPtr playlist ) +PlaylistProvider::addPlaylist( Playlists::PlaylistPtr ) { - Q_UNUSED( playlist ); return PlaylistPtr(); } = +void +PlaylistProvider::renamePlaylist( PlaylistPtr, const QString & ) +{ +} + +bool +PlaylistProvider::deletePlaylists( const PlaylistList & ) +{ + return false; +} + Meta::TrackPtr -PlaylistProvider::addTrack( Meta::TrackPtr track ) +PlaylistProvider::addTrack( Meta::TrackPtr ) { - Q_UNUSED( track ); return Meta::TrackPtr(); } diff --git a/src/core/playlists/PlaylistProvider.h b/src/core/playlists/Pla= ylistProvider.h index 4752225..95cafb3 100644 --- a/src/core/playlists/PlaylistProvider.h +++ b/src/core/playlists/PlaylistProvider.h @@ -20,9 +20,6 @@ #include "core/amarokcore_export.h" #include "core/playlists/Playlist.h" = -#include - -class QAction; class KIcon; = namespace Playlists { @@ -32,14 +29,16 @@ class AMAROK_CORE_EXPORT PlaylistProvider : public QObj= ect Q_OBJECT = public: - PlaylistProvider( QObject *parent =3D 0 ) : QObject( parent ) {} - virtual ~PlaylistProvider() {} + explicit PlaylistProvider( QObject *parent =3D 0 ); = /** - * @returns A translated string to identify this Provider. - */ + * A translated string to identify this Provider. + */ virtual QString prettyName() const =3D 0; = + /** + * A nice icon for this playlist provider. + */ virtual KIcon icon() const =3D 0; = /** @@ -48,24 +47,56 @@ class AMAROK_CORE_EXPORT PlaylistProvider : public QObj= ect */ virtual int category() const =3D 0; = - /** @returns the number of playlists this provider has or a negati= ve value if it + /** + * @returns the number of playlists this provider has or a negativ= e value if it * can not determine that before loading them all. + * + * Default implementation returns -1. */ virtual int playlistCount() const { return -1; } - virtual Playlists::PlaylistList playlists() =3D 0; = - virtual QActionList providerActions() { return QList();= } - virtual QActionList playlistActions( Playlists::PlaylistPtr playli= st ) =3D 0; - virtual QActionList trackActions( Playlists::PlaylistPtr playlist,= int trackIndex ) =3D 0; + /** + * Return a list of plyalists of this provider. If playlistCount()= is negative, + * this list may be incomplete. + */ + virtual PlaylistList playlists() =3D 0; = - /** Copy a playlist to the provider. - */ - virtual Playlists::PlaylistPtr addPlaylist( Playlists::PlaylistPtr= playlist ); + virtual QActionList providerActions(); + virtual QActionList playlistActions( const PlaylistList &playlists= ); + virtual QActionList trackActions( const QMultiHash &playlistTracks ); = - /** Copy a track directly to a playlist provider without being in = a playlist. - * It's up to the implementation to decide what to do but could f= or instance allow the - * creation of a new playlist from scratch. - */ + /** + * Return true if this providers supports modification made by the= user. + * + * I.e. whether addPlaylist(), renamePlaylist(), deletePlaylists()= make sense + * to be triggered by user action. + * + * Default implementation returns false. + */ + virtual bool isWritable(); + + /** + * Copy a playlist to the provider. + */ + virtual PlaylistPtr addPlaylist( PlaylistPtr playlist ); + + /** + * Rename a playlist of this provider. + */ + virtual void renamePlaylist( PlaylistPtr playlist, const QString &= newName ); + + /** + * Deletes a list of playlists. Returns true of successful, false = otherwise. + * + * Default implementation does nothing and returns false. + */ + virtual bool deletePlaylists( const PlaylistList &playlistlist ); + + /** + * Copy a track directly to a playlist provider without being in a= playlist. + * It's up to the implementation to decide what to do but could fo= r instance allow the + * creation of a new playlist from scratch. + */ virtual Meta::TrackPtr addTrack( Meta::TrackPtr track ); = signals: diff --git a/src/core/podcasts/PodcastProvider.h b/src/core/podcasts/Podcas= tProvider.h index 257aaee..f126ebe 100644 --- a/src/core/podcasts/PodcastProvider.h +++ b/src/core/podcasts/PodcastProvider.h @@ -21,12 +21,6 @@ #include "core/playlists/PlaylistProvider.h" #include "core/podcasts/PodcastMeta.h" = -#include -#include - -class KUrl; -class QAction; - namespace Podcasts { = /** @@ -38,8 +32,6 @@ class AMAROK_CORE_EXPORT PodcastProvider : public Collect= ions::TrackProvider, pu static bool couldBeFeed( const QString &urlString ); static KUrl toFeedUrl( const QString &urlString ); = - virtual ~PodcastProvider() {} - virtual bool possiblyContainsTrack( const KUrl &url ) const =3D 0; virtual Meta::TrackPtr trackForUrl( const KUrl &url ) =3D 0; = @@ -59,29 +51,12 @@ class AMAROK_CORE_EXPORT PodcastProvider : public Colle= ctions::TrackProvider, pu = virtual Podcasts::PodcastChannelList channels() =3D 0; = - virtual QList episodeActions( Podcasts::PodcastEpisodeL= ist ) - { return QList(); } - virtual QList channelActions( Podcasts::PodcastChannelL= ist ) - { return QList(); } - //TODO: need to move this to SqlPodcastProvider since it's provide= r specific. //perhaps use a more general transferprogress for playlists virtual void completePodcastDownloads() =3D 0; = // PlaylistProvider methods - virtual QString prettyName() const =3D 0; - virtual KIcon icon() const =3D 0; - - virtual int category() const { return (int)Playlists::PodcastChann= elPlaylist; } - - virtual Playlists::PlaylistList playlists() =3D 0; - - virtual QList providerActions() { return QList(); } - virtual QList playlistActions( Playlists::PlaylistPtr p= laylist ) - { Q_UNUSED( playlist ) return QList(); } - virtual QList trackActions( Playlists::PlaylistPtr play= list, - int trackIndex ) - { Q_UNUSED( playlist) Q_UNUSED( trackIndex ) return QList<= QAction *>(); } + virtual int category() const { return Playlists::PodcastChannelPla= ylist; } = /** convenience function that downcast the argument to PodcastChan= nel and calls addChannel() */ diff --git a/src/playlistmanager/PlaylistManager.cpp b/src/playlistmanager/= PlaylistManager.cpp index 281215e..860a8da 100644 --- a/src/playlistmanager/PlaylistManager.cpp +++ b/src/playlistmanager/PlaylistManager.cpp @@ -359,7 +359,7 @@ PlaylistManager::rename( PlaylistPtr playlist, const QS= tring &newName ) if( !provider || !provider->isWritable() ) return false; = - provider->rename( playlist, newName ); + provider->renamePlaylist( playlist, newName ); return true; } = diff --git a/src/playlistmanager/SyncedPlaylist.cpp b/src/playlistmanager/S= yncedPlaylist.cpp index ae6f9ab..985f087 100644 --- a/src/playlistmanager/SyncedPlaylist.cpp +++ b/src/playlistmanager/SyncedPlaylist.cpp @@ -104,24 +104,6 @@ SyncedPlaylist::removeTrack( int position ) m_playlists.first()->removeTrack( position ); } = -QActionList -SyncedPlaylist::actions() -{ - QActionList actions; - //only add actions from the master playlist - actions << playlists().first()->actions(); - return actions; -} - -QActionList -SyncedPlaylist::trackActions( int trackIndex ) -{ - QActionList actions; - //only add actions from the master playlist - actions << playlists().first()->trackActions( trackIndex ); - return actions; -} - void SyncedPlaylist::metadataChanged( Playlists::PlaylistPtr playlist ) { diff --git a/src/playlistmanager/SyncedPlaylist.h b/src/playlistmanager/Syn= cedPlaylist.h index 214bb5c..fd2f966 100644 --- a/src/playlistmanager/SyncedPlaylist.h +++ b/src/playlistmanager/SyncedPlaylist.h @@ -43,9 +43,6 @@ class SyncedPlaylist : public Playlists::Playlist, public= Playlists::PlaylistObs virtual void addTrack( Meta::TrackPtr track, int position =3D -1 ); virtual void removeTrack( int position ); = - virtual QActionList actions(); - virtual QActionList trackActions( int trackIndex ); - //PlaylistObserver methods virtual void metadataChanged( Playlists::PlaylistPtr playlist ); virtual void tracksLoaded( Playlists::PlaylistPtr); diff --git a/src/playlistmanager/file/PlaylistFileProvider.cpp b/src/playli= stmanager/file/PlaylistFileProvider.cpp index 5126c62..cd35efa 100644 --- a/src/playlistmanager/file/PlaylistFileProvider.cpp +++ b/src/playlistmanager/file/PlaylistFileProvider.cpp @@ -97,6 +97,11 @@ PlaylistFileProvider::prettyName() const return i18n( "Playlist Files on Disk" ); } = +KIcon PlaylistFileProvider::icon() const +{ + return KIcon( "folder-documents" ); +} + int PlaylistFileProvider::playlistCount() const { @@ -229,14 +234,14 @@ PlaylistFileProvider::import( const KUrl &path ) } = void -PlaylistFileProvider::rename( Playlists::PlaylistPtr playlist, const QStri= ng &newName ) +PlaylistFileProvider::renamePlaylist( Playlists::PlaylistPtr playlist, con= st QString &newName ) { DEBUG_BLOCK playlist->setName( newName ); } = bool -PlaylistFileProvider::deletePlaylists( Playlists::PlaylistList playlists ) +PlaylistFileProvider::deletePlaylists( const Playlists::PlaylistList &play= lists ) { Playlists::PlaylistFileList playlistFiles; foreach( Playlists::PlaylistPtr playlist, playlists ) diff --git a/src/playlistmanager/file/PlaylistFileProvider.h b/src/playlist= manager/file/PlaylistFileProvider.h index b15437a..58b2f41 100644 --- a/src/playlistmanager/file/PlaylistFileProvider.h +++ b/src/playlistmanager/file/PlaylistFileProvider.h @@ -22,12 +22,6 @@ #include "core-impl/playlists/providers/user/UserPlaylistProvider.h" #include "core-impl/playlists/types/file/PlaylistFileSupport.h" = -#include - -class KConfigGroup; -class KUrl; - -class QAction; class QTimer; = namespace Playlists { @@ -43,7 +37,7 @@ class PlaylistFileProvider : public Playlists::UserPlayli= stProvider virtual ~PlaylistFileProvider(); = virtual QString prettyName() const; - virtual KIcon icon() const { return KIcon( "folder-documents" ); } + virtual KIcon icon() const; = virtual int category() const { return Playlists::UserPlaylist; } = @@ -62,8 +56,8 @@ class PlaylistFileProvider : public Playlists::UserPlayli= stProvider virtual bool import( const KUrl &path ); = virtual bool isWritable() { return true; } - virtual void rename( Playlists::PlaylistPtr playlist, const QStrin= g &newName ); - virtual bool deletePlaylists( Playlists::PlaylistList playlistList= ); + virtual void renamePlaylist( Playlists::PlaylistPtr playlist, cons= t QString &newName ); + virtual bool deletePlaylists( const Playlists::PlaylistList &playl= ists ); = /* PlaylistFileProvider methods */ /** Schedules a PlaylistFile to be saved on the next iteration of = the mainloop. diff --git a/src/playlistmanager/sql/SqlUserPlaylistProvider.cpp b/src/play= listmanager/sql/SqlUserPlaylistProvider.cpp index 0ffb3d6..d9209d2 100644 --- a/src/playlistmanager/sql/SqlUserPlaylistProvider.cpp +++ b/src/playlistmanager/sql/SqlUserPlaylistProvider.cpp @@ -76,43 +76,11 @@ SqlUserPlaylistProvider::playlists() } = void -SqlUserPlaylistProvider::rename( Playlists::PlaylistPtr playlist, const QS= tring &newName ) +SqlUserPlaylistProvider::renamePlaylist( Playlists::PlaylistPtr playlist, = const QString &newName ) { playlist->setName( newName.trimmed() ); } = -void -SqlUserPlaylistProvider::slotDelete() -{ - QAction *action =3D qobject_cast( QObject::sender() ); - if( action =3D=3D 0 ) - return; - - Playlists::PlaylistList playlists =3D action->data().value(); - if( playlists.count() =3D=3D 0 ) - return; - - if( !m_debug ) - { - KDialog dialog; - dialog.setCaption( i18n( "Confirm Delete" ) ); - dialog.setButtons( KDialog::Ok | KDialog::Cancel ); - QLabel label( i18np( "Are you sure you want to delete this playlis= t?", - "Are you sure you want to delete these %1 pla= ylists?", - playlists.count() ) - , &dialog - ); - //TODO:include a text area with all the names of the playlists - dialog.setButtonText( KDialog::Ok, i18nc( "%1 is playlist provider= pretty name", - "Yes, delete from %1.", = prettyName() ) ); - dialog.setMainWidget( &label ); - if( dialog.exec() !=3D QDialog::Accepted ) - return; - } - - deletePlaylists( playlists ); -} - bool SqlUserPlaylistProvider::isWritable() { @@ -120,7 +88,7 @@ SqlUserPlaylistProvider::isWritable() } = bool -SqlUserPlaylistProvider::deletePlaylists( Playlists::PlaylistList playlist= List ) +SqlUserPlaylistProvider::deletePlaylists( const Playlists::PlaylistList &p= laylistList ) { Playlists::SqlPlaylistList sqlPlaylists; foreach( Playlists::PlaylistPtr playlist, playlistList ) diff --git a/src/playlistmanager/sql/SqlUserPlaylistProvider.h b/src/playli= stmanager/sql/SqlUserPlaylistProvider.h index 2ff9fda..273a050 100644 --- a/src/playlistmanager/sql/SqlUserPlaylistProvider.h +++ b/src/playlistmanager/sql/SqlUserPlaylistProvider.h @@ -53,19 +53,13 @@ class AMAROK_EXPORT SqlUserPlaylistProvider : public Us= erPlaylistProvider = /* UserPlaylistProvider functions */ virtual bool isWritable(); - virtual bool deletePlaylists( Playlists::PlaylistList playlistlist= ); - virtual void rename( Playlists::PlaylistPtr playlist, const QStrin= g &newName ); + virtual bool deletePlaylists( const Playlists::PlaylistList &playl= istlist ); + virtual void renamePlaylist( Playlists::PlaylistPtr playlist, cons= t QString &newName ); = Playlists::SqlPlaylistGroupPtr group( const QString &name ); = static Playlists::SqlPlaylistList toSqlPlaylists( Playlists::Playl= istList playlists ); = - private slots: - /** - * Overridden only bacause of the m_debug flag - */ - void slotDelete(); - private: void reloadFromDb(); Playlists::SqlPlaylistGroupPtr m_root; diff --git a/src/services/gpodder/GpodderProvider.cpp b/src/services/gpodde= r/GpodderProvider.cpp index 28102a5..d7ae60f 100644 --- a/src/services/gpodder/GpodderProvider.cpp +++ b/src/services/gpodder/GpodderProvider.cpp @@ -343,51 +343,39 @@ GpodderProvider::removeChannel( const QUrl &url ) } } = -QList +QActionList GpodderProvider::channelActions( PodcastChannelList channels ) { - DEBUG_BLOCK - - QList actions; + QActionList actions; + if( channels.isEmpty() ) + return actions; = if( m_removeAction =3D=3D 0 ) { - m_removeAction =3D new QAction( - KIcon( "edit-delete" ), - i18n( "&Delete Channel and Episodes" ), - this - ); - + m_removeAction =3D new QAction( KIcon( "edit-delete" ), + i18n( "&Delete Channel and Episodes" ), this ); m_removeAction->setProperty( "popupdropper_svg_id", "delete" ); - connect( m_removeAction, = - SIGNAL(triggered()), - SLOT(slotRemoveChannels()) ); + connect( m_removeAction, SIGNAL(triggered()), SLOT(slotRemoveChan= nels()) ); } - //Set the episode list as data that we'll retrieve in the slot - PodcastChannelList actionList =3D - m_removeAction->data().value(); - - actionList << channels; - m_removeAction->setData( QVariant::fromValue( actionList ) ); - + m_removeAction->setData( QVariant::fromValue( channels ) ); actions << m_removeAction; = return actions; } = -QList -GpodderProvider::playlistActions( Playlists::PlaylistPtr playlist ) +QActionList +GpodderProvider::playlistActions( const Playlists::PlaylistList &playlists= ) { - DEBUG_BLOCK - PodcastChannelList channels; - PodcastChannelPtr channel =3D PodcastChannelPtr::dynamicCast( playlist= ); - - if( channel.isNull() ) - return QList(); + foreach( const Playlists::PlaylistPtr &playlist, playlists ) + { + PodcastChannelPtr channel =3D PodcastChannelPtr::dynamicCast( play= list ); + if( channel ) + channels << channel; + } = - return channelActions( channels << channel ); + return channelActions( channels ); } = void diff --git a/src/services/gpodder/GpodderProvider.h b/src/services/gpodder/= GpodderProvider.h index a259d5d..f5dc268 100644 --- a/src/services/gpodder/GpodderProvider.h +++ b/src/services/gpodder/GpodderProvider.h @@ -77,8 +77,7 @@ public: /** Copy a playlist to the provider. */ virtual Playlists::PlaylistPtr addPlaylist( Playlists::PlaylistPtr pla= ylist ); - QList channelActions( PodcastChannelList episodes ); - QList playlistActions( Playlists::PlaylistPtr playlist ); + virtual QActionList playlistActions( const Playlists::PlaylistList &pl= aylists ); = private slots: void requestDeviceUpdates(); @@ -120,6 +119,8 @@ private slots: void slotEpisodeMarkedAsNew( Podcasts::PodcastEpisodePtr episode ); = private: + QActionList channelActions( PodcastChannelList episodes ); + ApiRequest *m_apiRequest; const QString m_username; const QString m_deviceName; diff --git a/tests/playlistmanager/file/TestPlaylistFileProvider.cpp b/test= s/playlistmanager/file/TestPlaylistFileProvider.cpp index 2d97ee2..d8723a9 100644 --- a/tests/playlistmanager/file/TestPlaylistFileProvider.cpp +++ b/tests/playlistmanager/file/TestPlaylistFileProvider.cpp @@ -125,7 +125,7 @@ void TestPlaylistFileProvider::testRename() tempList =3D m_testPlaylistFileProvider->playlists(); QCOMPARE( tempList.size(), 1 ); = - m_testPlaylistFileProvider->rename( tempList.at( 0 ), "New Test Name" = ); + m_testPlaylistFileProvider->renamePlaylist( tempList.at( 0 ), "New Tes= t Name" ); tempList =3D m_testPlaylistFileProvider->playlists(); QCOMPARE( tempList.at( 0 )->name(), QString( "New Test Name.m3u" ) ); = diff --git a/tests/playlistmanager/sql/TestSqlUserPlaylistProvider.cpp b/te= sts/playlistmanager/sql/TestSqlUserPlaylistProvider.cpp index b1a1f9d..83ea2bc 100644 --- a/tests/playlistmanager/sql/TestSqlUserPlaylistProvider.cpp +++ b/tests/playlistmanager/sql/TestSqlUserPlaylistProvider.cpp @@ -79,7 +79,7 @@ void TestSqlUserPlaylistProvider::testSave() void TestSqlUserPlaylistProvider::testRename() { Playlists::PlaylistList tempList =3D m_testSqlUserPlaylistProvider->pl= aylists(); - m_testSqlUserPlaylistProvider->rename( tempList.at( 0 ), "New Test Nam= e" ); + m_testSqlUserPlaylistProvider->renamePlaylist( tempList.at( 0 ), "New = Test Name" ); QCOMPARE( tempList.at( 0 )->name(), QString( "New Test Name" ) ); } =20