From c30f62e8344f2fe126ab35864c50e5cb3a4d9aae Mon Sep 17 00:00:00 2001 From: Leo Franchi Date: Sat, 4 Dec 2010 16:51:33 -0500 Subject: [PATCH] Add dynamic interface, and fix up some more dynamic playlist code --- include/tomahawk/playlist.h | 3 +- src/CMakeLists.txt | 5 ++- src/dynamic/dynamiccontrol.cpp | 0 src/dynamic/dynamiccontrol.h | 71 ++++++++++++++++++++++++++++++++ src/dynamic/dynamicplaylist.cpp | 59 +++++++++++++++++--------- src/dynamic/dynamicplaylist.h | 46 +++++++++++++++++---- src/dynamic/generatorfactory.cpp | 7 ++-- src/dynamic/generatorfactory.h | 16 +++---- src/dynamic/generatorinterface.h | 64 +++++++++++++++++++++++----- src/playlist.cpp | 3 +- src/query.cpp | 1 - 11 files changed, 220 insertions(+), 55 deletions(-) delete mode 100644 src/dynamic/dynamiccontrol.cpp diff --git a/include/tomahawk/playlist.h b/include/tomahawk/playlist.h index bef3cc76f..8245c5367 100644 --- a/include/tomahawk/playlist.h +++ b/include/tomahawk/playlist.h @@ -7,6 +7,7 @@ #include "tomahawk/query.h" #include "tomahawk/typedefs.h" +#include "typedefs.h" class DatabaseCommand_LoadAllPlaylists; class DatabaseCommand_SetPlaylistRevision; @@ -174,7 +175,7 @@ protected: const QString& creator, bool shared ); - QList newEntries( const QList< plentry_ptr >& entries ); + QList< plentry_ptr > newEntries( const QList< plentry_ptr >& entries ); PlaylistRevision setNewRevision( const QString& rev, const QList& neworderedguids, const QList& oldorderedguids, diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 8b2aa5b32..51e21e75e 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -92,8 +92,9 @@ SET( tomahawkSources ${tomahawkSources} database/databasecollection.cpp dynamic/dynamicplaylist.cpp - dynamic/dynamiccontrol.cpp dynamic/generatorfactory.cpp + dynamic/echonest/echonestgenerator.cpp + dynamic/echonest/echonestcontrol.cpp scrobbler.cpp xmppbot/xmppbot.cpp @@ -230,6 +231,8 @@ SET( tomahawkHeaders ${tomahawkHeaders} dynamic/dynamiccontrol.h dynamic/generatorfactory.h dynamic/generatorinterface.h + dynamic/echonest/echonestgenerator.h + dynamic/echonest/echonestcontrol.h musicscanner.h scriptresolver.h diff --git a/src/dynamic/dynamiccontrol.cpp b/src/dynamic/dynamiccontrol.cpp deleted file mode 100644 index e69de29bb..000000000 diff --git a/src/dynamic/dynamiccontrol.h b/src/dynamic/dynamiccontrol.h index c517f289e..b13b9a7b8 100644 --- a/src/dynamic/dynamiccontrol.h +++ b/src/dynamic/dynamiccontrol.h @@ -1,6 +1,77 @@ +/**************************************************************************************** + * Copyright (c) 2010 Leo Franchi * + * * + * 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 . * + ****************************************************************************************/ + #ifndef DYNAMIC_PLAYLIST_CONTROL #define DYNAMIC_PLAYLIST_CONTROL +#include +#include +#include +#include +namespace Tomahawk +{ + +/** + * A Dynamic Control is a single constraint that limits a dynamic playlist. Each generator creates controls specific to that generator. + * Each control has 3 pieces: + * - Type (string selector for what this control is matching) + * - Match selector (how to match the type to the input) + * - Input field (the user input field). + * + * Each control also has a list of TypeSelectors that comes from the generator, and only one is selected at once. + * + */ +class DynamicControl : public QObject +{ + Q_OBJECT + Q_PROPERTY( QString selectedType READ selectedType WRITE setSelectedType ) + Q_PROPERTY( QStringList typeSelectors READ typeSelectors ) + +public: + virtual ~DynamicControl(); + + /// The current type of this control + QString selectedType() const { return m_selectedType; } + /// The match selector widget based on this control's type + virtual QWidget* matchSelector() { return 0; } + /// The input field widget that is associated with this type + virtual QWidget* inputField() { return 0; } + + /// All the potential type selectors for this control + QStringList typeSelectors() const { return m_typeSelectors; } + +public slots: + /** + * Sets the type to the newly specified one. Note that this will update the matchSelector + * and inputField widgets, so you should fetch the new widgets for use immediately. + */ + virtual void setSelectedType( const QString& type ) { m_selectedType = type; } + +protected: + // Private constructor, you can't make one. Get it from your Generator. + explicit DynamicControl( const QString& type, const QStringList& typeSelectors, QObject* parent = 0 ) : QObject( parent ), m_selectedType( type ), m_typeSelectors( typeSelectors ) {} + +private: + QString m_selectedType; + QStringList m_typeSelectors; +}; + +typedef QSharedPointer dyncontrol_ptr; + +}; #endif diff --git a/src/dynamic/dynamicplaylist.cpp b/src/dynamic/dynamicplaylist.cpp index 6ac880e84..8348d595c 100644 --- a/src/dynamic/dynamicplaylist.cpp +++ b/src/dynamic/dynamicplaylist.cpp @@ -1,6 +1,25 @@ +/**************************************************************************************** + * Copyright (c) 2010 Leo Franchi * + * * + * 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 . * + ****************************************************************************************/ + #include "dynamicplaylist.h" #include "tomahawk/tomahawkapp.h" +#include "generatorfactory.h" +#include "database.h" +#include "databasecommand.h" using namespace Tomahawk; @@ -44,7 +63,7 @@ dynplaylist_ptr DynamicPlaylist::create( const Tomahawk::source_ptr& author, { // TODO default generator? QString type = "default_generator"; - dynplaylist_ptr dynplaylist = new dynplaylist_ptr( new DynamicPlaylist( author, guid, title, info, creator, type, shared ) ); + dynplaylist_ptr dynplaylist = dynplaylist_ptr( new DynamicPlaylist( author, guid, title, info, creator, type, shared ) ); DatabaseCommand_CreateDynamicPlaylist* cmd = new DatabaseCommand_CreateDynamicPlaylist( author, dynplaylist ); connect( cmd, SIGNAL(finished()), dynplaylist.data(), SIGNAL(created()) ); @@ -65,12 +84,12 @@ void DynamicPlaylist::createNewRevision( const QString& newrev, QList< plentry_ptr > added = newEntries( entries ); QStringList orderedguids; - foreach( plentry_ptr p, entries ) - orderedguids << p->guid(); + for( int i = 0; i < entries.size(); ++i ) + orderedguids << entries.at(i)->guid(); // no conflict resolution or partial updating for controls. all or nothing baby - // source making the change (localy user in this case) + // source making the change (local user in this case) source_ptr author = APP->sourcelist().getLocal(); // command writes new rev to DB and calls setRevision, which emits our signal DatabaseCommand_SetDynamicPlaylistRevision* cmd = @@ -81,7 +100,7 @@ void DynamicPlaylist::createNewRevision( const QString& newrev, orderedguids, added, type, - STATIC, + OnDemand, controls ); APP->database()->enqueue( QSharedPointer( cmd ) ); } @@ -101,7 +120,7 @@ void DynamicPlaylist::createNewRevision( const QString& newrev, newrev, oldrev, type, - STATIC, + Static, controls ); APP->database()->enqueue( QSharedPointer( cmd ) ); } @@ -113,7 +132,7 @@ void DynamicPlaylist::loadRevision( const QString& rev ) DatabaseCommand_LoadDynamicPlaylist* cmd = new DatabaseCommand_LoadDynamicPlaylist( rev.isEmpty() ? currentrevision() : rev, m_generator->mode() ); - if( m_generator->mode() == ONDEMAND ) { + if( m_generator->mode() == OnDemand ) { connect( cmd, SIGNAL( done( QString, bool, const QString, @@ -124,7 +143,7 @@ void DynamicPlaylist::loadRevision( const QString& rev ) const QString, QList< dyncontrol_ptr>, bool) ) ); - } else if( m_generator->mode() == STATIC ) { + } else if( m_generator->mode() == Static ) { connect( cmd, SIGNAL( done( QString, QList< QString >, QList< QString >, @@ -148,7 +167,7 @@ void DynamicPlaylist::loadRevision( const QString& rev ) bool DynamicPlaylist::remove( const Tomahawk::dynplaylist_ptr& playlist ) { - return remove( playlist.staticCast() ); + return Playlist::remove( playlist.staticCast() ); } void DynamicPlaylist::reportCreated( const Tomahawk::dynplaylist_ptr& self ) @@ -156,7 +175,7 @@ void DynamicPlaylist::reportCreated( const Tomahawk::dynplaylist_ptr& self ) qDebug() << Q_FUNC_INFO; Q_ASSERT( self.data() == this ); // will emit Collection::playlistCreated(...) - m_source->collection()->addPlaylist( self.staticCast() ); + author()->collection()->addPlaylist( self.staticCast() ); } void DynamicPlaylist::reportDeleted( const Tomahawk::dynplaylist_ptr& self ) @@ -164,7 +183,7 @@ void DynamicPlaylist::reportDeleted( const Tomahawk::dynplaylist_ptr& self ) qDebug() << Q_FUNC_INFO; Q_ASSERT( self.data() == this ); // will emit Collection::playlistCreated(...) - m_source->collection()->deletePlaylist( self.staticCast() ); + author()->collection()->deletePlaylist( self.staticCast() ); } // static version @@ -194,19 +213,19 @@ void DynamicPlaylist::setRevision( const QString& rev, return; } if( m_generator->type() != type ) { // new generator needed - m_generator = generatorinterface_ptr( GeneratorFactory::create( type ) ); + m_generator = GeneratorFactory::create( type ); } m_generator->setControls( controls ); - m_generator->setMode( ONDEMAND ) + m_generator->setMode( Static ); - DynamicPlaylistRevision pr = DynamicPlaylistRevision( setNewRevision( rev, neworderedguids, oldorderedguids, is_newest_rev, addedmap ) ); + DynamicPlaylistRevision pr = setNewRevision( rev, neworderedguids, oldorderedguids, is_newest_rev, addedmap ); pr.controls = controls; pr.type = type; - pr.mode = STATIC; + pr.mode = Static; if( applied ) - setCurrentRevision( rev ); + setCurrentrevision( rev ); pr.applied = applied; emit revisionLoaded( pr ); @@ -220,18 +239,18 @@ void DynamicPlaylist::setRevision( const QString& rev, bool applied ) { if( m_generator->type() != type ) { // new generator needed - m_generator = generatorinterface_ptr( GeneratorFactory::create( type ) ); + m_generator = geninterface_ptr( GeneratorFactory::create( type ) ); } m_generator->setControls( controls ); - m_generator->setMode( ONDEMAND ) + m_generator->setMode( OnDemand ); DynamicPlaylistRevision pr; - pr.oldrevisionguid = m_currentrevision; + pr.oldrevisionguid = currentrevision(); pr.revisionguid = rev; pr.controls = controls; pr.type = type; - pr.mode = ONDEMAND; + pr.mode = OnDemand; emit revisionLoaded( pr ); } diff --git a/src/dynamic/dynamicplaylist.h b/src/dynamic/dynamicplaylist.h index 6996b972d..407103211 100644 --- a/src/dynamic/dynamicplaylist.h +++ b/src/dynamic/dynamicplaylist.h @@ -1,3 +1,19 @@ +/**************************************************************************************** + * Copyright (c) 2010 Leo Franchi * + * * + * 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 . * + ****************************************************************************************/ + #ifndef DYNAMIC_PLAYLIST_H #define DYNAMIC_PLAYLIST_H @@ -15,6 +31,23 @@ namespace Tomahawk { * It uses normal PlaylistEntries but also has a mode, a generator, and a list of controls */ +struct DynamicPlaylistRevision : PlaylistRevision +{ + QList< dyncontrol_ptr > controls; + Tomahawk::GeneratorMode mode; + QString type; + + DynamicPlaylistRevision( const PlaylistRevision& other ) + { + revisionguid = other.revisionguid; + oldrevisionguid = other.oldrevisionguid; + newlist = other.newlist; + applied = other.applied; + } + + DynamicPlaylistRevision() {} +}; + class DynamicPlaylist : public Playlist { Q_OBJECT @@ -22,12 +55,7 @@ class DynamicPlaylist : public Playlist Q_PROPERTY( GeneratorMode mode WRITE setMode READ mode ) Q_PROPERTY( QString type WRITE setType READ type ) -public: - enum DynamicPlaylistMode { - OnDemand = 0, - StaticPlaylist - }; - +public: /// Generate an empty dynamic playlist with default generator static Tomahawk::dynplaylist_ptr create( const source_ptr& author, const QString& guid, @@ -42,7 +70,7 @@ public: GeneratorMode mode() const { return m_generator->mode(); } QString type() const { return m_generator->type(); } - generatorinterface_ptr generator() const { return m_generator; } + geninterface_ptr generator() const { return m_generator; } // // these need to exist and be public for the json serialization stuff @@ -55,7 +83,7 @@ public: } void setMode( GeneratorMode mode ) { m_generator->setMode( mode ); } void setType( const QString& type ) { /** TODO */; } - void setGenerator( const generatorinterface_ptr& gen_ptr ) { m_generator = gen_ptr; } + void setGenerator( const geninterface_ptr& gen_ptr ) { m_generator = gen_ptr; } // signals: @@ -112,7 +140,7 @@ private: bool shared ); private: - generatorinterface_ptr m_generator; + geninterface_ptr m_generator; }; }; // namespace diff --git a/src/dynamic/generatorfactory.cpp b/src/dynamic/generatorfactory.cpp index c9b0ddd16..b93dbbe2f 100644 --- a/src/dynamic/generatorfactory.cpp +++ b/src/dynamic/generatorfactory.cpp @@ -1,4 +1,5 @@ -#include "generatorfactory.h" +#include "dynamic/generatorfactory.h" +#include "dynamic/generatorinterface.h" Tomahawk::GeneratorFactory::GeneratorFactory() { @@ -12,9 +13,9 @@ Tomahawk::GeneratorFactory::~GeneratorFactory() generatorinterface_ptr Tomahawk::GeneratorFactory::create ( const QString& type ) { if( !m_factories.contains( type ) ) - return generatorinterface_ptr(); + return geninterface_ptr(); - return generatorinterface_ptr( m_factories.value( type )->create() ); + return geninterface_ptr( m_factories.value( type )->create() ); } void Tomahawk::GeneratorFactory::registerFactory ( const QString& type, Tomahawk::GeneratorFactoryInterface* interface ) diff --git a/src/dynamic/generatorfactory.h b/src/dynamic/generatorfactory.h index 8c68f112e..673895305 100644 --- a/src/dynamic/generatorfactory.h +++ b/src/dynamic/generatorfactory.h @@ -4,6 +4,8 @@ #include #include +#include "dynamic/generatorinterface.h" + namespace Tomahawk { /** @@ -12,7 +14,9 @@ namespace Tomahawk { class GeneratorFactoryInterface { public: - GeneratorInterface* create() = 0; + GeneratorFactoryInterface() {} + + virtual GeneratorInterface* create() = 0; }; /** @@ -21,15 +25,11 @@ public: class GeneratorFactory { public: - GeneratorFactory(); - ~GeneratorFactory(); - - generatorinterface_ptr create( const QString& type ); - - void registerFactory( const QString& type, GeneratorFactoryInterface* interface ); + static geninterface_ptr create( const QString& type ); + static void registerFactory( const QString& type, GeneratorFactoryInterface* interface ); private: - QHash m_factories; + static QHash m_factories; }; diff --git a/src/dynamic/generatorinterface.h b/src/dynamic/generatorinterface.h index 3823f649f..c89ae07e5 100644 --- a/src/dynamic/generatorinterface.h +++ b/src/dynamic/generatorinterface.h @@ -1,7 +1,27 @@ +/**************************************************************************************** + * Copyright (c) 2010 Leo Franchi * + * * + * 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 . * + ****************************************************************************************/ + #ifndef GENERATOR_INTERFACE_H #define GENERATOR_INTERFACE_H #include +#include + +#include "dynamic/dynamiccontrol.h" +#include namespace Tomahawk { @@ -14,7 +34,9 @@ enum GeneratorMode { * The abstract interface for Dynamic Playlist Generators. Generators have the following features: * - They create new DynamicControls that are appropriate for the generator * - They expose a list of controls that this generator currently is operating on - * - They have a state of OnDemand or Static + * - They have a mode of OnDemand or Static + * + * And they generate tracks */ class GeneratorInterface : public QObject { @@ -24,29 +46,49 @@ class GeneratorInterface : public QObject Q_ENUMS( GeneratorMode ) public: - explicit GeneratorInterface() {} + explicit GeneratorInterface( QObject* parent = 0 ) : QObject( parent ) {} virtual ~GeneratorInterface() {} // Can't make it pure otherwise we can't shove it in QVariants :-/ - virtual dyncontrol_ptr createControl() const {} + // empty QString means use default + virtual dyncontrol_ptr createControl( const QString& type = QString() ) const { return dyncontrol_ptr(); } + + /** + * Generate tracks from the controls in this playlist. If the current mode is + * OnDemand, then \p number is not taken into account. If this generator is in static + * mode, then it will return the desired number of tracks + * + * Connect to the generated() signal for the results. + * + */ + virtual void generate( int number = -1 ) {}; /// The type of this generator - virtual QString type() const { return m_type; } + QString type() const { return m_type; } - virtual GeneratorMode mode() const { return m_mode; } - virtual void setMode( GeneratorMode mode ) { m_mode = mode; } + GeneratorMode mode() const { return m_mode; } + void setMode( GeneratorMode mode ) { m_mode = mode; } // control functions - virtual QList< dyncontrol_ptr > controls() const { return m_controls; } - virtual void addControl( const dyncontrol_ptr& control ) { m_controls << control; } - virtual void clearControls() { m_controls.clear(); } - virtual void setControls( const QList< dyncontrol_ptr>& controls ) { m_controls = controls; } -private: + QList< dyncontrol_ptr > controls() const { return m_controls; } + void addControl( const dyncontrol_ptr& control ) { m_controls << control; } + void clearControls() { m_controls.clear(); } + void setControls( const QList< dyncontrol_ptr>& controls ) { m_controls = controls; } + + QStringList typeSelectors() const { return m_typeSelectors; } + +signals: + void generated( const QList< query_ptr>& queries ); + +protected: QString m_type; GeneratorMode m_mode; QList< dyncontrol_ptr > m_controls; + QStringList m_typeSelectors; }; +typedef QSharedPointer geninterface_ptr; + }; #endif diff --git a/src/playlist.cpp b/src/playlist.cpp index 0e47de8a2..916ea4fcc 100644 --- a/src/playlist.cpp +++ b/src/playlist.cpp @@ -312,6 +312,7 @@ Playlist::setNewRevision( const QString& rev, // << "total entries" << m_entries.size(); pr.newlist = entries; + return pr; } void Playlist::resolve() @@ -363,7 +364,7 @@ Playlist::addEntries( const QList& queries, const QString& oldrev ) } QList< plentry_ptr > -Playlist::newEntries( const QList< plentry_ptr > entries ) +Playlist::newEntries( const QList< plentry_ptr >& entries ) { QSet currentguids; foreach( plentry_ptr p, m_entries ) diff --git a/src/query.cpp b/src/query.cpp index b5de0088e..348c77708 100644 --- a/src/query.cpp +++ b/src/query.cpp @@ -8,7 +8,6 @@ Query::Query( const QVariant& v ) : m_v( v ) , m_solved( false ) { - // ensure a QID is present: QVariantMap m = m_v.toMap(); m_artist = m.value( "artist" ).toString();