mirror of
https://github.com/tomahawk-player/tomahawk.git
synced 2025-08-07 14:46:33 +02:00
Add spotify context menu action for local playlists
This also adds a generic way for runtime components to register actions to be shown for a certain category of items (e.g. playlists, tracks, artists, etc). Initiating a sync from Tomahawk is still a TODO
This commit is contained in:
@@ -19,13 +19,17 @@
|
|||||||
|
|
||||||
#include "SpotifyAccount.h"
|
#include "SpotifyAccount.h"
|
||||||
#include "playlist.h"
|
#include "playlist.h"
|
||||||
#include "utils/tomahawkutils.h"
|
|
||||||
#include "playlist/PlaylistUpdaterInterface.h"
|
#include "playlist/PlaylistUpdaterInterface.h"
|
||||||
#include "sourcelist.h"
|
#include "sourcelist.h"
|
||||||
#include "SpotifyAccountConfig.h"
|
#include "SpotifyAccountConfig.h"
|
||||||
#include "SpotifyPlaylistUpdater.h"
|
#include "SpotifyPlaylistUpdater.h"
|
||||||
#include "resolvers/scriptresolver.h"
|
#include "resolvers/scriptresolver.h"
|
||||||
|
#include "utils/tomahawkutils.h"
|
||||||
|
#include "actioncollection.h"
|
||||||
|
|
||||||
|
|
||||||
#include <QPixmap>
|
#include <QPixmap>
|
||||||
|
#include <QAction>
|
||||||
|
|
||||||
using namespace Tomahawk;
|
using namespace Tomahawk;
|
||||||
using namespace Accounts;
|
using namespace Accounts;
|
||||||
@@ -95,6 +99,83 @@ SpotifyAccount::init()
|
|||||||
msg[ "_msgtype" ] = "getCredentials";
|
msg[ "_msgtype" ] = "getCredentials";
|
||||||
m_spotifyResolver.data()->sendMessage( msg );
|
m_spotifyResolver.data()->sendMessage( msg );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QAction* action = new QAction( 0 );
|
||||||
|
action->setIcon( QIcon( RESPATH "images/spotify-logo.png" ) );
|
||||||
|
connect( action, SIGNAL( triggered( bool ) ), this, SLOT( syncActionTriggered( bool ) ) );
|
||||||
|
ActionCollection::instance()->addAction( ActionCollection::LocalPlaylists, action, this );
|
||||||
|
m_customActions.append( action );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
SpotifyAccount::aboutToShow( QAction* action, const playlist_ptr& playlist )
|
||||||
|
{
|
||||||
|
if ( !m_customActions.contains( action ) )
|
||||||
|
return;
|
||||||
|
|
||||||
|
// If it's not being synced, allow the option to sync
|
||||||
|
SpotifyPlaylistUpdater* updater = qobject_cast< SpotifyPlaylistUpdater* >( playlist->updater() );
|
||||||
|
if ( !updater || !updater->sync() )
|
||||||
|
{
|
||||||
|
action->setText( tr( "Sync with Spotify" ) );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
action->setText( tr( "Stop syncing with Spotify" ) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
SpotifyAccount::syncActionTriggered( bool checked )
|
||||||
|
{
|
||||||
|
Q_UNUSED( checked );
|
||||||
|
QAction* action = qobject_cast< QAction* >( sender() );
|
||||||
|
|
||||||
|
if ( !action || !m_customActions.contains( action ) )
|
||||||
|
return;
|
||||||
|
|
||||||
|
const playlist_ptr playlist = action->property( "payload" ).value< playlist_ptr >();
|
||||||
|
if ( playlist.isNull() )
|
||||||
|
{
|
||||||
|
qWarning() << "Got context menu spotify sync action triggered, but invalid playlist payload!";
|
||||||
|
Q_ASSERT( false );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
SpotifyPlaylistUpdater* updater = qobject_cast< SpotifyPlaylistUpdater* >( playlist->updater() );
|
||||||
|
|
||||||
|
if ( !updater )
|
||||||
|
{
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
SpotifyPlaylistInfo* info = 0;
|
||||||
|
foreach ( SpotifyPlaylistInfo* ifo, m_allSpotifyPlaylists )
|
||||||
|
{
|
||||||
|
if ( ifo->plid == updater->spotifyId() )
|
||||||
|
{
|
||||||
|
info = ifo;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( !updater->sync() )
|
||||||
|
{
|
||||||
|
info->sync = true;
|
||||||
|
if ( m_configWidget.data() )
|
||||||
|
m_configWidget.data()->setPlaylists( m_allSpotifyPlaylists );
|
||||||
|
|
||||||
|
startPlaylistSync( info );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
|
||||||
|
stopPlaylistSync( info, true );
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -303,12 +384,7 @@ SpotifyAccount::saveConfig()
|
|||||||
if ( pl->sync )
|
if ( pl->sync )
|
||||||
{
|
{
|
||||||
// Fetch full playlist contents, then begin the sync
|
// Fetch full playlist contents, then begin the sync
|
||||||
QVariantMap msg;
|
startPlaylistSync( pl );
|
||||||
msg[ "_msgtype" ] = "getPlaylist";
|
|
||||||
msg[ "playlistid" ] = pl->plid;
|
|
||||||
msg[ "sync" ] = pl->sync;
|
|
||||||
|
|
||||||
sendMessage( msg, this, "startPlaylistSyncWithPlaylist" );
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
stopPlaylistSync( pl );
|
stopPlaylistSync( pl );
|
||||||
@@ -318,6 +394,18 @@ SpotifyAccount::saveConfig()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
SpotifyAccount::startPlaylistSync( SpotifyPlaylistInfo* playlist )
|
||||||
|
{
|
||||||
|
QVariantMap msg;
|
||||||
|
msg[ "_msgtype" ] = "getPlaylist";
|
||||||
|
msg[ "playlistid" ] = playlist->plid;
|
||||||
|
msg[ "sync" ] = playlist->sync;
|
||||||
|
|
||||||
|
sendMessage( msg, this, "startPlaylistSyncWithPlaylist" );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
SpotifyAccount::startPlaylistSyncWithPlaylist( const QString& msgType, const QVariantMap& msg )
|
SpotifyAccount::startPlaylistSyncWithPlaylist( const QString& msgType, const QVariantMap& msg )
|
||||||
{
|
{
|
||||||
@@ -409,7 +497,7 @@ SpotifyAccount::deleteOnUnsync() const
|
|||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
SpotifyAccount::stopPlaylistSync( SpotifyPlaylistInfo* playlist )
|
SpotifyAccount::stopPlaylistSync( SpotifyPlaylistInfo* playlist, bool forceDontDelete )
|
||||||
{
|
{
|
||||||
QVariantMap msg;
|
QVariantMap msg;
|
||||||
msg[ "_msgtype" ] = "removeFromSyncList";
|
msg[ "_msgtype" ] = "removeFromSyncList";
|
||||||
@@ -422,7 +510,7 @@ SpotifyAccount::stopPlaylistSync( SpotifyPlaylistInfo* playlist )
|
|||||||
SpotifyPlaylistUpdater* updater = m_updaters[ playlist->plid ];
|
SpotifyPlaylistUpdater* updater = m_updaters[ playlist->plid ];
|
||||||
updater->setSync( false );
|
updater->setSync( false );
|
||||||
|
|
||||||
if ( deleteOnUnsync() )
|
if ( deleteOnUnsync() && !forceDontDelete )
|
||||||
{
|
{
|
||||||
playlist_ptr tomahawkPl = updater->playlist();
|
playlist_ptr tomahawkPl = updater->playlist();
|
||||||
|
|
||||||
|
@@ -20,11 +20,13 @@
|
|||||||
#ifndef SpotifyAccount_H
|
#ifndef SpotifyAccount_H
|
||||||
#define SpotifyAccount_H
|
#define SpotifyAccount_H
|
||||||
|
|
||||||
|
#include "accounts/ResolverAccount.h"
|
||||||
|
#include "sourcelist.h"
|
||||||
#include "playlist.h"
|
#include "playlist.h"
|
||||||
#include "utils/tomahawkutils.h"
|
#include "utils/tomahawkutils.h"
|
||||||
#include "sourcelist.h"
|
#include "utils/SmartPointerList.h"
|
||||||
#include "accounts/ResolverAccount.h"
|
|
||||||
|
|
||||||
|
class QAction;
|
||||||
class SpotifyPlaylistUpdater;
|
class SpotifyPlaylistUpdater;
|
||||||
class QTimer;
|
class QTimer;
|
||||||
|
|
||||||
@@ -91,6 +93,11 @@ public:
|
|||||||
void unregisterUpdater( const QString& plid );
|
void unregisterUpdater( const QString& plid );
|
||||||
|
|
||||||
bool deleteOnUnsync() const;
|
bool deleteOnUnsync() const;
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void aboutToShow( QAction* action, const Tomahawk::playlist_ptr& playlist );
|
||||||
|
void syncActionTriggered( bool );
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void resolverMessage( const QString& msgType, const QVariantMap& msg );
|
void resolverMessage( const QString& msgType, const QVariantMap& msg );
|
||||||
|
|
||||||
@@ -102,7 +109,8 @@ private:
|
|||||||
void init();
|
void init();
|
||||||
void loadPlaylists();
|
void loadPlaylists();
|
||||||
|
|
||||||
void stopPlaylistSync( SpotifyPlaylistInfo* playlist );
|
void startPlaylistSync( SpotifyPlaylistInfo* playlist );
|
||||||
|
void stopPlaylistSync( SpotifyPlaylistInfo* playlist, bool forceDontDelete = false );
|
||||||
void fetchFullPlaylist( SpotifyPlaylistInfo* playlist );
|
void fetchFullPlaylist( SpotifyPlaylistInfo* playlist );
|
||||||
|
|
||||||
void setSyncForPlaylist( const QString& spotifyPlaylistId, bool sync );
|
void setSyncForPlaylist( const QString& spotifyPlaylistId, bool sync );
|
||||||
@@ -116,6 +124,7 @@ private:
|
|||||||
QList< SpotifyPlaylistInfo* > m_allSpotifyPlaylists;
|
QList< SpotifyPlaylistInfo* > m_allSpotifyPlaylists;
|
||||||
QHash< QString, SpotifyPlaylistUpdater* > m_updaters;
|
QHash< QString, SpotifyPlaylistUpdater* > m_updaters;
|
||||||
|
|
||||||
|
SmartPointerList< QAction > m_customActions;
|
||||||
friend class ::SpotifyPlaylistUpdater;
|
friend class ::SpotifyPlaylistUpdater;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -51,6 +51,8 @@ public:
|
|||||||
bool sync() const;
|
bool sync() const;
|
||||||
void setSync( bool sync );
|
void setSync( bool sync );
|
||||||
|
|
||||||
|
QString spotifyId() const { return m_spotifyId; }
|
||||||
|
|
||||||
/// Spotify callbacks when we are directly instructed from the resolver
|
/// Spotify callbacks when we are directly instructed from the resolver
|
||||||
void spotifyTracksAdded( const QVariantList& tracks, const QString& startPosId, const QString& newRev, const QString& oldRev );
|
void spotifyTracksAdded( const QVariantList& tracks, const QString& startPosId, const QString& newRev, const QString& oldRev );
|
||||||
void spotifyTracksRemoved( const QVariantList& tracks, const QString& newRev, const QString& oldRev );
|
void spotifyTracksRemoved( const QVariantList& tracks, const QString& newRev, const QString& oldRev );
|
||||||
|
@@ -121,6 +121,7 @@ set( libGuiSources
|
|||||||
utils/tomahawkutilsgui.cpp
|
utils/tomahawkutilsgui.cpp
|
||||||
utils/closure.cpp
|
utils/closure.cpp
|
||||||
utils/PixmapDelegateFader.cpp
|
utils/PixmapDelegateFader.cpp
|
||||||
|
utils/SmartPointerList.h
|
||||||
|
|
||||||
widgets/animatedcounterlabel.cpp
|
widgets/animatedcounterlabel.cpp
|
||||||
widgets/checkdirtree.cpp
|
widgets/checkdirtree.cpp
|
||||||
|
@@ -2,6 +2,7 @@
|
|||||||
*
|
*
|
||||||
* Copyright 2010-2011, Christian Muehlhaeuser <muesli@tomahawk-player.org>
|
* Copyright 2010-2011, Christian Muehlhaeuser <muesli@tomahawk-player.org>
|
||||||
* Copyright 2010-2012, Jeff Mitchell <jeff@tomahawk-player.org>
|
* Copyright 2010-2012, Jeff Mitchell <jeff@tomahawk-player.org>
|
||||||
|
* Copyright 2012, Leo Franchi <lfranchi@kde.org>
|
||||||
*
|
*
|
||||||
* Tomahawk is free software: you can redistribute it and/or modify
|
* Tomahawk is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
@@ -37,6 +38,14 @@ ActionCollection::ActionCollection( QObject *parent )
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ActionCollection::~ActionCollection()
|
||||||
|
{
|
||||||
|
s_instance = 0;
|
||||||
|
foreach( QString key, m_actionCollection.keys() )
|
||||||
|
delete m_actionCollection[ key ];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
ActionCollection::initActions()
|
ActionCollection::initActions()
|
||||||
{
|
{
|
||||||
@@ -76,18 +85,54 @@ ActionCollection::initActions()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
ActionCollection::~ActionCollection()
|
void
|
||||||
|
ActionCollection::addAction( ActionCollection::ActionDestination category, QAction* action, QObject* notify )
|
||||||
{
|
{
|
||||||
s_instance = 0;
|
QList< QAction* > actions = m_categoryActions.value( category );
|
||||||
foreach( QString key, m_actionCollection.keys() )
|
actions.append( action );
|
||||||
delete m_actionCollection[ key ];
|
m_categoryActions[ category ] = actions;
|
||||||
|
|
||||||
|
if ( notify )
|
||||||
|
m_actionNotifiers[ action ] = notify;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
QAction*
|
QAction*
|
||||||
ActionCollection::getAction( const QString& name )
|
ActionCollection::getAction( const QString& name )
|
||||||
{
|
{
|
||||||
return m_actionCollection.contains( name ) ? m_actionCollection[ name ] : 0;
|
return m_actionCollection.value( name, 0 );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
QObject*
|
||||||
|
ActionCollection::actionNotifier( QAction* action )
|
||||||
|
{
|
||||||
|
return m_actionNotifiers.value( action, 0 );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
QList< QAction* >
|
||||||
|
ActionCollection::getAction( ActionCollection::ActionDestination category )
|
||||||
|
{
|
||||||
|
return m_categoryActions.value( category );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
ActionCollection::removeAction( QAction* action )
|
||||||
|
{
|
||||||
|
removeAction( action, LocalPlaylists );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
ActionCollection::removeAction( QAction* action, ActionCollection::ActionDestination category )
|
||||||
|
{
|
||||||
|
QList< QAction* > actions = m_categoryActions.value( category );
|
||||||
|
actions.removeAll( action );
|
||||||
|
m_categoryActions[ category ] = actions;
|
||||||
|
|
||||||
|
m_actionNotifiers.remove( action );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@@ -1,7 +1,8 @@
|
|||||||
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
|
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
|
||||||
*
|
*
|
||||||
* Copyright 2010-2011, Christian Muehlhaeuser <muesli@tomahawk-player.org>
|
* Copyright 2010-2011, Christian Muehlhaeuser <muesli@tomahawk-player.org
|
||||||
* Copyright 2010-2012, Jeff Mitchell <jeff@tomahawk-player.org>
|
* Copyright 2010-2012, Jeff Mitchell <jeff@tomahawk-player.org>>
|
||||||
|
* Copyright 2012, Leo Franchi <lfranchi@kde.org>
|
||||||
*
|
*
|
||||||
* Tomahawk is free software: you can redistribute it and/or modify
|
* Tomahawk is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
@@ -29,6 +30,12 @@ class DLLEXPORT ActionCollection : public QObject
|
|||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
// Categories for custom-registered actions
|
||||||
|
enum ActionDestination {
|
||||||
|
// Tracks, TODO
|
||||||
|
LocalPlaylists = 0
|
||||||
|
};
|
||||||
|
|
||||||
static ActionCollection* instance();
|
static ActionCollection* instance();
|
||||||
|
|
||||||
ActionCollection( QObject *parent);
|
ActionCollection( QObject *parent);
|
||||||
@@ -37,6 +44,34 @@ public:
|
|||||||
void initActions();
|
void initActions();
|
||||||
|
|
||||||
QAction* getAction( const QString& name );
|
QAction* getAction( const QString& name );
|
||||||
|
QList< QAction* > getAction( ActionDestination category );
|
||||||
|
QObject* actionNotifier( QAction* );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add an action for a specific category. The action will show up
|
||||||
|
* where the relevant category is displayed.
|
||||||
|
*
|
||||||
|
* e.g. if you register a Playlist action, it will be shown when
|
||||||
|
* there is a context menu shown for a playlist.
|
||||||
|
*
|
||||||
|
* When the QAction* is shown, it will have a "payload" property that is set
|
||||||
|
* to the <specific type> that is being shown.
|
||||||
|
*
|
||||||
|
* Additionally you can pass a QObject* that will be notified before the given
|
||||||
|
* action is shown. The slot "aboutToShow( QAction*, <specific type> ) will be called,
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* <specific type> corresponds to the category: playlist_ptr for Playlists, etc.
|
||||||
|
*
|
||||||
|
* The Action Collection takes ownership of the action. It's time to let go.
|
||||||
|
*/
|
||||||
|
void addAction( ActionDestination category, QAction* action, QObject* notify = 0 );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove an action from one or all specific categories
|
||||||
|
*/
|
||||||
|
void removeAction( QAction* action );
|
||||||
|
void removeAction( QAction* action, ActionDestination category );
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void togglePrivateListeningMode();
|
void togglePrivateListeningMode();
|
||||||
@@ -48,6 +83,8 @@ private:
|
|||||||
static ActionCollection* s_instance;
|
static ActionCollection* s_instance;
|
||||||
|
|
||||||
QHash< QString, QAction* > m_actionCollection;
|
QHash< QString, QAction* > m_actionCollection;
|
||||||
|
QHash< ActionDestination, QList< QAction* > > m_categoryActions;
|
||||||
|
QHash< QAction*, QObject* > m_actionNotifiers;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@@ -33,9 +33,8 @@
|
|||||||
namespace Tomahawk
|
namespace Tomahawk
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* If a playlist needs updating, implement a updater interface.
|
* PlaylistUpdaters are attached to playlists. They usually manipulate the playlist in some way
|
||||||
*
|
* due to external input (spotify syncing) or timers (xspf updating)
|
||||||
* Default is auto-updating and on a periodic timer.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
class PlaylistUpdaterFactory;
|
class PlaylistUpdaterFactory;
|
||||||
|
204
src/libtomahawk/utils/SmartPointerList.h
Normal file
204
src/libtomahawk/utils/SmartPointerList.h
Normal file
@@ -0,0 +1,204 @@
|
|||||||
|
/****************************************************************************************
|
||||||
|
* Copyright (c) 2009 Mark Kretschmann <kretschmann@kde.org> *
|
||||||
|
* Copyright (c) 2009 Ian Monroe <ian@monroe.nu> *
|
||||||
|
* Copyright (c) 2009 Max Howell <max@last.fm> *
|
||||||
|
* *
|
||||||
|
* This program is free software; you can redistribute it and/or modify it under *
|
||||||
|
* the terms of the GNU General Public License as published by the Free Software *
|
||||||
|
* Foundation; either version 2 of the License, or (at your option) any later *
|
||||||
|
* version. *
|
||||||
|
* *
|
||||||
|
* This program is distributed in the hope that it will be useful, but WITHOUT ANY *
|
||||||
|
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A *
|
||||||
|
* PARTICULAR PURPOSE. See the GNU General Public License for more details. *
|
||||||
|
* *
|
||||||
|
* You should have received a copy of the GNU General Public License along with *
|
||||||
|
* this program. If not, see <http://www.gnu.org/licenses/>. *
|
||||||
|
****************************************************************************************/
|
||||||
|
|
||||||
|
#ifndef SMART_POINTER_LIST_H
|
||||||
|
#define SMART_POINTER_LIST_H
|
||||||
|
|
||||||
|
#include <QList> //baseclass
|
||||||
|
#include <QObject> //baseclass
|
||||||
|
|
||||||
|
#include "dllmacro.h"
|
||||||
|
|
||||||
|
class DLLEXPORT SmartPointerListDaddy : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
QList<QObject*>& m_list;
|
||||||
|
|
||||||
|
public:
|
||||||
|
SmartPointerListDaddy( QList<QObject*>* list ) : m_list( *list )
|
||||||
|
{}
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void onDestroyed()
|
||||||
|
{
|
||||||
|
m_list.removeAll( sender() );
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/** QList has no virtual functions, so we inherit privately and define the
|
||||||
|
* interface exactly to ensure users can't write code that breaks the
|
||||||
|
* class's internal behaviour.
|
||||||
|
*
|
||||||
|
* I deliberately didn't define clear. I worried people would assume it
|
||||||
|
* deleted the pointers. Or assume it didn't. I didn't expose a few other
|
||||||
|
* functions for that reason.
|
||||||
|
*
|
||||||
|
* non-const iterator functions are not exposed as they access the QList
|
||||||
|
* baseclass, and then the Daddy wouldn't be watching newly inserted items.
|
||||||
|
*
|
||||||
|
* --mxcl
|
||||||
|
* Exposed clear. This class doesn't have a QPtrList autodelete functionality
|
||||||
|
* ever, so if people think that, they're really confused! -- Ian Monroe
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
template <class T> class SmartPointerList : private QList<T*>
|
||||||
|
{
|
||||||
|
class SmartPointerListDaddy* m_daddy;
|
||||||
|
|
||||||
|
public:
|
||||||
|
SmartPointerList() : m_daddy( new SmartPointerListDaddy( (QList<QObject*>*)this ) )
|
||||||
|
{}
|
||||||
|
|
||||||
|
~SmartPointerList()
|
||||||
|
{
|
||||||
|
delete m_daddy;
|
||||||
|
}
|
||||||
|
|
||||||
|
SmartPointerList( const SmartPointerList<T>& that )
|
||||||
|
: QList<T*>()
|
||||||
|
, m_daddy( new SmartPointerListDaddy( (QList<QObject*>*)this ) )
|
||||||
|
{
|
||||||
|
QListIterator<T*> i( that );
|
||||||
|
while (i.hasNext())
|
||||||
|
append( i.next() );
|
||||||
|
}
|
||||||
|
|
||||||
|
SmartPointerList& operator=( const SmartPointerList<T>& that )
|
||||||
|
{
|
||||||
|
QListIterator<T*> i( *this);
|
||||||
|
while (i.hasNext())
|
||||||
|
QObject::disconnect( m_daddy, 0, i.next(), 0 );
|
||||||
|
|
||||||
|
QList<T*>::operator=( that );
|
||||||
|
|
||||||
|
if (this != &that) {
|
||||||
|
QListIterator<T*> i( that );
|
||||||
|
while (i.hasNext())
|
||||||
|
m_daddy->connect( i.next(), SIGNAL(destroyed()), SLOT(onDestroyed()) );
|
||||||
|
}
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// keep same function names as Qt
|
||||||
|
void append( T* o )
|
||||||
|
{
|
||||||
|
m_daddy->connect( o, SIGNAL(destroyed()), SLOT(onDestroyed()) );
|
||||||
|
QList<T*>::append( o );
|
||||||
|
}
|
||||||
|
|
||||||
|
void prepend( T* o )
|
||||||
|
{
|
||||||
|
m_daddy->connect( o, SIGNAL(destroyed()), SLOT(onDestroyed()) );
|
||||||
|
QList<T*>::prepend( o );
|
||||||
|
}
|
||||||
|
|
||||||
|
SmartPointerList& operator+=( T* o )
|
||||||
|
{
|
||||||
|
append( o );
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
SmartPointerList& operator<<( T* o )
|
||||||
|
{
|
||||||
|
return operator+=( o );
|
||||||
|
}
|
||||||
|
|
||||||
|
SmartPointerList operator+( const SmartPointerList that )
|
||||||
|
{
|
||||||
|
SmartPointerList<T> copy = *this;
|
||||||
|
QListIterator<T*> i( that );
|
||||||
|
while (i.hasNext())
|
||||||
|
copy.append( i.next() );
|
||||||
|
return copy;
|
||||||
|
}
|
||||||
|
|
||||||
|
SmartPointerList& operator+=( const SmartPointerList that )
|
||||||
|
{
|
||||||
|
QListIterator<T*> i( that );
|
||||||
|
while (i.hasNext())
|
||||||
|
append( i.next() );
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
void push_back( T* o )
|
||||||
|
{
|
||||||
|
append( o );
|
||||||
|
}
|
||||||
|
|
||||||
|
void push_front( T* o )
|
||||||
|
{
|
||||||
|
prepend( o );
|
||||||
|
}
|
||||||
|
|
||||||
|
void replace( int i, T* o )
|
||||||
|
{
|
||||||
|
QList<T*>::replace( i, o );
|
||||||
|
m_daddy->connect( o, SIGNAL(destroyed()), SLOT(onDestroyed()) );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** this is a "safe" class. We always bounds check */
|
||||||
|
inline T* operator[]( int index ) const { return QList<T*>::value( index ); }
|
||||||
|
inline T* at( int index ) const { return QList<T*>::value( index ); }
|
||||||
|
|
||||||
|
// make public safe functions again
|
||||||
|
using QList<T*>::back;
|
||||||
|
using QList<T*>::constBegin;
|
||||||
|
using QList<T*>::constEnd;
|
||||||
|
using typename QList<T*>::const_iterator;
|
||||||
|
using QList<T*>::contains;
|
||||||
|
using QList<T*>::count;
|
||||||
|
using QList<T*>::empty;
|
||||||
|
using QList<T*>::erase;
|
||||||
|
using QList<T*>::first;
|
||||||
|
using QList<T*>::front;
|
||||||
|
using QList<T*>::indexOf;
|
||||||
|
using QList<T*>::insert;
|
||||||
|
using QList<T*>::isEmpty;
|
||||||
|
using QList<T*>::last;
|
||||||
|
using QList<T*>::lastIndexOf;
|
||||||
|
using QList<T*>::mid;
|
||||||
|
using QList<T*>::move;
|
||||||
|
using QList<T*>::pop_back;
|
||||||
|
using QList<T*>::pop_front;
|
||||||
|
using QList<T*>::size;
|
||||||
|
using QList<T*>::swap;
|
||||||
|
using QList<T*>::value;
|
||||||
|
using QList<T*>::operator!=;
|
||||||
|
using QList<T*>::operator==;
|
||||||
|
|
||||||
|
// can't use using directive here since we only want the const versions
|
||||||
|
typename QList<T*>::const_iterator begin() const { return QList<T*>::constBegin(); }
|
||||||
|
typename QList<T*>::const_iterator end() const { return QList<T*>::constEnd(); }
|
||||||
|
|
||||||
|
// it can lead to poor performance situations if we don't disconnect
|
||||||
|
// but I think it's not worth making this class more complicated for such
|
||||||
|
// an edge case
|
||||||
|
using QList<T*>::clear;
|
||||||
|
using QList<T*>::removeAll;
|
||||||
|
using QList<T*>::removeAt;
|
||||||
|
using QList<T*>::removeFirst;
|
||||||
|
using QList<T*>::removeLast;
|
||||||
|
using QList<T*>::removeOne;
|
||||||
|
using QList<T*>::takeAt;
|
||||||
|
using QList<T*>::takeFirst;
|
||||||
|
using QList<T*>::takeLast;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif //HEADER_GUARD
|
||||||
|
|
@@ -143,11 +143,13 @@ SourceTreeView::setupMenus()
|
|||||||
|
|
||||||
bool readonly = true;
|
bool readonly = true;
|
||||||
SourcesModel::RowType type = ( SourcesModel::RowType )model()->data( m_contextMenuIndex, SourcesModel::SourceTreeItemTypeRole ).toInt();
|
SourcesModel::RowType type = ( SourcesModel::RowType )model()->data( m_contextMenuIndex, SourcesModel::SourceTreeItemTypeRole ).toInt();
|
||||||
|
|
||||||
|
const PlaylistItem* item = itemFromIndex< PlaylistItem >( m_contextMenuIndex );
|
||||||
|
const playlist_ptr playlist = item->playlist();
|
||||||
|
|
||||||
if ( type == SourcesModel::StaticPlaylist || type == SourcesModel::AutomaticPlaylist || type == SourcesModel::Station )
|
if ( type == SourcesModel::StaticPlaylist || type == SourcesModel::AutomaticPlaylist || type == SourcesModel::Station )
|
||||||
{
|
{
|
||||||
|
|
||||||
PlaylistItem* item = itemFromIndex< PlaylistItem >( m_contextMenuIndex );
|
|
||||||
playlist_ptr playlist = item->playlist();
|
|
||||||
if ( !playlist.isNull() )
|
if ( !playlist.isNull() )
|
||||||
{
|
{
|
||||||
readonly = !playlist->author()->isLocal();
|
readonly = !playlist->author()->isLocal();
|
||||||
@@ -190,7 +192,7 @@ SourceTreeView::setupMenus()
|
|||||||
|
|
||||||
QString addToText = QString( "Add to my %1" );
|
QString addToText = QString( "Add to my %1" );
|
||||||
if ( type == SourcesModel::StaticPlaylist )
|
if ( type == SourcesModel::StaticPlaylist )
|
||||||
addToText = addToText.arg( "Playlists" );
|
addToText = addToText.arg( "playlists" );
|
||||||
if ( type == SourcesModel::AutomaticPlaylist )
|
if ( type == SourcesModel::AutomaticPlaylist )
|
||||||
addToText = addToText.arg( "Automatic Playlists" );
|
addToText = addToText.arg( "Automatic Playlists" );
|
||||||
else if ( type == SourcesModel::Station )
|
else if ( type == SourcesModel::Station )
|
||||||
@@ -203,6 +205,24 @@ SourceTreeView::setupMenus()
|
|||||||
renamePlaylistAction->setEnabled( !readonly );
|
renamePlaylistAction->setEnabled( !readonly );
|
||||||
addToLocalAction->setEnabled( readonly );
|
addToLocalAction->setEnabled( readonly );
|
||||||
|
|
||||||
|
// Handle any custom actions registered for playlists
|
||||||
|
if ( !ActionCollection::instance()->getAction( ActionCollection::LocalPlaylists ).isEmpty() )
|
||||||
|
{
|
||||||
|
m_playlistMenu.addSeparator();
|
||||||
|
|
||||||
|
foreach ( QAction* action, ActionCollection::instance()->getAction( ActionCollection::LocalPlaylists ) )
|
||||||
|
{
|
||||||
|
if ( QObject* notifier = ActionCollection::instance()->actionNotifier( action ) )
|
||||||
|
{
|
||||||
|
QMetaObject::invokeMethod( notifier, "aboutToShow", Qt::DirectConnection, Q_ARG( QAction*, action ), Q_ARG( Tomahawk::playlist_ptr, playlist ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
action->setProperty( "payload", QVariant::fromValue< playlist_ptr >( playlist ) );
|
||||||
|
m_playlistMenu.addAction( action );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
if ( type == SourcesModel::StaticPlaylist )
|
if ( type == SourcesModel::StaticPlaylist )
|
||||||
copyPlaylistAction->setText( tr( "&Export Playlist" ) );
|
copyPlaylistAction->setText( tr( "&Export Playlist" ) );
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user