diff --git a/CMakeLists.txt b/CMakeLists.txt index e0fdfaa29..b43598761 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -39,6 +39,11 @@ INCLUDE( MacroLogFeature ) set(LIBLASTFM_FOUND true) find_package(Phonon REQUIRED) +if(PHONON_VERSION STRLESS "4.5.0") + message(FATAL_ERROR "Phonon version 4.5.0 or higher is required, you have version ${PHONON_VERSION}") +else() + message(STATUS "Phonon found; ensure that phonon-vlc is at least 0.4") +endif() macro_optional_find_package(LibEchonest 1.1.1) macro_log_feature(LIBECHONEST_FOUND "Echonest" "Qt library for communicating with The Echo Nest" "http://projects.kde.org/libechonest" TRUE "" "libechonest is needed for dynamic playlists and the infosystem") diff --git a/CMakeModules/FindPhonon.cmake b/CMakeModules/FindPhonon.cmake index 28969c718..77dda657b 100644 --- a/CMakeModules/FindPhonon.cmake +++ b/CMakeModules/FindPhonon.cmake @@ -31,11 +31,11 @@ else(PHONON_FOUND) endif(PHONON_INCLUDE_DIR AND PHONON_LIBRARY) # As discussed on kde-buildsystem: first look at CMAKE_PREFIX_PATH, then at the suggested PATHS (kde4 install dir) - find_library(PHONON_LIBRARY NAMES phonon phonon4 PATHS ${KDE4_LIB_INSTALL_DIR} ${QT_LIBRARY_DIR} NO_SYSTEM_ENVIRONMENT_PATH NO_CMAKE_SYSTEM_PATH) + find_library(PHONON_LIBRARY NAMES phonon phonon4 PATHS ${KDE4_LIB_INSTALL_DIR} ${QT_LIBRARY_DIR}) # then at the default system locations (CMAKE_SYSTEM_PREFIX_PATH, i.e. /usr etc.) find_library(PHONON_LIBRARY NAMES phonon phonon4) - find_path(PHONON_INCLUDE_DIR NAMES phonon/phonon_export.h PATHS ${KDE4_INCLUDE_INSTALL_DIR} ${QT_INCLUDE_DIR} ${INCLUDE_INSTALL_DIR} ${QT_LIBRARY_DIR} NO_SYSTEM_ENVIRONMENT_PATH NO_CMAKE_SYSTEM_PATH) + find_path(PHONON_INCLUDE_DIR NAMES phonon/phonon_export.h PATHS ${KDE4_INCLUDE_INSTALL_DIR} ${QT_INCLUDE_DIR} ${INCLUDE_INSTALL_DIR} ${QT_LIBRARY_DIR}) find_path(PHONON_INCLUDE_DIR NAMES phonon/phonon_export.h) if(PHONON_INCLUDE_DIR AND PHONON_LIBRARY) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 45cf481d1..3473eb270 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -39,6 +39,7 @@ SET( tomahawkSources ${tomahawkSources} musicscanner.cpp shortcuthandler.cpp + globalactionmanager.cpp scanmanager.cpp tomahawkapp.cpp main.cpp @@ -83,6 +84,7 @@ SET( tomahawkHeaders ${tomahawkHeaders} musicscanner.h scanmanager.h shortcuthandler.h + globalactionmanager.h ) IF(LIBLASTFM_FOUND) diff --git a/src/globalactionmanager.cpp b/src/globalactionmanager.cpp new file mode 100644 index 000000000..2f9d64279 --- /dev/null +++ b/src/globalactionmanager.cpp @@ -0,0 +1,360 @@ +/* + Copyright (C) 2011 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, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "globalactionmanager.h" + +#include "audio/audioengine.h" +#include "utils/xspfloader.h" +#include "sourcelist.h" +#include "playlist/dynamic/GeneratorInterface.h" +#include "viewmanager.h" +#include "playlist/topbar/topbar.h" +#include "pipeline.h" +#include "database/localcollection.h" +#include "playlist/playlistview.h" +#include "echonest/Playlist.h" + +#include +#include + +GlobalActionManager* GlobalActionManager::s_instance = 0; + +GlobalActionManager* +GlobalActionManager::instance() +{ + if( !s_instance ) + s_instance = new GlobalActionManager; + + return s_instance; +} + + +GlobalActionManager::GlobalActionManager( QObject* parent ) + : QObject( parent ) +{ + +} + +GlobalActionManager::~GlobalActionManager() +{} + +bool +GlobalActionManager::parseTomahawkLink( const QString& url ) +{ + if( url.contains( "tomahawk://" ) ) { + QString cmd = url.mid( 11 ); + qDebug() << "Parsing tomahawk link command" << cmd; + + QString cmdType = cmd.split( "/" ).first(); + QUrl u( cmd ); + + // for backwards compatibility + if( cmdType == "load" ) { + if( u.hasQueryItem( "xspf" ) ) { + QUrl xspf = QUrl::fromUserInput( u.queryItemValue( "xspf" ) ); + XSPFLoader* l = new XSPFLoader( true, this ); + qDebug() << "Loading spiff:" << xspf.toString(); + l->load( xspf ); + connect( l, SIGNAL( ok( Tomahawk::playlist_ptr ) ), ViewManager::instance(), SLOT( show( Tomahawk::playlist_ptr ) ) ); + + return true; + } + } + + if( cmdType == "playlist" ) { + return handlePlaylistCommand( u ); + } else if( cmdType == "collection" ) { + return handleCollectionCommand( u ); + } else if( cmdType == "queue" ) { + return handleQueueCommand( u ); + } else if( cmdType == "station" ) { + return handleStationCommand( u ); + } else if( cmdType == "search" ) { + return handleSearchCommand( u ); + } else if( cmdType == "play" ) { + return handlePlayCommand( u ); + } else { + qDebug() << "Tomahawk link not supported, command not known!" << cmdType << u.path(); + return false; + } + } else { + qDebug() << "Not a tomahawk:// link!"; + return false; + } +} + +bool +GlobalActionManager::handlePlaylistCommand( const QUrl& url ) +{ + QStringList parts = url.path().split( "/" ).mid( 1 ); // get the rest of the command + if( parts.isEmpty() ) { + qDebug() << "No specific playlist command:" << url.toString(); + return false; + } + + if( parts[ 0 ] == "import" ) { + if( !url.hasQueryItem( "xspf" ) ) { + qDebug() << "No xspf to load.."; + return false; + } + QUrl xspf = QUrl( url.queryItemValue( "xspf" ) ); + QString title = url.hasQueryItem( "title" ) ? url.queryItemValue( "title" ) : QString(); + XSPFLoader* l= new XSPFLoader( true, this ); + l->setOverrideTitle( title ); + l->load( xspf ); + connect( l, SIGNAL( ok( Tomahawk::playlist_ptr ) ), ViewManager::instance(), SLOT( show( Tomahawk::playlist_ptr ) ) ); + + } else if( parts [ 0 ] == "new" ) { + if( !url.hasQueryItem( "title" ) ) { + qDebug() << "New playlist command needs a title..."; + return false; + } + Tomahawk::playlist_ptr pl = Tomahawk::Playlist::create( SourceList::instance()->getLocal(), uuid(), url.queryItemValue( "title" ), QString(), QString(), false ); + pl->createNewRevision( uuid(), pl->currentrevision(), QList< Tomahawk::plentry_ptr >() ); + ViewManager::instance()->show( pl ); + } else if( parts[ 0 ] == "add" ) { + if( !url.hasQueryItem( "playlistid" ) || !url.hasQueryItem( "track" ) || !url.hasQueryItem( "artist" ) ) { + qDebug() << "Add to playlist command needs playlistid, track, and artist..." << url.toString(); + return false; + } + // TODO implement. Let the user select what playlist to add to + return false; + } + + return false; +} + +bool +GlobalActionManager::handleCollectionCommand( const QUrl& url ) +{ + QStringList parts = url.path().split( "/" ).mid( 1 ); // get the rest of the command + if( parts.isEmpty() ) { + qDebug() << "No specific collection command:" << url.toString(); + return false; + } + + if( parts[ 0 ] == "add" ) { + // TODO implement + } + + return false; +} + +bool +GlobalActionManager::handleQueueCommand( const QUrl& url ) +{ + QStringList parts = url.path().split( "/" ).mid( 1 ); // get the rest of the command + if( parts.isEmpty() ) { + qDebug() << "No specific queue command:" << url.toString(); + return false; + } + + if( parts[ 0 ] == "add" ) { + if( parts.size() > 1 && parts[ 1 ] == "track" ) { + QPair< QString, QString > pair; + foreach( pair, url.queryItems() ) { + if( pair.first != "url" ) + continue; + QUrl track = QUrl::fromUserInput( pair.second ); + //FIXME: isLocalFile is Qt 4.8 + if( track.toString().startsWith( "file://" ) ) { // it's local, so we see if it's in the DB and load it if so + // TODO + } else { // give it a web result hint + // TODO actually read the tags + QFileInfo info( track.path() ); + Tomahawk::query_ptr q = Tomahawk::Query::get( QString(), info.baseName(), QString() ); + q->setResultHint( track.toString() ); + Tomahawk::Pipeline::instance()->resolve( q, true ); + + ViewManager::instance()->queue()->model()->append( q ); + ViewManager::instance()->showQueue(); + } + return true; + } + } else { + qDebug() << "Only queue/add/track is support at the moment, got:" << parts; + return false; + } + } + + return false; +} + +bool +GlobalActionManager::handleSearchCommand( const QUrl& url ) +{ + // open the super collection and set this as the search filter + QStringList query; + if( url.hasQueryItem( "artist" ) ) + query << url.queryItemValue( "artist" ); + if( url.hasQueryItem( "album" ) ) + query << url.queryItemValue( "album" ); + if( url.hasQueryItem( "track" ) ) + query << url.queryItemValue( "track" ); + QString queryStr = query.join( " " ); + + if( queryStr.isEmpty() ) + return false; + + ViewManager::instance()->showSuperCollection(); + ViewManager::instance()->topbar()->setFilter( queryStr ); + return true; +} + +bool +GlobalActionManager::handleStationCommand( const QUrl& url ) +{ + QStringList parts = url.path().split( "/" ).mid( 1 ); // get the rest of the command + if( parts.isEmpty() ) { + qDebug() << "No specific station command:" << url.toString(); + return false; + } + + if( parts[ 0 ] == "create" ) { + if( !url.hasQueryItem( "title" ) || !url.hasQueryItem( "type" ) ) { + qDebug() << "Station create command needs title and type..." << url.toString(); + return false; + } + QString title = url.queryItemValue( "title" ); + QString type = url.queryItemValue( "type" ); + Tomahawk::dynplaylist_ptr pl = Tomahawk::DynamicPlaylist::create( SourceList::instance()->getLocal(), uuid(), title, QString(), QString(), Tomahawk::OnDemand, false, type ); + QList< Tomahawk::dyncontrol_ptr > controls; + QPair< QString, QString > param; + foreach( param, url.queryItems() ) { + if( param.first == "artist" ) { + Tomahawk::dyncontrol_ptr c = pl->generator()->createControl( "Artist" ); + c->setInput( param.second ); + c->setMatch( QString::number( (int)Echonest::DynamicPlaylist::ArtistRadioType ) ); + controls << c; + } /*else if( param.first == "hotttnesss" ) { TODO + Tomahawk::dyncontrol_ptr c = pl->generator()->createControl( "Artist" ); + c->setInput( param.second ); + c->setMatch( 0 ); + controls << c; + } */ + } + pl->createNewRevision( uuid(), pl->currentrevision(), type, controls ); + return true; + } + + return false; +} + +bool +GlobalActionManager::handlePlayCommand( const QUrl& url ) +{ + QStringList parts = url.path().split( "/" ).mid( 1 ); // get the rest of the command + if( parts.isEmpty() ) { + qDebug() << "No specific play command:" << url.toString(); + return false; + } + + if( parts[ 0 ] == "track" ) { + QPair< QString, QString > pair; + QString title, artist, album, urlStr; + foreach( pair, url.queryItems() ) { + if( pair.first == "track" ) + title = pair.second; + else if( pair.first == "artist" ) + artist = pair.second; + else if( pair.first == "album" ) + album = pair.second; + else if( pair.first == "url" ) + urlStr = pair.second; + } + Tomahawk::query_ptr q = Tomahawk::Query::get( artist, title, album ); + if( !urlStr.isEmpty() ) + q->setResultHint( urlStr ); + Tomahawk::Pipeline::instance()->resolve( q, true ); + + // now we add it to the special "bookmarks" playlist, creating it if it doesn't exist. if nothing is playing, start playing the track + QSharedPointer< LocalCollection > col = SourceList::instance()->getLocal()->collection().dynamicCast< LocalCollection >(); + Tomahawk::playlist_ptr bookmarkpl = col->bookmarksPlaylist(); + if( bookmarkpl.isNull() ) { // create it and do the deed then + m_waitingToBookmark = q; + col->createBookmarksPlaylist(); + connect( col.data(), SIGNAL( bookmarkPlaylistCreated( Tomahawk::playlist_ptr ) ), this, SLOT( bookmarkPlaylistCreated( Tomahawk::playlist_ptr ) ), Qt::UniqueConnection ); + } else { + doBookmark( bookmarkpl, q ); + } + + return true; + } + + return false; +} + +void +GlobalActionManager::bookmarkPlaylistCreated( const Tomahawk::playlist_ptr& pl ) +{ + Q_ASSERT( !m_waitingToBookmark.isNull() ); + doBookmark( pl, m_waitingToBookmark ); +} + +void +GlobalActionManager::doBookmark( const Tomahawk::playlist_ptr& pl, const Tomahawk::query_ptr& q ) +{ + Tomahawk::plentry_ptr e( new Tomahawk::PlaylistEntry ); + e->setGuid( uuid() ); + + if ( q->results().count() ) + e->setDuration( q->results().at( 0 )->duration() ); + else + e->setDuration( 0 ); + + e->setLastmodified( 0 ); + e->setAnnotation( "" ); // FIXME + e->setQuery( q ); + + pl->createNewRevision( uuid(), pl->currentrevision(), QList< Tomahawk::plentry_ptr >( pl->entries() ) << e ); + connect( pl.data(), SIGNAL( revisionLoaded( Tomahawk::PlaylistRevision ) ), this, SLOT( showPlaylist() ) ); + + m_toShow = pl; + + // if nothing is playing, lets start this + // TODO + if( !AudioEngine::instance()->isPlaying() ) { + connect( q.data(), SIGNAL( resolvingFinished( bool ) ), this, SLOT( waitingForResolved( bool ) ) ); + m_waitingToPlay = q; + } + + m_waitingToBookmark.clear(); +} + +void +GlobalActionManager::showPlaylist() +{ + if( m_toShow.isNull() ) + return; + + ViewManager::instance()->show( m_toShow ); + + m_toShow.clear(); +} + +void +GlobalActionManager::waitingForResolved( bool success ) +{ + if( success && !m_waitingToPlay.isNull() && !m_waitingToPlay->results().isEmpty() ) { // play it! + AudioEngine::instance()->playItem( AudioEngine::instance()->playlist(), m_waitingToPlay->results().first() ); + } + + m_waitingToPlay.clear(); +} + + diff --git a/src/globalactionmanager.h b/src/globalactionmanager.h new file mode 100644 index 000000000..d4b94af37 --- /dev/null +++ b/src/globalactionmanager.h @@ -0,0 +1,61 @@ +/* + Copyright (C) 2011 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, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + + +#ifndef GLOBALACTIONMANAGER_H +#define GLOBALACTIONMANAGER_H + +#include "playlist.h" + +#include +#include + +class GlobalActionManager : public QObject +{ + Q_OBJECT +public: + static GlobalActionManager* instance(); + virtual ~GlobalActionManager(); + +public slots: + bool parseTomahawkLink( const QString& link ); + void waitingForResolved( bool ); + +private slots: + void bookmarkPlaylistCreated( const Tomahawk::playlist_ptr& pl ); + void showPlaylist(); + +private: + explicit GlobalActionManager( QObject* parent = 0 ); + void doBookmark( const Tomahawk::playlist_ptr& pl, const Tomahawk::query_ptr& q ); + + bool handlePlaylistCommand( const QUrl& url ); + bool handleCollectionCommand(const QUrl& url ); + bool handleQueueCommand(const QUrl& url ); + bool handleStationCommand(const QUrl& url ); + bool handleSearchCommand(const QUrl& url ); + bool handlePlayCommand(const QUrl& url ); + + Tomahawk::playlist_ptr m_toShow; + Tomahawk::query_ptr m_waitingToBookmark; + Tomahawk::query_ptr m_waitingToPlay; + + static GlobalActionManager* s_instance; +}; + +#endif // GLOBALACTIONMANAGER_H diff --git a/src/libtomahawk/CMakeLists.txt b/src/libtomahawk/CMakeLists.txt index a8d868939..7db52f153 100644 --- a/src/libtomahawk/CMakeLists.txt +++ b/src/libtomahawk/CMakeLists.txt @@ -21,6 +21,7 @@ set( libSources artist.cpp album.cpp collection.cpp + webcollection.cpp playlist.cpp resolver.cpp query.cpp @@ -38,6 +39,7 @@ set( libSources database/database.cpp database/fuzzyindex.cpp database/databasecollection.cpp + database/localcollection.cpp database/databaseworker.cpp database/databaseimpl.cpp database/databaseresolver.cpp @@ -66,7 +68,6 @@ set( libSources database/databasecommand_renameplaylist.cpp database/databasecommand_loadops.cpp database/databasecommand_updatesearchindex.cpp - database/databasecollection.cpp database/databasecommand_setdynamicplaylistrevision.cpp database/databasecommand_createdynamicplaylist.cpp database/databasecommand_loaddynamicplaylist.cpp @@ -79,6 +80,7 @@ set( libSources infosystem/infosystemcache.cpp infosystem/infosystem.cpp + infosystem/infosystemworker.cpp infosystem/infoplugins/echonestplugin.cpp infosystem/infoplugins/lastfmplugin.cpp infosystem/infoplugins/musixmatchplugin.cpp @@ -174,6 +176,7 @@ set( libHeaders aclsystem.h collection.h + webcollection.h query.h resolver.h result.h @@ -223,6 +226,7 @@ set( libHeaders database/databasecommand_loadops.h database/databasecommand_updatesearchindex.h database/databasecollection.h + database/localcollection.h database/databasecommand_setdynamicplaylistrevision.h database/databasecommand_createdynamicplaylist.h database/databasecommand_loaddynamicplaylist.h @@ -233,6 +237,7 @@ set( libHeaders database/databasecommand_clientauthvalid.h infosystem/infosystem.h + infosystem/infosystemworker.h infosystem/infosystemcache.h infosystem/infoplugins/echonestplugin.h infosystem/infoplugins/lastfmplugin.h diff --git a/src/libtomahawk/audio/audioengine.cpp b/src/libtomahawk/audio/audioengine.cpp index daf259d77..657c38c7a 100644 --- a/src/libtomahawk/audio/audioengine.cpp +++ b/src/libtomahawk/audio/audioengine.cpp @@ -113,12 +113,6 @@ AudioEngine::stop() m_expectStop = true; m_mediaObject->stop(); - if ( !m_input.isNull() ) - { - m_input->close(); - m_input.clear(); - } - setCurrentTrack( Tomahawk::result_ptr() ); emit stopped(); } @@ -198,8 +192,6 @@ AudioEngine::loadTrack( const Tomahawk::result_ptr& result ) m_expectStop = true; } - m_input = io; - if ( !m_currentTrack->url().startsWith( "http://" ) ) { m_mediaObject->setCurrentSource( io.data() ); @@ -215,6 +207,9 @@ AudioEngine::loadTrack( const Tomahawk::result_ptr& result ) } m_mediaObject->setCurrentSource( furl ); } + + m_input = io; + m_mediaObject->currentSource().setAutoDelete( true ); m_mediaObject->play(); diff --git a/src/libtomahawk/database/databasecommand_resolve.cpp b/src/libtomahawk/database/databasecommand_resolve.cpp index f6f2b1c42..7af53ba26 100644 --- a/src/libtomahawk/database/databasecommand_resolve.cpp +++ b/src/libtomahawk/database/databasecommand_resolve.cpp @@ -1,5 +1,5 @@ /* === This file is part of Tomahawk Player - === - * + * * Copyright 2010-2011, Christian Muehlhaeuser * * Tomahawk is free software: you can redistribute it and/or modify @@ -41,7 +41,7 @@ DatabaseCommand_Resolve::exec( DatabaseImpl* lib ) { qDebug() << "Using result-hint to speed up resolving:" << m_query->resultHint(); - Tomahawk::result_ptr result = lib->result( m_query->resultHint() ); + Tomahawk::result_ptr result = lib->resultFromHint( m_query ); if ( !result.isNull() && result->collection()->source()->isOnline() ) { res << result; @@ -119,7 +119,7 @@ DatabaseCommand_Resolve::exec( DatabaseImpl* lib ) s = SourceList::instance()->get( files_query.value( 13 ).toUInt() ); if( s.isNull() ) { - Q_ASSERT( false ); + qDebug() << "WTF: Could not find source" << files_query.value( 13 ).toUInt(); continue; } diff --git a/src/libtomahawk/database/databasecommand_setplaylistrevision.cpp b/src/libtomahawk/database/databasecommand_setplaylistrevision.cpp index 62eec9d6e..cceced691 100644 --- a/src/libtomahawk/database/databasecommand_setplaylistrevision.cpp +++ b/src/libtomahawk/database/databasecommand_setplaylistrevision.cpp @@ -1,5 +1,5 @@ /* === This file is part of Tomahawk Player - === - * + * * Copyright 2010-2011, Christian Muehlhaeuser * * Tomahawk is free software: you can redistribute it and/or modify @@ -60,7 +60,7 @@ DatabaseCommand_SetPlaylistRevision::postCommitHook() qDebug() << "Source has gone offline, not emitting to GUI."; return; } - + if ( m_localOnly ) return; @@ -149,6 +149,8 @@ DatabaseCommand_SetPlaylistRevision::exec( DatabaseImpl* lib ) QString resultHint; if ( !e->query()->results().isEmpty() ) resultHint = e->query()->results().first()->url(); + else if ( !e->query()->resultHint().isEmpty() ) + resultHint = e->query()->resultHint(); adde.bindValue( 0, e->guid() ); adde.bindValue( 1, m_playlistguid ); diff --git a/src/libtomahawk/database/databaseimpl.cpp b/src/libtomahawk/database/databaseimpl.cpp index e052368f0..f18270948 100644 --- a/src/libtomahawk/database/databaseimpl.cpp +++ b/src/libtomahawk/database/databaseimpl.cpp @@ -528,8 +528,9 @@ DatabaseImpl::album( int id ) Tomahawk::result_ptr -DatabaseImpl::result( const QString& url ) +DatabaseImpl::resultFromHint( const Tomahawk::query_ptr& origquery ) { + QString url = origquery->resultHint(); TomahawkSqlQuery query = newquery(); Tomahawk::source_ptr s; Tomahawk::result_ptr res; @@ -552,7 +553,18 @@ DatabaseImpl::result( const QString& url ) else { // Q_ASSERT( false ); - qDebug() << "We don't support non-servent / non-file result-hints yet."; +// qDebug() << "We don't support non-servent / non-file result-hints yet."; + res = Tomahawk::result_ptr( new Tomahawk::Result() ); + s = SourceList::instance()->webSource(); + res->setUrl( url ); + res->setCollection( s->collection() ); + res->setRID( uuid() ); + res->setScore( 1.0 ); + res->setArtist( Tomahawk::artist_ptr( new Tomahawk::Artist( 0, origquery->artist() ) ) ); + res->setAlbum( Tomahawk::album_ptr( new Tomahawk::Album( 0, origquery->album(), res->artist() ) ) ); + res->setTrack( origquery->track() ); + res->setDuration( origquery->duration() ); + res->setFriendlySource( url ); return res; } diff --git a/src/libtomahawk/database/databaseimpl.h b/src/libtomahawk/database/databaseimpl.h index f3551b634..243f04edd 100644 --- a/src/libtomahawk/database/databaseimpl.h +++ b/src/libtomahawk/database/databaseimpl.h @@ -64,7 +64,7 @@ public: QVariantMap album( int id ); QVariantMap track( int id ); Tomahawk::result_ptr file( int fid ); - Tomahawk::result_ptr result( const QString& url ); + Tomahawk::result_ptr resultFromHint( const Tomahawk::query_ptr& query ); static bool scorepairSorter( const QPair& left, const QPair& right ) { diff --git a/src/libtomahawk/database/localcollection.cpp b/src/libtomahawk/database/localcollection.cpp new file mode 100644 index 000000000..9a8882aab --- /dev/null +++ b/src/libtomahawk/database/localcollection.cpp @@ -0,0 +1,61 @@ +/* + Copyright (C) 2011 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, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + + +#include "localcollection.h" + +#include "sourcelist.h" +#include "viewmanager.h" +#include + +LocalCollection::LocalCollection( const Tomahawk::source_ptr& source, QObject* parent ) + : DatabaseCollection( source, parent ) +{ + +} + +Tomahawk::playlist_ptr +LocalCollection::bookmarksPlaylist() +{ + if( TomahawkSettings::instance()->bookmarkPlaylist().isEmpty() ) + return Tomahawk::playlist_ptr(); + + return playlist( TomahawkSettings::instance()->bookmarkPlaylist() ); +} + +void +LocalCollection::createBookmarksPlaylist() +{ + if( bookmarksPlaylist().isNull() ) { + QString guid = uuid(); + Tomahawk::playlist_ptr p = Tomahawk::Playlist::create( SourceList::instance()->getLocal(), guid, tr( "Bookmarks" ), tr( "Saved tracks" ), QString(), false ); + ViewManager::instance()->createPageForPlaylist( p ); +// connect( p.data(), SIGNAL( revisionLoaded( Tomahawk::PlaylistRevision ) ), this, SLOT( loaded( Tomahawk::PlaylistRevision ) ), Qt::QueuedConnection ); + connect( p.data(), SIGNAL( created() ), this, SLOT( created() ) ); + + TomahawkSettings::instance()->setBookmarkPlaylist( guid ); +// p->createNewRevision( uuid(), p->currentrevision(), QList< Tomahawk::plentry_ptr >() ); + } +} + +void +LocalCollection::created() +{ + emit bookmarkPlaylistCreated( bookmarksPlaylist() ); +} diff --git a/src/libtomahawk/database/localcollection.h b/src/libtomahawk/database/localcollection.h new file mode 100644 index 000000000..e9ebccf24 --- /dev/null +++ b/src/libtomahawk/database/localcollection.h @@ -0,0 +1,46 @@ +/* + Copyright (C) 2011 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, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + + +#ifndef LOCALCOLLECTION_H +#define LOCALCOLLECTION_H + +#include "databasecollection.h" +#include "dllmacro.h" +#include "playlist.h" + +class DLLEXPORT LocalCollection : public DatabaseCollection +{ + Q_OBJECT +public: + explicit LocalCollection( const Tomahawk::source_ptr& source, QObject* parent = 0 ); + + // gets the playlist used for storing stuff from the web, if it already exists. if the returned playlist + // is invalid ask to create and listen to the signal + Tomahawk::playlist_ptr bookmarksPlaylist(); + void createBookmarksPlaylist(); + +signals: + void bookmarkPlaylistCreated( const Tomahawk::playlist_ptr& p ); + +private slots: + void created(); + +}; + +#endif // LOCALCOLLECTION_H diff --git a/src/libtomahawk/infosystem/infoplugins/echonestplugin.cpp b/src/libtomahawk/infosystem/infoplugins/echonestplugin.cpp index a153525e5..73e540b85 100644 --- a/src/libtomahawk/infosystem/infoplugins/echonestplugin.cpp +++ b/src/libtomahawk/infosystem/infoplugins/echonestplugin.cpp @@ -25,13 +25,16 @@ using namespace Echonest; // for internal neatness -EchoNestPlugin::EchoNestPlugin(QObject *parent) +EchoNestPlugin::EchoNestPlugin(InfoSystemWorker *parent) : InfoPlugin(parent) + , m_infoSystemWorker( parent ) { qDebug() << Q_FUNC_INFO; 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); + parent->registerInfoTypes( this, supportedTypes, QSet< InfoType>() ); + + connect( parent, SIGNAL( namChanged() ), SLOT( namChangedSlot() ) ); } EchoNestPlugin::~EchoNestPlugin() @@ -39,6 +42,13 @@ EchoNestPlugin::~EchoNestPlugin() qDebug() << Q_FUNC_INFO; } +void +EchoNestPlugin::namChangedSlot() +{ + qDebug() << Q_FUNC_INFO; + Echonest::Config::instance()->setNetworkAccessManager( m_infoSystemWorker->nam() ); +} + void EchoNestPlugin::getInfo(const QString caller, const Tomahawk::InfoSystem::InfoType type, const QVariant input, const Tomahawk::InfoSystem::InfoCustomData customData) { diff --git a/src/libtomahawk/infosystem/infoplugins/echonestplugin.h b/src/libtomahawk/infosystem/infoplugins/echonestplugin.h index b443960c8..d8492e4a0 100644 --- a/src/libtomahawk/infosystem/infoplugins/echonestplugin.h +++ b/src/libtomahawk/infosystem/infoplugins/echonestplugin.h @@ -20,6 +20,7 @@ #define ECHONESTPLUGIN_H #include "infosystem/infosystem.h" +#include "infosystem/infosystemworker.h" #include @@ -39,12 +40,31 @@ class EchoNestPlugin : public InfoPlugin Q_OBJECT public: - EchoNestPlugin(QObject *parent); + EchoNestPlugin( InfoSystemWorker *parent ); virtual ~EchoNestPlugin(); protected slots: virtual void getInfo( const QString caller, const Tomahawk::InfoSystem::InfoType type, const QVariant input, const Tomahawk::InfoSystem::InfoCustomData customData ); + virtual void pushInfo( const QString caller, const Tomahawk::InfoSystem::InfoType type, const QVariant data ) + { + Q_UNUSED( caller ); + Q_UNUSED( type ); + Q_UNUSED( data ); + } + + virtual void notInCacheSlot( const Tomahawk::InfoSystem::InfoCriteriaHash criteria, const QString caller, const Tomahawk::InfoSystem::InfoType type, const QVariant input, const Tomahawk::InfoSystem::InfoCustomData customData ) + { + Q_UNUSED( criteria ); + Q_UNUSED( caller ); + Q_UNUSED( type ); + Q_UNUSED( input ); + Q_UNUSED( customData ); + } + +public slots: + void namChangedSlot(); + private: void getSongProfile( const QString &caller, const QVariant &input, const InfoCustomData &customData, const QString &item = QString() ); void getArtistBiography ( const QString &caller, const QVariant &input, const InfoCustomData &customData ); @@ -67,6 +87,7 @@ private slots: private: QHash< QNetworkReply*, InfoCustomData > m_replyMap; QHash< QNetworkReply*, QString > m_callerMap; + InfoSystemWorker* m_infoSystemWorker; }; } diff --git a/src/libtomahawk/infosystem/infoplugins/lastfmplugin.cpp b/src/libtomahawk/infosystem/infoplugins/lastfmplugin.cpp index c7c892944..a9c29c5fe 100644 --- a/src/libtomahawk/infosystem/infoplugins/lastfmplugin.cpp +++ b/src/libtomahawk/infosystem/infoplugins/lastfmplugin.cpp @@ -41,14 +41,18 @@ md5( const QByteArray& src ) } -LastFmPlugin::LastFmPlugin( QObject* parent ) +LastFmPlugin::LastFmPlugin( InfoSystemWorker* parent ) : InfoPlugin(parent) , m_scrobbler( 0 ) , m_authJob( 0 ) + , m_infoSystemWorker( parent ) { - QSet< InfoType > supportedTypes; - supportedTypes << InfoMiscSubmitScrobble << InfoMiscSubmitNowPlaying << InfoAlbumCoverArt << InfoArtistImages; - qobject_cast< InfoSystem* >(parent)->registerInfoTypes(this, supportedTypes); + QSet< InfoType > supportedGetTypes, supportedPushTypes; + supportedGetTypes << InfoAlbumCoverArt << InfoArtistImages; + supportedPushTypes << InfoSubmitScrobble << InfoSubmitNowPlaying; + parent->registerInfoTypes( this, supportedGetTypes, supportedPushTypes ); + + connect( parent, SIGNAL( namChanged() ), SLOT( namChangedSlot() ) ); /* Your API Key is 7194b85b6d1f424fe1668173a78c0c4a @@ -94,6 +98,14 @@ LastFmPlugin::~LastFmPlugin() } +void +LastFmPlugin::namChangedSlot() +{ + qDebug() << Q_FUNC_INFO; + lastfm::setNetworkAccessManager( m_infoSystemWorker->nam() ); +} + + void LastFmPlugin::dataError( const QString &caller, const Tomahawk::InfoSystem::InfoType type, const QVariant &input, const Tomahawk::InfoSystem::InfoCustomData &customData ) { @@ -109,14 +121,6 @@ LastFmPlugin::getInfo( const QString caller, const Tomahawk::InfoSystem::InfoTyp switch ( type ) { - case InfoMiscSubmitNowPlaying: - nowPlaying( caller, type, input, customData ); - break; - - case InfoMiscSubmitScrobble: - scrobble( caller, type, input, customData ); - break; - case InfoArtistImages: fetchArtistImages( caller, type, input, customData ); break; @@ -132,19 +136,34 @@ LastFmPlugin::getInfo( const QString caller, const Tomahawk::InfoSystem::InfoTyp void -LastFmPlugin::nowPlaying( const QString &caller, const InfoType type, const QVariant &input, const Tomahawk::InfoSystem::InfoCustomData &customData ) +LastFmPlugin::pushInfo( const QString caller, const Tomahawk::InfoSystem::InfoType type, const QVariant input ) +{ + qDebug() << Q_FUNC_INFO; + + switch ( type ) + { + case InfoSubmitNowPlaying: + nowPlaying( caller, type, input ); + break; + + case InfoSubmitScrobble: + scrobble( caller, type, input ); + break; + + default: + return; + } +} + +void +LastFmPlugin::nowPlaying( const QString &caller, const InfoType type, const QVariant &input ) { if ( !input.canConvert< Tomahawk::InfoSystem::InfoCriteriaHash >() || !m_scrobbler ) - { - dataError( caller, type, input, customData ); return; - } + InfoCriteriaHash hash = input.value< Tomahawk::InfoSystem::InfoCriteriaHash >(); if ( !hash.contains( "title" ) || !hash.contains( "artist" ) || !hash.contains( "album" ) || !hash.contains( "duration" ) ) - { - dataError( caller, type, input, customData ); return; - } m_track = lastfm::MutableTrack(); m_track.stamp(); @@ -157,26 +176,20 @@ LastFmPlugin::nowPlaying( const QString &caller, const InfoType type, const QVar m_track.setSource( lastfm::Track::Player ); m_scrobbler->nowPlaying( m_track ); - emit info( caller, type, input, QVariant(), customData ); } void -LastFmPlugin::scrobble( const QString &caller, const InfoType type, const QVariant &input, const Tomahawk::InfoSystem::InfoCustomData &customData ) +LastFmPlugin::scrobble( const QString &caller, const InfoType type, const QVariant &input ) { Q_ASSERT( QThread::currentThread() == thread() ); if ( !m_scrobbler || m_track.isNull() ) - { - dataError( caller, type, input, customData ); return; - } qDebug() << Q_FUNC_INFO << m_track.toString(); m_scrobbler->cache( m_track ); m_scrobbler->submit(); - - emit info( caller, type, input, QVariant(), customData ); } @@ -241,7 +254,7 @@ LastFmPlugin::notInCacheSlot( const QHash criteria, const QStr QString imgurl = "http://ws.audioscrobbler.com/2.0/?method=album.imageredirect&artist=%1&album=%2&autocorrect=1&size=medium&api_key=7a90f6672a04b809ee309af169f34b8b"; QNetworkRequest req( imgurl.arg( artistName ).arg( albumName ) ); - QNetworkReply* reply = TomahawkUtils::nam()->get( req ); + QNetworkReply* reply = m_infoSystemWorker->nam()->get( req ); reply->setProperty( "customData", QVariant::fromValue( customData ) ); reply->setProperty( "origData", input ); reply->setProperty( "caller", caller ); @@ -257,7 +270,7 @@ LastFmPlugin::notInCacheSlot( const QHash criteria, const QStr QString imgurl = "http://ws.audioscrobbler.com/2.0/?method=artist.imageredirect&artist=%1&autocorrect=1&size=medium&api_key=7a90f6672a04b809ee309af169f34b8b"; QNetworkRequest req( imgurl.arg( artistName ) ); - QNetworkReply* reply = TomahawkUtils::nam()->get( req ); + QNetworkReply* reply = m_infoSystemWorker->nam()->get( req ); reply->setProperty( "customData", QVariant::fromValue( customData ) ); reply->setProperty( "origData", input ); reply->setProperty( "caller", caller ); @@ -312,7 +325,7 @@ LastFmPlugin::coverArtReturned() { // Follow HTTP redirect QNetworkRequest req( redir ); - QNetworkReply* newReply = TomahawkUtils::nam()->get( req ); + QNetworkReply* newReply = m_infoSystemWorker->nam()->get( req ); newReply->setProperty( "origData", reply->property( "origData" ) ); newReply->setProperty( "customData", reply->property( "customData" ) ); newReply->setProperty( "caller", reply->property( "caller" ) ); @@ -362,7 +375,7 @@ LastFmPlugin::artistImagesReturned() { // Follow HTTP redirect QNetworkRequest req( redir ); - QNetworkReply* newReply = TomahawkUtils::nam()->get( req ); + QNetworkReply* newReply = m_infoSystemWorker->nam()->get( req ); newReply->setProperty( "origData", reply->property( "origData" ) ); newReply->setProperty( "customData", reply->property( "customData" ) ); newReply->setProperty( "caller", reply->property( "caller" ) ); diff --git a/src/libtomahawk/infosystem/infoplugins/lastfmplugin.h b/src/libtomahawk/infosystem/infoplugins/lastfmplugin.h index d42ee411b..62b345b28 100644 --- a/src/libtomahawk/infosystem/infoplugins/lastfmplugin.h +++ b/src/libtomahawk/infosystem/infoplugins/lastfmplugin.h @@ -19,6 +19,7 @@ #ifndef LASTFMPLUGIN_H #define LASTFMPLUGIN_H #include "infosystem/infosystem.h" +#include "infosystem/infosystemworker.h" #include "result.h" #include @@ -40,7 +41,7 @@ class LastFmPlugin : public InfoPlugin Q_OBJECT public: - LastFmPlugin( QObject *parent ); + LastFmPlugin( InfoSystemWorker *parent ); virtual ~LastFmPlugin(); public slots: @@ -50,17 +51,21 @@ public slots: void coverArtReturned(); void artistImagesReturned(); + void namChangedSlot(); + protected slots: virtual void getInfo( const QString caller, const Tomahawk::InfoSystem::InfoType type, const QVariant input, const Tomahawk::InfoSystem::InfoCustomData customData ); virtual void notInCacheSlot( const Tomahawk::InfoSystem::InfoCriteriaHash criteria, const QString caller, const Tomahawk::InfoSystem::InfoType type, const QVariant input, const Tomahawk::InfoSystem::InfoCustomData customData ); + + virtual void pushInfo( const QString caller, const Tomahawk::InfoSystem::InfoType type, const QVariant data ); private: void fetchCoverArt( const QString &caller, const Tomahawk::InfoSystem::InfoType type, const QVariant &input, const Tomahawk::InfoSystem::InfoCustomData &customData ); void fetchArtistImages( const QString &caller, const Tomahawk::InfoSystem::InfoType type, const QVariant &input, const Tomahawk::InfoSystem::InfoCustomData &customData ); void createScrobbler(); - void scrobble( const QString &caller, const Tomahawk::InfoSystem::InfoType type, const QVariant &input, const Tomahawk::InfoSystem::InfoCustomData &customData ); - void nowPlaying( const QString &caller, const Tomahawk::InfoSystem::InfoType type, const QVariant &input, const Tomahawk::InfoSystem::InfoCustomData &customData ); + void scrobble( const QString &caller, const Tomahawk::InfoSystem::InfoType type, const QVariant &input ); + void nowPlaying( const QString &caller, const Tomahawk::InfoSystem::InfoType type, const QVariant &input ); void dataError( const QString &caller, const Tomahawk::InfoSystem::InfoType type, const QVariant &input, const Tomahawk::InfoSystem::InfoCustomData &customData ); @@ -71,6 +76,8 @@ private: QList< QUrl > m_badUrls; QNetworkReply* m_authJob; + + InfoSystemWorker* m_infoSystemWorker; }; } diff --git a/src/libtomahawk/infosystem/infoplugins/musixmatchplugin.cpp b/src/libtomahawk/infosystem/infoplugins/musixmatchplugin.cpp index c595843c9..f8698c0f4 100644 --- a/src/libtomahawk/infosystem/infoplugins/musixmatchplugin.cpp +++ b/src/libtomahawk/infosystem/infoplugins/musixmatchplugin.cpp @@ -27,14 +27,15 @@ using namespace Tomahawk::InfoSystem; // for internal neatness -MusixMatchPlugin::MusixMatchPlugin(QObject *parent) +MusixMatchPlugin::MusixMatchPlugin(InfoSystemWorker *parent) : InfoPlugin(parent) , m_apiKey("61be4ea5aea7dd942d52b2f1311dd9fe") + , m_infoSystemWorker( parent ) { qDebug() << Q_FUNC_INFO; QSet< InfoType > supportedTypes; supportedTypes << Tomahawk::InfoSystem::InfoTrackLyrics; - qobject_cast< InfoSystem* >(parent)->registerInfoTypes(this, supportedTypes); + parent->registerInfoTypes( this, supportedTypes, QSet< InfoType>() ); } MusixMatchPlugin::~MusixMatchPlugin() @@ -62,7 +63,7 @@ MusixMatchPlugin::getInfo( const QString caller, const Tomahawk::InfoSystem::Inf url.addQueryItem("apikey", m_apiKey); url.addQueryItem("q_artist", artist); url.addQueryItem("q_track", track); - QNetworkReply* reply = TomahawkUtils::nam()->get(QNetworkRequest(url)); + QNetworkReply* reply = m_infoSystemWorker->nam()->get(QNetworkRequest(url)); reply->setProperty("customData", QVariant::fromValue(customData)); reply->setProperty("origData", input); reply->setProperty("caller", caller); @@ -120,7 +121,7 @@ MusixMatchPlugin::trackSearchSlot() QUrl url(requestString); url.addQueryItem("apikey", m_apiKey); url.addQueryItem("track_id", track_id); - QNetworkReply* newReply = TomahawkUtils::nam()->get(QNetworkRequest(url)); + QNetworkReply* newReply = m_infoSystemWorker->nam()->get(QNetworkRequest(url)); newReply->setProperty("origData", oldReply->property("origData")); newReply->setProperty("customData", oldReply->property("customData")); newReply->setProperty("caller", oldReply->property("caller")); diff --git a/src/libtomahawk/infosystem/infoplugins/musixmatchplugin.h b/src/libtomahawk/infosystem/infoplugins/musixmatchplugin.h index 0f816dc0f..f2d7e87b8 100644 --- a/src/libtomahawk/infosystem/infoplugins/musixmatchplugin.h +++ b/src/libtomahawk/infosystem/infoplugins/musixmatchplugin.h @@ -20,6 +20,7 @@ #define MUSIXMATCHPLUGIN_H #include "infosystem/infosystem.h" +#include "infosystem/infosystemworker.h" class QNetworkReply; @@ -34,7 +35,7 @@ class MusixMatchPlugin : public InfoPlugin Q_OBJECT public: - MusixMatchPlugin( QObject *parent ); + MusixMatchPlugin( InfoSystemWorker *parent ); virtual ~MusixMatchPlugin(); public slots: @@ -43,11 +44,29 @@ public slots: protected slots: virtual void getInfo( const QString caller, const Tomahawk::InfoSystem::InfoType type, const QVariant input, const Tomahawk::InfoSystem::InfoCustomData customData ); + + virtual void pushInfo( const QString caller, const Tomahawk::InfoSystem::InfoType type, const QVariant data ) + { + Q_UNUSED( caller ); + Q_UNUSED( type ); + Q_UNUSED( data ); + } + + virtual void notInCacheSlot( const Tomahawk::InfoSystem::InfoCriteriaHash criteria, const QString caller, const Tomahawk::InfoSystem::InfoType type, const QVariant input, const Tomahawk::InfoSystem::InfoCustomData customData ) + { + Q_UNUSED( criteria ); + Q_UNUSED( caller ); + Q_UNUSED( type ); + Q_UNUSED( input ); + Q_UNUSED( customData ); + } private: bool isValidTrackData( const QString &caller, const QVariant &input, const InfoCustomData &customData ); QString m_apiKey; + + InfoSystemWorker* m_infoSystemWorker; }; } diff --git a/src/libtomahawk/infosystem/infosystem.cpp b/src/libtomahawk/infosystem/infosystem.cpp index 0fec3a881..e42edda5b 100644 --- a/src/libtomahawk/infosystem/infosystem.cpp +++ b/src/libtomahawk/infosystem/infosystem.cpp @@ -19,6 +19,7 @@ #include #include "infosystem.h" +#include "tomahawksettings.h" #include "utils/tomahawkutils.h" #include "infosystemcache.h" #include "infoplugins/echonestplugin.h" @@ -31,11 +32,11 @@ namespace Tomahawk namespace InfoSystem { -InfoPlugin::InfoPlugin(QObject *parent) - :QObject( parent ) - { - qDebug() << Q_FUNC_INFO; - } +InfoPlugin::InfoPlugin( InfoSystemWorker *parent ) + :QObject( parent ) +{ + qDebug() << Q_FUNC_INFO; +} InfoSystem* InfoSystem::s_instance = 0; @@ -59,59 +60,33 @@ InfoSystem::InfoSystem(QObject *parent) m_cache->moveToThread( m_infoSystemCacheThreadController ); m_infoSystemCacheThreadController->start( QThread::IdlePriority ); - InfoPluginPtr enptr( new EchoNestPlugin( this ) ); - m_plugins.append( enptr ); - InfoPluginPtr mmptr( new MusixMatchPlugin( this ) ); - m_plugins.append( mmptr ); - InfoPluginPtr lfmptr( new LastFmPlugin( this ) ); - m_plugins.append( lfmptr ); + m_infoSystemWorkerThreadController = new QThread( this ); + m_worker = new InfoSystemWorker(); + m_worker->moveToThread( m_infoSystemWorkerThreadController ); + m_infoSystemWorkerThreadController->start(); - Q_FOREACH( InfoPluginPtr plugin, m_plugins ) - { - connect( - plugin.data(), - SIGNAL( info( QString, Tomahawk::InfoSystem::InfoType, QVariant, QVariant, Tomahawk::InfoSystem::InfoCustomData ) ), - this, - SLOT( infoSlot( QString, Tomahawk::InfoSystem::InfoType, QVariant, QVariant, Tomahawk::InfoSystem::InfoCustomData ) ), - Qt::UniqueConnection - ); + QMetaObject::invokeMethod( m_worker, "init", Qt::QueuedConnection ); + + connect( TomahawkSettings::instance(), SIGNAL( changed() ), SLOT( newNam() ) ); - connect( - plugin.data(), - SIGNAL( getCachedInfo( Tomahawk::InfoSystem::InfoCriteriaHash, qint64, QString, Tomahawk::InfoSystem::InfoType, QVariant, Tomahawk::InfoSystem::InfoCustomData ) ), - m_cache, - SLOT( getCachedInfoSlot( Tomahawk::InfoSystem::InfoCriteriaHash, qint64, QString, Tomahawk::InfoSystem::InfoType, QVariant, Tomahawk::InfoSystem::InfoCustomData ) ) - ); - connect( - m_cache, - SIGNAL( notInCache( Tomahawk::InfoSystem::InfoCriteriaHash, QString, Tomahawk::InfoSystem::InfoType, QVariant, Tomahawk::InfoSystem::InfoCustomData ) ), - plugin.data(), - SLOT( notInCacheSlot( Tomahawk::InfoSystem::InfoCriteriaHash, QString, Tomahawk::InfoSystem::InfoType, QVariant, Tomahawk::InfoSystem::InfoCustomData ) ) - ); - connect( - plugin.data(), - SIGNAL( updateCache( Tomahawk::InfoSystem::InfoCriteriaHash, qint64, Tomahawk::InfoSystem::InfoType, QVariant ) ), - m_cache, - SLOT( updateCacheSlot( Tomahawk::InfoSystem::InfoCriteriaHash, qint64, Tomahawk::InfoSystem::InfoType, QVariant ) ) - ); - } connect( m_cache, SIGNAL( info( QString, Tomahawk::InfoSystem::InfoType, QVariant, QVariant, Tomahawk::InfoSystem::InfoCustomData ) ), this, SLOT( infoSlot( QString, Tomahawk::InfoSystem::InfoType, QVariant, QVariant, Tomahawk::InfoSystem::InfoCustomData ) ), Qt::UniqueConnection ); + + connect( m_worker, SIGNAL( info( QString, Tomahawk::InfoSystem::InfoType, QVariant, QVariant, Tomahawk::InfoSystem::InfoCustomData ) ), + this, SLOT( infoSlot( QString, Tomahawk::InfoSystem::InfoType, QVariant, QVariant, Tomahawk::InfoSystem::InfoCustomData ) ), Qt::UniqueConnection ); } InfoSystem::~InfoSystem() { qDebug() << Q_FUNC_INFO; - Q_FOREACH( InfoPluginPtr plugin, m_plugins ) - { - if( plugin ) - delete plugin.data(); - } + if ( m_infoSystemCacheThreadController ) + m_infoSystemCacheThreadController->quit(); + if ( m_infoSystemWorkerThreadController ) + m_infoSystemWorkerThreadController->quit(); + if( m_infoSystemCacheThreadController ) { - m_infoSystemCacheThreadController->quit(); - while( !m_infoSystemCacheThreadController->isFinished() ) { QCoreApplication::processEvents( QEventLoop::AllEvents, 200 ); @@ -127,56 +102,73 @@ InfoSystem::~InfoSystem() delete m_infoSystemCacheThreadController; m_infoSystemCacheThreadController = 0; } + + if( m_infoSystemWorkerThreadController ) + { + while( !m_infoSystemWorkerThreadController->isFinished() ) + { + QCoreApplication::processEvents( QEventLoop::AllEvents, 200 ); + TomahawkUtils::Sleep::msleep( 100 ); + } + + if( m_worker ) + { + delete m_worker; + m_worker = 0; + } + + delete m_infoSystemWorkerThreadController; + m_infoSystemWorkerThreadController = 0; + } } -void InfoSystem::registerInfoTypes(const InfoPluginPtr &plugin, const QSet< InfoType >& types) + +void +InfoSystem::newNam() const { qDebug() << Q_FUNC_INFO; - Q_FOREACH(InfoType type, types) - m_infoMap[type].append(plugin); + QMetaObject::invokeMethod( m_worker, "newNam", Qt::QueuedConnection ); } -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& input, InfoCustomData customData) +void +InfoSystem::getInfo( const QString &caller, const InfoType type, const QVariant& input, InfoCustomData customData ) { qDebug() << Q_FUNC_INFO; - QLinkedList< InfoPluginPtr > providers = determineOrderedMatches(type); - if (providers.isEmpty()) - { - emit info(caller, InfoNoInfo, QVariant(), QVariant(), customData); - emit finished(caller); - return; - } - - InfoPluginPtr ptr = providers.first(); - if (!ptr) - { - emit info(caller, InfoNoInfo, QVariant(), QVariant(), customData); - emit finished(caller); - return; - } m_dataTracker[caller][type] = m_dataTracker[caller][type] + 1; qDebug() << "current count in dataTracker for type" << type << "is" << m_dataTracker[caller][type]; - QMetaObject::invokeMethod( ptr.data(), "getInfo", Qt::QueuedConnection, Q_ARG( QString, caller ), Q_ARG( Tomahawk::InfoSystem::InfoType, type ), Q_ARG( QVariant, input ), Q_ARG( Tomahawk::InfoSystem::InfoCustomData, customData ) ); + QMetaObject::invokeMethod( m_worker, "getInfo", Qt::QueuedConnection, Q_ARG( QString, caller ), Q_ARG( Tomahawk::InfoSystem::InfoType, type ), Q_ARG( QVariant, input ), Q_ARG( Tomahawk::InfoSystem::InfoCustomData, customData ) ); } -void InfoSystem::getInfo(const QString &caller, const InfoMap &input, InfoCustomData customData) + +void +InfoSystem::getInfo( const QString &caller, const InfoMap &input, InfoCustomData customData ) { Q_FOREACH( InfoType type, input.keys() ) - getInfo(caller, type, input[type], customData); + getInfo( caller, type, input[type], customData ); } -void InfoSystem::infoSlot(QString target, InfoType type, QVariant input, QVariant output, InfoCustomData customData) + +void +InfoSystem::pushInfo( const QString &caller, const InfoType type, const QVariant& input ) +{ + qDebug() << Q_FUNC_INFO; + + QMetaObject::invokeMethod( m_worker, "pushInfo", Qt::QueuedConnection, Q_ARG( QString, caller ), Q_ARG( Tomahawk::InfoSystem::InfoType, type ), Q_ARG( QVariant, input ) ); +} + + +void +InfoSystem::pushInfo( const QString &caller, const InfoMap &input ) +{ + Q_FOREACH( InfoType type, input.keys() ) + pushInfo( caller, type, input[type] ); +} + + +void +InfoSystem::infoSlot( QString target, InfoType type, QVariant input, QVariant output, InfoCustomData customData ) { qDebug() << Q_FUNC_INFO; qDebug() << "current count in dataTracker is " << m_dataTracker[target][type]; diff --git a/src/libtomahawk/infosystem/infosystem.h b/src/libtomahawk/infosystem/infosystem.h index 006174b54..f4fe24871 100644 --- a/src/libtomahawk/infosystem/infosystem.h +++ b/src/libtomahawk/infosystem/infosystem.h @@ -36,62 +36,63 @@ namespace Tomahawk { namespace InfoSystem { class InfoSystemCache; +class InfoSystemWorker; -enum InfoType { +enum InfoType { // as items are saved in cache, mark them here to not change them InfoTrackID = 0, - InfoTrackArtist, - InfoTrackAlbum, - InfoTrackGenre, - InfoTrackComposer, - InfoTrackDate, - InfoTrackNumber, - InfoTrackDiscNumber, - InfoTrackBitRate, - InfoTrackLength, - InfoTrackSampleRate, - InfoTrackFileSize, - InfoTrackBPM, - InfoTrackReplayGain, - InfoTrackReplayPeakGain, - InfoTrackLyrics, - InfoTrackLocation, - InfoTrackProfile, - InfoTrackEnergy, - InfoTrackDanceability, - InfoTrackTempo, - InfoTrackLoudness, + InfoTrackArtist = 1, + InfoTrackAlbum = 2, + InfoTrackGenre = 3, + InfoTrackComposer = 4, + InfoTrackDate = 5, + InfoTrackNumber = 6, + InfoTrackDiscNumber = 7, + InfoTrackBitRate = 8, + InfoTrackLength = 9, + InfoTrackSampleRate = 10, + InfoTrackFileSize = 11, + InfoTrackBPM = 12, + InfoTrackReplayGain = 13, + InfoTrackReplayPeakGain = 14, + InfoTrackLyrics = 15, + InfoTrackLocation = 16, + InfoTrackProfile = 17, + InfoTrackEnergy = 18, + InfoTrackDanceability = 19, + InfoTrackTempo = 20, + InfoTrackLoudness = 21, - InfoArtistID, - InfoArtistName, - InfoArtistBiography, - InfoArtistBlog, - InfoArtistFamiliarity, - InfoArtistHotttness, - InfoArtistImages, - InfoArtistNews, - InfoArtistProfile, - InfoArtistReviews, - InfoArtistSongs, - InfoArtistSimilars, - InfoArtistTerms, - InfoArtistLinks, - InfoArtistVideos, + InfoArtistID = 22, + InfoArtistName = 23, + InfoArtistBiography = 24, + InfoArtistBlog = 25, + InfoArtistFamiliarity = 26, + InfoArtistHotttness = 27, + InfoArtistImages = 28, + InfoArtistNews = 29, + InfoArtistProfile = 30, + InfoArtistReviews = 31, + InfoArtistSongs = 32, + InfoArtistSimilars = 33, + InfoArtistTerms = 34, + InfoArtistLinks = 35, + InfoArtistVideos = 36, - InfoAlbumID, - InfoAlbumName, - InfoAlbumArtist, - InfoAlbumDate, - InfoAlbumGenre, - InfoAlbumComposer, - InfoAlbumCoverArt, + InfoAlbumID = 37, + InfoAlbumName = 38, + InfoAlbumArtist = 39, + InfoAlbumDate = 40, + InfoAlbumGenre = 41, + InfoAlbumComposer = 42, + InfoAlbumCoverArt = 43, //cached -- do not change - InfoMiscTopHotttness, - InfoMiscTopTerms, + InfoMiscTopHotttness = 44, + InfoMiscTopTerms = 45, - InfoMiscSubmitNowPlaying, - InfoMiscSubmitScrobble, + InfoSubmitNowPlaying = 46, + InfoSubmitScrobble = 47, - InfoNoInfo + InfoNoInfo = 48 }; typedef QMap< InfoType, QVariant > InfoMap; @@ -104,12 +105,9 @@ class DLLEXPORT InfoPlugin : public QObject Q_OBJECT public: - InfoPlugin( QObject *parent ); + InfoPlugin( InfoSystemWorker *parent ); - virtual ~InfoPlugin() - { - qDebug() << Q_FUNC_INFO; - } + virtual ~InfoPlugin() {} signals: void getCachedInfo( Tomahawk::InfoSystem::InfoCriteriaHash criteria, qint64 newMaxAge, QString caller, Tomahawk::InfoSystem::InfoType type, QVariant input, Tomahawk::InfoSystem::InfoCustomData customData ); @@ -119,16 +117,8 @@ signals: protected slots: virtual void getInfo( const QString caller, const Tomahawk::InfoSystem::InfoType type, const QVariant data, const Tomahawk::InfoSystem::InfoCustomData customData ) = 0; - - //FIXME: Make pure virtual when everything supports it - virtual void notInCacheSlot( const Tomahawk::InfoSystem::InfoCriteriaHash criteria, const QString caller, const Tomahawk::InfoSystem::InfoType type, const QVariant input, const Tomahawk::InfoSystem::InfoCustomData customData ) - { - Q_UNUSED( criteria ); - Q_UNUSED( caller ); - Q_UNUSED( type ); - Q_UNUSED( input ); - Q_UNUSED( customData ); - } + virtual void pushInfo( const QString caller, const Tomahawk::InfoSystem::InfoType type, const QVariant data ) = 0; + virtual void notInCacheSlot( const Tomahawk::InfoSystem::InfoCriteriaHash criteria, const QString caller, const Tomahawk::InfoSystem::InfoType type, const QVariant input, const Tomahawk::InfoSystem::InfoCustomData customData ) = 0; protected: InfoType m_type; @@ -139,13 +129,6 @@ private: typedef QWeakPointer< InfoPlugin > InfoPluginPtr; -class DLLEXPORT InfoSystemWorker : public QObject -{ - Q_OBJECT - InfoSystemWorker() {}; - ~InfoSystemWorker() {}; -}; - class DLLEXPORT InfoSystem : public QObject { Q_OBJECT @@ -156,12 +139,12 @@ public: InfoSystem( QObject *parent ); ~InfoSystem(); - void registerInfoTypes( const InfoPluginPtr &plugin, const QSet< InfoType > &types ); - void getInfo( const QString &caller, const InfoType type, const QVariant &input, InfoCustomData customData ); void getInfo( const QString &caller, const InfoMap &input, InfoCustomData customData ); + void pushInfo( const QString &caller, const InfoType type, const QVariant &input ); + void pushInfo( const QString &caller, const InfoMap &input ); - InfoSystemCache* getCache() { return m_cache; } + InfoSystemCache* getCache() const { return m_cache; } signals: void info( QString caller, Tomahawk::InfoSystem::InfoType, QVariant input, QVariant output, Tomahawk::InfoSystem::InfoCustomData customData ); @@ -170,14 +153,9 @@ signals: public slots: void infoSlot( const QString target, const Tomahawk::InfoSystem::InfoType type, const QVariant input, const QVariant output, const Tomahawk::InfoSystem::InfoCustomData customData ); + void newNam() const; + private: - QLinkedList< InfoPluginPtr > determineOrderedMatches( const InfoType type ) const; - - QMap< InfoType, QLinkedList< InfoPluginPtr > > m_infoMap; - - // For now, statically instantiate plugins; this is just somewhere to keep them - QLinkedList< InfoPluginPtr > m_plugins; - QHash< QString, QHash< InfoType, int > > m_dataTracker; InfoSystemCache* m_cache; diff --git a/src/libtomahawk/infosystem/infosystemworker.cpp b/src/libtomahawk/infosystem/infosystemworker.cpp new file mode 100644 index 000000000..c70cbe019 --- /dev/null +++ b/src/libtomahawk/infosystem/infosystemworker.cpp @@ -0,0 +1,190 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2010-2011, Christian Muehlhaeuser + * + * Tomahawk 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 3 of the License, or + * (at your option) any later version. + * + * Tomahawk 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 Tomahawk. If not, see . + */ + +#include +#include +#include + +#include "infosystemworker.h" +#include "utils/tomahawkutils.h" +#include "infosystemcache.h" +#include "infoplugins/echonestplugin.h" +#include "infoplugins/musixmatchplugin.h" +#include "infoplugins/lastfmplugin.h" + +namespace Tomahawk +{ + +namespace InfoSystem +{ + +InfoSystemWorker::InfoSystemWorker() + : m_nam( 0 ) +{ + qDebug() << Q_FUNC_INFO; +} + + +InfoSystemWorker::~InfoSystemWorker() +{ + qDebug() << Q_FUNC_INFO; + Q_FOREACH( InfoPluginPtr plugin, m_plugins ) + { + if( plugin ) + delete plugin.data(); + } +} + + +void InfoSystemWorker::init() +{ + InfoPluginPtr enptr( new EchoNestPlugin( this ) ); + m_plugins.append( enptr ); + InfoPluginPtr mmptr( new MusixMatchPlugin( this ) ); + m_plugins.append( mmptr ); + InfoPluginPtr lfmptr( new LastFmPlugin( this ) ); + m_plugins.append( lfmptr ); + + InfoSystemCache *cache = InfoSystem::instance()->getCache(); + + Q_FOREACH( InfoPluginPtr plugin, m_plugins ) + { + connect( + plugin.data(), + SIGNAL( info( QString, Tomahawk::InfoSystem::InfoType, QVariant, QVariant, Tomahawk::InfoSystem::InfoCustomData ) ), + InfoSystem::instance(), + SLOT( infoSlot( QString, Tomahawk::InfoSystem::InfoType, QVariant, QVariant, Tomahawk::InfoSystem::InfoCustomData ) ), + Qt::UniqueConnection + ); + + connect( + plugin.data(), + SIGNAL( getCachedInfo( Tomahawk::InfoSystem::InfoCriteriaHash, qint64, QString, Tomahawk::InfoSystem::InfoType, QVariant, Tomahawk::InfoSystem::InfoCustomData ) ), + cache, + SLOT( getCachedInfoSlot( Tomahawk::InfoSystem::InfoCriteriaHash, qint64, QString, Tomahawk::InfoSystem::InfoType, QVariant, Tomahawk::InfoSystem::InfoCustomData ) ) + ); + connect( + cache, + SIGNAL( notInCache( Tomahawk::InfoSystem::InfoCriteriaHash, QString, Tomahawk::InfoSystem::InfoType, QVariant, Tomahawk::InfoSystem::InfoCustomData ) ), + plugin.data(), + SLOT( notInCacheSlot( Tomahawk::InfoSystem::InfoCriteriaHash, QString, Tomahawk::InfoSystem::InfoType, QVariant, Tomahawk::InfoSystem::InfoCustomData ) ) + ); + connect( + plugin.data(), + SIGNAL( updateCache( Tomahawk::InfoSystem::InfoCriteriaHash, qint64, Tomahawk::InfoSystem::InfoType, QVariant ) ), + cache, + SLOT( updateCacheSlot( Tomahawk::InfoSystem::InfoCriteriaHash, qint64, Tomahawk::InfoSystem::InfoType, QVariant ) ) + ); + } + + QMetaObject::invokeMethod( this, "newNam" ); +} + + +void +InfoSystemWorker::registerInfoTypes( const InfoPluginPtr &plugin, const QSet< InfoType >& getTypes, const QSet< InfoType >& pushTypes ) +{ + qDebug() << Q_FUNC_INFO; + Q_FOREACH( InfoType type, getTypes ) + m_infoGetMap[type].append( plugin ); + Q_FOREACH( InfoType type, pushTypes ) + m_infoPushMap[type].append( plugin ); +} + + +QLinkedList< InfoPluginPtr > +InfoSystemWorker::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_infoGetMap[type] ) + providers << ptr; + return providers; +} + + +void +InfoSystemWorker::getInfo( QString caller, InfoType type, QVariant input, InfoCustomData customData ) +{ + qDebug() << Q_FUNC_INFO; + QLinkedList< InfoPluginPtr > providers = determineOrderedMatches(type); + if ( providers.isEmpty() ) + { + emit info( caller, type, QVariant(), QVariant(), customData ); + return; + } + + InfoPluginPtr ptr = providers.first(); + if ( !ptr ) + { + emit info( caller, type, QVariant(), QVariant(), customData ); + return; + } + + QMetaObject::invokeMethod( ptr.data(), "getInfo", Qt::QueuedConnection, Q_ARG( QString, caller ), Q_ARG( Tomahawk::InfoSystem::InfoType, type ), Q_ARG( QVariant, input ), Q_ARG( Tomahawk::InfoSystem::InfoCustomData, customData ) ); +} + + +void +InfoSystemWorker::pushInfo( const QString caller, const InfoType type, const QVariant input ) +{ + qDebug() << Q_FUNC_INFO; + + Q_FOREACH( InfoPluginPtr ptr, m_infoPushMap[type] ) + { + if( ptr ) + QMetaObject::invokeMethod( ptr.data(), "pushInfo", Qt::QueuedConnection, Q_ARG( QString, caller ), Q_ARG( Tomahawk::InfoSystem::InfoType, type ), Q_ARG( QVariant, input ) ); + } +} + + +QNetworkAccessManager* +InfoSystemWorker::nam() const +{ + return m_nam; +} + + +void +InfoSystemWorker::newNam() +{ + qDebug() << Q_FUNC_INFO; + QNetworkAccessManager *newNam = new QNetworkAccessManager(); + if ( m_nam ) + { + delete m_nam; + } + QNetworkAccessManager *oldNam = TomahawkUtils::nam(); + if ( !oldNam ) + { + m_nam = newNam; + return; + } + newNam->setConfiguration( oldNam->configuration() ); + newNam->setNetworkAccessible( oldNam->networkAccessible() ); + newNam->setProxy( oldNam->proxy() ); + m_nam = newNam; + + emit namChanged(); +} + + +} //namespace InfoSystem + +} //namespace Tomahawk diff --git a/src/libtomahawk/infosystem/infosystemworker.h b/src/libtomahawk/infosystem/infosystemworker.h new file mode 100644 index 000000000..e42f89a36 --- /dev/null +++ b/src/libtomahawk/infosystem/infosystemworker.h @@ -0,0 +1,76 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2010-2011, Christian Muehlhaeuser + * + * Tomahawk 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 3 of the License, or + * (at your option) any later version. + * + * Tomahawk 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 Tomahawk. If not, see . + */ + +#ifndef TOMAHAWK_INFOSYSTEMWORKER_H +#define TOMAHAWK_INFOSYSTEMWORKER_H + +#include "infosystem/infosystem.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dllmacro.h" + +namespace Tomahawk { + +namespace InfoSystem { + +class DLLEXPORT InfoSystemWorker : public QObject +{ + Q_OBJECT + +public: + InfoSystemWorker(); + ~InfoSystemWorker(); + + void registerInfoTypes( const InfoPluginPtr &plugin, const QSet< InfoType > &getTypes, const QSet< InfoType > &pushTypes ); + QNetworkAccessManager* nam() const; + +signals: + void info( QString target, Tomahawk::InfoSystem::InfoType, QVariant input, QVariant output, Tomahawk::InfoSystem::InfoCustomData customData ); + void namChanged(); + +public slots: + void init(); + void getInfo( const QString caller, const Tomahawk::InfoSystem::InfoType type, const QVariant input, const Tomahawk::InfoSystem::InfoCustomData customData ); + void pushInfo( const QString caller, const Tomahawk::InfoSystem::InfoType type, const QVariant input ); + void newNam(); + +private: + QLinkedList< InfoPluginPtr > determineOrderedMatches( const InfoType type ) const; + + // For now, statically instantiate plugins; this is just somewhere to keep them + QLinkedList< InfoPluginPtr > m_plugins; + + QMap< InfoType, QLinkedList< InfoPluginPtr > > m_infoGetMap; + QMap< InfoType, QLinkedList< InfoPluginPtr > > m_infoPushMap; + + QNetworkAccessManager *m_nam; +}; + +} + +} + +#endif // TOMAHAWK_INFOSYSTEMWORKER_H diff --git a/src/libtomahawk/network/servent.cpp b/src/libtomahawk/network/servent.cpp index a1ddff6a2..447818785 100644 --- a/src/libtomahawk/network/servent.cpp +++ b/src/libtomahawk/network/servent.cpp @@ -496,13 +496,13 @@ Servent::connectToPeer( const QString& ha, int port, const QString &key, const Q void Servent::connectToPeer( const QString& ha, int port, const QString &key, Connection* conn ) { + qDebug() << "Servent::connectToPeer:" << ha << ":" << port + << thread() << QThread::currentThread(); + Q_ASSERT( port > 0 ); Q_ASSERT( conn ); // Q_ASSERT( this->thread() == QThread::currentThread() ); - qDebug() << "Servent::connectToPeer:" << ha << ":" << port - << thread() << QThread::currentThread(); - if ( ( ha == m_externalAddress.toString() || ha == m_externalHostname ) && ( port == m_externalPort ) ) { diff --git a/src/libtomahawk/playlist/dynamic/DynamicPlaylist.cpp b/src/libtomahawk/playlist/dynamic/DynamicPlaylist.cpp index 5122f8260..5b6481a6f 100644 --- a/src/libtomahawk/playlist/dynamic/DynamicPlaylist.cpp +++ b/src/libtomahawk/playlist/dynamic/DynamicPlaylist.cpp @@ -30,7 +30,7 @@ using namespace Tomahawk; DynamicPlaylist::DynamicPlaylist(const Tomahawk::source_ptr& author, const QString& type ) - : Playlist(author) +: Playlist(author) { qDebug() << Q_FUNC_INFO << "JSON"; m_generator = geninterface_ptr( GeneratorFactory::create( type ) ); @@ -54,7 +54,7 @@ DynamicPlaylist::DynamicPlaylist ( const Tomahawk::source_ptr& src, bool shared, int lastmod, const QString& guid ) - : Playlist( src, currentrevision, title, info, creator, createdOn, shared, lastmod, guid ) +: Playlist( src, currentrevision, title, info, creator, createdOn, shared, lastmod, guid ) { qDebug() << "Creating Dynamic Playlist 1"; // TODO instantiate generator @@ -72,7 +72,7 @@ DynamicPlaylist::DynamicPlaylist ( const Tomahawk::source_ptr& author, const QString& type, GeneratorMode mode, bool shared ) - : Playlist ( author, guid, title, info, creator, shared ) +: Playlist ( author, guid, title, info, creator, shared ) { qDebug() << "Creating Dynamic Playlist 2"; m_generator = geninterface_ptr( GeneratorFactory::create( type ) ); @@ -113,15 +113,15 @@ DynamicPlaylist::setMode( int mode ) dynplaylist_ptr DynamicPlaylist::create( const Tomahawk::source_ptr& author, - const QString& guid, - const QString& title, - const QString& info, - const QString& creator, - GeneratorMode mode, - bool shared ) + const QString& guid, + const QString& title, + const QString& info, + const QString& creator, + GeneratorMode mode, + bool shared, + const QString& type +) { - // default generator - QString type = ""; dynplaylist_ptr dynplaylist = dynplaylist_ptr( new DynamicPlaylist( author, guid, title, info, creator, type, mode, shared ) ); DatabaseCommand_CreateDynamicPlaylist* cmd = new DatabaseCommand_CreateDynamicPlaylist( author, dynplaylist ); @@ -148,10 +148,10 @@ DynamicPlaylist::createNewRevision( const QString& newUuid ) // create a new revision that will be a static playlist, as it has entries void DynamicPlaylist::createNewRevision( const QString& newrev, - const QString& oldrev, - const QString& type, - const QList< dyncontrol_ptr>& controls, - const QList< plentry_ptr >& entries ) + const QString& oldrev, + const QString& type, + const QList< dyncontrol_ptr>& controls, + const QList< plentry_ptr >& entries ) { // get the newly added tracks QList< plentry_ptr > added = newEntries( entries ); @@ -162,29 +162,29 @@ DynamicPlaylist::createNewRevision( const QString& newrev, // no conflict resolution or partial updating for controls. all or nothing baby - // source making the change (local user in this case) - source_ptr author = SourceList::instance()->getLocal(); - // command writes new rev to DB and calls setRevision, which emits our signal - DatabaseCommand_SetDynamicPlaylistRevision* cmd = - new DatabaseCommand_SetDynamicPlaylistRevision( author, - guid(), - newrev, - oldrev, - orderedguids, - added, - entries, - type, - Static, - controls ); - Database::instance()->enqueue( QSharedPointer( cmd ) ); + // source making the change (local user in this case) + source_ptr author = SourceList::instance()->getLocal(); + // command writes new rev to DB and calls setRevision, which emits our signal + DatabaseCommand_SetDynamicPlaylistRevision* cmd = + new DatabaseCommand_SetDynamicPlaylistRevision( author, + guid(), + newrev, + oldrev, + orderedguids, + added, + entries, + type, + Static, + controls ); + Database::instance()->enqueue( QSharedPointer( cmd ) ); } // create a new revision that will be an ondemand playlist, as it has no entries void DynamicPlaylist::createNewRevision( const QString& newrev, - const QString& oldrev, - const QString& type, - const QList< dyncontrol_ptr>& controls ) + const QString& oldrev, + const QString& type, + const QList< dyncontrol_ptr>& controls ) { // can skip the entry stuff. just overwrite with new info source_ptr author = SourceList::instance()->getLocal(); @@ -237,7 +237,7 @@ DynamicPlaylist::loadRevision( const QString& rev ) bool ) ) ); } - Database::instance()->enqueue( QSharedPointer( cmd ) ); + Database::instance()->enqueue( QSharedPointer( cmd ) ); } bool @@ -257,8 +257,8 @@ DynamicPlaylist::reportCreated( const Tomahawk::dynplaylist_ptr& self ) Q_ASSERT( !author().isNull() ); Q_ASSERT( !author()->collection().isNull() ); // will emit Collection::playlistCreated(...) -// qDebug() << "Creating dynplaylist belonging to:" << author().data() << author().isNull(); -// qDebug() << "REPORTING DYNAMIC PLAYLIST CREATED:" << this << author()->friendlyName(); + // qDebug() << "Creating dynplaylist belonging to:" << author().data() << author().isNull(); + // qDebug() << "REPORTING DYNAMIC PLAYLIST CREATED:" << this << author()->friendlyName(); if( self->mode() == Static ) author()->collection()->addAutoPlaylist( self ); else @@ -339,33 +339,33 @@ void DynamicPlaylist::setRevision( const QString& rev, setCurrentrevision( rev ); } // qDebug() << "EMITTING REVISION LOADED 1!"; - emit dynamicRevisionLoaded( dpr ); + emit dynamicRevisionLoaded( dpr ); } void DynamicPlaylist::setRevision( const QString& rev, - const QList< QString >& neworderedguids, - const QList< QString >& oldorderedguids, - const QString& type, - const QList< QVariantMap>& controlsV, - bool is_newest_rev, - const QMap< QString, Tomahawk::plentry_ptr >& addedmap, - bool applied ) + const QList< QString >& neworderedguids, + const QList< QString >& oldorderedguids, + const QString& type, + const QList< QVariantMap>& controlsV, + bool is_newest_rev, + const QMap< QString, Tomahawk::plentry_ptr >& addedmap, + bool applied ) { if( QThread::currentThread() != thread() ) { QMetaObject::invokeMethod( this, - "setRevision", - Qt::BlockingQueuedConnection, - Q_ARG( QString, rev ), - Q_ARG( QList , neworderedguids ), - Q_ARG( QList , oldorderedguids ), - Q_ARG( QString , type ), - QGenericArgument( "QList< QVariantMap > " , (const void*)&controlsV ), - Q_ARG( bool, is_newest_rev ), - QGenericArgument( "QMap< QString,Tomahawk::plentry_ptr > " , (const void*)&addedmap ), - Q_ARG( bool, applied ) ); + "setRevision", + Qt::BlockingQueuedConnection, + Q_ARG( QString, rev ), + Q_ARG( QList , neworderedguids ), + Q_ARG( QList , oldorderedguids ), + Q_ARG( QString , type ), + QGenericArgument( "QList< QVariantMap > " , (const void*)&controlsV ), + Q_ARG( bool, is_newest_rev ), + QGenericArgument( "QMap< QString,Tomahawk::plentry_ptr > " , (const void*)&addedmap ), + Q_ARG( bool, applied ) ); return; } @@ -416,10 +416,10 @@ void DynamicPlaylist::setRevision( const QString& rev, void DynamicPlaylist::setRevision( const QString& rev, - bool is_newest_rev, - const QString& type, - const QList< QVariantMap >& controlsV, - bool applied ) + bool is_newest_rev, + const QString& type, + const QList< QVariantMap >& controlsV, + bool applied ) { if( QThread::currentThread() != thread() ) { diff --git a/src/libtomahawk/playlist/dynamic/DynamicPlaylist.h b/src/libtomahawk/playlist/dynamic/DynamicPlaylist.h index 7db146fd1..c53c96dd2 100644 --- a/src/libtomahawk/playlist/dynamic/DynamicPlaylist.h +++ b/src/libtomahawk/playlist/dynamic/DynamicPlaylist.h @@ -82,7 +82,8 @@ public: const QString& info, const QString& creator, GeneratorMode mode, - bool shared + bool shared, + const QString& type = QString() ); static bool remove( const dynplaylist_ptr& playlist ); diff --git a/src/libtomahawk/sourcelist.cpp b/src/libtomahawk/sourcelist.cpp index 917a96c00..0f3576808 100644 --- a/src/libtomahawk/sourcelist.cpp +++ b/src/libtomahawk/sourcelist.cpp @@ -1,5 +1,5 @@ /* === This file is part of Tomahawk Player - === - * + * * Copyright 2010-2011, Christian Muehlhaeuser * * Tomahawk is free software: you can redistribute it and/or modify @@ -55,6 +55,18 @@ SourceList::getLocal() return m_local; } +void +SourceList::setWebSource( const source_ptr& websrc ) +{ + m_dummy = websrc; +} + +const +source_ptr SourceList::webSource() const +{ + return m_dummy; +} + void SourceList::loadSources() diff --git a/src/libtomahawk/sourcelist.h b/src/libtomahawk/sourcelist.h index 3ec7304ef..7073f713d 100644 --- a/src/libtomahawk/sourcelist.h +++ b/src/libtomahawk/sourcelist.h @@ -1,5 +1,5 @@ /* === This file is part of Tomahawk Player - === - * + * * Copyright 2010-2011, Christian Muehlhaeuser * * Tomahawk is free software: you can redistribute it and/or modify @@ -40,6 +40,9 @@ public: const Tomahawk::source_ptr& getLocal(); void setLocal( const Tomahawk::source_ptr& localSrc ); + void setWebSource( const Tomahawk::source_ptr& websrc ); + const Tomahawk::source_ptr webSource() const; + void removeAllRemote(); QList sources( bool onlyOnline = false ) const; @@ -57,7 +60,7 @@ signals: private slots: void setSources( const QList& sources ); void sourceSynced(); - + private: void loadSources(); void add( const Tomahawk::source_ptr& source ); @@ -66,8 +69,9 @@ private: QMap< int, QString > m_sources_id2name; Tomahawk::source_ptr m_local; + Tomahawk::source_ptr m_dummy; mutable QMutex m_mut; // mutable so const methods can use a lock - + static SourceList* s_instance; }; diff --git a/src/libtomahawk/tomahawksettings.cpp b/src/libtomahawk/tomahawksettings.cpp index 7f4d27fab..19af83fe2 100644 --- a/src/libtomahawk/tomahawksettings.cpp +++ b/src/libtomahawk/tomahawksettings.cpp @@ -377,6 +377,18 @@ TomahawkSettings::appendRecentlyPlayedPlaylist( const Tomahawk::playlist_ptr& pl setValue( "playlists/recentlyPlayed", playlist_guids ); } +QString +TomahawkSettings::bookmarkPlaylist() const +{ + return value( "playlists/bookmark", QString() ).toString(); +} + +void +TomahawkSettings::setBookmarkPlaylist( const QString& guid ) +{ + setValue( "playlists/bookmark", guid ); +} + QStringList TomahawkSettings::sipPlugins() const { diff --git a/src/libtomahawk/tomahawksettings.h b/src/libtomahawk/tomahawksettings.h index 587cce5bc..50cacb7ac 100644 --- a/src/libtomahawk/tomahawksettings.h +++ b/src/libtomahawk/tomahawksettings.h @@ -73,6 +73,9 @@ public: void setSipPlugins( const QStringList& plugins ); QStringList sipPlugins() const; + void setBookmarkPlaylist( const QString& guid ); + QString bookmarkPlaylist() const; + // just the enabled sip plugins. void setEnabledSipPlugins( const QStringList& list ); QStringList enabledSipPlugins() const; diff --git a/src/libtomahawk/utils/xspfloader.cpp b/src/libtomahawk/utils/xspfloader.cpp index d8b31d40d..9b1543375 100644 --- a/src/libtomahawk/utils/xspfloader.cpp +++ b/src/libtomahawk/utils/xspfloader.cpp @@ -29,6 +29,12 @@ using namespace Tomahawk; +void +XSPFLoader::setOverrideTitle( const QString& newTitle ) +{ + m_overrideTitle = newTitle; +} + void XSPFLoader::load( const QUrl& url ) @@ -116,13 +122,15 @@ XSPFLoader::gotBody() m_title = origTitle; if ( m_title.isEmpty() ) m_title = tr( "New Playlist" ); + if( !m_overrideTitle.isEmpty() ) + m_title = m_overrideTitle; bool shownError = false; for ( unsigned int i = 0; i < tracklist.length(); i++ ) { QDomNode e = tracklist.at( i ); - QString artist, album, track, duration, annotation; + QString artist, album, track, duration, annotation, url; QDomElement n = e.firstChildElement(); for ( ; !n.isNull(); n = n.nextSiblingElement() ) { if (n.namespaceURI() == m_NS && n.localName() == "duration") { @@ -135,6 +143,8 @@ XSPFLoader::gotBody() album = n.text(); } else if (n.namespaceURI() == m_NS && n.localName() == "title") { track = n.text(); + } else if (n.namespaceURI() == m_NS && n.localName() == "url") { + url = n.text(); } } @@ -154,6 +164,8 @@ XSPFLoader::gotBody() p->setQuery( Tomahawk::Query::get( artist, track, album, uuid() ) ); p->query()->setDuration( duration.toInt() / 1000 ); + if( !url.isEmpty() ) + p->query()->setResultHint( url ); m_entries << p; } diff --git a/src/libtomahawk/utils/xspfloader.h b/src/libtomahawk/utils/xspfloader.h index 39e5088f7..e058faf22 100644 --- a/src/libtomahawk/utils/xspfloader.h +++ b/src/libtomahawk/utils/xspfloader.h @@ -53,6 +53,7 @@ public: QList< Tomahawk::plentry_ptr > entries() const { return m_entries; } + void setOverrideTitle( const QString& newTitle ); signals: void failed(); void ok( const Tomahawk::playlist_ptr& ); @@ -70,7 +71,7 @@ private: void gotBody(); bool m_autoCreate; - QString m_NS; + QString m_NS,m_overrideTitle; QList< Tomahawk::plentry_ptr > m_entries; QString m_title, m_info, m_creator; diff --git a/src/libtomahawk/viewmanager.cpp b/src/libtomahawk/viewmanager.cpp index bc1359461..fa55d13d8 100644 --- a/src/libtomahawk/viewmanager.cpp +++ b/src/libtomahawk/viewmanager.cpp @@ -149,6 +149,22 @@ ViewManager::queue() const return m_queueView->queue(); } +PlaylistView* +ViewManager::createPageForPlaylist( const playlist_ptr& pl ) +{ + PlaylistView* view = new PlaylistView(); + + PlaylistModel* model = new PlaylistModel(); + view->setPlaylistModel( model ); + view->setFrameShape( QFrame::NoFrame ); + view->setAttribute( Qt::WA_MacShowFocusRect, 0 ); + model->loadPlaylist( pl ); + pl->resolve(); + + m_playlistViews.insert( pl, view ); + return view; +} + Tomahawk::ViewPage* ViewManager::show( const Tomahawk::playlist_ptr& playlist ) @@ -156,15 +172,7 @@ ViewManager::show( const Tomahawk::playlist_ptr& playlist ) PlaylistView* view; if ( !m_playlistViews.contains( playlist ) ) { - view = new PlaylistView(); - PlaylistModel* model = new PlaylistModel(); - view->setPlaylistModel( model ); - view->setFrameShape( QFrame::NoFrame ); - view->setAttribute( Qt::WA_MacShowFocusRect, 0 ); - model->loadPlaylist( playlist ); - playlist->resolve(); - - m_playlistViews.insert( playlist, view ); + view = createPageForPlaylist( playlist ); } else { diff --git a/src/libtomahawk/viewmanager.h b/src/libtomahawk/viewmanager.h index e8a68f67b..65b4f6fa3 100644 --- a/src/libtomahawk/viewmanager.h +++ b/src/libtomahawk/viewmanager.h @@ -67,6 +67,7 @@ public: QWidget* widget() const { return m_widget; } PlaylistView* queue() const; + TopBar* topbar() const { return m_topbar; } bool isSuperCollectionVisible() const; bool isNewPlaylistPageVisible() const; @@ -76,14 +77,6 @@ public: Tomahawk::ViewPage* pageForInterface( PlaylistInterface* interface ) const; int positionInHistory( Tomahawk::ViewPage* page ) const; - // Returns the shown viewpage - Tomahawk::ViewPage* show( const Tomahawk::playlist_ptr& playlist ); - Tomahawk::ViewPage* show( const Tomahawk::dynplaylist_ptr& playlist ); - Tomahawk::ViewPage* show( const Tomahawk::artist_ptr& artist ); - Tomahawk::ViewPage* show( const Tomahawk::album_ptr& album ); - Tomahawk::ViewPage* show( const Tomahawk::collection_ptr& collection ); - Tomahawk::ViewPage* show( const Tomahawk::source_ptr& source ); - Tomahawk::ViewPage* show( Tomahawk::ViewPage* page ); Tomahawk::ViewPage* welcomeWidget() const { return m_welcomeWidget; } @@ -94,6 +87,9 @@ public: Tomahawk::ViewPage* pageForDynPlaylist( const Tomahawk::dynplaylist_ptr& pl ) const; Tomahawk::ViewPage* pageForCollection( const Tomahawk::collection_ptr& pl ) const; + // only use this is you need to create a playlist and show it directly and want it to be + // linked to the sidebar. call it right after creating the playlist + PlaylistView* createPageForPlaylist( const Tomahawk::playlist_ptr& pl ); signals: void numSourcesChanged( unsigned int sources ); void numTracksChanged( unsigned int tracks ); @@ -127,6 +123,14 @@ public slots: Tomahawk::ViewPage* showWelcomePage(); void showCurrentTrack(); + // Returns the shown viewpage + Tomahawk::ViewPage* show( const Tomahawk::playlist_ptr& playlist ); + Tomahawk::ViewPage* show( const Tomahawk::dynplaylist_ptr& playlist ); + Tomahawk::ViewPage* show( const Tomahawk::artist_ptr& artist ); + Tomahawk::ViewPage* show( const Tomahawk::album_ptr& album ); + Tomahawk::ViewPage* show( const Tomahawk::collection_ptr& collection ); + Tomahawk::ViewPage* show( const Tomahawk::source_ptr& source ); + void historyBack(); void historyForward(); void showHistory( int historyPosition ); diff --git a/src/libtomahawk/webcollection.cpp b/src/libtomahawk/webcollection.cpp new file mode 100644 index 000000000..80a8785de --- /dev/null +++ b/src/libtomahawk/webcollection.cpp @@ -0,0 +1,32 @@ +/* + Copyright (C) 2011 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, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + + +#include "webcollection.h" + +using namespace Tomahawk; + +WebCollection::WebCollection( const source_ptr& source, const QString& name, QObject* parent ) + : Collection( source, name, parent ) +{ + +} + +WebCollection::~WebCollection() +{ +} diff --git a/src/libtomahawk/webcollection.h b/src/libtomahawk/webcollection.h new file mode 100644 index 000000000..ba016f023 --- /dev/null +++ b/src/libtomahawk/webcollection.h @@ -0,0 +1,43 @@ +/* + Copyright (C) 2011 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, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + + +#ifndef WEBCOLLECTION_H +#define WEBCOLLECTION_H + +#include "collection.h" +#include "dllmacro.h" + +namespace Tomahawk +{ + +class DLLEXPORT WebCollection : public Collection +{ + Q_OBJECT +public: + WebCollection( const source_ptr& source, const QString& name = QString(), QObject* parent = 0 ); + virtual ~WebCollection(); + + virtual void removeTracks( const QDir& ) {} + virtual void addTracks( const QList< QVariant >& ) {} +}; + + +} + +#endif // WEBCOLLECTION_H diff --git a/src/scrobbler.cpp b/src/scrobbler.cpp index 2c5d572b4..bcaee8e0f 100644 --- a/src/scrobbler.cpp +++ b/src/scrobbler.cpp @@ -80,9 +80,9 @@ Scrobbler::trackStarted( const Tomahawk::result_ptr& track ) trackInfo["artist"] = track->artist()->name(); trackInfo["album"] = track->album()->name(); trackInfo["duration"] = QString::number( track->duration() ); - Tomahawk::InfoSystem::InfoSystem::instance()->getInfo( - s_scInfoIdentifier, Tomahawk::InfoSystem::InfoMiscSubmitNowPlaying, - QVariant::fromValue< Tomahawk::InfoSystem::InfoCriteriaHash >( trackInfo ), Tomahawk::InfoSystem::InfoCustomData() ); + Tomahawk::InfoSystem::InfoSystem::instance()->pushInfo( + s_scInfoIdentifier, Tomahawk::InfoSystem::InfoSubmitNowPlaying, + QVariant::fromValue< Tomahawk::InfoSystem::InfoCriteriaHash >( trackInfo ) ); m_scrobblePoint = ScrobblePoint( track->duration() / 2 ); } @@ -128,9 +128,9 @@ Scrobbler::scrobble() { Q_ASSERT( QThread::currentThread() == thread() ); - Tomahawk::InfoSystem::InfoSystem::instance()->getInfo( - s_scInfoIdentifier, Tomahawk::InfoSystem::InfoMiscSubmitScrobble, - QVariant(), Tomahawk::InfoSystem::InfoCustomData() ); + Tomahawk::InfoSystem::InfoSystem::instance()->pushInfo( + s_scInfoIdentifier, Tomahawk::InfoSystem::InfoSubmitScrobble, + QVariant() ); } @@ -141,13 +141,7 @@ Scrobbler::infoSystemInfo( QString caller, Tomahawk::InfoSystem::InfoType type, Q_UNUSED( output ); Q_UNUSED( customData ); if ( caller == s_scInfoIdentifier ) - { qDebug() << Q_FUNC_INFO; - if ( type == Tomahawk::InfoSystem::InfoMiscSubmitNowPlaying ) - qDebug() << "Scrobbler received now playing response from InfoSystem"; - else if ( type == Tomahawk::InfoSystem::InfoMiscSubmitScrobble ) - qDebug() << "Scrobbler received scrobble response from InfoSystem"; - } } diff --git a/src/sip/jreen/CMakeLists.txt b/src/sip/jreen/CMakeLists.txt index 86897bf9c..d439d5164 100644 --- a/src/sip/jreen/CMakeLists.txt +++ b/src/sip/jreen/CMakeLists.txt @@ -8,7 +8,6 @@ add_definitions( -DSIPDLLEXPORT_PRO ) set( jabberSources jabber.cpp - jabber_p.cpp tomahawksipmessage.cpp tomahawksipmessagefactory.cpp avatarmanager.cpp @@ -16,7 +15,6 @@ set( jabberSources set( jabberHeaders jabber.h - jabber_p.h tomahawksipmessage.h tomahawksipmessagefactory.h avatarmanager.h diff --git a/src/sip/jreen/googlewrapper/CMakeLists.txt b/src/sip/jreen/googlewrapper/CMakeLists.txt index dfc37878f..44774bfd7 100644 --- a/src/sip/jreen/googlewrapper/CMakeLists.txt +++ b/src/sip/jreen/googlewrapper/CMakeLists.txt @@ -10,7 +10,6 @@ set( googleHeaders set( googleSources ../jabber.cpp - ../jabber_p.cpp ../tomahawksipmessage.cpp ../tomahawksipmessagefactory.cpp ../avatarmanager.cpp diff --git a/src/sip/jreen/jabber.cpp b/src/sip/jreen/jabber.cpp index 01e034f5a..18186b78f 100644 --- a/src/sip/jreen/jabber.cpp +++ b/src/sip/jreen/jabber.cpp @@ -19,13 +19,27 @@ #include "jabber.h" +#include "config.h" + #include "tomahawksettings.h" +#include "tomahawksipmessage.h" +#include "tomahawksipmessagefactory.h" + +#include +#include +#include +#include + +#include +#include #include #include #include #include #include +#include +#include #include "ui_configwidget.h" @@ -41,14 +55,16 @@ JabberFactory::icon() const return QIcon( ":/jabber-icon.png" ); } - JabberPlugin::JabberPlugin( const QString& pluginId ) : SipPlugin( pluginId ) - , p( 0 ) , m_menu( 0 ) , m_addFriendAction( 0 ) , m_state( Disconnected ) { + qDebug() << Q_FUNC_INFO; + + qsrand(QDateTime::currentDateTime().toTime_t()); + m_configWidget = QWeakPointer< QWidget >( new QWidget ); m_ui = new Ui_JabberConfig; m_ui->setupUi( m_configWidget.data() ); @@ -60,17 +76,56 @@ JabberPlugin::JabberPlugin( const QString& pluginId ) m_ui->jabberServer->setText( readServer() ); m_ui->jabberPort->setValue( readPort() ); + // setup JID object + Jreen::JID jid = Jreen::JID( accountName() ); + + // general client setup + m_client = new Jreen::Client( jid, m_currentPassword ); + m_client->registerStanzaExtension(new TomahawkSipMessageFactory); + m_currentResource = QString::fromAscii( "tomahawk%1" ).arg( QString::number( qrand() % 10000 ) ); + m_client->setResource( m_currentResource ); + + // add VCardUpdate extension to own presence + m_client->presence().addExtension( new Jreen::VCardUpdate() ); + + // initialize the AvatarManager + m_avatarManager = new AvatarManager( m_client ); + + // setup disco + m_client->disco()->setSoftwareVersion( "Tomahawk Player", TOMAHAWK_VERSION, CMAKE_SYSTEM ); + m_client->disco()->addIdentity( Jreen::Disco::Identity( "client", "type", "tomahawk", "en" ) ); + m_client->disco()->addFeature( TOMAHAWK_FEATURE ); + + // setup caps node, legacy peer detection - used before 0.1 + Jreen::Capabilities::Ptr caps = m_client->presence().findExtension(); + caps->setNode( TOMAHAWK_CAP_NODE_NAME ); + //FIXME: caps->setVersion( TOMAHAWK_VERSION ); + + // print connection parameters + qDebug() << "Our JID set to:" << m_client->jid().full(); + qDebug() << "Our Server set to:" << m_client->server(); + qDebug() << "Our Port set to" << m_client->port(); + + // setup slots + connect(m_client->connection(), SIGNAL(error(SocketError)), SLOT(onError(SocketError))); + connect(m_client, SIGNAL(serverFeaturesReceived(QSet)), SLOT(onConnect())); + connect(m_client, SIGNAL(disconnected(Jreen::Client::DisconnectReason)), SLOT(onDisconnect(Jreen::Client::DisconnectReason))); + connect(m_client, SIGNAL(newMessage(Jreen::Message)), SLOT(onNewMessage(Jreen::Message))); + connect(m_client, SIGNAL(newPresence(Jreen::Presence)), SLOT(onNewPresence(Jreen::Presence))); + connect(m_client, SIGNAL(newIQ(Jreen::IQ)), SLOT(onNewIq(Jreen::IQ))); + + connect(m_avatarManager, SIGNAL(newAvatar(QString)), SLOT(onNewAvatar(QString))); } JabberPlugin::~JabberPlugin() { - delete p; + } void JabberPlugin::setProxy( QNetworkProxy* proxy ) { - p->setProxy( proxy ); + qDebug() << Q_FUNC_INFO << "Not implemented"; } @@ -124,32 +179,13 @@ JabberPlugin::connectPlugin( bool startup ) QString password = m_currentPassword = readPassword(); unsigned int port = m_currentPort = readPort(); - QStringList splitJid = jid.split( '@', QString::SkipEmptyParts ); - if ( splitJid.size() < 2 ) - { - qDebug() << "JID did not have an @ in it, could not find a server part"; - return false; - } + qDebug() << "Connecting to the XMPP server..." << (m_state == Connected); + qDebug() << m_client->jid().full(); + //m_client->setServer( m_client->jid().domain() ); + qDebug() << m_client->server() << m_client->port(); - if ( port < 1 || port > 65535 || jid.isEmpty() || password.isEmpty() ) - { - qDebug() << "Jabber credentials look wrong, not connecting"; - return false; - } - - delete p; - p = new Jabber_p( jid, password, ( server.isEmpty() ? QString() : server ), port ); - - QObject::connect( p, SIGNAL( peerOnline( QString ) ), SIGNAL( peerOnline( QString ) ) ); - QObject::connect( p, SIGNAL( peerOffline( QString ) ), SIGNAL( peerOffline( QString ) ) ); - QObject::connect( p, SIGNAL( msgReceived( QString, QString ) ), SIGNAL( msgReceived( QString, QString ) ) ); - - QObject::connect( p, SIGNAL( connected() ), SLOT( onConnected() ) ); - QObject::connect( p, SIGNAL( disconnected() ), SLOT( onDisconnected() ) ); - - QObject::connect( p, SIGNAL( authError( int, QString ) ), SLOT( onAuthError( int, QString ) ) ); - QObject::connect( p, SIGNAL( avatarReceived( QString, QPixmap ) ), SIGNAL( avatarReceived( QString, QPixmap ) ) ); - QObject::connect( p, SIGNAL( avatarReceived( QPixmap ) ), SIGNAL( avatarReceived( QPixmap) ) ); + QTimer::singleShot(1000, m_client, SLOT( connectToServer() ) ); + //m_client->connectToServer(); m_state = Connecting; emit stateChanged( m_state ); @@ -159,58 +195,131 @@ JabberPlugin::connectPlugin( bool startup ) void JabberPlugin::disconnectPlugin() { - onDisconnected(); + qDebug() << Q_FUNC_INFO << (m_state == Connected); - if ( p ) - p->disconnect(); + if( m_state == Disconnected ) + return; - delete p; - p = 0; + foreach(const Jreen::JID &peer, m_peers.keys()) + { + handlePeerStatus(peer, Jreen::Presence::Unavailable); + } + + + //m_roster->deleteLater(); + //m_roster = 0; + //m_room->deleteLater(); + //m_room = 0; + + m_peers.clear(); + m_legacy_peers.clear(); + + m_client->disconnectFromServer(true); } void -JabberPlugin::onConnected() +JabberPlugin::onConnect() { - if( !m_menu ) { - m_menu = new QMenu( QString( "JREEN (" ).append( accountName() ).append(")" ) ); - m_addFriendAction = m_menu->addAction( "Add Friend..." ); - QAction *connectAction = m_menu->addAction( "Connect" ); + qDebug() << Q_FUNC_INFO; - connect( m_addFriendAction, SIGNAL(triggered() ), - this, SLOT( showAddFriendDialog() ) ); - connect( connectAction, SIGNAL( triggered() ), SLOT( connectPlugin() ) ); - - emit addMenu( m_menu ); + // update jid resource, servers like gtalk use resource binding and may + // have changed our requested /resource + if ( m_client->jid().resource() != m_currentResource ) + { + m_currentResource = m_client->jid().resource(); + emit jidChanged( m_client->jid().full() ); } + qDebug() << "Connected as:" << m_client->jid().full(); + + // set presence to least valid value + m_client->setPresence(Jreen::Presence::XA, "Got Tomahawk? http://gettomahawk.com", -127); + + // set ping timeout to 15 secs (TODO: verify if this works) + m_client->setPingInterval(1000); + + // load roster + m_roster = new Jreen::SimpleRoster( m_client ); + m_roster->load(); + + //FIXME: this implementation is totally broken atm, so it's disabled to avoid harm :P + // join MUC with bare jid as nickname + //TODO: make the room a list of rooms and make that configurable + QString mucNickname = QString( "tomahawk@conference.qutim.org/" ).append( QString( m_client->jid().bare() ).replace( "@", "-" ) ); + //m_room = new Jreen::MUCRoom(m_client, Jreen::JID( mucNickname ) ); + //m_room->setHistorySeconds(0); + //m_room->join(); + + // treat muc participiants like contacts + //connect( m_room, SIGNAL( messageReceived( Jreen::Message, bool ) ), this, SLOT( onNewMessage( Jreen::Message ) ) ); + //connect( m_room, SIGNAL( presenceReceived( Jreen::Presence, const Jreen::MUCRoom::Participant* ) ), this, SLOT( onNewPresence( Jreen::Presence ) ) ); + m_state = Connected; - emit stateChanged( m_state ); + + addMenuHelper(); } void -JabberPlugin::onDisconnected() +JabberPlugin::onDisconnect( Jreen::Client::DisconnectReason reason ) { - if( m_menu && m_addFriendAction ) { - emit removeMenu( m_menu ); + qDebug() << Q_FUNC_INFO; - delete m_menu; - m_menu = 0; - m_addFriendAction = 0; // deleted by menu + QString errorMessage; + bool reconnect = false; + int reconnectInSeconds = 0; + + switch( reason ) + { + case Jreen::Client::User: + errorMessage = "User Interaction"; + break; + case Jreen::Client::HostUnknown: + errorMessage = "Host is unknown"; + break; + case Jreen::Client::ItemNotFound: + errorMessage = "Item not found"; + break; + case Jreen::Client::AuthorizationError: + errorMessage = "Authorization Error"; + break; + case Jreen::Client::RemoteStreamError: + errorMessage = "Remote Stream Error"; + reconnect = true; + break; + case Jreen::Client::RemoteConnectionFailed: + errorMessage = "Remote Connection failed"; + break; + case Jreen::Client::InternalServerError: + errorMessage = "Internal Server Error"; + reconnect = true; + break; + case Jreen::Client::SystemShutdown: + errorMessage = "System shutdown"; + reconnect = true; + reconnectInSeconds = 60; + break; + case Jreen::Client::Conflict: + errorMessage = "Conflict"; + break; + + case Jreen::Client::Unknown: + errorMessage = "Unknown"; + break; + + default: + qDebug() << "Not all Client::DisconnectReasons checked"; + Q_ASSERT(false); + break; } - m_state = Disconnected; - - emit stateChanged( m_state ); -} - -void -JabberPlugin::onAuthError( int code, const QString& msg ) -{ - switch( code ) + switch( reason ) { + case Jreen::Client::User: + break; + case Jreen::Client::AuthorizationError: - emit error( SipPlugin::AuthError, msg ); + emit error( SipPlugin::AuthError, errorMessage ); break; case Jreen::Client::HostUnknown: @@ -221,7 +330,7 @@ JabberPlugin::onAuthError( int code, const QString& msg ) case Jreen::Client::SystemShutdown: case Jreen::Client::Conflict: case Jreen::Client::Unknown: - emit error( SipPlugin::ConnectionError, msg ); + emit error( SipPlugin::ConnectionError, errorMessage ); break; default: @@ -230,29 +339,97 @@ JabberPlugin::onAuthError( int code, const QString& msg ) break; } m_state = Disconnected; - emit stateChanged( m_state ); + + removeMenuHelper(); + + if(reconnect) + QTimer::singleShot(reconnectInSeconds*1000, this, SLOT(connectPlugin())); +} + +void +JabberPlugin::onAuthError( int code, const QString& msg ) +{ + } void JabberPlugin::sendMsg(const QString& to, const QString& msg) { - if ( p ) - p->sendMsg( to, msg ); + qDebug() << Q_FUNC_INFO << to << msg; + + if ( !m_client ) { + return; + } + + if( m_legacy_peers.contains( to ) ) + { + qDebug() << Q_FUNC_INFO << to << "Send legacy message" << msg; + Jreen::Message m( Jreen::Message::Chat, Jreen::JID(to), msg); + m_client->send( m ); + + return; + } + + + /******************************************************* + * Obsolete this by a SipMessage class + */ + QJson::Parser parser; + bool ok; + QVariant v = parser.parse( msg.toAscii(), &ok ); + if ( !ok || v.type() != QVariant::Map ) + { + qDebug() << "Invalid JSON in XMPP msg"; + return; + } + QVariantMap m = v.toMap(); + /*******************************************************/ + + TomahawkSipMessage *sipMessage; + if(m["visible"].toBool()) + { + sipMessage = new TomahawkSipMessage(m["ip"].toString(), + m["port"].toInt(), + m["uniqname"].toString(), + m["key"].toString(), + m["visible"].toBool() + ); + } + else + { + sipMessage = new TomahawkSipMessage(); + } + + + qDebug() << "Send sip messsage to " << to; + Jreen::IQ iq( Jreen::IQ::Set, to ); + iq.addExtension( sipMessage ); + + m_client->send( iq, this, SLOT( onNewIq( Jreen::IQ, int ) ), SipMessageSent ); } void JabberPlugin::broadcastMsg(const QString& msg) { - if ( p ) - p->broadcastMsg( msg ); + qDebug() << Q_FUNC_INFO; + + if ( !m_client ) + return; + + foreach( const Jreen::JID& jid, m_peers.keys() ) + { + sendMsg( jid.full(), msg ); + } } void JabberPlugin::addContact(const QString& jid, const QString& msg) { - if ( p ) - p->addContact( jid, msg ); + // Add contact to the Tomahawk group on the roster + m_roster->subscribe( jid, msg, jid, QStringList() << "Tomahawk" ); + + return; } void @@ -287,13 +464,299 @@ JabberPlugin::checkSettings() m_currentServer = readServer(); m_currentPort = readPort(); - if ( reconnect && ( p || readAutoConnect() ) ) + if ( reconnect ) { + qDebug() << Q_FUNC_INFO << "Reconnecting jreen plugin..."; disconnectPlugin(); + + m_currentUsername = accountName(); + m_currentPassword = readPassword(); + m_currentServer = readServer(); + m_currentPort = readPort(); + + Jreen::JID jid = Jreen::JID( accountName() ); + m_client->setJID( jid ); + m_client->setPassword( m_currentPassword ); + m_client->setServer( m_currentServer ); + m_client->setPort( m_currentPort ); + + qDebug() << Q_FUNC_INFO << "Updated settings"; connectPlugin( false ); } } +void JabberPlugin::addMenuHelper() +{ + if( !m_menu ) + { + m_menu = new QMenu( QString( "JREEN (" ).append( accountName() ).append(")" ) ); + m_addFriendAction = m_menu->addAction( "Add Friend..." ); + + connect( m_addFriendAction, SIGNAL( triggered() ), this, SLOT( showAddFriendDialog() ) ); + + emit addMenu( m_menu ); + } +} + +void JabberPlugin::removeMenuHelper() +{ + if( m_menu && m_addFriendAction ) + { + emit removeMenu( m_menu ); + + delete m_menu; + m_menu = 0; + m_addFriendAction = 0; // deleted by menu + } +} + +void JabberPlugin::onNewMessage(const Jreen::Message& message) +{ + QString from = message.from().full(); + QString msg = message.body(); + + if ( msg.isEmpty() ) + return; + + QJson::Parser parser; + bool ok; + QVariant v = parser.parse( msg.toAscii(), &ok ); + if ( !ok || v.type() != QVariant::Map ) + { + QString to = from; + QString response = QString( tr("I'm sorry -- I'm just an automatic presence used by Tomahawk Player" + " (http://gettomahawk.com). If you are getting this message, the person you" + " are trying to reach is probably not signed on, so please try again later!") ); + + // this is not a sip message, so we send it directly through the client + m_client->send( Jreen::Message ( Jreen::Message::Chat, Jreen::JID(to), response) ); + + return; + } + + qDebug() << Q_FUNC_INFO << "From:" << message.from().full() << ":" << message.body(); + emit msgReceived( from, msg ); +} + + +void JabberPlugin::onNewPresence( const Jreen::Presence& presence) +{ + Jreen::JID jid = presence.from(); + QString fulljid( jid.full() ); + + + qDebug() << Q_FUNC_INFO << "* New presence: " << fulljid << presence.subtype(); + + if( jid == m_client->jid() ) + return; + + if ( presence.error() ) { + //qDebug() << Q_FUNC_INFO << fulljid << "Running tomahawk: no" << "presence error"; + return; + } + + // ignore anyone not Running tomahawk: + Jreen::Capabilities::Ptr caps = presence.findExtension(); + if ( caps && ( caps->node() == TOMAHAWK_CAP_NODE_NAME ) ) + { + // must be a jreen resource, implementation in gloox was broken + qDebug() << Q_FUNC_INFO << fulljid << "Running tomahawk: yes" << "caps " << caps->node(); + handlePeerStatus( jid, presence.subtype() ); + } + else if( caps ) + { + qDebug() << Q_FUNC_INFO << fulljid << "Running tomahawk: maybe" << "caps " << caps->node() + << "requesting disco.."; + + // request disco features + QString node = caps->node() + '#' + caps->ver(); + + Jreen::IQ iq( Jreen::IQ::Get, jid ); + iq.addExtension( new Jreen::Disco::Info( node ) ); + + m_client->send( iq, this, SLOT( onNewIq( Jreen::IQ, int ) ), RequestDisco ); + } + else if( !caps ) + { + qDebug() << Q_FUNC_INFO << "Running tomahawk: no" << "no caps"; + } +} + +void JabberPlugin::onNewIq(const Jreen::IQ& iq, int context) +{ + if( context == RequestDisco ) + { + qDebug() << Q_FUNC_INFO << "Received disco IQ..."; + Jreen::Disco::Info *discoInfo = iq.findExtension().data(); + if(!discoInfo) + return; + iq.accept(); + + Jreen::JID jid = iq.from(); + + Jreen::DataForm::Ptr form = discoInfo->form(); + + if(discoInfo->features().contains( TOMAHAWK_FEATURE )) + { + qDebug() << Q_FUNC_INFO << jid.full() << "Running tomahawk/feature enabled: yes"; + + // the actual presence doesn't matter, it just needs to be "online" + handlePeerStatus( jid, Jreen::Presence::Available ); + } + else + { + qDebug() << Q_FUNC_INFO << jid.full() << "Running tomahawk/feature enabled: no"; + + //LEGACY: accept resources starting with tomahawk too + if( jid.resource().startsWith("tomahawk") ) + { + qDebug() << Q_FUNC_INFO << jid.full() << "Detected legacy tomahawk.."; + + // add to legacy peers, so we can send text messages instead of iqs + m_legacy_peers.append( jid ); + + handlePeerStatus( jid, Jreen::Presence::Available ); + } + } + } + else if(context == RequestedDisco) + { + qDebug() << "Sent IQ(Set), what should be happening here?"; + } + else if(context == SipMessageSent ) + { + qDebug() << "Sent SipMessage... what now?!"; + } + /*else if(context == RequestedVCard ) + { + qDebug() << "Requested VCard... what now?!"; + }*/ + else + { + + TomahawkSipMessage *sipMessage = iq.findExtension().data(); + if(sipMessage) + { + iq.accept(); + + qDebug() << Q_FUNC_INFO << "Got SipMessage ..."; + qDebug() << "ip" << sipMessage->ip(); + qDebug() << "port" << sipMessage->port(); + qDebug() << "uniqname" << sipMessage->uniqname(); + qDebug() << "key" << sipMessage->key(); + qDebug() << "visible" << sipMessage->visible(); + + + QVariantMap m; + if( sipMessage->visible() ) + { + m["visible"] = true; + m["ip"] = sipMessage->ip(); + m["port"] = sipMessage->port(); + m["key"] = sipMessage->key(); + m["uniqname"] = sipMessage->uniqname(); + } + else + { + m["visible"] = false; + } + + + QJson::Serializer ser; + QByteArray ba = ser.serialize( m ); + QString msg = QString::fromAscii( ba ); + + QString from = iq.from().full(); + qDebug() << Q_FUNC_INFO << "From:" << from << ":" << msg; + emit msgReceived( from, msg ); + } + } +} + +bool JabberPlugin::presenceMeansOnline(Jreen::Presence::Type p) +{ + switch(p) + { + case Jreen::Presence::Invalid: + case Jreen::Presence::Unavailable: + case Jreen::Presence::Error: + return false; + break; + default: + return true; + } +} + +void JabberPlugin::handlePeerStatus(const Jreen::JID& jid, Jreen::Presence::Type presenceType) +{ + QString fulljid = jid.full(); + + // "going offline" event + if ( !presenceMeansOnline( presenceType ) && + ( !m_peers.contains( jid ) || + presenceMeansOnline( m_peers.value( jid ) ) + ) + ) + { + m_peers[ jid ] = presenceType; + qDebug() << Q_FUNC_INFO << "* Peer goes offline:" << fulljid; + + // remove peer from legacy peers + if( m_legacy_peers.contains( jid ) ) + { + m_legacy_peers.removeAll( jid ); + } + + emit peerOffline( fulljid ); + return; + } + + // "coming online" event + if( presenceMeansOnline( presenceType ) && + ( !m_peers.contains( jid ) || + !presenceMeansOnline( m_peers.value( jid ) ) + ) + ) + { + m_peers[ jid ] = presenceType; + qDebug() << Q_FUNC_INFO << "* Peer goes online:" << fulljid; + + emit peerOnline( fulljid ); + + if(!m_avatarManager->avatar(jid.bare()).isNull()) + onNewAvatar( jid.bare() ); + + return; + } + + //qDebug() << "Updating presence data for" << fulljid; + m_peers[ jid ] = presenceType; +} + +void JabberPlugin::onNewAvatar(const QString& jid) +{ + qDebug() << Q_FUNC_INFO << jid; + Q_ASSERT(!m_avatarManager->avatar( jid ).isNull()); + + // find peers for the jid + QList peers = m_peers.keys(); + foreach(const Jreen::JID &peer, peers) + { + if( peer.bare() == jid ) + { + emit avatarReceived ( peer.full(), m_avatarManager->avatar( jid ) ); + } + } + + if( jid == m_client->jid().bare() ) + // own avatar + emit avatarReceived ( m_avatarManager->avatar( jid ) ); + else + // someone else's avatar + emit avatarReceived ( jid, m_avatarManager->avatar( jid ) ); +} + + QString JabberPlugin::readPassword() { diff --git a/src/sip/jreen/jabber.h b/src/sip/jreen/jabber.h index e7e7d2ec8..5d448eb21 100644 --- a/src/sip/jreen/jabber.h +++ b/src/sip/jreen/jabber.h @@ -21,11 +21,29 @@ #define JABBER_H #include "sip/SipPlugin.h" -#include "jabber_p.h" -#include "../sipdllmacro.h" +#include "avatarmanager.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include #define MYNAME "SIPJREEN" +#define TOMAHAWK_FEATURE QLatin1String( "tomahawk:sip:v1" ) +#define TOMAHAWK_CAP_NODE_NAME QLatin1String( "http://tomahawk-player.org/" ) + +#include "../sipdllmacro.h" class Ui_JabberConfig; @@ -64,6 +82,8 @@ public: virtual void saveConfig(); void setProxy( QNetworkProxy* proxy ); +signals: + void jidChanged( const QString& ); public slots: virtual bool connectPlugin( bool startup ); @@ -78,17 +98,31 @@ protected: private slots: void showAddFriendDialog(); - void onConnected(); - void onDisconnected(); + void onConnect(); + void onDisconnect(Jreen::Client::DisconnectReason reason); void onAuthError(int code, const QString &msg); + void onNewPresence( const Jreen::Presence& presence ); + void onNewMessage( const Jreen::Message& message ); + void onError( const Jreen::Connection::SocketError& e ) + { + qDebug() << e; + } + void onNewIq( const Jreen::IQ &iq, int context = NoContext ); + void onNewAvatar( const QString &jid ); + private: QString readPassword(); QString readServer(); bool readAutoConnect(); int readPort(); - Jabber_p* p; + void addMenuHelper(); + void removeMenuHelper(); + + bool presenceMeansOnline( Jreen::Presence::Type p ); + void handlePeerStatus( const Jreen::JID &jid, Jreen::Presence::Type presenceType ); + QMenu* m_menu; QAction* m_addFriendAction; @@ -99,6 +133,18 @@ private: ConnectionState m_state; QWeakPointer< QWidget > m_configWidget; + + QString m_currentResource; + + // sort out + Jreen::Client *m_client; + + Jreen::MUCRoom *m_room; + Jreen::SimpleRoster *m_roster; + QHash m_peers; + enum IqContext { NoContext, RequestDisco, RequestedDisco, SipMessageSent, RequestedVCard }; + QStringList m_legacy_peers; + AvatarManager *m_avatarManager; }; #endif diff --git a/src/sip/jreen/jabber_p.cpp b/src/sip/jreen/jabber_p.cpp deleted file mode 100644 index c7c9e02f9..000000000 --- a/src/sip/jreen/jabber_p.cpp +++ /dev/null @@ -1,576 +0,0 @@ -/* === This file is part of Tomahawk Player - === - * - * Copyright 2010-2011, Dominik Schmidt - * Copyright 2010-2011, Christian Muehlhaeuser - * - * Tomahawk 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 3 of the License, or - * (at your option) any later version. - * - * Tomahawk 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 Tomahawk. If not, see . - */ - -#include "jabber_p.h" -#include "tomahawksipmessage.h" -#include "tomahawksipmessagefactory.h" - -#include "config.h" -#include "utils/tomahawkutils.h" - -#include -#include -#include -#include - -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -//remove -#include -#include - -#define TOMAHAWK_FEATURE QLatin1String( "tomahawk:sip:v1" ) - -#define TOMAHAWK_CAP_NODE_NAME QLatin1String( "http://tomahawk-player.org/" ) - -Jabber_p::Jabber_p( const QString& jid, const QString& password, const QString& server, const int port ) - : QObject() - , m_server() -{ - qDebug() << Q_FUNC_INFO; - qsrand(QDateTime::currentDateTime().toTime_t()); - - // setup JID object - m_jid = Jreen::JID( jid ); - - // general client setup - m_client = new Jreen::Client( jid, password ); - if ( !server.isEmpty() ) - { - m_client->setServer( server ); - m_client->setPort( port ); - } - m_client->registerStanzaExtension(new TomahawkSipMessageFactory); - m_client->setResource( QString( "tomahawk%1" ).arg( QString::number( qrand() % 10000 ) ) ); - - // add VCardUpdate extension to own presence - m_client->presence().addExtension( new Jreen::VCardUpdate() ); - - // initialize the AvatarManager - m_avatarManager = new AvatarManager(m_client); - - // setup disco - m_client->disco()->setSoftwareVersion( "Tomahawk Player", TOMAHAWK_VERSION, CMAKE_SYSTEM ); - m_client->disco()->addIdentity( Jreen::Disco::Identity( "client", "type", "tomahawk", "en" ) ); - m_client->disco()->addFeature( TOMAHAWK_FEATURE ); - - // setup caps node, legacy peer detection - used before 0.1 - Jreen::Capabilities::Ptr caps = m_client->presence().findExtension(); - caps->setNode( TOMAHAWK_CAP_NODE_NAME ); - - // print connection parameters - qDebug() << "Our JID set to:" << m_client->jid().full(); - qDebug() << "Our Server set to:" << m_client->server(); - qDebug() << "Our Port set to" << m_client->port(); - - m_client->setConnectionImpl( new Jreen::TcpConnection( m_client->server(), m_client->port() ) ); - - // setup slots - connect(qobject_cast(m_client->connection()), SIGNAL(error(const Jreen::Connection::SocketError&)), SLOT(onError(const Jreen::Connection::SocketError&))); - connect(m_client, SIGNAL(serverFeaturesReceived(QSet)), SLOT(onConnect())); - connect(m_client, SIGNAL(disconnected(Jreen::Client::DisconnectReason)), SLOT(onDisconnect(Jreen::Client::DisconnectReason))); - connect(m_client, SIGNAL(destroyed(QObject*)), this, SLOT(onDestroy(QObject*))); - connect(m_client, SIGNAL(newMessage(Jreen::Message)), SLOT(onNewMessage(Jreen::Message))); - connect(m_client, SIGNAL(newPresence(Jreen::Presence)), SLOT(onNewPresence(Jreen::Presence))); - connect(m_client, SIGNAL(newIQ(Jreen::IQ)), SLOT(onNewIq(Jreen::IQ))); - - connect(m_avatarManager, SIGNAL(newAvatar(QString)), SLOT(onNewAvatar(QString))); - - - // connect - qDebug() << "Connecting to the XMPP server..."; - m_client->connectToServer(); -} - - -Jabber_p::~Jabber_p() -{ - delete m_client; -} - -void -Jabber_p::setProxy( QNetworkProxy* proxy ) -{ - qDebug() << Q_FUNC_INFO << "NOT IMPLEMENTED"; -} - -void -Jabber_p::disconnect() -{ - if ( m_client ) - { - m_client->disconnect(); - } -} - - -void -Jabber_p::sendMsg( const QString& to, const QString& msg ) -{ - qDebug() << Q_FUNC_INFO << to << msg; - - if ( !m_client ) { - return; - } - - if( m_legacy_peers.contains( to ) ) - { - qDebug() << Q_FUNC_INFO << to << "Send legacy message" << msg; - Jreen::Message m( Jreen::Message::Chat, Jreen::JID(to), msg); - m_client->send( m ); - - return; - } - - - /******************************************************* - * Obsolete this by a SipMessage class - */ - QJson::Parser parser; - bool ok; - QVariant v = parser.parse( msg.toAscii(), &ok ); - if ( !ok || v.type() != QVariant::Map ) - { - qDebug() << "Invalid JSON in XMPP msg"; - return; - } - QVariantMap m = v.toMap(); - /*******************************************************/ - - TomahawkSipMessage *sipMessage; - if(m["visible"].toBool()) - { - sipMessage = new TomahawkSipMessage(m["ip"].toString(), - m["port"].toInt(), - m["uniqname"].toString(), - m["key"].toString(), - m["visible"].toBool() - ); - } - else - { - sipMessage = new TomahawkSipMessage(); - } - - - qDebug() << "Send sip messsage to " << to; - Jreen::IQ iq( Jreen::IQ::Set, to ); - iq.addExtension( sipMessage ); - - m_client->send( iq, this, SLOT( onNewIq( Jreen::IQ, int ) ), SipMessageSent ); -} - - -void -Jabber_p::broadcastMsg( const QString &msg ) -{ - qDebug() << Q_FUNC_INFO; - - if ( !m_client ) - return; - - foreach( const QString& jidstr, m_peers.keys() ) - { - sendMsg( jidstr, msg ); - } -} - - -void -Jabber_p::addContact( const QString& jid, const QString& msg ) -{ - // Add contact to the Tomahawk group on the roster - m_roster->add( jid, jid, QStringList() << "Tomahawk" ); - - return; -} - -void -Jabber_p::onConnect() -{ - qDebug() << Q_FUNC_INFO; - - // update jid resource, servers like gtalk use resource binding and may - // have changed our requested /resource - if ( m_client->jid().resource() != m_jid.resource() ) - { - // TODO: check if this is still neccessary with jreen - m_jid.setResource( m_client->jid().resource() ); - QString jidstr( m_jid.full() ); - emit jidChanged( jidstr ); - } - - emit connected(); - qDebug() << "Connected as:" << m_jid.full(); - - // set presence to least valid value - m_client->setPresence(Jreen::Presence::XA, "Got Tomahawk? http://gettomahawk.com", -127); - - // set ping timeout to 15 secs (TODO: verify if this works) - m_client->setPingInterval(15000); - - // load roster - m_roster = new Jreen::SimpleRoster( m_client ); - m_roster->load(); - - //FIXME: this implementation is totally broken atm, so it's disabled to avoid harm :P - // join MUC with bare jid as nickname - //TODO: make the room a list of rooms and make that configurable - QString mucNickname = QString( "tomahawk@conference.qutim.org/" ).append( QString( m_jid.bare() ).replace( "@", "-" ) ); - m_room = new Jreen::MUCRoom(m_client, Jreen::JID( mucNickname ) ); - //m_room->setHistorySeconds(0); - //m_room->join(); - - // treat muc participiants like contacts - connect( m_room, SIGNAL( messageReceived( Jreen::Message, bool ) ), this, SLOT( onNewMessage( Jreen::Message ) ) ); - connect( m_room, SIGNAL( presenceReceived( Jreen::Presence, const Jreen::MUCRoom::Participant* ) ), this, SLOT( onNewPresence( Jreen::Presence ) ) ); -} - - -void -Jabber_p::onDisconnect( Jreen::Client::DisconnectReason reason ) -{ - QString error; - bool reconnect = false; - int reconnectInSeconds = 0; - - switch( reason ) - { - case Jreen::Client::User: - error = "User Interaction"; - break; - case Jreen::Client::HostUnknown: - error = "Host is unknown"; - break; - case Jreen::Client::ItemNotFound: - error = "Item not found"; - break; - case Jreen::Client::AuthorizationError: - error = "Authorization Error"; - break; - case Jreen::Client::RemoteStreamError: - error = "Remote Stream Error"; - reconnect = true; - break; - case Jreen::Client::RemoteConnectionFailed: - error = "Remote Connection failed"; - break; - case Jreen::Client::InternalServerError: - error = "Internal Server Error"; - reconnect = true; - break; - case Jreen::Client::SystemShutdown: - error = "System shutdown"; - reconnect = true; - reconnectInSeconds = 60; - break; - case Jreen::Client::Conflict: - error = "Conflict"; - break; - - case Jreen::Client::Unknown: - error = "Unknown"; - break; - - default: - qDebug() << "Not all Client::DisconnectReasons checked"; - Q_ASSERT(false); - break; - } - - qDebug() << "Disconnected from server:" << error; - if( reason != Jreen::Client::User ) - { - emit authError( reason, error ); - } - - if(reconnect) - QTimer::singleShot(reconnectInSeconds*1000, m_client, SLOT(connectToServer())); - - emit disconnected(); -} - -void -Jabber_p::onNewMessage( const Jreen::Message& m ) -{ - QString from = m.from().full(); - QString msg = m.body(); - - if ( msg.isEmpty() ) - return; - - QJson::Parser parser; - bool ok; - QVariant v = parser.parse( msg.toAscii(), &ok ); - if ( !ok || v.type() != QVariant::Map ) - { - if ( m.from().domain().contains( "googlemail." ) - || m.from().domain().contains( "gmail." ) - || m.from().domain().contains( "gtalk." ) - ) - return; - - QString to = from; - QString response = QString( tr("I'm sorry -- I'm just an automatic presence used by Tomahawk Player" - " (http://gettomahawk.com). If you are getting this message, the person you" - " are trying to reach is probably not signed on, so please try again later!") ); - - // this is not a sip message, so we send it directly through the client - m_client->send( Jreen::Message ( Jreen::Message::Chat, Jreen::JID(to), response) ); - - return; - } - - qDebug() << Q_FUNC_INFO << "From:" << m.from().full() << ":" << m.body(); - emit msgReceived( from, msg ); -} - - -void Jabber_p::onNewPresence( const Jreen::Presence& presence) -{ - Jreen::JID jid = presence.from(); - QString fulljid( jid.full() ); - - - qDebug() << Q_FUNC_INFO << "* New presence: " << fulljid << presence.subtype(); - - if( jid == m_jid ) - return; - - if ( presence.error() ) { - //qDebug() << Q_FUNC_INFO << fulljid << "Running tomahawk: no" << "presence error"; - return; - } - - // ignore anyone not Running tomahawk: - Jreen::Capabilities::Ptr caps = presence.findExtension(); - if ( caps && ( caps->node() == TOMAHAWK_CAP_NODE_NAME ) ) - { - // must be a jreen resource, implementation in gloox was broken - qDebug() << Q_FUNC_INFO << fulljid << "Running tomahawk: yes" << "caps " << caps->node(); - handlePeerStatus( fulljid, presence.subtype() ); - } - else if( caps ) - { - qDebug() << Q_FUNC_INFO << fulljid << "Running tomahawk: maybe" << "caps " << caps->node() - << "requesting disco.."; - - // request disco features - QString node = caps->node() + '#' + caps->ver(); - - Jreen::IQ iq( Jreen::IQ::Get, jid ); - iq.addExtension( new Jreen::Disco::Info( node ) ); - - m_client->send( iq, this, SLOT( onNewIq( Jreen::IQ, int ) ), RequestDisco ); - } - else if( !caps ) - { - qDebug() << Q_FUNC_INFO << "Running tomahawk: no" << "no caps"; - } -} - -void -Jabber_p::onNewIq( const Jreen::IQ &iq, int context ) -{ - if( context == RequestDisco ) - { - qDebug() << Q_FUNC_INFO << "Received disco IQ..."; - Jreen::Disco::Info *discoInfo = iq.findExtension().data(); - if(!discoInfo) - return; - iq.accept(); - - QString fulljid = iq.from().full(); - Jreen::DataForm::Ptr form = discoInfo->form(); - - if(discoInfo->features().contains( TOMAHAWK_FEATURE )) - { - qDebug() << Q_FUNC_INFO << fulljid << "Running tomahawk/feature enabled: yes"; - - // the actual presence doesn't matter, it just needs to be "online" - handlePeerStatus( fulljid, Jreen::Presence::Available ); - } - else - { - qDebug() << Q_FUNC_INFO << fulljid << "Running tomahawk/feature enabled: no"; - - //LEGACY: accept resources starting with tomahawk too - if( iq.from().resource().startsWith("tomahawk") ) - { - qDebug() << Q_FUNC_INFO << fulljid << "Detected legacy tomahawk.."; - - // add to legacy peers, so we can send text messages instead of iqs - m_legacy_peers.append( fulljid ); - - handlePeerStatus( fulljid, Jreen::Presence::Available ); - } - } - } - else if(context == RequestedDisco) - { - qDebug() << "Sent IQ(Set), what should be happening here?"; - } - else if(context == SipMessageSent ) - { - qDebug() << "Sent SipMessage... what now?!"; - } - /*else if(context == RequestedVCard ) - { - qDebug() << "Requested VCard... what now?!"; - }*/ - else - { - - TomahawkSipMessage *sipMessage = iq.findExtension().data(); - if(sipMessage) - { - iq.accept(); - - qDebug() << Q_FUNC_INFO << "Got SipMessage ..."; - qDebug() << "ip" << sipMessage->ip(); - qDebug() << "port" << sipMessage->port(); - qDebug() << "uniqname" << sipMessage->uniqname(); - qDebug() << "key" << sipMessage->key(); - qDebug() << "visible" << sipMessage->visible(); - - - QVariantMap m; - if( sipMessage->visible() ) - { - m["visible"] = true; - m["ip"] = sipMessage->ip(); - m["port"] = sipMessage->port(); - m["key"] = sipMessage->key(); - m["uniqname"] = sipMessage->uniqname(); - } - else - { - m["visible"] = false; - } - - - QJson::Serializer ser; - QByteArray ba = ser.serialize( m ); - QString msg = QString::fromAscii( ba ); - - QString from = iq.from().full(); - qDebug() << Q_FUNC_INFO << "From:" << from << ":" << msg; - emit msgReceived( from, msg ); - } - } -} - -bool -Jabber_p::presenceMeansOnline( Jreen::Presence::Type p ) -{ - switch(p) - { - case Jreen::Presence::Invalid: - case Jreen::Presence::Unavailable: - case Jreen::Presence::Error: - return false; - break; - default: - return true; - } -} - -void -Jabber_p::handlePeerStatus( const Jreen::JID& jid, Jreen::Presence::Type presenceType ) -{ - QString fulljid = jid.full(); - - // "going offline" event - if ( !presenceMeansOnline( presenceType ) && - ( !m_peers.contains( fulljid ) || - presenceMeansOnline( m_peers.value( fulljid ) ) - ) - ) - { - m_peers[ fulljid ] = presenceType; - qDebug() << Q_FUNC_INFO << "* Peer goes offline:" << fulljid; - - // remove peer from legacy peers - if( m_legacy_peers.contains( fulljid ) ) - { - m_legacy_peers.removeAll( fulljid ); - } - - emit peerOffline( fulljid ); - return; - } - - // "coming online" event - if( presenceMeansOnline( presenceType ) && - ( !m_peers.contains( fulljid ) || - !presenceMeansOnline( m_peers.value( fulljid ) ) - ) - ) - { - m_peers[ fulljid ] = presenceType; - qDebug() << Q_FUNC_INFO << "* Peer goes online:" << fulljid; - - emit peerOnline( fulljid ); - - if(!m_avatarManager->avatar(jid.bare()).isNull()) - onNewAvatar( jid.bare() ); - - return; - } - - //qDebug() << "Updating presence data for" << fulljid; - m_peers[ fulljid ] = presenceType; -} - -void Jabber_p::onNewAvatar(const QString& jid) -{ - qDebug() << Q_FUNC_INFO << jid; - Q_ASSERT(!m_avatarManager->avatar( jid ).isNull()); - - // find peers for the jid - QStringList peers = m_peers.keys(); - foreach(const QString &peer, peers) - { - if( peer.startsWith(jid) ) - { - emit avatarReceived ( peer, m_avatarManager->avatar( jid ) ); - } - } - - if( jid == m_client->jid().bare() ) - // own avatar - emit avatarReceived ( m_avatarManager->avatar( jid ) ); - else - // someone else's avatar - emit avatarReceived ( jid, m_avatarManager->avatar( jid ) ); -} diff --git a/src/sip/jreen/jabber_p.h b/src/sip/jreen/jabber_p.h deleted file mode 100644 index c8c77a2ee..000000000 --- a/src/sip/jreen/jabber_p.h +++ /dev/null @@ -1,114 +0,0 @@ -/* === This file is part of Tomahawk Player - === - * - * Copyright 2010-2011, Dominik Schmidt - * Copyright 2010-2011, Christian Muehlhaeuser - * - * Tomahawk 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 3 of the License, or - * (at your option) any later version. - * - * Tomahawk 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 Tomahawk. If not, see . - */ - -#ifndef JABBER_P_H -#define JABBER_P_H - -#include "../sipdllmacro.h" - -#include "avatarmanager.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - - -#if defined( WIN32 ) || defined( _WIN32 ) -#include -#endif - -class SIPDLLEXPORT Jabber_p : - public QObject -{ -Q_OBJECT - -public: - explicit Jabber_p( const QString& jid, const QString& password, const QString& server = "", const int port = -1 ); - virtual ~Jabber_p(); - - void setProxy( QNetworkProxy* proxy ); - -signals: - void msgReceived( const QString&, const QString& ); //from, msg - void peerOnline( const QString& ); - void peerOffline( const QString& ); - void connected(); - void disconnected(); - void jidChanged( const QString& ); - void avatarReceived( const QPixmap& avatar ); - void avatarReceived( const QString&, const QPixmap& avatar ); - void authError( int, const QString& ); - -public slots: - void sendMsg( const QString& to, const QString& msg ); - void broadcastMsg( const QString& msg ); - void addContact( const QString& jid, const QString& msg = QString() ); - void disconnect(); - - void onDisconnect(Jreen::Client::DisconnectReason reason); - void onConnect(); - -private slots: - virtual void onNewPresence( const Jreen::Presence& presence ); - virtual void onNewMessage( const Jreen::Message& msg ); - virtual void onError( const Jreen::Connection::SocketError& e ) - { - qDebug() << e; - } - virtual void onDestroy( QObject */*object*/ ) - { - qDebug() << Q_FUNC_INFO; - } - virtual void onNewIq( const Jreen::IQ &iq, int context = NoContext ); - virtual void onNewAvatar( const QString &jid ); - -private: - bool presenceMeansOnline( Jreen::Presence::Type p ); - void handlePeerStatus( const Jreen::JID &jid, Jreen::Presence::Type presenceType ); - - Jreen::Client *m_client; - Jreen::MUCRoom *m_room; - Jreen::SimpleRoster *m_roster; - Jreen::JID m_jid; - QMap m_presences; - QMap m_peers; - QString m_server; - - enum IqContext { NoContext, RequestDisco, RequestedDisco, SipMessageSent, RequestedVCard }; - - QStringList m_legacy_peers; - - AvatarManager *m_avatarManager; -}; - -#endif // JABBER_H diff --git a/src/tomahawkapp.cpp b/src/tomahawkapp.cpp index 2f923d363..d1de19265 100644 --- a/src/tomahawkapp.cpp +++ b/src/tomahawkapp.cpp @@ -48,6 +48,9 @@ #include "shortcuthandler.h" #include "scanmanager.h" #include "tomahawksettings.h" +#include "globalactionmanager.h" +#include "webcollection.h" +#include "database/localcollection.h" #include "audio/audioengine.h" #include "utils/xspfloader.h" @@ -223,7 +226,7 @@ TomahawkApp::init() #else qDebug() << "Setting NAM."; - TomahawkUtils::setNam( new QNetworkAccessManager ); + TomahawkUtils::setNam( new QNetworkAccessManager() ); #endif // Set up proxy @@ -241,7 +244,6 @@ TomahawkApp::init() else TomahawkUtils::setProxy( new QNetworkProxy( QNetworkProxy::NoProxy ) ); - Echonest::Config::instance()->setAPIKey( "JRIHWEP6GPOER2QQ6" ); Echonest::Config::instance()->setNetworkAccessManager( TomahawkUtils::nam() ); @@ -249,7 +251,6 @@ TomahawkApp::init() qDebug() << "Init SIP system."; - #ifndef TOMAHAWK_HEADLESS if ( !m_headless ) { @@ -462,12 +463,19 @@ void TomahawkApp::initLocalCollection() { source_ptr src( new Source( 0, "My Collection" ) ); - collection_ptr coll( new DatabaseCollection( src ) ); + collection_ptr coll( new LocalCollection( src ) ); src->addCollection( coll ); SourceList::instance()->setLocal( src ); // src->collection()->tracks(); + // dummy source/collection for web-based result-hints. + source_ptr dummy( new Source( -1, "" ) ); + dummy->setOnline(); + collection_ptr dummycol( new WebCollection( dummy ) ); + dummy->addCollection( dummycol ); + SourceList::instance()->setWebSource( dummy ); + // to make the stats signal be emitted by our local source // this will update the sidebar, etc. DatabaseCommand_CollectionStats* cmd = new DatabaseCommand_CollectionStats( src ); @@ -503,7 +511,9 @@ TomahawkApp::setupSIP() qDebug() << "Connecting SIP classes"; SipHandler::instance()->loadFromConfig( true ); -// SipHandler::instance()->setProxy( *TomahawkUtils::proxy() ); + +// m_sipHandler->setProxy( *TomahawkUtils::proxy() ); + } } @@ -520,28 +530,22 @@ TomahawkApp::activate() bool TomahawkApp::loadUrl( const QString& url ) { - if( url.contains( "tomahawk://" ) ) { - QString cmd = url.mid( 11 ); - qDebug() << "tomahawk!s" << cmd; - if( cmd.startsWith( "load/?" ) ) { - cmd = cmd.mid( 6 ); - qDebug() << "loading.." << cmd; - if( cmd.startsWith( "xspf=" ) ) { - XSPFLoader* l = new XSPFLoader( true, this ); - qDebug() << "Loading spiff:" << cmd.mid( 5 ); - l->load( QUrl( cmd.mid( 5 ) ) ); - } - } - } else { + if( url.startsWith( "tomahawk://" ) ) + return GlobalActionManager::instance()->parseTomahawkLink( url ); + else + { QFile f( url ); QFileInfo info( f ); if( f.exists() && info.suffix() == "xspf" ) { XSPFLoader* l = new XSPFLoader( true, this ); qDebug() << "Loading spiff:" << url; l->load( QUrl::fromUserInput( url ) ); + + return true; } } - return true; + + return false; } @@ -555,6 +559,7 @@ TomahawkApp::instanceStarted( KDSingleApplicationGuard::Instance instance ) return; } - loadUrl( instance.arguments.at( 1 ) ); + QString arg1 = instance.arguments[ 1 ]; + loadUrl( arg1 ); } diff --git a/src/tomahawkwindow.cpp b/src/tomahawkwindow.cpp index 021fa75fc..ebd2985b7 100644 --- a/src/tomahawkwindow.cpp +++ b/src/tomahawkwindow.cpp @@ -21,6 +21,8 @@ #include #include +#include +#include #include #include #include @@ -161,7 +163,7 @@ TomahawkWindow::TomahawkWindow( QWidget* parent ) qtsparkle::Updater* updater = new qtsparkle::Updater( updaterUrl, this ); updater->SetNetworkAccessManager( TomahawkUtils::nam() ); - updater->SetVersion( VERSION ); + updater->SetVersion( TOMAHAWK_VERSION ); ui->menu_Help->addSeparator(); QAction* checkForUpdates = ui->menu_Help->addAction( tr( "Check For Updates...") ); @@ -267,6 +269,13 @@ TomahawkWindow::setupSignals() connect( ui->actionCreate_New_Station, SIGNAL( triggered() ), SLOT( createStation() )); connect( ui->actionAboutTomahawk, SIGNAL( triggered() ), SLOT( showAboutTomahawk() ) ); connect( ui->actionExit, SIGNAL( triggered() ), APP, SLOT( quit() ) ); +#if defined( Q_OS_DARWIN ) + connect( ui->actionMinimize, SIGNAL( triggered() ), SLOT( minimize() ) ); + connect( ui->actionZoom, SIGNAL( triggered() ), SLOT( maximize() ) ); +#else + ui->menuWindow->clear(); + ui->menuWindow->menuAction()->setVisible( false ); +#endif // connect( APP->sipHandler(), SIGNAL( connected() ), SLOT( onSipConnected() ) ); @@ -294,7 +303,6 @@ TomahawkWindow::changeEvent( QEvent* e ) } } - void TomahawkWindow::closeEvent( QCloseEvent* e ) { @@ -310,6 +318,27 @@ TomahawkWindow::closeEvent( QCloseEvent* e ) e->accept(); } +void +TomahawkWindow::showEvent( QShowEvent* e ) +{ + QMainWindow::showEvent( e ); + +#if defined( Q_OS_DARWIN ) + ui->actionMinimize->setDisabled( false ); + ui->actionZoom->setDisabled( false ); +#endif +} + +void +TomahawkWindow::hideEvent( QHideEvent* e ) +{ + QMainWindow::hideEvent( e ); + +#if defined( Q_OS_DARWIN ) + ui->actionMinimize->setDisabled( true ); + ui->actionZoom->setDisabled( true ); +#endif +} void TomahawkWindow::showSettingsDialog() @@ -530,3 +559,25 @@ TomahawkWindow::checkForUpdates() Tomahawk::checkForUpdates(); #endif } + +void +TomahawkWindow::minimize() +{ + if ( isMinimized() ) + { + showNormal(); + } else { + showMinimized(); + } +} + +void +TomahawkWindow::maximize() +{ + if ( isMaximized() ) + { + showNormal(); + } else { + showMaximized(); + } +} diff --git a/src/tomahawkwindow.h b/src/tomahawkwindow.h index d52914649..f86a5b54d 100644 --- a/src/tomahawkwindow.h +++ b/src/tomahawkwindow.h @@ -57,6 +57,8 @@ public: protected: void changeEvent( QEvent* e ); void closeEvent( QCloseEvent* e ); + void showEvent( QShowEvent* e ); + void hideEvent( QHideEvent* e ); public slots: void createAutomaticPlaylist(); @@ -84,6 +86,10 @@ private slots: void onSipPluginAdded( SipPlugin* p ); void onSipPluginRemoved( SipPlugin* p ); + + void minimize(); + void maximize(); + private: void loadSettings(); void saveSettings(); diff --git a/src/tomahawkwindow.ui b/src/tomahawkwindow.ui index 6b142c616..c4fee4d69 100644 --- a/src/tomahawkwindow.ui +++ b/src/tomahawkwindow.ui @@ -72,6 +72,13 @@ + + + &Window + + + + &Help @@ -82,6 +89,7 @@ + @@ -157,6 +165,22 @@ Hide Offline Sources + + + Minimize + + + Ctrl+M + + + + + Zoom + + + Meta+Ctrl+Z + + diff --git a/thirdparty/jreen b/thirdparty/jreen index 040ca3f3c..40fd6b0a3 160000 --- a/thirdparty/jreen +++ b/thirdparty/jreen @@ -1 +1 @@ -Subproject commit 040ca3f3cb9b30b4845fc23054c833fda4717460 +Subproject commit 40fd6b0a3dbc729fdf1db8490848b43d5b1f57f5 diff --git a/thirdparty/liblastfm2/src/CMakeLists.txt b/thirdparty/liblastfm2/src/CMakeLists.txt index a9cd2622e..2859d826c 100644 --- a/thirdparty/liblastfm2/src/CMakeLists.txt +++ b/thirdparty/liblastfm2/src/CMakeLists.txt @@ -78,7 +78,7 @@ if(UNIX) endif(APPLE) endif(UNIX) if(WIN32) - set(SOURCES ${SOURCES} ws/win/WNetworkConnectionMonitor_win.cpp) + set(SOURCES ${SOURCES} ws/win/WNetworkConnectionMonitor_win.cpp ws/win/Pac.cpp ws/win/NdisEvents.cpp ) set(MOC_HEADERS ${MOC_HEADERS} ws/win/WNetworkConnectionMonitor.h) endif(WIN32) @@ -100,9 +100,10 @@ ENDIF() target_link_libraries(tomahawk_lastfm2 ${QT_LIBRARIES} ${QT_QTDBUS_LIBRARY} + ) -set_target_properties(tomahawk_lastfm2 PROPERTIES COMPILE_FLAGS "-DLASTFM_OHAI_QMAKE" ) +set_target_properties(tomahawk_lastfm2 PROPERTIES COMPILE_FLAGS "-DLASTFM_OHAI_QMAKE" ) if(APPLE) target_link_libraries(tomahawk_lastfm2 diff --git a/thirdparty/liblastfm2/src/ws/NetworkAccessManager.cpp b/thirdparty/liblastfm2/src/ws/NetworkAccessManager.cpp index 2d628b1ad..95845c0ff 100644 --- a/thirdparty/liblastfm2/src/ws/NetworkAccessManager.cpp +++ b/thirdparty/liblastfm2/src/ws/NetworkAccessManager.cpp @@ -1,5 +1,5 @@ /* - Copyright 2009 Last.fm Ltd. + Copyright 2009 Last.fm Ltd. - Primarily authored by Max Howell, Jono Cole and Doug Mansell This file is part of liblastfm. @@ -34,11 +34,11 @@ static struct NetworkAccessManagerInit { - // We do this upfront because then our Firehose QTcpSocket will have a proxy + // We do this upfront because then our Firehose QTcpSocket will have a proxy // set by default. As well as any plain QNetworkAcessManager stuff, and the // scrobbler - // In theory we should do this every request in case the configuration - // changes but that is fairly unlikely use case, init? Maybe we should + // In theory we should do this every request in case the configuration + // changes but that is fairly unlikely use case, init? Maybe we should // anyway.. NetworkAccessManagerInit() @@ -50,7 +50,7 @@ static struct NetworkAccessManagerInit // at two seconds, so that hangs startup if (!s.fAutoDetect && s.lpszProxy) { - QUrl url( QString::fromUtf16(s.lpszProxy) ); + QUrl url( QString::fromUtf16((const unsigned short*)s.lpszProxy) ); QNetworkProxy proxy( QNetworkProxy::HttpProxy ); proxy.setHostName( url.host() ); proxy.setPort( url.port() ); @@ -69,10 +69,10 @@ static struct NetworkAccessManagerInit } #endif } -} init; +} init; -namespace lastfm +namespace lastfm { LASTFM_DLLEXPORT QByteArray UserAgent; } @@ -106,12 +106,12 @@ lastfm::NetworkAccessManager::~NetworkAccessManager() QNetworkProxy lastfm::NetworkAccessManager::proxy( const QNetworkRequest& request ) -{ +{ Q_UNUSED( request ); - + #ifdef WIN32 IeSettings s; - if (s.fAutoDetect) + if (s.fAutoDetect) { if (!m_pac) { m_pac = new Pac; @@ -122,9 +122,9 @@ lastfm::NetworkAccessManager::proxy( const QNetworkRequest& request ) } } return m_pac->resolve( request, s.lpszAutoConfigUrl ); - } + } #endif - + return QNetworkProxy::applicationProxy(); } @@ -136,7 +136,7 @@ lastfm::NetworkAccessManager::createRequest( Operation op, const QNetworkRequest request.setAttribute( QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferCache ); request.setRawHeader( "User-Agent", lastfm::UserAgent ); - + #ifdef WIN32 // PAC proxies can vary by domain, so we have to check everytime :( QNetworkProxy proxy = this->proxy( request ); @@ -152,7 +152,7 @@ void lastfm::NetworkAccessManager::onConnectivityChanged( bool up ) { Q_UNUSED( up ); - + #ifdef WIN32 if (up && m_pac) m_pac->resetFailedState(); #endif diff --git a/thirdparty/liblastfm2/src/ws/win/ComSetup.h b/thirdparty/liblastfm2/src/ws/win/ComSetup.h index fc3816bd7..7389f0976 100644 --- a/thirdparty/liblastfm2/src/ws/win/ComSetup.h +++ b/thirdparty/liblastfm2/src/ws/win/ComSetup.h @@ -1,5 +1,5 @@ /* - Copyright 2009 Last.fm Ltd. + Copyright 2009 Last.fm Ltd. - Primarily authored by Max Howell, Jono Cole and Doug Mansell This file is part of liblastfm. @@ -23,12 +23,13 @@ #endif #include -#include -#include - +//#include +//#include +//#include +#include /** @brief WsConnectionMonitor needs Com to work as early as possible so we do this - * @author + * @author */ class ComSetup { @@ -37,27 +38,27 @@ public: { HRESULT hr = CoInitialize(0); m_bComInitialised = SUCCEEDED(hr); - _ASSERT(m_bComInitialised); + //_ASSERT(m_bComInitialised); if (m_bComInitialised) { setupSecurity(); } } - + void setupSecurity() { - CSecurityDescriptor sd; - sd.InitializeFromThreadToken(); - HRESULT hr = CoInitializeSecurity(sd, -1, NULL, NULL, RPC_C_AUTHN_LEVEL_PKT, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE, NULL); - _ASSERT(SUCCEEDED(hr)); + //CSecurityDescriptor sd; + //sd.InitializeFromThreadToken(); + //HRESULT hr = CoInitializeSecurity(sd, -1, NULL, NULL, RPC_C_AUTHN_LEVEL_PKT, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE, NULL); + //_ASSERT(SUCCEEDED(hr)); } - + ~ComSetup() { if (m_bComInitialised) { CoUninitialize(); } } - + private: bool m_bComInitialised; }; diff --git a/thirdparty/liblastfm2/src/ws/win/IeSettings.h b/thirdparty/liblastfm2/src/ws/win/IeSettings.h index 5d756a5ea..c86774b8d 100644 --- a/thirdparty/liblastfm2/src/ws/win/IeSettings.h +++ b/thirdparty/liblastfm2/src/ws/win/IeSettings.h @@ -1,5 +1,5 @@ /* - Copyright 2009 Last.fm Ltd. + Copyright 2009 Last.fm Ltd. - Primarily authored by Max Howell, Jono Cole and Doug Mansell This file is part of liblastfm. @@ -26,6 +26,7 @@ */ struct IeSettings : WINHTTP_CURRENT_USER_IE_PROXY_CONFIG { +#ifndef WIN32 IeSettings() { if (!WinHttpGetIEProxyConfigForCurrentUser(this)) { @@ -33,7 +34,8 @@ struct IeSettings : WINHTTP_CURRENT_USER_IE_PROXY_CONFIG lpszAutoConfigUrl = lpszProxy = lpszProxyBypass = 0; } } - +#endif + ~IeSettings() { if (lpszAutoConfigUrl) GlobalFree(lpszAutoConfigUrl); diff --git a/thirdparty/liblastfm2/src/ws/win/NdisEvents.cpp b/thirdparty/liblastfm2/src/ws/win/NdisEvents.cpp index 7263af24b..7b5822ac6 100644 --- a/thirdparty/liblastfm2/src/ws/win/NdisEvents.cpp +++ b/thirdparty/liblastfm2/src/ws/win/NdisEvents.cpp @@ -1,5 +1,5 @@ /* - Copyright 2009 Last.fm Ltd. + Copyright 2009 Last.fm Ltd. - Primarily authored by Max Howell, Jono Cole and Doug Mansell This file is part of liblastfm. @@ -32,35 +32,37 @@ NdisEvents::NdisEvents() NdisEvents::~NdisEvents() { +#ifndef WIN32 if (m_pSink) m_pSink->disconnect(); - if (m_pServices && m_pSink) - m_pServices->CancelAsyncCall(m_pSink); +#endif + //if (m_pServices && m_pSink) + //m_pServices->CancelAsyncCall(m_pSink); // and reference counting will take care of the WmiSink object } HRESULT NdisEvents::registerForNdisEvents() { - HRESULT hr = m_pLocator.CoCreateInstance(CLSID_WbemLocator); + HRESULT hr = 0; //m_pLocator.CoCreateInstance(CLSID_WbemLocator); if (FAILED(hr)) return hr; // Connect to the root\wmi namespace with the current user. - hr = m_pLocator->ConnectServer(CComBSTR("ROOT\\WMI"), // strNetworkResource + hr = 0; /*m_pLocator->ConnectServer(CComBSTR("ROOT\\WMI"), // strNetworkResource NULL, // strUser NULL, // strPassword - NULL, // strLocale + NULL, // strLocale 0, // lSecurityFlags - CComBSTR(""), // strAuthority + CComBSTR(""), // strAuthority NULL, // pCtx &m_pServices - ); + );*/ if (FAILED(hr)) return hr; - +#ifndef WIN32 m_pSink = new WmiSink(this); - +#endif ////////////////////////// // other notifications we're not interested in right now include... @@ -75,12 +77,12 @@ NdisEvents::registerForNdisEvents() // MSNdis_StatusProtocolUnbind // MSNdis_StatusMediaSpecificIndication - CComBSTR wql("WQL"); + /*CComBSTR wql("WQL"); CComBSTR query("SELECT * FROM MSNdis_StatusMediaDisconnect"); hr = m_pServices->ExecNotificationQueryAsync(wql, query, 0, 0, m_pSink); query = "SELECT * FROM MSNdis_StatusMediaConnect"; - hr = m_pServices->ExecNotificationQueryAsync(wql, query, 0, 0, m_pSink); + hr = m_pServices->ExecNotificationQueryAsync(wql, query, 0, 0, m_pSink);*/ return S_OK; } diff --git a/thirdparty/liblastfm2/src/ws/win/NdisEvents.h b/thirdparty/liblastfm2/src/ws/win/NdisEvents.h index 1e1cfcf9c..8b4efe0e1 100644 --- a/thirdparty/liblastfm2/src/ws/win/NdisEvents.h +++ b/thirdparty/liblastfm2/src/ws/win/NdisEvents.h @@ -1,5 +1,5 @@ /* - Copyright 2009 Last.fm Ltd. + Copyright 2009 Last.fm Ltd. - Primarily authored by Max Howell, Jono Cole and Doug Mansell This file is part of liblastfm. @@ -21,8 +21,8 @@ #define NDIS_EVENTS_H #include -#include -#include +//#include +//#include class NdisEvents { @@ -35,8 +35,8 @@ public: virtual void onConnectionDown(BSTR name) = 0; private: - CComPtr m_pLocator; - CComPtr m_pServices; + //CComPtr m_pLocator; + //CComPtr m_pServices; class WmiSink *m_pSink; }; diff --git a/thirdparty/liblastfm2/src/ws/win/Pac.cpp b/thirdparty/liblastfm2/src/ws/win/Pac.cpp index 3e3d72b12..b0a836c7f 100644 --- a/thirdparty/liblastfm2/src/ws/win/Pac.cpp +++ b/thirdparty/liblastfm2/src/ws/win/Pac.cpp @@ -1,5 +1,5 @@ /* - Copyright 2009 Last.fm Ltd. + Copyright 2009 Last.fm Ltd. - Primarily authored by Max Howell, Jono Cole and Doug Mansell This file is part of liblastfm. @@ -21,8 +21,9 @@ #include #include #include -#include -#include +//#include +//#include +#include static bool @@ -45,7 +46,7 @@ parsePacServer(const QString &s, QNetworkProxy &p) static QList parsePacResult(const QString &pacResult) { - // msdn says: "The proxy server list contains one or more of the + // msdn says: "The proxy server list contains one or more of the // following strings separated by semicolons or whitespace." // ([=]["://"][":"]) @@ -73,8 +74,10 @@ lastfm::Pac::Pac() lastfm::Pac::~Pac() { +#ifndef WIN32 if (m_hSession) WinHttpCloseHandle(m_hSession); +#endif } QNetworkProxy @@ -86,42 +89,45 @@ lastfm::Pac::resolve(const QNetworkRequest &request, const wchar_t* pacUrl) if (!m_hSession) { QByteArray user_agent = request.rawHeader("user-agent"); - m_hSession = WinHttpOpen(CA2W(user_agent), WINHTTP_ACCESS_TYPE_NO_PROXY, 0, 0, WINHTTP_FLAG_ASYNC); + //m_hSession = WinHttpOpen(CA2W(user_agent), WINHTTP_ACCESS_TYPE_NO_PROXY, 0, 0, WINHTTP_FLAG_ASYNC); } if (m_hSession) { WINHTTP_PROXY_INFO info; WINHTTP_AUTOPROXY_OPTIONS opts; memset(&opts, 0, sizeof(opts)); - if (pacUrl) + if (pacUrl) { - opts.dwFlags = WINHTTP_AUTOPROXY_CONFIG_URL; - opts.lpszAutoConfigUrl = pacUrl; - } + //opts.dwFlags = WINHTTP_AUTOPROXY_CONFIG_URL; + //opts.lpszAutoConfigUrl = pacUrl; + } else { - opts.dwFlags = WINHTTP_AUTOPROXY_AUTO_DETECT; - opts.dwAutoDetectFlags = WINHTTP_AUTO_DETECT_TYPE_DHCP | WINHTTP_AUTO_DETECT_TYPE_DNS_A; + //opts.dwFlags = WINHTTP_AUTOPROXY_AUTO_DETECT; + //opts.dwAutoDetectFlags = WINHTTP_AUTO_DETECT_TYPE_DHCP | WINHTTP_AUTO_DETECT_TYPE_DNS_A; } opts.fAutoLogonIfChallenged = TRUE; - - if (WinHttpGetProxyForUrl(m_hSession, request.url().toString().utf16(), &opts, &info)) { - if (info.lpszProxy) +#ifndef WIN32 + if (WinHttpGetProxyForUrl(m_hSession, (const WCHAR*)(request.url().toString().utf16()), &opts, &info)) { + if (info.lpszProxy) { - QList proxies = parsePacResult(QString::fromUtf16(info.lpszProxy)); + QList proxies = parsePacResult(QString::fromUtf16((const ushort*)info.lpszProxy)); if (!proxies.empty()) { out = proxies.at(0); } - GlobalFree(info.lpszProxy); + // pay attention! casting away constness + GlobalFree((void*)info.lpszProxy); } if (info.lpszProxyBypass) { - GlobalFree(info.lpszProxyBypass); + // pay attention! casting away constness + GlobalFree((void*)info.lpszProxyBypass); } } else { m_bFailed = true; } +#endif } return out; diff --git a/thirdparty/liblastfm2/src/ws/win/Pac.h b/thirdparty/liblastfm2/src/ws/win/Pac.h index 075d128f6..98d3e6b0e 100644 --- a/thirdparty/liblastfm2/src/ws/win/Pac.h +++ b/thirdparty/liblastfm2/src/ws/win/Pac.h @@ -1,5 +1,5 @@ /* - Copyright 2009 Last.fm Ltd. + Copyright 2009 Last.fm Ltd. - Primarily authored by Max Howell, Jono Cole and Doug Mansell This file is part of liblastfm. @@ -24,14 +24,14 @@ #include #include class QNetworkRequest; - + namespace lastfm { - /** @brief simple wrapper to do per url automatic proxy detection + /** @brief simple wrapper to do per url automatic proxy detection * @author */ class Pac - { + { HINTERNET m_hSession; bool m_bFailed; @@ -49,4 +49,4 @@ namespace lastfm }; } -#endif \ No newline at end of file +#endif \ No newline at end of file diff --git a/thirdparty/liblastfm2/src/ws/win/WmiSink.h b/thirdparty/liblastfm2/src/ws/win/WmiSink.h index 1bce28fdd..8f58a1f4a 100644 --- a/thirdparty/liblastfm2/src/ws/win/WmiSink.h +++ b/thirdparty/liblastfm2/src/ws/win/WmiSink.h @@ -1,5 +1,5 @@ /* - Copyright 2009 Last.fm Ltd. + Copyright 2009 Last.fm Ltd. - Primarily authored by Max Howell, Jono Cole and Doug Mansell This file is part of liblastfm. @@ -20,7 +20,7 @@ #ifndef WMISINK_WIN_H #define WMISINK_WIN_H -#include "WbemCli.h" +#include "wbemcli.h" // Sink object for WMI NDIS notifications class WmiSink : public IWbemObjectSink diff --git a/thirdparty/liblastfm2/src/ws/ws.cpp b/thirdparty/liblastfm2/src/ws/ws.cpp index 89c6ece6b..0625218e8 100644 --- a/thirdparty/liblastfm2/src/ws/ws.cpp +++ b/thirdparty/liblastfm2/src/ws/ws.cpp @@ -25,9 +25,13 @@ #include #include #include +#include +#include #include -static QNetworkAccessManager* nam = 0; +static QMap< QThread*, QNetworkAccessManager* > threadNamHash; +static QMap< QThread*, bool > ourNamHash; +static QMutex namAccessMutex; QString lastfm::ws::host() @@ -191,18 +195,34 @@ lastfm::ws::parse( QNetworkReply* reply ) throw( ParseError ) QNetworkAccessManager* lastfm::nam() -{ - if (!::nam) ::nam = new NetworkAccessManager( qApp ); - return ::nam; +{ + QMutexLocker l( &namAccessMutex ); + QThread* thread = QThread::currentThread(); + if ( !threadNamHash.contains( thread ) ) + { + NetworkAccessManager* newNam = new NetworkAccessManager(); + threadNamHash[thread] = newNam; + ourNamHash[thread] = true; + return newNam; + } + + return threadNamHash[thread]; } void lastfm::setNetworkAccessManager( QNetworkAccessManager* nam ) { - delete ::nam; - ::nam = nam; - nam->setParent( qApp ); // ensure it isn't deleted out from under us + if ( !nam ) + return; + + QMutexLocker l( &namAccessMutex ); + QThread* thread = QThread::currentThread(); + if ( threadNamHash.contains( thread ) && ourNamHash.contains( thread ) && ourNamHash[thread] ) + delete threadNamHash[thread]; + + threadNamHash[thread] = nam; + ourNamHash[thread] = false; }