diff --git a/README b/README index 3697bef9c..d0dcad602 100644 --- a/README +++ b/README @@ -30,12 +30,21 @@ QJson (Qt JSON library) $ ./configure && make $ sudo make install +libEchonest 0.1 +--------------- + See: http://projects.kde.org/projects/playground/libs/libechonest/ + + $ git clone git://git.kde.org/libechonest.git + $ cd libechonest + $ mkdir build && cd build + $ cmake .. + $ make + $ sudo make install Now compile Tomahawk ------------------- $ sudo ldconfig -v | grep -Ei 'qjson|gloox' - $ mkdir build - $ cd build + $ mkdir build && cd build $ cmake .. $ make $ ./tomahawk @@ -57,7 +66,8 @@ Dependencies libmad 0.15.1b http://www.underbit.com/products/mad/ libvorbis 1.2.3 http://xiph.org/vorbis/ libogg 1.1.4 http://xiph.org/ogg/ - liblastfm 0.3.0 http://github.com/mxcl/liblastfm/ + liblastfm 0.3.3 http://github.com/mxcl/liblastfm/ + libechonest 0.1.2 http://projects.kde.org/projects/playground/libs/libechonest/ Third party libraries that we ship with our source: diff --git a/include/tomahawk/infosystem.h b/include/tomahawk/infosystem.h new file mode 100644 index 000000000..983305c47 --- /dev/null +++ b/include/tomahawk/infosystem.h @@ -0,0 +1,148 @@ +#ifndef TOMAHAWK_INFOSYSTEM_H +#define TOMAHAWK_INFOSYSTEM_H + +#include +#include +#include +#include +#include +#include +#include + + +namespace Tomahawk { + +namespace InfoSystem { + +enum InfoType { + InfoTrackID, + InfoTrackArtist, + InfoTrackAlbum, + InfoTrackGenre, + InfoTrackComposer, + InfoTrackDate, + InfoTrackNumber, + InfoTrackDiscNumber, + InfoTrackBitRate, + InfoTrackLength, + InfoTrackSampleRate, + InfoTrackFileSize, + InfoTrackBPM, + InfoTrackReplayGain, + InfoTrackReplayPeakGain, + InfoTrackLyrics, + InfoTrackLocation, + InfoTrackProfile, + InfoTrackEnergy, + InfoTrackDanceability, + InfoTrackTempo, + InfoTrackLoudness, + + InfoArtistID, + InfoArtistName, + InfoArtistBiography, + InfoArtistBlog, + InfoArtistFamiliarity, + InfoArtistHotttness, + InfoArtistImages, + InfoArtistNews, + InfoArtistProfile, + InfoArtistReviews, + InfoArtistSongs, + InfoArtistSimilars, + InfoArtistTerms, + InfoArtistLinks, + InfoArtistVideos, + + InfoAlbumID, + InfoAlbumName, + InfoAlbumArtist, + InfoAlbumDate, + InfoAlbumGenre, + InfoAlbumComposer, + InfoMiscTopHotttness, + InfoMiscTopTerms, + + InfoNoInfo +}; + +typedef QMap< InfoType, QVariant > InfoMap; +typedef QMap< QString, QMap< QString, QString > > InfoGenericMap; +typedef QHash InfoCustomDataHash; +typedef QHash MusixMatchHash; + +class InfoPlugin : public QObject +{ + Q_OBJECT + +public: + InfoPlugin(QObject *parent) + :QObject(parent) + { + qDebug() << Q_FUNC_INFO; + } + ~InfoPlugin() + { + qDebug() << Q_FUNC_INFO; + } + + virtual void getInfo(const QString &caller, const InfoType type, const QVariant &data, Tomahawk::InfoSystem::InfoCustomDataHash customData) = 0; + +signals: + void info(QString caller, Tomahawk::InfoSystem::InfoType type, QVariant input, QVariant output, Tomahawk::InfoSystem::InfoCustomDataHash customData); + void finished(QString, Tomahawk::InfoSystem::InfoType); + +protected: + InfoType m_type; +}; + +typedef QWeakPointer< InfoPlugin > InfoPluginPtr; + +class InfoSystem : public QObject +{ + Q_OBJECT + +public: + + + InfoSystem(QObject *parent); + ~InfoSystem() + { + qDebug() << Q_FUNC_INFO; + } + + void registerInfoTypes(const InfoPluginPtr &plugin, const QSet< InfoType > &types); + + void getInfo(const QString &caller, const InfoType type, const QVariant &data, InfoCustomDataHash customData); + void getInfo(const QString &caller, const InfoMap &input, InfoCustomDataHash customData); + +signals: + void info(QString caller, Tomahawk::InfoSystem::InfoType, QVariant input, QVariant output, Tomahawk::InfoSystem::InfoCustomDataHash customData); + void finished(QString target); + +public slots: + void infoSlot(QString target, Tomahawk::InfoSystem::InfoType type, QVariant input, QVariant output, Tomahawk::InfoSystem::InfoCustomDataHash customData); + void finishedSlot(QString target,Tomahawk::InfoSystem::InfoType type); + +private: + + QLinkedList< InfoPluginPtr > determineOrderedMatches(const InfoType type) const; + + QMap< InfoType, QLinkedList > m_infoMap; + + // For now, statically instantiate plugins; this is just somewhere to keep them + QLinkedList m_plugins; + + QHash< QString, QHash< Tomahawk::InfoSystem::InfoType, int > > m_dataTracker; + +}; + +} + +} + +Q_DECLARE_METATYPE(Tomahawk::InfoSystem::InfoGenericMap) +Q_DECLARE_METATYPE(Tomahawk::InfoSystem::InfoCustomDataHash); +Q_DECLARE_METATYPE(Tomahawk::InfoSystem::MusixMatchHash) + +#endif // TOMAHAWK_INFOSYSTEM_H diff --git a/include/tomahawk/query.h b/include/tomahawk/query.h index f92c84384..3ce6064eb 100644 --- a/include/tomahawk/query.h +++ b/include/tomahawk/query.h @@ -51,13 +51,13 @@ public: signals: void resultsAdded( const QList& ); - void resultsRemoved( Tomahawk::result_ptr ); + void resultsRemoved( const Tomahawk::result_ptr& ); void solvedStateChanged( bool state ); public slots: /// (indirectly) called by resolver plugins when results are found void addResults( const QList< Tomahawk::result_ptr >& ); - void removeResult( Tomahawk::result_ptr ); + void removeResult( const Tomahawk::result_ptr& ); private slots: void resultUnavailable(); diff --git a/include/tomahawk/tomahawkapp.h b/include/tomahawk/tomahawkapp.h index d60f8a5b7..32df60925 100644 --- a/include/tomahawk/tomahawkapp.h +++ b/include/tomahawk/tomahawkapp.h @@ -29,9 +29,18 @@ class Database; class Jabber; +class XMPPBot; class TomahawkZeroconf; class TomahawkSettings; +namespace Tomahawk +{ + namespace InfoSystem + { + class InfoSystem; + } +} + #ifndef TOMAHAWK_HEADLESS class AudioEngine; class TomahawkWindow; @@ -64,6 +73,9 @@ public: SourceList& sourcelist() { return m_sources; } Servent& servent() { return m_servent; } QNetworkAccessManager* nam() { return m_nam; } + QNetworkProxy* proxy() { return m_proxy; } + Tomahawk::InfoSystem::InfoSystem* infoSystem() { return m_infoSystem; } + XMPPBot* xmppBot() { return m_xmppBot; } const QString& nodeID() const; #ifndef TOMAHAWK_HEADLESS @@ -114,6 +126,7 @@ private: SourceList m_sources; TomahawkZeroconf* m_zeroconf; QSharedPointer m_jabber; + XMPPBot* m_xmppBot; #ifndef TOMAHAWK_HEADLESS TomahawkWindow* m_mainwindow; @@ -129,6 +142,9 @@ private: TomahawkSettings* m_settings; QNetworkAccessManager* m_nam; + QNetworkProxy* m_proxy; + + Tomahawk::InfoSystem::InfoSystem* m_infoSystem; QxtHttpServerConnector m_connector; QxtHttpSessionManager m_session; diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index ef29179dc..3718af5dd 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -36,6 +36,10 @@ SET( tomahawkSources ${tomahawkSources} utils/tomahawkutils.cpp jabber/jabber_p.cpp + infosystem/infosystem.cpp + infosystem/infoplugins/echonestplugin.cpp + infosystem/infoplugins/musixmatchplugin.cpp + bufferiodevice.cpp connection.cpp msgprocessor.cpp @@ -73,6 +77,8 @@ SET( tomahawkSources ${tomahawkSources} database/databasecommand_updatesearchindex.cpp database/databasecollection.cpp + xmppbot/xmppbot.cpp + web/api_v1.cpp tomahawksettings.cpp @@ -139,6 +145,8 @@ SET( tomahawkHeaders ${tomahawkHeaders} "${TOMAHAWK_INC_DIR}/tomahawk/track.h" "${TOMAHAWK_INC_DIR}/tomahawk/playlist.h" + "${TOMAHAWK_INC_DIR}/tomahawk/infosystem.h" + "${TOMAHAWK_INC_DIR}/tomahawk/functimeout.h" # "${TOMAHAWK_INC_DIR}/tomahawk/tomahawkplugin.h" @@ -170,6 +178,9 @@ SET( tomahawkHeaders ${tomahawkHeaders} jabber/jabber.h jabber/jabber_p.h + infosystem/infoplugins/echonestplugin.h + infosystem/infoplugins/musixmatchplugin.h + bufferiodevice.h connection.h msgprocessor.h @@ -183,6 +194,8 @@ SET( tomahawkHeaders ${tomahawkHeaders} scriptresolver.h tomahawksettings.h + xmppbot/xmppbot.h + web/api_v1.h ) @@ -231,12 +244,13 @@ SET( tomahawkHeadersGui ${tomahawkHeadersGui} ) SET( tomahawkUI ${tomahawkUI} - tomahawkwindow.ui - settingsdialog.ui + tomahawkwindow.ui + settingsdialog.ui + proxydialog.ui - audiocontrols.ui - sourcetree/sourcetreeitemwidget.ui - topbar/topbar.ui + audiocontrols.ui + sourcetree/sourcetreeitemwidget.ui + topbar/topbar.ui ) INCLUDE_DIRECTORIES( @@ -258,6 +272,8 @@ INCLUDE_DIRECTORIES( /usr/include/taglib /usr/local/include/taglib + /usr/include/echonest + /usr/local/include/echonest /usr/local/include ) @@ -304,6 +320,7 @@ TARGET_LINK_LIBRARIES( tomahawk ${QT_LIBRARIES} ${MAC_EXTRA_LIBS} ${OS_SPECIFIC_LINK_LIBRARIES} + echonest portfwd ) diff --git a/src/CMakeLists.unix.txt b/src/CMakeLists.unix.txt index a2d31ad89..19a6ee285 100644 --- a/src/CMakeLists.unix.txt +++ b/src/CMakeLists.unix.txt @@ -25,4 +25,4 @@ ELSE() SET( tomahawkSourcesGui ${tomahawkSourcesGui} audio/vorbistranscode.cpp scrobbler.cpp ) SET( tomahawkHeadersGui ${tomahawkHeadersGui} audio/vorbistranscode.h scrobbler.h ) -ENDIF() \ No newline at end of file +ENDIF() diff --git a/src/CPack.txt b/src/CPack.txt index fac0a04c5..46174a970 100644 --- a/src/CPack.txt +++ b/src/CPack.txt @@ -13,8 +13,8 @@ SET(CPACK_DEBIAN_PACKAGE_MAINTAINER "rj@tomahawk.org") SET(CPACK_PACKAGE_DESCRIPTION_FILE "${CMAKE_CURRENT_SOURCE_DIR}/../README") SET(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/../LICENSE.txt") SET(CPACK_PACKAGE_VERSION_MAJOR "0") -SET(CPACK_PACKAGE_VERSION_MINOR "1") -SET(CPACK_PACKAGE_VERSION_PATCH "3") +SET(CPACK_PACKAGE_VERSION_MINOR "0") +SET(CPACK_PACKAGE_VERSION_PATCH "1") SET(CPACK_PACKAGE_VERSION "${CPACK_PACKAGE_VERSION_MAJOR}.${CPACK_PACKAGE_VERSION_MINOR}.${CPACK_PACKAGE_VERSION_PATCH}") #SET(CPACK_DEBIAN_PACKAGE_ARCHITECTURE "i386") # Default: Output of dpkg --print-architecture or i386 @@ -51,7 +51,7 @@ ENDIF(WIN32 AND NOT UNIX) # Nsis only? SET(CPACK_PACKAGE_EXECUTABLES "tomahawk" "tomahawk") #gnutls is in here because gloox needs it, and we link statically to gloox: -SET(CPACK_DEBIAN_PACKAGE_DEPENDS "libqtgui4 (>=4:4.6.2-0ubuntu5), libtag1c2a (>=1.6.2-0ubuntu1), liblastfm-dev (>=0.4.0~git20090710-1), libqt4-sql-sqlite (>=4:4.6.2-0ubuntu5), libvorbis0a (>=1.2.3-3ubuntu1), libmad0 (>=0.15.1b-4ubuntu1), libasound2 (>=1.0.22-0ubuntu7), zlib1g (>=1:1.2.3.3.dfsg-15ubuntu1), libqjson-dev (>=0.7.1-1), libgnutls26 (>= 2.7.14-0)") +SET(CPACK_DEBIAN_PACKAGE_DEPENDS "libqtgui4 (>=4:4.7.0-0ubuntu1), libtag1c2a (>=1.6.2-0ubuntu1), liblastfm-dev (>=0.4.0~really0.3.3-0ubuntu1), libqt4-sql-sqlite (>=4:4.7.0-0ubuntu1), libvorbis0a (>=1.2.3-3ubuntu1), libmad0 (>=0.15.1b-4ubuntu1), libasound2 (>=1.0.22-0ubuntu7), zlib1g (>=1:1.2.3.3.dfsg-15ubuntu1), libqjson-dev (>=0.7.1-1), libgnutls26 (>= 2.7.14-0), libgloox8 (>=1.0-1)") #SET(CPACK_DEBIAN_PACKAGE_SECTION "music") diff --git a/src/database/databasecommand_loadplaylistentries.cpp b/src/database/databasecommand_loadplaylistentries.cpp index 44787fb85..507cabffb 100644 --- a/src/database/databasecommand_loadplaylistentries.cpp +++ b/src/database/databasecommand_loadplaylistentries.cpp @@ -91,6 +91,7 @@ DatabaseCommand_LoadPlaylistEntries::exec( DatabaseImpl* dbi ) if( !query_entries_old.next() ) { + return; Q_ASSERT( false ); } diff --git a/src/infosystem/infoplugins/echonestplugin.cpp b/src/infosystem/infoplugins/echonestplugin.cpp new file mode 100644 index 000000000..5dc0c2c4f --- /dev/null +++ b/src/infosystem/infoplugins/echonestplugin.cpp @@ -0,0 +1,260 @@ +#include "tomahawk/infosystem.h" +#include "tomahawk/tomahawkapp.h" +#include "echonestplugin.h" +#include +#include + +using namespace Tomahawk::InfoSystem; +using namespace Echonest; + +// for internal neatness + +EchoNestPlugin::EchoNestPlugin(QObject *parent) + : InfoPlugin(parent) +{ + qDebug() << Q_FUNC_INFO; + Config::instance()->setAPIKey("JGJCRKWLXLBZIFAZB"); + QSet< InfoType > supportedTypes; + supportedTypes << Tomahawk::InfoSystem::InfoArtistBiography << Tomahawk::InfoSystem::InfoArtistFamiliarity << Tomahawk::InfoSystem::InfoArtistHotttness << Tomahawk::InfoSystem::InfoArtistTerms << Tomahawk::InfoSystem::InfoMiscTopTerms; + qobject_cast< InfoSystem* >(parent)->registerInfoTypes(this, supportedTypes); +} + +EchoNestPlugin::~EchoNestPlugin() +{ + qDebug() << Q_FUNC_INFO; +} + +void EchoNestPlugin::getInfo(const QString &caller, const InfoType type, const QVariant& data, InfoCustomDataHash customData) +{ + switch (type) + { + case Tomahawk::InfoSystem::InfoArtistBiography: + return getArtistBiography(caller, data, customData); + case Tomahawk::InfoSystem::InfoArtistFamiliarity: + return getArtistFamiliarity(caller, data, customData); + case Tomahawk::InfoSystem::InfoArtistHotttness: + return getArtistHotttnesss(caller, data, customData); + case Tomahawk::InfoSystem::InfoArtistTerms: + return getArtistTerms(caller, data, customData); + case Tomahawk::InfoSystem::InfoTrackEnergy: + return getSongProfile(caller, data, customData, "energy"); + case Tomahawk::InfoSystem::InfoMiscTopTerms: + return getMiscTopTerms(caller, data, customData); + default: + { + emit info(caller, Tomahawk::InfoSystem::InfoNoInfo, QVariant(), QVariant(), customData); + return; + } + } +} + +void EchoNestPlugin::getSongProfile(const QString &caller, const QVariant& data, InfoCustomDataHash &customData, const QString &item) +{ + //WARNING: Totally not implemented yet + + if( !isValidTrackData( caller, data, customData ) ) + return; + +// Track track( data.toString() ); +// Artist artist( customData.data()->property("artistName").toString() ); +// reply->setProperty("artist", QVariant::fromValue(artist)); +// reply->setProperty( "data", data ); +// m_replyMap[reply] = customData; +// connect(reply, SIGNAL(finished()), SLOT(getArtistBiographySlot())); +} + +void EchoNestPlugin::getArtistBiography(const QString &caller, const QVariant& data, InfoCustomDataHash &customData) +{ + if( !isValidArtistData( caller, data, customData ) ) + return; + + Artist artist( data.toString() ); + QNetworkReply *reply = artist.fetchBiographies(); + reply->setProperty("artist", QVariant::fromValue(artist)); + reply->setProperty( "data", data ); + m_replyMap[reply] = customData; + m_callerMap[reply] = caller; + connect(reply, SIGNAL(finished()), SLOT(getArtistBiographySlot())); +} + +void EchoNestPlugin::getArtistFamiliarity(const QString &caller, const QVariant& data, InfoCustomDataHash &customData) +{ + if( !isValidArtistData( caller, data, customData ) ) + return; + + qDebug() << "Fetching artist familiarity!" << data; + Artist artist( data.toString() ); + QNetworkReply* reply = artist.fetchFamiliarity(); + reply->setProperty( "artist", QVariant::fromValue(artist)); + reply->setProperty( "data", data ); + m_replyMap[reply] = customData; + m_callerMap[reply] = caller; + connect(reply, SIGNAL(finished()), SLOT(getArtistFamiliaritySlot())); +} + +void EchoNestPlugin::getArtistHotttnesss(const QString &caller, const QVariant& data, InfoCustomDataHash &customData) +{ + if( !isValidArtistData( caller, data, customData ) ) + return; + + Artist artist( data.toString() ); + QNetworkReply* reply = artist.fetchHotttnesss(); + reply->setProperty( "artist", QVariant::fromValue(artist)); + reply->setProperty( "data", data ); + m_replyMap[reply] = customData; + m_callerMap[reply] = caller; + connect(reply, SIGNAL(finished()), SLOT(getArtistHotttnesssSlot())); +} + +void EchoNestPlugin::getArtistTerms(const QString &caller, const QVariant& data, InfoCustomDataHash &customData) +{ + if( !isValidArtistData( caller, data, customData ) ) + return; + + Artist artist( data.toString() ); + QNetworkReply* reply = artist.fetchTerms( Echonest::Artist::Weight ); + reply->setProperty( "artist", QVariant::fromValue(artist)); + reply->setProperty( "data", data ); + m_replyMap[reply] = customData; + m_callerMap[reply] = caller; + connect(reply, SIGNAL(finished()), SLOT(getArtistTermsSlot())); +} + +void EchoNestPlugin::getMiscTopTerms(const QString &caller, const QVariant& data, InfoCustomDataHash& customData) +{ + QNetworkReply* reply = Artist::topTerms( 20 ); + m_replyMap[reply] = customData; + m_callerMap[reply] = caller; + connect( reply,SIGNAL(finished()), SLOT( getMiscTopSlot())); +} + + +void EchoNestPlugin::getArtistBiographySlot() +{ + QNetworkReply* reply = qobject_cast( sender() ); + Artist artist = artistFromReply( reply ); + BiographyList biographies = artist.biographies(); + InfoGenericMap biographyMap; + Q_FOREACH(const Biography& biography, biographies) + { + biographyMap[biography.site()]["site"] = biography.site(); + biographyMap[biography.site()]["url"] = biography.url().toString(); + biographyMap[biography.site()]["text"] = biography.text(); + biographyMap[biography.site()]["attribution"] = biography.license().attribution; + biographyMap[biography.site()]["licensetype"] = biography.license().type; + biographyMap[biography.site()]["attribution"] = biography.license().url.toString(); + + } + emit info( m_callerMap[reply], Tomahawk::InfoSystem::InfoArtistBiography, reply->property( "data" ), QVariant::fromValue(biographyMap), m_replyMap[reply] ); + emit finished( m_callerMap[reply], Tomahawk::InfoSystem::InfoArtistBiography); + m_replyMap.remove(reply); + m_callerMap.remove(reply); + reply->deleteLater(); +} + +void EchoNestPlugin::getArtistFamiliaritySlot() +{ + QNetworkReply* reply = qobject_cast( sender() ); + Artist artist = artistFromReply( reply ); + qreal familiarity = artist.familiarity(); + emit info( m_callerMap[reply], Tomahawk::InfoSystem::InfoArtistFamiliarity, reply->property( "data" ), familiarity, m_replyMap[reply] ); + emit finished( m_callerMap[reply], Tomahawk::InfoSystem::InfoArtistFamiliarity); + m_replyMap.remove(reply); + m_callerMap.remove(reply); + reply->deleteLater(); +} + +void EchoNestPlugin::getArtistHotttnesssSlot() +{ + QNetworkReply* reply = qobject_cast( sender() ); + Artist artist = artistFromReply( reply ); + qreal hotttnesss = artist.hotttnesss(); + emit info( m_callerMap[reply], Tomahawk::InfoSystem::InfoArtistHotttness, reply->property( "data" ), hotttnesss, m_replyMap[reply] ); + emit finished( m_callerMap[reply], Tomahawk::InfoSystem::InfoArtistHotttness); + m_replyMap.remove(reply); + m_callerMap.remove(reply); + reply->deleteLater(); +} + +void EchoNestPlugin::getArtistTermsSlot() +{ + QNetworkReply* reply = qobject_cast( sender() ); + Artist artist = artistFromReply( reply ); + TermList terms = artist.terms(); + InfoGenericMap termsMap; + Q_FOREACH( const Echonest::Term& term, terms ) { + QMap< QString, QString > termMap; + termMap[ "weight" ] = QString::number(term.weight()); + termMap[ "frequency" ] = QString::number(term.frequency()); + termsMap[ term.name() ] = termMap; + } + emit info( m_callerMap[reply], Tomahawk::InfoSystem::InfoArtistTerms, reply->property( "data" ), QVariant::fromValue(termsMap), m_replyMap[reply] ); + emit finished( m_callerMap[reply], Tomahawk::InfoSystem::InfoArtistTerms); + m_replyMap.remove(reply); + m_callerMap.remove(reply); + reply->deleteLater(); +} + +void EchoNestPlugin::getMiscTopSlot() +{ + QNetworkReply* reply = qobject_cast( sender() ); + TermList terms = Artist::parseTopTerms( reply ); + InfoGenericMap termsMap; + Q_FOREACH( const Echonest::Term& term, terms ) { + QMap< QString, QString > termMap; + termMap[ "weight" ] = QString::number( term.weight() ); + termMap[ "frequency" ] = QString::number( term.frequency() ); + termsMap[ term.name().toLower() ] = termMap; + } + emit info( m_callerMap[reply], Tomahawk::InfoSystem::InfoMiscTopTerms, QVariant(), QVariant::fromValue(termsMap), m_replyMap[reply] ); + emit finished( m_callerMap[reply], Tomahawk::InfoSystem::InfoMiscTopTerms); + m_replyMap.remove(reply); + m_callerMap.remove(reply); + reply->deleteLater(); +} + +bool EchoNestPlugin::isValidArtistData(const QString &caller, const QVariant& data, InfoCustomDataHash &customData) +{ + if (data.isNull() || !data.isValid() || !data.canConvert()) + { + emit info(caller, Tomahawk::InfoSystem::InfoNoInfo, QVariant(), QVariant(), customData); + return false; + } + QString artistName = data.toString(); + if (artistName.isEmpty() ) + { + emit info(caller, Tomahawk::InfoSystem::InfoNoInfo, QVariant(), QVariant(), customData); + return false; + } + return true; +} + +bool EchoNestPlugin::isValidTrackData(const QString &caller, const QVariant& data, InfoCustomDataHash &customData) +{ + if (data.isNull() || !data.isValid() || !data.canConvert()) + { + emit info(caller, Tomahawk::InfoSystem::InfoNoInfo, QVariant(), QVariant(), customData); + return false; + } + QString trackName = data.toString(); + if (trackName.isEmpty() ) + { + emit info(caller, Tomahawk::InfoSystem::InfoNoInfo, QVariant(), QVariant(), customData); + return false; + } + if (!customData.contains("artistName") || + customData["artistName"].toString().isEmpty()) + return false; + return true; +} + +Artist EchoNestPlugin::artistFromReply(QNetworkReply* reply) +{ + Artist artist = reply->property("artist").value(); + try { + artist.parseProfile(reply); + } catch( const Echonest::ParseError& e ) { + qWarning() << "Caught parser error from echonest!" << e.what(); + } + return artist; +} diff --git a/src/infosystem/infoplugins/echonestplugin.h b/src/infosystem/infoplugins/echonestplugin.h new file mode 100644 index 000000000..8ffb120b2 --- /dev/null +++ b/src/infosystem/infoplugins/echonestplugin.h @@ -0,0 +1,54 @@ +#ifndef ECHONESTPLUGIN_H +#define ECHONESTPLUGIN_H +#include "tomahawk/infosystem.h" + +class QNetworkReply; +namespace Echonest { +class Artist; +} + +namespace Tomahawk +{ + +namespace InfoSystem +{ + +class EchoNestPlugin : public InfoPlugin +{ + Q_OBJECT + +public: + EchoNestPlugin(QObject *parent); + virtual ~EchoNestPlugin(); + + void getInfo( const QString &caller, const InfoType type, const QVariant &data, InfoCustomDataHash customData ); + +private: + void getSongProfile( const QString &caller, const QVariant &data, InfoCustomDataHash &customData, const QString &item = QString() ); + void getArtistBiography ( const QString &caller, const QVariant &data, InfoCustomDataHash &customData ); + void getArtistFamiliarity( const QString &caller, const QVariant &data, InfoCustomDataHash &customData ); + void getArtistHotttnesss( const QString &caller, const QVariant &data, InfoCustomDataHash &customData ); + void getArtistTerms( const QString &caller, const QVariant &data, InfoCustomDataHash &customData ); + void getMiscTopTerms( const QString &caller, const QVariant &data, InfoCustomDataHash &customData ); + + bool isValidArtistData( const QString &caller, const QVariant& data, InfoCustomDataHash& customData ); + bool isValidTrackData( const QString &caller, const QVariant& data, InfoCustomDataHash& customData ); + Echonest::Artist artistFromReply( QNetworkReply* ); + +private slots: + void getArtistBiographySlot(); + void getArtistFamiliaritySlot(); + void getArtistHotttnesssSlot(); + void getArtistTermsSlot(); + void getMiscTopSlot(); + +private: + QHash< QNetworkReply*, InfoCustomDataHash > m_replyMap; + QHash< QNetworkReply*, QString > m_callerMap; +}; + +} + +} + +#endif // ECHONESTPLUGIN_H diff --git a/src/infosystem/infoplugins/musixmatchplugin.cpp b/src/infosystem/infoplugins/musixmatchplugin.cpp new file mode 100644 index 000000000..85757ff7e --- /dev/null +++ b/src/infosystem/infoplugins/musixmatchplugin.cpp @@ -0,0 +1,134 @@ +#include "tomahawk/infosystem.h" +#include "tomahawk/tomahawkapp.h" +#include "musixmatchplugin.h" +#include + +using namespace Tomahawk::InfoSystem; + +// for internal neatness + +MusixMatchPlugin::MusixMatchPlugin(QObject *parent) + : InfoPlugin(parent) + , m_apiKey("61be4ea5aea7dd942d52b2f1311dd9fe") +{ + qDebug() << Q_FUNC_INFO; + QSet< InfoType > supportedTypes; + supportedTypes << Tomahawk::InfoSystem::InfoTrackLyrics; + qobject_cast< InfoSystem* >(parent)->registerInfoTypes(this, supportedTypes); +} + +MusixMatchPlugin::~MusixMatchPlugin() +{ + qDebug() << Q_FUNC_INFO; +} + +void MusixMatchPlugin::getInfo(const QString &caller, const InfoType type, const QVariant& data, Tomahawk::InfoSystem::InfoCustomDataHash customData) +{ + qDebug() << Q_FUNC_INFO; + if( !isValidTrackData(caller, data, customData) || !data.canConvert()) + return; + Tomahawk::InfoSystem::MusixMatchHash hash = data.value(); + QString artist = hash["artistName"]; + QString track = hash["trackName"]; + if( artist.isEmpty() || track.isEmpty() ) + { + emit info(caller, Tomahawk::InfoSystem::InfoTrackLyrics, data, QVariant(), customData); + emit finished(caller, Tomahawk::InfoSystem::InfoTrackLyrics); + return; + } + qDebug() << "artist is " << artist << ", track is " << track; + QString requestString("http://api.musixmatch.com/ws/1.1/track.search?format=xml&page_size=1&f_has_lyrics=1"); + QUrl url(requestString); + url.addQueryItem("apikey", m_apiKey); + url.addQueryItem("q_artist", artist); + url.addQueryItem("q_track", track); + QNetworkReply* reply = TomahawkApp::instance()->nam()->get(QNetworkRequest(url)); + reply->setProperty("customData", QVariant::fromValue(customData)); + reply->setProperty("origData", data); + reply->setProperty("caller", caller); + + connect(reply, SIGNAL(finished()), SLOT(trackSearchSlot())); +} + +bool MusixMatchPlugin::isValidTrackData(const QString &caller, const QVariant& data, Tomahawk::InfoSystem::InfoCustomDataHash &customData) +{ + qDebug() << Q_FUNC_INFO; + if (data.isNull() || !data.isValid() || !data.canConvert()) + { + emit info(caller, Tomahawk::InfoSystem::InfoTrackLyrics, data, QVariant(), customData); + emit finished(caller, Tomahawk::InfoSystem::InfoTrackLyrics); + qDebug() << "MusixMatchPlugin::isValidTrackData: Data null, invalid, or can't convert"; + return false; + } + MusixMatchHash hash = data.value(); + if (hash["trackName"].isEmpty() ) + { + emit info(caller, Tomahawk::InfoSystem::InfoTrackLyrics, data, QVariant(), customData); + emit finished(caller, Tomahawk::InfoSystem::InfoTrackLyrics); + qDebug() << "MusixMatchPlugin::isValidTrackData: Track name is empty"; + return false; + } + if (hash["artistName"].isEmpty() ) + { + emit info(caller, Tomahawk::InfoSystem::InfoTrackLyrics, data, QVariant(), customData); + emit finished(caller, Tomahawk::InfoSystem::InfoTrackLyrics); + qDebug() << "MusixMatchPlugin::isValidTrackData: No artist name found"; + return false; + } + return true; +} + +void MusixMatchPlugin::trackSearchSlot() +{ + qDebug() << Q_FUNC_INFO; + QNetworkReply* oldReply = qobject_cast( sender() ); + if (!oldReply) + { + emit info(QString(), Tomahawk::InfoSystem::InfoTrackLyrics, QVariant(), QVariant(), Tomahawk::InfoSystem::InfoCustomDataHash()); + return; + } + QDomDocument doc; + doc.setContent(oldReply->readAll()); + qDebug() << doc.toString(); + QDomNodeList domNodeList = doc.elementsByTagName("track_id"); + if (domNodeList.isEmpty()) + { + emit info(oldReply->property("caller").toString(), Tomahawk::InfoSystem::InfoTrackLyrics, oldReply->property("origData"), QVariant(), oldReply->property("customData").value()); + emit finished(oldReply->property("caller").toString(), Tomahawk::InfoSystem::InfoTrackLyrics); + return; + } + QString track_id = domNodeList.at(0).toElement().text(); + QString requestString("http://api.musixmatch.com/ws/1.1/track.lyrics.get?track_id=%1&format=xml&apikey=%2"); + QUrl url(requestString); + url.addQueryItem("apikey", m_apiKey); + url.addQueryItem("track_id", track_id); + QNetworkReply* newReply = TomahawkApp::instance()->nam()->get(QNetworkRequest(url)); + newReply->setProperty("origData", oldReply->property("origData")); + newReply->setProperty("customData", oldReply->property("customData")); + newReply->setProperty("caller", oldReply->property("caller")); + connect(newReply, SIGNAL(finished()), SLOT(trackLyricsSlot())); +} + +void MusixMatchPlugin::trackLyricsSlot() +{ + qDebug() << Q_FUNC_INFO; + QNetworkReply* reply = qobject_cast( sender() ); + if (!reply) + { + emit info(QString(), Tomahawk::InfoSystem::InfoTrackLyrics, QVariant(), QVariant(), Tomahawk::InfoSystem::InfoCustomDataHash()); + return; + } + QDomDocument doc; + doc.setContent(reply->readAll()); + QDomNodeList domNodeList = doc.elementsByTagName("lyrics_body"); + if (domNodeList.isEmpty()) + { + emit info(reply->property("caller").toString(), Tomahawk::InfoSystem::InfoTrackLyrics, reply->property("origData"), QVariant(), reply->property("customData").value()); + emit finished(reply->property("caller").toString(), Tomahawk::InfoSystem::InfoTrackLyrics); + return; + } + QString lyrics = domNodeList.at(0).toElement().text(); + qDebug() << "Emitting lyrics: " << lyrics; + emit info(reply->property("caller").toString(), Tomahawk::InfoSystem::InfoTrackLyrics, reply->property("origData"), QVariant(lyrics), reply->property("customData").value()); + emit finished(reply->property("caller").toString(), Tomahawk::InfoSystem::InfoTrackLyrics); +} \ No newline at end of file diff --git a/src/infosystem/infoplugins/musixmatchplugin.h b/src/infosystem/infoplugins/musixmatchplugin.h new file mode 100644 index 000000000..63301fa87 --- /dev/null +++ b/src/infosystem/infoplugins/musixmatchplugin.h @@ -0,0 +1,38 @@ +#ifndef MUSIXMATCHPLUGIN_H +#define MUSIXMATCHPLUGIN_H +#include "tomahawk/infosystem.h" + +class QNetworkReply; + +namespace Tomahawk +{ + +namespace InfoSystem +{ + +class MusixMatchPlugin : public InfoPlugin +{ + Q_OBJECT + +public: + MusixMatchPlugin(QObject *parent); + virtual ~MusixMatchPlugin(); + + void getInfo(const QString &caller, const InfoType type, const QVariant &data, InfoCustomDataHash customData); + +private: + bool isValidTrackData( const QString &caller, const QVariant& data, InfoCustomDataHash &customData ); + +public slots: + void trackSearchSlot(); + void trackLyricsSlot(); + +private: + QString m_apiKey; +}; + +} + +} + +#endif // MUSIXMATCHPLUGIN_H diff --git a/src/infosystem/infosystem.cpp b/src/infosystem/infosystem.cpp new file mode 100644 index 000000000..66b107d5e --- /dev/null +++ b/src/infosystem/infosystem.cpp @@ -0,0 +1,96 @@ +#include "tomahawk/infosystem.h" +#include "infoplugins/echonestplugin.h" +#include "infoplugins/musixmatchplugin.h" + +using namespace Tomahawk::InfoSystem; + +InfoSystem::InfoSystem(QObject *parent) + : QObject( parent ) +{ + qDebug() << Q_FUNC_INFO; + qRegisterMetaType > >("Tomahawk::InfoSystem::InfoGenericMap"); + qRegisterMetaType >("Tomahawk::InfoSystem::InfoCustomDataHash"); + qRegisterMetaType >("Tomahawk::InfoSystem::MusixMatchHash"); + InfoPluginPtr enptr(new EchoNestPlugin(this)); + m_plugins.append(enptr); + InfoPluginPtr mmptr(new MusixMatchPlugin(this)); + m_plugins.append(mmptr); +} + +void InfoSystem::registerInfoTypes(const InfoPluginPtr &plugin, const QSet< InfoType >& types) +{ + qDebug() << Q_FUNC_INFO; + Q_FOREACH(InfoType type, types) + m_infoMap[type].append(plugin); +} + +QLinkedList< InfoPluginPtr > InfoSystem::determineOrderedMatches(const InfoType type) const +{ + //Dummy function for now that returns the various items in the QSet; at some point this will + //probably need to support ordering based on the data source + QLinkedList< InfoPluginPtr > providers; + Q_FOREACH(InfoPluginPtr ptr, m_infoMap[type]) + providers << ptr; + return providers; +} + +void InfoSystem::getInfo(const QString &caller, const InfoType type, const QVariant& data, InfoCustomDataHash customData) +{ + qDebug() << Q_FUNC_INFO; + QLinkedList< InfoPluginPtr > providers = determineOrderedMatches(type); + if (providers.isEmpty()) + { + emit info(QString(), Tomahawk::InfoSystem::InfoNoInfo, QVariant(), QVariant(), customData); + return; + } + + InfoPluginPtr ptr = providers.first(); + if (!ptr) + { + emit info(QString(), Tomahawk::InfoSystem::InfoNoInfo, QVariant(), QVariant(), customData); + return; + } + + m_dataTracker[caller][type] = m_dataTracker[caller][type] + 1; + qDebug() << "current count in dataTracker for type" << type << "is" << m_dataTracker[caller][type]; + connect(ptr.data(), SIGNAL(info(QString, Tomahawk::InfoSystem::InfoType, QVariant, QVariant, Tomahawk::InfoSystem::InfoCustomDataHash)), + this, SLOT(infoSlot(QString, Tomahawk::InfoSystem::InfoType, QVariant, QVariant, Tomahawk::InfoSystem::InfoCustomDataHash)), Qt::UniqueConnection); + connect(ptr.data(), SIGNAL(finished(QString, Tomahawk::InfoSystem::InfoType)), + this, SLOT(finishedSlot(QString, Tomahawk::InfoSystem::InfoType)), Qt::UniqueConnection); + ptr.data()->getInfo(caller, type, data, customData); +} + +void InfoSystem::getInfo(const QString &caller, const InfoMap &input, InfoCustomDataHash customData) +{ + Q_FOREACH( InfoType type, input.keys() ) + getInfo(caller, type, input[type], customData); +} + +void InfoSystem::infoSlot(QString target, Tomahawk::InfoSystem::InfoType type, QVariant input, QVariant output, Tomahawk::InfoSystem::InfoCustomDataHash customData) +{ + qDebug() << Q_FUNC_INFO; + qDebug() << "current count in dataTracker is " << m_dataTracker[target][type]; + if (m_dataTracker[target][type] == 0) + { + qDebug() << "Caller was not waiting for that type of data!"; + return; + } + emit info(target, type, input, output, customData); +} + +void InfoSystem::finishedSlot(QString target, Tomahawk::InfoSystem::InfoType type) +{ + qDebug() << Q_FUNC_INFO; + m_dataTracker[target][type] = m_dataTracker[target][type] - 1; + qDebug() << "current count in dataTracker is " << m_dataTracker[target][type]; + Q_FOREACH(Tomahawk::InfoSystem::InfoType testtype, m_dataTracker[target].keys()) + { + if (m_dataTracker[target][testtype] != 0) + { + qDebug() << "found outstanding request of type" << testtype; + return; + } + } + qDebug() << "emitting finished with target" << target; + emit finished(target); +} \ No newline at end of file diff --git a/src/jabber/jabber.h b/src/jabber/jabber.h index 76b3a6913..1fa66e172 100644 --- a/src/jabber/jabber.h +++ b/src/jabber/jabber.h @@ -11,11 +11,8 @@ class Jabber : public QObject Q_OBJECT public: - Jabber(const QString &jid, - const QString password, - const QString server = "", - const int port=-1) - : p( jid, password, server, port ) + Jabber( const QString &jid, const QString password, const QString server = "", const int port=-1 ) + : p( jid, password, server, port ) { } @@ -24,6 +21,11 @@ public: // p.disconnect(); } + void setProxy( QNetworkProxy* proxy ) + { + p.setProxy( proxy ); + } + public slots: void start() diff --git a/src/jabber/jabber_p.cpp b/src/jabber/jabber_p.cpp index 0890dac2c..6d9e1e69d 100644 --- a/src/jabber/jabber_p.cpp +++ b/src/jabber/jabber_p.cpp @@ -2,6 +2,8 @@ #include #include +#include +#include using namespace gloox; using namespace std; @@ -62,6 +64,40 @@ Jabber_p::~Jabber_p() } } + +void +Jabber_p::setProxy( QNetworkProxy* proxy ) +{ + qDebug() << Q_FUNC_INFO; + + if( !m_client || !proxy ) + { + qDebug() << "No client or no proxy"; + return; + } + + QNetworkProxy appProx = QNetworkProxy::applicationProxy(); + QNetworkProxy* prox = proxy->type() == QNetworkProxy::DefaultProxy ? &appProx : proxy; + + if( prox->type() == QNetworkProxy::NoProxy ) + { + qDebug() << "Setting proxy to none"; + m_client->setConnectionImpl( new gloox::ConnectionTCPClient( m_client.data(), m_client->logInstance(), m_client->server(), m_client->port() ) ); + } + else if( proxy->type() == QNetworkProxy::Socks5Proxy ) + { + qDebug() << "Setting proxy to SOCKS5"; + m_client->setConnectionImpl( new gloox::ConnectionSOCKS5Proxy( m_client.data(), + new gloox::ConnectionTCPClient( m_client->logInstance(), proxy->hostName().toStdString(), proxy->port() ), + m_client->logInstance(), m_client->server(), m_client->port() ) ); + } + else + { + qDebug() << "Proxy type unknown"; + } +} + + void Jabber_p::go() { @@ -79,8 +115,10 @@ Jabber_p::go() m_client->setPresence( Presence::Available, 1, "Tomahawk available" ); - // m_client->connect(); - // return; + // m_client->connect(); + // return; + + // Handle proxy if( m_client->connect( false ) ) { @@ -108,7 +146,7 @@ Jabber_p::doJabberRecv() void Jabber_p::disconnect() { - if(m_client) + if ( m_client ) { m_client->disconnect(); } @@ -400,14 +438,21 @@ Jabber_p::handleRosterPresence( const RosterItem& item, const std::string& resou return; // ignore anyone not running tomahawk: - if( jid.full().find( "/tomahawk" ) == string::npos ) + // convert to QString to get proper regex support + QString res( jid.resource().c_str() ); + QRegExp regex( "tomahawk\\d+" ); + if( res != "tomahawk-tomahawk" && !res.contains( regex ) ) { + qDebug() << "not considering resource of " << res; // Disco them to check if they are tomahawk-capable + //qDebug() << "No tomahawk resource, DISCOing... " << jid.full().c_str(); //m_client->disco()->getDiscoInfo( jid, "", this, 0 ); return; } + qDebug() << "handling presence for resource of " << res; + //qDebug() << Q_FUNC_INFO << "jid: " << QString::fromStdString(item.jid()) // << " resource: " << QString::fromStdString(resource) // << " presencetype " << presence; @@ -425,7 +470,7 @@ Jabber_p::handleRosterPresence( const RosterItem& item, const std::string& resou return; } - // "coming online " event + // "coming online" event if( presenceMeansOnline( presence ) && ( !m_peers.contains( fulljid ) || !presenceMeansOnline( m_peers.value( fulljid ) ) @@ -478,6 +523,20 @@ Jabber_p::handleNonrosterPresence( const Presence& presence ) /// END ROSTER STUFF +void +Jabber_p::handleVCard( const JID& jid, const VCard* vcard ) +{ + qDebug() << "VCARD RECEIVED!" << jid.bare().c_str(); +} + + +void +Jabber_p::handleVCardResult( VCardContext context, const JID& jid, StanzaError se ) +{ + qDebug() << "VCARD RESULT RECEIVED!" << jid.bare().c_str(); +} + + /// DISCO STUFF void Jabber_p::handleDiscoInfo( const JID& from, const Disco::Info& info, int context) @@ -525,8 +584,3 @@ bool Jabber_p::presenceMeansOnline( Presence::PresenceType p ) return true; } } - - - - - diff --git a/src/jabber/jabber_p.h b/src/jabber/jabber_p.h index ddf39d29a..fd888b6cf 100644 --- a/src/jabber/jabber_p.h +++ b/src/jabber/jabber_p.h @@ -9,6 +9,7 @@ #include #include #include +#include #include #include @@ -41,6 +42,9 @@ #include #include #include +#include +#include +#include #if defined( WIN32 ) || defined( _WIN32 ) # include @@ -51,6 +55,7 @@ class Jabber_p : public gloox::ConnectionListener, public gloox::RosterListener, public gloox::MessageHandler, + public gloox::VCardHandler, gloox::LogHandler //public gloox::DiscoHandler, { @@ -60,6 +65,8 @@ public: explicit Jabber_p( const QString& jid, const QString& password, const QString& server = "", const int port = -1 ); virtual ~Jabber_p(); + void setProxy( QNetworkProxy* proxy ); + void disconnect(); /// GLOOX IMPLEMENTATION STUFF FOLLOWS @@ -91,6 +98,9 @@ public: virtual void handleNonrosterPresence( const gloox::Presence& presence ); /// END ROSTER STUFF + virtual void handleVCard( const gloox::JID& jid, const gloox::VCard* vcard ); + virtual void handleVCardResult( gloox::VCardHandler::VCardContext context, const gloox::JID& jid, gloox::StanzaError se ); + /// DISCO STUFF virtual void handleDiscoInfo( const gloox::JID& from, const gloox::Disco::Info& info, int context); virtual void handleDiscoItems( const gloox::JID& /*iq*/, const gloox::Disco::Items&, int /*context*/ ); @@ -115,7 +125,6 @@ public slots: void broadcastMsg( const QString &msg ); private slots: - void doJabberRecv(); private: @@ -125,6 +134,7 @@ private: gloox::JID m_jid; QMap m_presences; QMap m_peers; + QSharedPointer m_vcardManager; QTimer m_timer; // for recv() }; diff --git a/src/pipeline.cpp b/src/pipeline.cpp index c78a2d6db..b5a294221 100644 --- a/src/pipeline.cpp +++ b/src/pipeline.cpp @@ -17,6 +17,7 @@ Pipeline::Pipeline( QObject* parent ) } + void Pipeline::databaseReady() { @@ -24,6 +25,7 @@ Pipeline::databaseReady() APP->database()->loadIndex(); } + void Pipeline::indexReady() { qDebug() << Q_FUNC_INFO << "shuting this many pending queries:" << m_queries_pending.size(); @@ -36,12 +38,14 @@ void Pipeline::indexReady() m_queries_pending.clear(); } + void Pipeline::removeResolver( Resolver* r ) { m_resolvers.removeAll( r ); } + void Pipeline::addResolver( Resolver* r, bool sort ) { diff --git a/src/playlist.cpp b/src/playlist.cpp index 57c7c8eb2..892e3191e 100644 --- a/src/playlist.cpp +++ b/src/playlist.cpp @@ -299,8 +299,6 @@ Playlist::setRevision( const QString& rev, m_currentrevision = rev; pr.applied = applied; - - emit revisionLoaded( pr ); } diff --git a/src/playlist/collectionflatmodel.cpp b/src/playlist/collectionflatmodel.cpp index 16361de1f..87a02af83 100644 --- a/src/playlist/collectionflatmodel.cpp +++ b/src/playlist/collectionflatmodel.cpp @@ -12,6 +12,8 @@ CollectionFlatModel::CollectionFlatModel( QObject* parent ) { qDebug() << Q_FUNC_INFO; m_rootItem = new PlItem( 0, this ); + + connect( &APP->sourcelist(), SIGNAL( sourceRemoved( Tomahawk::source_ptr ) ), SLOT( onSourceOffline( Tomahawk::source_ptr ) ) ); } @@ -65,7 +67,7 @@ CollectionFlatModel::removeCollection( const collection_ptr& collection ) disconnect( collection.data(), SIGNAL( tracksFinished( Tomahawk::collection_ptr ) ), this, SLOT( onTracksAddingFinished( Tomahawk::collection_ptr ) ) ); - QList plitems = m_collectionIndex.values( collection ); +// QList plitems = m_collectionIndex.values( collection ); QList< QPair< int, int > > rows; QList< QPair< int, int > > sortrows; QPair< int, int > row; @@ -111,11 +113,15 @@ CollectionFlatModel::removeCollection( const collection_ptr& collection ) qDebug() << "Removing rows:" << row.first << row.second; emit beginRemoveRows( QModelIndex(), row.first, row.second ); + for ( int i = row.second; i >= row.first; i-- ) + { + PlItem* item = itemFromIndex( index( i, 0, QModelIndex() ) ); + delete item; + } emit endRemoveRows(); } - qDeleteAll( plitems ); - m_collectionIndex.remove( collection ); +// m_collectionIndex.remove( collection ); } @@ -148,7 +154,7 @@ CollectionFlatModel::onTracksAdded( const QList& tracks, const collect connect( plitem, SIGNAL( dataChanged() ), SLOT( onDataChanged() ) ); - m_collectionIndex.insertMulti( collection, plitem ); +// m_collectionIndex.insertMulti( collection, plitem ); } m_collectionRows.insertMulti( collection, crows ); @@ -174,3 +180,15 @@ CollectionFlatModel::onDataChanged() // emit itemSizeChanged( p->index ); emit dataChanged( p->index, p->index.sibling( p->index.row(), columnCount() - 1 ) ); } + + +void +CollectionFlatModel::onSourceOffline( const Tomahawk::source_ptr& src ) +{ + qDebug() << Q_FUNC_INFO; + + if ( m_collectionRows.contains( src->collection() ) ) + { + removeCollection( src->collection() ); + } +} diff --git a/src/playlist/collectionflatmodel.h b/src/playlist/collectionflatmodel.h index 02647f34b..01cc60dca 100644 --- a/src/playlist/collectionflatmodel.h +++ b/src/playlist/collectionflatmodel.h @@ -47,8 +47,9 @@ private slots: void onTracksAdded( const QList& tracks, const Tomahawk::collection_ptr& collection ); void onTracksAddingFinished( const Tomahawk::collection_ptr& collection ); + void onSourceOffline( const Tomahawk::source_ptr& src ); + private: - QMap< Tomahawk::collection_ptr, PlItem* > m_collectionIndex; QMap< Tomahawk::collection_ptr, QPair< int, int > > m_collectionRows; }; diff --git a/src/playlist/collectionmodel.cpp b/src/playlist/collectionmodel.cpp index 7bfd392b8..841b8da9c 100644 --- a/src/playlist/collectionmodel.cpp +++ b/src/playlist/collectionmodel.cpp @@ -11,6 +11,8 @@ CollectionModel::CollectionModel( QObject* parent ) : QAbstractItemModel( parent ) { qDebug() << Q_FUNC_INFO; + + connect( &APP->sourcelist(), SIGNAL( sourceRemoved( Tomahawk::source_ptr ) ), SLOT( onSourceOffline( Tomahawk::source_ptr ) ) ); } @@ -242,3 +244,15 @@ CollectionModel::onTracksAddingFinished( const Tomahawk::collection_ptr& /* coll qDebug() << "Finished loading tracks"; emit loadingFinished(); } + + +void +CollectionModel::onSourceOffline( Tomahawk::source_ptr src ) +{ + qDebug() << Q_FUNC_INFO; + + if ( m_collectionIndex.contains( src->collection() ) ) + { + removeCollection( src->collection() ); + } +} diff --git a/src/playlist/collectionmodel.h b/src/playlist/collectionmodel.h index 97ba9b7fb..a7afde6d9 100644 --- a/src/playlist/collectionmodel.h +++ b/src/playlist/collectionmodel.h @@ -63,6 +63,8 @@ private slots: void onTracksAdded( const QList& tracks, const Tomahawk::collection_ptr& collection ); void onTracksAddingFinished( const Tomahawk::collection_ptr& collection ); + void onSourceOffline( Tomahawk::source_ptr src ); + private: QMap< Tomahawk::collection_ptr, PlItem* > m_collectionIndex; }; diff --git a/src/playlist/playlistitemdelegate.cpp b/src/playlist/playlistitemdelegate.cpp index 505c650e1..b21629fbf 100644 --- a/src/playlist/playlistitemdelegate.cpp +++ b/src/playlist/playlistitemdelegate.cpp @@ -38,6 +38,9 @@ void PlaylistItemDelegate::paint( QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index ) const { PlItem* item = m_model->itemFromIndex( m_model->mapToSource( index ) ); + if ( !item ) + return; + if ( item->query()->results().count() ) painter->setOpacity( item->query()->results().at( 0 )->score() ); else diff --git a/src/playlist/playlistmanager.cpp b/src/playlist/playlistmanager.cpp index cb8e1a28a..f46d8f525 100644 --- a/src/playlist/playlistmanager.cpp +++ b/src/playlist/playlistmanager.cpp @@ -21,6 +21,8 @@ PlaylistManager::PlaylistManager( QObject* parent ) , m_currentMode( 0 ) , m_superCollectionVisible( true ) { + m_widget->setMinimumWidth( 620 ); + m_superCollectionViews << new CollectionView(); m_superCollectionViews.first()->setModel( m_superCollectionFlatModel ); m_widget->addWidget( m_superCollectionViews.first() ); diff --git a/src/playlist/playlistmodel.cpp b/src/playlist/playlistmodel.cpp index eef6de35f..4e7321ad5 100644 --- a/src/playlist/playlistmodel.cpp +++ b/src/playlist/playlistmodel.cpp @@ -9,6 +9,7 @@ using namespace Tomahawk; PlaylistModel::PlaylistModel( QObject* parent ) : TrackModel( parent ) + , m_waitForUpdate( false ) { qDebug() << Q_FUNC_INFO; m_rootItem = new PlItem( 0, this ); @@ -51,13 +52,15 @@ PlaylistModel::loadPlaylist( const Tomahawk::playlist_ptr& playlist ) { emit beginRemoveRows( QModelIndex(), 0, rowCount( QModelIndex() ) - 1 ); delete m_rootItem; - m_rootItem = new PlItem( 0, this ); emit endRemoveRows(); + m_rootItem = new PlItem( 0, this ); } m_playlist = playlist; connect( playlist.data(), SIGNAL( revisionLoaded( Tomahawk::PlaylistRevision ) ), SLOT( onRevisionLoaded( Tomahawk::PlaylistRevision ) ) ); + setReadOnly( !m_playlist->author()->isLocal() ); + PlItem* plitem; QList entries = playlist->entries(); int c = rowCount( QModelIndex() ); @@ -68,6 +71,7 @@ PlaylistModel::loadPlaylist( const Tomahawk::playlist_ptr& playlist ) foreach( const plentry_ptr& entry, entries ) { + qDebug() << entry->query()->toString(); plitem = new PlItem( entry, m_rootItem ); plitem->index = createIndex( m_rootItem->children.count() - 1, 0, plitem ); @@ -95,5 +99,124 @@ PlaylistModel::onRevisionLoaded( Tomahawk::PlaylistRevision revision ) { qDebug() << Q_FUNC_INFO; - loadPlaylist( m_playlist ); + if ( m_waitForUpdate ) + { + qDebug() << m_playlist->currentrevision() << revision.revisionguid; + m_waitForUpdate = false; + } + else + loadPlaylist( m_playlist ); +} + + +bool +PlaylistModel::dropMimeData( const QMimeData* data, Qt::DropAction action, int row, int column, const QModelIndex& parent ) +{ + if ( action == Qt::IgnoreAction || isReadOnly() ) + return true; + + if ( !data->hasFormat( "application/tomahawk.query.list" ) && !data->hasFormat( "application/tomahawk.plentry.list" ) ) + return false; + + int beginRow; + if ( row != -1 ) + beginRow = row; + else if ( parent.isValid() ) + beginRow = parent.row(); + else + beginRow = rowCount( QModelIndex() ); + + qDebug() << data->formats(); + + if ( data->hasFormat( "application/tomahawk.query.list" ) ) + { + QByteArray itemData = data->data( "application/tomahawk.query.list" ); + QDataStream stream( &itemData, QIODevice::ReadOnly ); + QList queries; + + while ( !stream.atEnd() ) + { + qlonglong qptr; + stream >> qptr; + + Tomahawk::query_ptr* query = reinterpret_cast(qptr); + if ( query && !query->isNull() ) + { + qDebug() << "Dropped query item:" << query->data()->artist() << "-" << query->data()->track(); + queries << *query; + } + } + + emit beginInsertRows( QModelIndex(), beginRow, beginRow + queries.count() - 1 ); + foreach( const Tomahawk::query_ptr& query, queries ) + { + plentry_ptr e( new PlaylistEntry() ); + e->setGuid( uuid() ); + + if ( query->results().count() ) + e->setDuration( query->results().at( 0 )->duration() ); + else + e->setDuration( 0 ); + + e->setLastmodified( 0 ); + e->setAnnotation( "" ); // FIXME + e->setQuery( query ); + + PlItem* plitem = new PlItem( e, m_rootItem, beginRow ); + plitem->index = createIndex( beginRow++, 0, plitem ); + + connect( plitem, SIGNAL( dataChanged() ), SLOT( onDataChanged() ) ); + } + emit endInsertRows(); + } + + return true; +} + + +void +PlaylistModel::onPlaylistChanged() +{ + qDebug() << Q_FUNC_INFO; + + QList l = playlistEntries(); + foreach( const plentry_ptr& ple, l ) + { + qDebug() << "updateinternal:" << ple->query()->toString(); + } + + QString newrev = uuid(); + m_playlist->createNewRevision( newrev, m_playlist->currentrevision(), l ); +} + + +QList +PlaylistModel::playlistEntries() const +{ + QList l; + for ( int i = 0; i < rowCount( QModelIndex() ); i++ ) + { + QModelIndex idx = index( i, 0, QModelIndex() ); + if ( !idx.isValid() ) + continue; + + PlItem* item = itemFromIndex( idx ); + if ( item ) + l << item->entry(); + } + + return l; +} + + +void +PlaylistModel::removeIndex( const QModelIndex& index ) +{ + if ( isReadOnly() ) + return; + + TrackModel::removeIndex( index ); + + m_waitForUpdate = true; + onPlaylistChanged(); } diff --git a/src/playlist/playlistmodel.h b/src/playlist/playlistmodel.h index 10c133267..cc93a6d6a 100644 --- a/src/playlist/playlistmodel.h +++ b/src/playlist/playlistmodel.h @@ -28,8 +28,12 @@ public: QVariant data( const QModelIndex& index, int role ) const; QVariant headerData( int section, Qt::Orientation orientation, int role ) const; + virtual bool dropMimeData( const QMimeData* data, Qt::DropAction action, int row, int column, const QModelIndex& parent ); + void loadPlaylist( const Tomahawk::playlist_ptr& playlist ); + virtual void removeIndex( const QModelIndex& index ); + signals: void repeatModeChanged( PlaylistInterface::RepeatMode mode ); void shuffleModeChanged( bool enabled ); @@ -43,9 +47,13 @@ private slots: void onDataChanged(); void onRevisionLoaded( Tomahawk::PlaylistRevision revision ); + void onPlaylistChanged(); private: + QList playlistEntries() const; + Tomahawk::playlist_ptr m_playlist; + bool m_waitForUpdate; }; #endif // PLAYLISTMODEL_H diff --git a/src/playlist/playlistview.cpp b/src/playlist/playlistview.cpp index 388bef5f5..d345cc239 100644 --- a/src/playlist/playlistview.cpp +++ b/src/playlist/playlistview.cpp @@ -1,6 +1,7 @@ #include "playlistview.h" #include +#include #include "playlist/playlistproxymodel.h" @@ -11,6 +12,9 @@ PlaylistView::PlaylistView( QWidget* parent ) : TrackView( parent ) { setProxyModel( new PlaylistProxyModel( this ) ); + + setContextMenuPolicy( Qt::CustomContextMenu ); + connect( this, SIGNAL( customContextMenuRequested( const QPoint& ) ), SLOT( onCustomContextMenu( const QPoint& ) ) ); } @@ -18,3 +22,77 @@ PlaylistView::~PlaylistView() { qDebug() << Q_FUNC_INFO; } + + +void +PlaylistView::setupMenus() +{ + m_itemMenu.clear(); + + m_playItemAction = m_itemMenu.addAction( tr( "&Play" ) ); + m_itemMenu.addSeparator(); + m_addItemsToPlaylistAction = m_itemMenu.addAction( tr( "&Add to Playlist" ) ); + m_itemMenu.addSeparator(); + m_deleteItemAction = m_itemMenu.addAction( tr( "&Delete Item" ) ); + + if ( model() ) + m_deleteItemAction->setEnabled( !model()->isReadOnly() ); + + connect( m_playItemAction, SIGNAL( triggered() ), SLOT( playItem() ) ); + connect( m_addItemsToPlaylistAction, SIGNAL( triggered() ), SLOT( addItemsToPlaylist() ) ); + connect( m_deleteItemAction, SIGNAL( triggered() ), SLOT( deleteItem() ) ); +} + + +void +PlaylistView::onCustomContextMenu( const QPoint& pos ) +{ + qDebug() << Q_FUNC_INFO; + setupMenus(); + + QModelIndex idx = indexAt( pos ); + idx = idx.sibling( idx.row(), 0 ); + m_contextMenuIndex = idx; + + if ( !idx.isValid() ) + return; + + m_itemMenu.exec( mapToGlobal( pos ) ); +} + + +void +PlaylistView::keyPressEvent( QKeyEvent* event ) +{ + qDebug() << Q_FUNC_INFO; + QTreeView::keyPressEvent( event ); + + if ( !model() ) + return; + + if ( event->key() == Qt::Key_Delete ) + { + qDebug() << "Removing selected items"; + proxyModel()->removeIndexes( selectedIndexes() ); + } +} + + +void +PlaylistView::playItem() +{ + onItemActivated( m_contextMenuIndex ); +} + + +void +PlaylistView::addItemsToPlaylist() +{ +} + + +void +PlaylistView::deleteItem() +{ + proxyModel()->removeIndex( m_contextMenuIndex ); +} diff --git a/src/playlist/playlistview.h b/src/playlist/playlistview.h index 9ed1138d2..db796564e 100644 --- a/src/playlist/playlistview.h +++ b/src/playlist/playlistview.h @@ -1,6 +1,8 @@ #ifndef PLAYLISTVIEW_H #define PLAYLISTVIEW_H +#include + #include "tomahawk/tomahawkapp.h" #include "trackview.h" @@ -11,6 +13,26 @@ Q_OBJECT public: explicit PlaylistView( QWidget* parent = 0 ); ~PlaylistView(); + +protected: + virtual void keyPressEvent( QKeyEvent* event ); + +private slots: + void onCustomContextMenu( const QPoint& pos ); + + void playItem(); + void addItemsToPlaylist(); + void deleteItem(); + +private: + void setupMenus(); + + QModelIndex m_contextMenuIndex; + + QMenu m_itemMenu; + QAction* m_playItemAction; + QAction* m_addItemsToPlaylistAction; + QAction* m_deleteItemAction; }; #endif // PLAYLISTVIEW_H diff --git a/src/playlist/plitem.cpp b/src/playlist/plitem.cpp index 3f77c1bcb..efaa0eb70 100644 --- a/src/playlist/plitem.cpp +++ b/src/playlist/plitem.cpp @@ -9,12 +9,16 @@ using namespace Tomahawk; PlItem::~PlItem() { - qDeleteAll( children ); + // Don't use qDeleteAll here! The children will remove themselves + // from the list when they get deleted and the qDeleteAll iterator + // will fail badly! + for ( int i = children.count() - 1; i >= 0; i-- ) + delete children.at( i ); -// Q_ASSERT( parent->children.at( m_parentPos ) == this ); - - if ( parent ) - parent->children.removeAt( m_parentPos ); + if ( parent && index.isValid() ) + { + parent->children.removeAt( index.row() ); + } } @@ -28,7 +32,6 @@ PlItem::PlItem( PlItem* parent, QAbstractItemModel* model ) if ( parent ) { parent->children.append( this ); - m_parentPos = parent->children.count() - 1; } } @@ -45,38 +48,42 @@ PlItem::PlItem( const QString& caption, PlItem* parent ) if ( parent ) { parent->children.append( this ); - m_parentPos = parent->children.count() - 1; } } -PlItem::PlItem( const Tomahawk::query_ptr& query, PlItem* parent ) +PlItem::PlItem( const Tomahawk::query_ptr& query, PlItem* parent, int row ) : QObject( parent ) { - setupItem( query, parent ); + setupItem( query, parent, row ); } -PlItem::PlItem( const Tomahawk::plentry_ptr& entry, PlItem* parent ) +PlItem::PlItem( const Tomahawk::plentry_ptr& entry, PlItem* parent, int row ) : QObject( parent ) , m_entry( entry ) { - setupItem( entry->query(), parent ); + setupItem( entry->query(), parent, row ); } void -PlItem::setupItem( const Tomahawk::query_ptr& query, PlItem* parent ) +PlItem::setupItem( const Tomahawk::query_ptr& query, PlItem* parent, int row ) { this->parent = parent; if ( parent ) { - parent->children.append( this ); - m_parentPos = parent->children.count() - 1; - this->model = parent->model; + if ( row < 0 ) + { + parent->children.append( this ); + row = parent->children.count() - 1; + } + else + { + parent->children.insert( row, this ); + } - connect( model, SIGNAL( rowsRemoved( QModelIndex, int, int ) ), - SLOT( onModelRowsRemoved( QModelIndex, int, int ) ) ); + this->model = parent->model; } m_isPlaying = false; @@ -85,8 +92,11 @@ PlItem::setupItem( const Tomahawk::query_ptr& query, PlItem* parent ) if ( query->numResults() ) onResultsAdded( query->results() ); - connect( query.data(), SIGNAL( resultsAdded( const QList& ) ), - SLOT( onResultsAdded( const QList& ) ), Qt::DirectConnection ); + connect( query.data(), SIGNAL( resultsAdded( QList ) ), + SLOT( onResultsAdded( QList ) ), Qt::DirectConnection ); + + connect( query.data(), SIGNAL( resultsRemoved( Tomahawk::result_ptr ) ), + SLOT( onResultsRemoved( Tomahawk::result_ptr ) ), Qt::DirectConnection ); } @@ -99,18 +109,7 @@ PlItem::onResultsAdded( const QList& results ) void -PlItem::onModelRowsRemoved( const QModelIndex& index, int start, int end ) +PlItem::onResultsRemoved( const Tomahawk::result_ptr& result ) { - if ( !toberemoved && this->parent->index == index ) - { - if ( ( start <= m_parentPos ) && ( m_parentPos <= end ) ) - toberemoved = true; - else - { - if ( start < m_parentPos ) - { - m_parentPos -= ( end - start ) + 1; - } - } - } + emit dataChanged(); } diff --git a/src/playlist/plitem.h b/src/playlist/plitem.h index 222077835..9c06999ac 100644 --- a/src/playlist/plitem.h +++ b/src/playlist/plitem.h @@ -2,7 +2,7 @@ #define PLITEM_H #include -#include +#include #include #include "tomahawk/query.h" @@ -17,8 +17,8 @@ public: explicit PlItem( PlItem* parent = 0, QAbstractItemModel* model = 0 ); explicit PlItem( const QString& caption, PlItem* parent = 0 ); - explicit PlItem( const Tomahawk::query_ptr& query, PlItem* parent = 0 ); - explicit PlItem( const Tomahawk::plentry_ptr& entry, PlItem* parent = 0 ); + explicit PlItem( const Tomahawk::query_ptr& query, PlItem* parent = 0, int row = -1 ); + explicit PlItem( const Tomahawk::plentry_ptr& entry, PlItem* parent = 0, int row = -1 ); const Tomahawk::plentry_ptr& entry() const { return m_entry; }; const Tomahawk::query_ptr& query() const { return m_query; }; @@ -31,7 +31,7 @@ public: QHash hash; QString caption; int childCount; - QModelIndex index; + QPersistentModelIndex index; QAbstractItemModel* model; bool toberemoved; @@ -40,15 +40,14 @@ signals: private slots: void onResultsAdded( const QList& result ); - void onModelRowsRemoved( const QModelIndex& index, int start, int end ); + void onResultsRemoved( const Tomahawk::result_ptr& result ); private: - void setupItem( const Tomahawk::query_ptr& query, PlItem* parent ); + void setupItem( const Tomahawk::query_ptr& query, PlItem* parent, int row = -1 ); Tomahawk::plentry_ptr m_entry; Tomahawk::query_ptr m_query; bool m_isPlaying; - int m_parentPos; }; #endif // PLITEM_H diff --git a/src/playlist/trackmodel.cpp b/src/playlist/trackmodel.cpp index 617bd30c8..67a5a32ea 100644 --- a/src/playlist/trackmodel.cpp +++ b/src/playlist/trackmodel.cpp @@ -11,6 +11,7 @@ using namespace Tomahawk; TrackModel::TrackModel( QObject* parent ) : QAbstractItemModel( parent ) + , m_readOnly( true ) { qDebug() << Q_FUNC_INFO; } @@ -260,3 +261,31 @@ TrackModel::mimeData( const QModelIndexList &indexes ) const return mimeData; } + + +void +TrackModel::removeIndex( const QModelIndex& index ) +{ + qDebug() << Q_FUNC_INFO; + + if ( index.column() > 0 ) + return; + + PlItem* item = itemFromIndex( index ); + if ( item ) + { + emit beginRemoveRows( index.parent(), index.row(), index.row() ); + delete item; + emit endRemoveRows(); + } +} + + +void +TrackModel::removeIndexes( const QList& indexes ) +{ + foreach( const QModelIndex& idx, indexes ) + { + removeIndex( idx ); + } +} diff --git a/src/playlist/trackmodel.h b/src/playlist/trackmodel.h index d090735bb..dce349caa 100644 --- a/src/playlist/trackmodel.h +++ b/src/playlist/trackmodel.h @@ -18,6 +18,8 @@ public: virtual QModelIndex index( int row, int column, const QModelIndex& parent ) const; virtual QModelIndex parent( const QModelIndex& child ) const; + virtual bool isReadOnly() const { return m_readOnly; } + virtual int trackCount() const { return rowCount( QModelIndex() ); } virtual int rowCount( const QModelIndex& parent ) const; @@ -26,6 +28,9 @@ public: virtual QVariant data( const QModelIndex& index, int role = Qt::DisplayRole ) const; virtual QVariant headerData( int section, Qt::Orientation orientation, int role ) const; + virtual void removeIndex( const QModelIndex& index ); + virtual void removeIndexes( const QList& indexes ); + virtual PlItem* previousItem() { return 0; } virtual PlItem* nextItem() { return 0; } virtual PlItem* siblingItem( int direction ) { return 0; } @@ -49,8 +54,12 @@ public slots: virtual void setRepeatMode( PlaylistInterface::RepeatMode mode ) {} virtual void setShuffled( bool shuffled ) {} +protected: + virtual void setReadOnly( bool b ) { m_readOnly = b; } + private: QPersistentModelIndex m_currentIndex; + bool m_readOnly; }; #endif // TRACKMODEL_H diff --git a/src/playlist/trackproxymodel.cpp b/src/playlist/trackproxymodel.cpp index 2ece99d0f..9edd1b99f 100644 --- a/src/playlist/trackproxymodel.cpp +++ b/src/playlist/trackproxymodel.cpp @@ -168,3 +168,30 @@ TrackProxyModel::filterAcceptsRow( int sourceRow, const QModelIndex& sourceParen return found; } + + +void +TrackProxyModel::removeIndex( const QModelIndex& index ) +{ + qDebug() << Q_FUNC_INFO; + + if ( !sourceModel() ) + return; + if ( index.column() > 0 ) + return; + + sourceModel()->removeIndex( mapToSource( index ) ); +} + + +void +TrackProxyModel::removeIndexes( const QList& indexes ) +{ + if ( !sourceModel() ) + return; + + foreach( const QModelIndex& idx, indexes ) + { + removeIndex( idx ); + } +} diff --git a/src/playlist/trackproxymodel.h b/src/playlist/trackproxymodel.h index dc307b28a..70143c016 100644 --- a/src/playlist/trackproxymodel.h +++ b/src/playlist/trackproxymodel.h @@ -21,6 +21,9 @@ public: virtual int trackCount() const { return rowCount( QModelIndex() ); } + virtual void removeIndex( const QModelIndex& index ); + virtual void removeIndexes( const QList& indexes ); + virtual PlItem* previousItem(); virtual PlItem* nextItem(); virtual PlItem* siblingItem( int itemsAway ); diff --git a/src/playlist/trackview.cpp b/src/playlist/trackview.cpp index 08517137a..4b2d93b33 100644 --- a/src/playlist/trackview.cpp +++ b/src/playlist/trackview.cpp @@ -29,7 +29,7 @@ TrackView::TrackView( QWidget* parent ) setDragEnabled( true ); setDropIndicatorShown( false ); setDragDropMode( QAbstractItemView::InternalMove ); - setDragDropOverwriteMode ( false ); + setDragDropOverwriteMode( false ); setAllColumnsShowFocus( true ); header()->setMinimumSectionSize( 60 ); @@ -186,39 +186,6 @@ TrackView::resizeColumns() } -void -TrackView::keyPressEvent( QKeyEvent* event ) -{ -// qDebug() << Q_FUNC_INFO; - QTreeView::keyPressEvent( event ); - - if ( !m_model ) - return; - - if ( event->key() == Qt::Key_Delete ) - { -/* if ( m_model->isPlaylistBacked() && selectedIndexes().count() ) - { - qDebug() << "Removing selected items"; - QList items; - - QModelIndexList sidxs = selectedIndexes(); - foreach( const QModelIndex& idx, sidxs ) - { - if ( idx.column() > 0 ) - continue; - - PlaylistItem* item = PlaylistModel::indexToPlaylistItem( idx ); - if ( item ) - items << item; - } - - m_model->removeItems( items ); - }*/ - } -} - - void TrackView::dragEnterEvent( QDragEnterEvent* event ) { @@ -241,6 +208,12 @@ TrackView::dragMoveEvent( QDragMoveEvent* event ) { QTreeView::dragMoveEvent( event ); + if ( model()->isReadOnly() ) + { + event->ignore(); + return; + } + if ( event->mimeData()->hasFormat( "application/tomahawk.query.list" ) || event->mimeData()->hasFormat( "application/tomahawk.plentry.list" ) ) { setDirtyRegion( m_dropRect ); @@ -329,24 +302,25 @@ TrackView::onFilterChanged( const QString& ) void TrackView::startDrag( Qt::DropActions supportedActions ) { + QList pindexes; QModelIndexList indexes = selectedIndexes(); - qDebug() << "Dragging" << indexes.count() << "indexes"; for( int i = indexes.count() - 1 ; i >= 0; --i ) { - if( !( m_proxyModel->flags( indexes.at( i ) ) & Qt::ItemIsDragEnabled ) ) + if ( !( m_proxyModel->flags( indexes.at( i ) ) & Qt::ItemIsDragEnabled ) ) indexes.removeAt( i ); + else + pindexes << indexes.at( i ); } - if( indexes.count() == 0 ) + if ( indexes.count() == 0 ) return; qDebug() << "Dragging" << indexes.count() << "indexes"; - - QMimeData *data = m_proxyModel->mimeData( indexes ); + QMimeData* data = m_proxyModel->mimeData( indexes ); if ( !data ) return; - QDrag *drag = new QDrag( this ); + QDrag* drag = new QDrag( this ); drag->setMimeData( data ); const QPixmap p = createDragPixmap( indexes.count() ); drag->setPixmap( p ); @@ -355,7 +329,15 @@ TrackView::startDrag( Qt::DropActions supportedActions ) // NOTE: if we support moving items in the model // in the future, if exec() returns Qt::MoveAction // we need to clean up ourselves. - drag->exec( supportedActions, Qt::CopyAction ); + Qt::DropAction action = drag->exec( supportedActions, Qt::CopyAction ); + + if ( action == Qt::MoveAction ) + { + foreach ( const QPersistentModelIndex& idx, pindexes ) + { + m_proxyModel->removeIndex( idx ); + } + } } @@ -396,22 +378,22 @@ TrackView::createDragPixmap( int itemCount ) const QPixmap dragPixmap( xCount * size + xCount - 1, yCount * size + yCount - 1 ); dragPixmap.fill( Qt::transparent ); - QPainter painter(&dragPixmap); + QPainter painter( &dragPixmap ); painter.setRenderHint( QPainter::Antialiasing ); int x = 0; int y = 0; for( int i = 0; i < itemCount; ++i ) { - const QPixmap pixmap = QPixmap( QString( RESPATH "icons/audio-x-generic-%2.png" ).arg( size ) ); - painter.drawPixmap(x, y, pixmap); + const QPixmap pixmap = QPixmap( QString( ":/data/icons/audio-x-generic-%2.png" ).arg( size ) ); + painter.drawPixmap( x, y, pixmap ); x += size + 1; - if (x >= dragPixmap.width()) + if ( x >= dragPixmap.width() ) { x = 0; y += size + 1; } - if (y >= dragPixmap.height()) + if ( y >= dragPixmap.height() ) { break; } diff --git a/src/playlist/trackview.h b/src/playlist/trackview.h index 010f011dd..e376ff6f6 100644 --- a/src/playlist/trackview.h +++ b/src/playlist/trackview.h @@ -26,9 +26,11 @@ public: void setModel( TrackModel* model ); +public slots: + void onItemActivated( const QModelIndex& index ); + protected: virtual void resizeEvent( QResizeEvent* event ); - virtual void keyPressEvent( QKeyEvent* event ); virtual void startDrag( Qt::DropActions supportedActions ); virtual void dragEnterEvent( QDragEnterEvent* event ); @@ -39,7 +41,6 @@ protected: void paintEvent( QPaintEvent* event ); private slots: - void onItemActivated( const QModelIndex& index ); void onItemResized( const QModelIndex& index ); void resizeColumns(); diff --git a/src/proxydialog.ui b/src/proxydialog.ui new file mode 100644 index 000000000..12806644b --- /dev/null +++ b/src/proxydialog.ui @@ -0,0 +1,174 @@ + + + ProxyDialog + + + Qt::WindowModal + + + + 0 + 0 + 400 + 200 + + + + Proxy Settings + + + + + 30 + 160 + 341 + 32 + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + 10 + 10 + 381 + 141 + + + + + 16 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + + + + + Host + + + + + + + Port + + + + + + + + + + + + + User + + + + + + + Password + + + + + + + QLineEdit::Password + + + + + + + Type + + + + + + + + + + buttonBox + accepted() + ProxyDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + ProxyDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/src/query.cpp b/src/query.cpp index 409db7a1b..b5de0088e 100644 --- a/src/query.cpp +++ b/src/query.cpp @@ -50,31 +50,34 @@ Query::resultUnavailable() Result* result = (Result*) sender(); Q_ASSERT( result ); - for(int i = 0; i < m_results.length(); ++i ) + for ( int i = 0; i < m_results.length(); ++i ) { - if( m_results.value( i ).data() == result ) + if ( m_results.value( i ).data() == result ) { + result_ptr r = m_results.value( i ); m_results.removeAt( i ); + + emit resultsRemoved( r ); break; } } + + if ( m_results.isEmpty() ) // FIXME proper score checking + emit solvedStateChanged( false ); } void -Query::removeResult( Tomahawk::result_ptr result ) +Query::removeResult( const Tomahawk::result_ptr& result ) { - bool becameUnsolved = false; { QMutexLocker lock( &m_mut ); m_results.removeAll( result ); - - if ( m_results.isEmpty() ) // FIXME proper score checking - becameUnsolved = true; - } emit resultsRemoved( result ); - if( becameUnsolved ) emit solvedStateChanged( false ); + + if ( m_results.isEmpty() ) // FIXME proper score checking + emit solvedStateChanged( false ); } diff --git a/src/settingsdialog.cpp b/src/settingsdialog.cpp index e8cfde839..f3e18b5f0 100644 --- a/src/settingsdialog.cpp +++ b/src/settingsdialog.cpp @@ -1,5 +1,6 @@ #include "settingsdialog.h" #include "ui_settingsdialog.h" +#include "ui_proxydialog.h" #include #include @@ -17,7 +18,8 @@ #include -static QString md5( const QByteArray& src ) +static QString +md5( const QByteArray& src ) { QByteArray const digest = QCryptographicHash::hash( src, QCryptographicHash::Md5 ); return QString::fromLatin1( digest.toHex() ).rightJustified( 32, '0' ); @@ -27,6 +29,7 @@ static QString md5( const QByteArray& src ) SettingsDialog::SettingsDialog( QWidget *parent ) : QDialog( parent ) , ui( new Ui::SettingsDialog ) + , m_proxySettings( this ) , m_rejected( false ) , m_testLastFmQuery( 0 ) { @@ -39,9 +42,10 @@ SettingsDialog::SettingsDialog( QWidget *parent ) // JABBER ui->checkBoxJabberAutoConnect->setChecked( s->jabberAutoConnect() ); ui->jabberUsername->setText( s->jabberUsername() ); - ui->jabberPassword->setText(s->jabberPassword() ); - ui->jabberServer->setText( s->jabberServer() ); + ui->jabberPassword->setText( s->jabberPassword() ); + ui->jabberServer->setText( s->jabberServer() ); ui->jabberPort->setValue( s->jabberPort() ); + ui->proxyButton->setVisible( false ); if ( ui->jabberPort->text().toInt() != 5222 || !ui->jabberServer->text().isEmpty() ) { @@ -67,6 +71,7 @@ SettingsDialog::SettingsDialog( QWidget *parent ) connect( ui->pushButtonTestLastfmLogin, SIGNAL( clicked( bool) ), this, SLOT( testLastFmLogin() ) ); connect( ui->buttonBrowse, SIGNAL( clicked() ), SLOT( showPathSelector() ) ); + connect( ui->proxyButton, SIGNAL( clicked() ), SLOT( showProxySettings() ) ); connect( this, SIGNAL( rejected() ), SLOT( onRejected() ) ); } @@ -117,7 +122,6 @@ SettingsDialog::~SettingsDialog() { APP->reconnectJabber(); } - } else qDebug() << "Settings dialog cancelled, NOT saving prefs."; @@ -126,7 +130,8 @@ SettingsDialog::~SettingsDialog() } -void SettingsDialog::showPathSelector() +void +SettingsDialog::showPathSelector() { QString path = QFileDialog::getExistingDirectory( this, @@ -141,7 +146,8 @@ void SettingsDialog::showPathSelector() } -void SettingsDialog::doScan() +void +SettingsDialog::doScan() { // TODO this doesnt really belong here.. QString path = ui->lineEditMusicPath->text(); @@ -155,13 +161,15 @@ void SettingsDialog::doScan() } -void SettingsDialog::onRejected() +void +SettingsDialog::onRejected() { m_rejected = true; } -void SettingsDialog::changeEvent( QEvent *e ) +void +SettingsDialog::changeEvent( QEvent *e ) { QDialog::changeEvent( e ); switch ( e->type() ) @@ -176,7 +184,17 @@ void SettingsDialog::changeEvent( QEvent *e ) } -void SettingsDialog::testLastFmLogin() +void +SettingsDialog::showProxySettings() +{ + m_proxySettings.exec(); + if ( m_proxySettings.result() == QDialog::Accepted ) + m_proxySettings.saveSettings(); +} + + +void +SettingsDialog::testLastFmLogin() { #ifndef NO_LIBLASTFM ui->pushButtonTestLastfmLogin->setEnabled( false ); @@ -196,7 +214,8 @@ void SettingsDialog::testLastFmLogin() } -void SettingsDialog::onLastFmFinished() +void +SettingsDialog::onLastFmFinished() { #ifndef NO_LIBLASTFM lastfm::XmlQuery lfm = lastfm::XmlQuery( m_testLastFmQuery->readAll() ); @@ -233,3 +252,61 @@ void SettingsDialog::onLastFmFinished() #endif } + +ProxyDialog::ProxyDialog( QWidget *parent ) + : QDialog( parent ) + , ui( new Ui::ProxyDialog ) +{ + ui->setupUi( this ); + + // ugly, I know, but... + QHash enumMap; + int i = 0; + ui->typeBox->insertItem( i, "No Proxy", QNetworkProxy::NoProxy ); + enumMap[QNetworkProxy::NoProxy] = i++; + ui->typeBox->insertItem( i, "SOCKS 5", QNetworkProxy::Socks5Proxy ); + enumMap[QNetworkProxy::Socks5Proxy] = i++; + + TomahawkSettings* s = TomahawkApp::instance()->settings(); + + ui->typeBox->setCurrentIndex( enumMap[s->proxyType()] ); + ui->hostLineEdit->setText( s->proxyHost() ); + ui->portLineEdit->setText( QString::number( s->proxyPort() ) ); + ui->userLineEdit->setText( s->proxyUsername() ); + ui->passwordLineEdit->setText( s->proxyPassword() ); +} + + +void +ProxyDialog::saveSettings() +{ + qDebug() << Q_FUNC_INFO; + + //First set settings + TomahawkSettings* s = TomahawkApp::instance()->settings(); + s->setProxyHost( ui->hostLineEdit->text() ); + + bool ok; + qulonglong port = ui->portLineEdit->text().toULongLong( &ok ); + if( ok ) + s->setProxyPort( port ); + + s->setProxyUsername( ui->userLineEdit->text() ); + s->setProxyPassword( ui->passwordLineEdit->text() ); + s->setProxyType( ui->typeBox->itemData( ui->typeBox->currentIndex() ).toInt() ); + + // Now, if a proxy is defined, set QNAM + if( s->proxyType() == QNetworkProxy::NoProxy || s->proxyHost().isEmpty() ) + return; + + QNetworkProxy proxy( static_cast(s->proxyType()), s->proxyHost(), s->proxyPort(), s->proxyUsername(), s->proxyPassword() ); + QNetworkAccessManager* nam = TomahawkApp::instance()->nam(); + nam->setProxy( proxy ); + QNetworkProxy* globalProxy = TomahawkApp::instance()->proxy(); + QNetworkProxy* oldProxy = globalProxy; + globalProxy = new QNetworkProxy( proxy ); + if( oldProxy ) + delete oldProxy; + + QNetworkProxy::setApplicationProxy( proxy ); +} diff --git a/src/settingsdialog.h b/src/settingsdialog.h index 57a031110..3d71d82c5 100644 --- a/src/settingsdialog.h +++ b/src/settingsdialog.h @@ -8,8 +8,23 @@ class QNetworkReply; namespace Ui { class SettingsDialog; + class ProxyDialog; } +class ProxyDialog : public QDialog +{ +Q_OBJECT + +public: + explicit ProxyDialog( QWidget *parent = 0 ); + ~ProxyDialog() {}; + + void saveSettings(); + +private: + Ui::ProxyDialog *ui; +}; + class SettingsDialog : public QDialog { Q_OBJECT @@ -31,12 +46,15 @@ private slots: void showPathSelector(); void doScan(); + void showProxySettings(); + void testLastFmLogin(); void onLastFmFinished(); private: Ui::SettingsDialog *ui; + ProxyDialog m_proxySettings; bool m_rejected; QNetworkReply* m_testLastFmQuery; }; diff --git a/src/settingsdialog.ui b/src/settingsdialog.ui index 1bada64e1..820c61ba8 100644 --- a/src/settingsdialog.ui +++ b/src/settingsdialog.ui @@ -224,6 +224,43 @@ + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Proxy Settings... + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + @@ -234,7 +271,7 @@ 20 - 146 + 73 @@ -531,5 +568,21 @@ + + checkBoxJabberAdvanced + toggled(bool) + proxyButton + setVisible(bool) + + + 310 + 110 + + + 368 + 194 + + + diff --git a/src/source.cpp b/src/source.cpp index bc4055bb2..1bbc38e53 100644 --- a/src/source.cpp +++ b/src/source.cpp @@ -76,7 +76,9 @@ void Source::remove() { qDebug() << Q_FUNC_INFO; + m_cc = 0; + emit offline(); APP->sourcelist().remove( this ); m_collections.clear(); } diff --git a/src/sourcetree/sourcetreeview.cpp b/src/sourcetree/sourcetreeview.cpp index 4774f304f..8d331f418 100644 --- a/src/sourcetree/sourcetreeview.cpp +++ b/src/sourcetree/sourcetreeview.cpp @@ -31,7 +31,8 @@ SourceTreeView::SourceTreeView( QWidget* parent ) setDropIndicatorShown( false ); setAllColumnsShowFocus( false ); - setupMenus(); + setContextMenuPolicy( Qt::CustomContextMenu ); + connect( this, SIGNAL( customContextMenuRequested( const QPoint& ) ), SLOT( onCustomContextMenu( const QPoint& ) ) ); m_model = new SourcesModel( this ); setModel( m_model ); @@ -50,15 +51,30 @@ SourceTreeView::SourceTreeView( QWidget* parent ) void SourceTreeView::setupMenus() { + m_playlistMenu.clear(); + m_loadPlaylistAction = m_playlistMenu.addAction( tr( "&Load Playlist" ) ); m_playlistMenu.addSeparator(); m_deletePlaylistAction = m_playlistMenu.addAction( tr( "&Delete Playlist" ) ); + bool readonly = true; + int type = SourcesModel::indexType( m_contextMenuIndex ); + if ( type == 1 ) + { + playlist_ptr playlist = SourcesModel::indexToPlaylist( m_contextMenuIndex ); + if ( !playlist.isNull() ) + { + readonly = !playlist->author()->isLocal(); + } + } + + if ( readonly ) + { + m_deletePlaylistAction->setEnabled( !readonly ); + } + connect( m_loadPlaylistAction, SIGNAL( triggered() ), SLOT( loadPlaylist() ) ); connect( m_deletePlaylistAction, SIGNAL( triggered() ), SLOT( deletePlaylist() ) ); - - setContextMenuPolicy( Qt::CustomContextMenu ); - connect( this, SIGNAL( customContextMenuRequested( const QPoint& ) ), SLOT( onCustomContextMenu( const QPoint& ) ) ); } @@ -66,18 +82,9 @@ void SourceTreeView::onSourceOffline( Tomahawk::source_ptr src ) { qDebug() << Q_FUNC_INFO; - - if ( APP->playlistManager()->superCollections().contains( src->collection() ) ) - { - qDebug() << "Removing source from active view"; - APP->playlistManager()->show( src->collection() ); - } - else - { - qDebug() << "not removing source from active view (not active)"; - } } + void SourceTreeView::onItemActivated( const QModelIndex& index ) { @@ -171,6 +178,8 @@ SourceTreeView::onCustomContextMenu( const QPoint& pos ) if ( !idx.isValid() ) return; + setupMenus(); + if ( SourcesModel::indexType( idx ) ) { m_playlistMenu.exec( mapToGlobal( pos ) ); diff --git a/src/tomahawkapp.cpp b/src/tomahawkapp.cpp index de8a3a18d..1be3b5f5c 100644 --- a/src/tomahawkapp.cpp +++ b/src/tomahawkapp.cpp @@ -7,12 +7,14 @@ #include #include "tomahawk/collection.h" +#include "tomahawk/infosystem.h" #include "database/database.h" #include "database/databasecollection.h" #include "database/databasecommand_collectionstats.h" #include "database/databaseresolver.h" #include "jabber/jabber.h" #include "utils/tomahawkutils.h" +#include "xmppbot/xmppbot.h" #include "web/api_v1.h" #include "scriptresolver.h" @@ -101,6 +103,8 @@ TomahawkApp::TomahawkApp( int& argc, char *argv[] ) , m_zeroconf( 0 ) , m_settings( 0 ) , m_nam( 0 ) + , m_proxy( 0 ) + , m_infoSystem( 0 ) { qsrand( QTime( 0, 0, 0 ).secsTo( QTime::currentTime() ) ); @@ -157,6 +161,24 @@ TomahawkApp::TomahawkApp( int& argc, char *argv[] ) } #endif + // Set up proxy + if( m_settings->proxyType() != QNetworkProxy::NoProxy && !m_settings->proxyHost().isEmpty() ) + { + qDebug() << "Setting proxy to saved values"; + m_proxy = new QNetworkProxy( static_cast(m_settings->proxyType()), m_settings->proxyHost(), m_settings->proxyPort(), m_settings->proxyUsername(), m_settings->proxyPassword() ); + qDebug() << "Proxy type = " << QString::number( static_cast(m_proxy->type()) ); + qDebug() << "Proxy host = " << m_proxy->hostName(); + QNetworkAccessManager* nam = TomahawkApp::instance()->nam(); + nam->setProxy( *m_proxy ); + } + else + m_proxy = new QNetworkProxy( QNetworkProxy::NoProxy ); + + QNetworkProxy::setApplicationProxy( *m_proxy ); + + m_infoSystem = new Tomahawk::InfoSystem::InfoSystem( this ); + m_xmppBot = new XMPPBot( this ); + boost::function(result_ptr)> fac = boost::bind( &TomahawkApp::httpIODeviceFactory, this, _1 ); this->registerIODeviceFactory( "http", fac ); @@ -449,6 +471,7 @@ TomahawkApp::setupJabber() //const QString& jid, const QString& pass, const QStr connect( m_jabber.data(), SIGNAL( connected() ), SLOT( jabberConnected() ) ); connect( m_jabber.data(), SIGNAL( authError(int, const QString&) ), SLOT( jabberAuthError(int,const QString&) ) ); + m_jabber->setProxy( m_proxy ); m_jabber->start(); } @@ -663,7 +686,6 @@ TomahawkApp::httpIODeviceFactory( const Tomahawk::result_ptr& result ) } - const QString& TomahawkApp::nodeID() const { diff --git a/src/tomahawksettings.cpp b/src/tomahawksettings.cpp index 41d5e8d11..d2c8441d0 100644 --- a/src/tomahawksettings.cpp +++ b/src/tomahawksettings.cpp @@ -21,8 +21,8 @@ TomahawkSettings::TomahawkSettings( QObject* parent ) else if( value( "configversion" ).toUInt() != SettingsDialog::VERSION ) { qDebug() << "Config version outdated, old:" << value( "configversion" ).toUInt() - << "new:" << SettingsDialog::VERSION - << "Doing upgrade, if any..."; + << "new:" << SettingsDialog::VERSION + << "Doing upgrade, if any..."; // insert upgrade code here as required setValue( "configversion", SettingsDialog::VERSION ); @@ -36,7 +36,8 @@ TomahawkSettings::~TomahawkSettings() } -QString TomahawkSettings::scannerPath() const +QString +TomahawkSettings::scannerPath() const { #ifndef TOMAHAWK_HEADLESS return value( "scannerpath", QDesktopServices::storageLocation( QDesktopServices::MusicLocation ) ).toString(); @@ -46,181 +47,337 @@ QString TomahawkSettings::scannerPath() const } -void TomahawkSettings::setScannerPath(const QString& path) +void +TomahawkSettings::setScannerPath( const QString& path ) { setValue( "scannerpath", path ); } -bool TomahawkSettings::hasScannerPath() const +bool +TomahawkSettings::hasScannerPath() const { return contains( "scannerpath" ); } -bool TomahawkSettings::httpEnabled() const +bool +TomahawkSettings::httpEnabled() const { return value( "network/http", true ).toBool(); } -void TomahawkSettings::setHttpEnabled(bool enable) +void +TomahawkSettings::setHttpEnabled( bool enable ) { setValue( "network/http", enable ); } -QByteArray TomahawkSettings::mainWindowGeometry() const +QString +TomahawkSettings::proxyHost() const +{ + return value( "network/proxy/host", QString() ).toString(); +} + + +void +TomahawkSettings::setProxyHost( const QString& host ) +{ + setValue( "network/proxy/host", host ); +} + + +qulonglong +TomahawkSettings::proxyPort() const +{ + return value( "network/proxy/port", 1080 ).toULongLong(); +} + + +void +TomahawkSettings::setProxyPort( const qulonglong port ) +{ + setValue( "network/proxy/port", port ); +} + + +QString +TomahawkSettings::proxyUsername() const +{ + return value( "network/proxy/username", QString() ).toString(); +} + + +void +TomahawkSettings::setProxyUsername( const QString& username ) +{ + setValue( "network/proxy/username", username ); +} + + +QString +TomahawkSettings::proxyPassword() const +{ + return value( "network/proxy/password", QString() ).toString(); +} + + +void +TomahawkSettings::setProxyPassword( const QString& password ) +{ + setValue( "network/proxy/password", password ); +} + + +int +TomahawkSettings::proxyType() const +{ + return value( "network/proxy/type", 0 ).toInt(); +} + + +void +TomahawkSettings::setProxyType( const int type ) +{ + setValue( "network/proxy/type", type ); +} + + +QByteArray +TomahawkSettings::mainWindowGeometry() const { return value( "ui/mainwindow/geometry" ).toByteArray(); } -void TomahawkSettings::setMainWindowGeometry(const QByteArray& geom) +void +TomahawkSettings::setMainWindowGeometry( const QByteArray& geom ) { setValue( "ui/mainwindow/geometry", geom ); } -QByteArray TomahawkSettings::mainWindowState() const +QByteArray +TomahawkSettings::mainWindowState() const { return value( "ui/mainwindow/state" ).toByteArray(); } -void TomahawkSettings::setMainWindowState(const QByteArray& state) +void +TomahawkSettings::setMainWindowState( const QByteArray& state ) { setValue( "ui/mainwindow/state", state ); } -QList TomahawkSettings::playlistColumnSizes() const +QList +TomahawkSettings::playlistColumnSizes() const { return value( "ui/playlist/columnSize" ).toList(); } -void TomahawkSettings::setPlaylistColumnSizes(const QList& cols) +void +TomahawkSettings::setPlaylistColumnSizes( const QList& cols ) { setValue( "ui/playlist/geometry", cols ); } -bool TomahawkSettings::jabberAutoConnect() const +bool +TomahawkSettings::jabberAutoConnect() const { return value( "jabber/autoconnect", true ).toBool(); } -void TomahawkSettings::setJabberAutoConnect(bool autoconnect) +void +TomahawkSettings::setJabberAutoConnect( bool autoconnect ) { setValue( "jabber/autoconnect", autoconnect ); } -int TomahawkSettings::jabberPort() const +int +TomahawkSettings::jabberPort() const { return value( "jabber/port", 5222 ).toInt(); } -void TomahawkSettings::setJabberPort(int port) +void +TomahawkSettings::setJabberPort( int port ) { setValue( "jabber/port", port ); } -QString TomahawkSettings::jabberServer() const +QString +TomahawkSettings::jabberServer() const { return value( "jabber/server" ).toString(); } -void TomahawkSettings::setJabberServer(const QString& server) +void +TomahawkSettings::setJabberServer( const QString& server ) { setValue( "jabber/server", server ); } -QString TomahawkSettings::jabberUsername() const +QString +TomahawkSettings::jabberUsername() const { return value( "jabber/username" ).toString(); } -void TomahawkSettings::setJabberUsername(const QString& username) +void +TomahawkSettings::setJabberUsername( const QString& username ) { setValue( "jabber/username", username ); } -QString TomahawkSettings::jabberPassword() const +QString +TomahawkSettings::jabberPassword() const { return value( "jabber/password" ).toString(); } -void TomahawkSettings::setJabberPassword(const QString& pw) +void +TomahawkSettings::setJabberPassword( const QString& pw ) { setValue( "jabber/password", pw ); } -bool TomahawkSettings::upnpEnabled() const +bool +TomahawkSettings::upnpEnabled() const { return value( "network/upnp", true ).toBool(); } -void TomahawkSettings::setUPnPEnabled(bool enable) +void +TomahawkSettings::setUPnPEnabled( bool enable ) { setValue( "network/upnp", enable ); } -QString TomahawkSettings::lastFmPassword() const +QString +TomahawkSettings::lastFmPassword() const { return value( "lastfm/password" ).toString(); } -void TomahawkSettings::setLastFmPassword(const QString& password) +void +TomahawkSettings::setLastFmPassword( const QString& password ) { setValue( "lastfm/password", password ); } -QByteArray TomahawkSettings::lastFmSessionKey() const +QByteArray +TomahawkSettings::lastFmSessionKey() const { return value( "lastfm/sessionkey" ).toByteArray(); } -void TomahawkSettings::setLastFmSessionKey(const QByteArray& key) +void +TomahawkSettings::setLastFmSessionKey( const QByteArray& key ) { setValue( "lastfm/sessionkey", key ); } -QString TomahawkSettings::lastFmUsername() const +QString +TomahawkSettings::lastFmUsername() const { return value( "lastfm/username" ).toString(); } -void TomahawkSettings::setLastFmUsername(const QString& username ) +void +TomahawkSettings::setLastFmUsername( const QString& username ) { setValue( "lastfm/username", username ); } -bool TomahawkSettings::scrobblingEnabled() const +bool +TomahawkSettings::scrobblingEnabled() const { return value( "lastfm/enablescrobbling", false ).toBool(); } -void TomahawkSettings::setScrobblingEnabled(bool enable) +void +TomahawkSettings::setScrobblingEnabled( bool enable ) { setValue( "lastfm/enablescrobbling", enable ); } + + +QString +TomahawkSettings::xmppBotServer() const +{ + return value( "xmppBot/server", QString() ).toString(); +} + + +void +TomahawkSettings::setXmppBotServer( const QString& server ) +{ + setValue( "xmppBot/server", server ); +} + + +QString +TomahawkSettings::xmppBotJid() const +{ + return value( "xmppBot/jid", QString() ).toString(); +} + + +void +TomahawkSettings::setXmppBotJid( const QString& component ) +{ + setValue( "xmppBot/jid", component ); +} + + +QString +TomahawkSettings::xmppBotPassword() const +{ + return value( "xmppBot/password", QString() ).toString(); +} + + +void +TomahawkSettings::setXmppBotPassword( const QString& password ) +{ + setValue( "xmppBot/password", password ); +} + + +int +TomahawkSettings::xmppBotPort() const +{ + return value( "xmppBot/port", -1 ).toInt(); +} + + +void +TomahawkSettings::setXmppBotPort( const int port ) +{ + setValue( "xmppBot/port", -1 ); +} diff --git a/src/tomahawksettings.h b/src/tomahawksettings.h index af865b864..fa7ac3b0a 100644 --- a/src/tomahawksettings.h +++ b/src/tomahawksettings.h @@ -50,7 +50,22 @@ public: bool upnpEnabled() const; /// true by default void setUPnPEnabled( bool enable ); - + + QString proxyHost() const; + void setProxyHost( const QString &host ); + + qulonglong proxyPort() const; + void setProxyPort( const qulonglong port ); + + QString proxyUsername() const; + void setProxyUsername( const QString &username ); + + QString proxyPassword() const; + void setProxyPassword( const QString &password ); + + int proxyType() const; + void setProxyType( const int type ); + /// Last.fm settings bool scrobblingEnabled() const; /// false by default void setScrobblingEnabled( bool enable ); @@ -63,6 +78,19 @@ public: QByteArray lastFmSessionKey() const; void setLastFmSessionKey( const QByteArray& key ); + + /// XMPP Component Settings + QString xmppBotServer() const; + void setXmppBotServer( const QString &server ); + + QString xmppBotJid() const; + void setXmppBotJid( const QString &component ); + + QString xmppBotPassword() const; + void setXmppBotPassword( const QString &password ); + + int xmppBotPort() const; + void setXmppBotPort( const int port ); }; #endif diff --git a/src/tomahawkwindow.cpp b/src/tomahawkwindow.cpp index 203afb554..b15d2a530 100644 --- a/src/tomahawkwindow.cpp +++ b/src/tomahawkwindow.cpp @@ -49,8 +49,6 @@ TomahawkWindow::TomahawkWindow( QWidget* parent ) #endif ui->setupUi( this ); -// ui->playlistView->connectProgressBar( ui->actionProgress ); -// ui->playlistView->setFocus(); #ifndef Q_WS_MAC ui->centralWidget->layout()->setContentsMargins( 4, 4, 4, 2 ); @@ -60,7 +58,10 @@ TomahawkWindow::TomahawkWindow( QWidget* parent ) ui->sourceTreeView->setAttribute( Qt::WA_MacShowFocusRect, 0 ); #endif - ui->mainLayout->addWidget( m_playlistManager->widget() ); + delete ui->playlistWidget; + ui->splitter->addWidget( m_playlistManager->widget() ); + ui->splitter->setStretchFactor( 0, 1 ); + ui->splitter->setStretchFactor( 1, 3 ); QToolBar* toolbar = addToolBar( "TomahawkToolbar" ); toolbar->setObjectName( "TomahawkToolbar" ); diff --git a/src/tomahawkwindow.ui b/src/tomahawkwindow.ui index 7cb72bd69..3d17e9977 100644 --- a/src/tomahawkwindow.ui +++ b/src/tomahawkwindow.ui @@ -11,42 +11,25 @@ - EchoChamber + Tomahawk - + - - - - - - 0 - 0 - - - - - 250 - 0 - - - - - 250 - 16777215 - - - - - - - - 0 - - - - + + + Qt::Horizontal + + + + + 250 + 0 + + + + + diff --git a/src/xmppbot/xmppbot.cpp b/src/xmppbot/xmppbot.cpp new file mode 100644 index 000000000..a71f16dd1 --- /dev/null +++ b/src/xmppbot/xmppbot.cpp @@ -0,0 +1,396 @@ +#include "xmppbot.h" + +#include "tomahawk/tomahawkapp.h" +#include "tomahawk/infosystem.h" +#include +#include