diff --git a/CMakeLists.txt b/CMakeLists.txt index 7472b2971..5bd4a8d59 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -127,8 +127,16 @@ ENDIF() #macro_log_feature(LIBLASTFM_FOUND "LastFm" "Qt library for the Last.fm webservices" "https://github.com/mxcl/liblastfm" FALSE "" "liblastfm is needed for scrobbling tracks to Last.fm and fetching cover artwork") set(LIBLASTFM_FOUND true) +#### submodules start # this installs headers and such and should really be handled in a separate package by packagers +IF( INTERNAL_JREEN OR INTERNAL_QTWEETLIB ) + IF(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/.gitmodules) + EXECUTE_PROCESS(COMMAND git submodule init WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} ) + EXECUTE_PROCESS(COMMAND git submodule update WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} ) + ENDIF(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/.gitmodules) +ENDIF() + IF( INTERNAL_JREEN ) ADD_SUBDIRECTORY( ${THIRDPARTY_DIR}/jreen ) SET( LIBJREEN_INCLUDE_DIR ${CMAKE_CURRENT_BINARY_DIR}/thirdparty/jreen/include ) @@ -141,7 +149,6 @@ ELSE( INTERNAL_JREEN ) ENDIF( INTERNAL_JREEN ) macro_log_feature(LIBJREEN_FOUND "Jreen" "Qt XMPP Library" "https://github.com/euroelessar/jreen" FALSE "" "Jreen is needed for the Jabber SIP plugin. \n\n Use -DINTERNAL_JREEN=ON to build the git submodule inside Tomahawk \n Be aware this installs a full jreen with headers and everything!\n") -# this installs headers and such and should really be handled in a separate package by packagers IF( INTERNAL_QTWEETLIB ) ADD_SUBDIRECTORY( ${THIRDPARTY_DIR}/qtweetlib ) # copy headers to build/QTweetLib so we can use proper includes inside the code @@ -155,6 +162,7 @@ ELSE( INTERNAL_QTWEETLIB ) macro_optional_find_package(QTweetLib) ENDIF( INTERNAL_QTWEETLIB ) macro_log_feature(QTWEETLIB_FOUND "QTweetLib" "Qt Twitter Library" "https://github.com/minimoog/QTweetLib" FALSE "" "QTweetLib is needed for the Twitter SIP plugin. \n\n Use -DINTERNAL_QTWEETLIB=ON to build the git submodule inside Tomahawk \n") +#### submodules end ### libportfwd SET( LIBPORTFWD_INCLUDE_DIR ${THIRDPARTY_DIR}/libportfwd/include ) diff --git a/CMakeModules/FindCLucene.cmake b/CMakeModules/FindCLucene.cmake index eb0eafc3e..83c569a38 100644 --- a/CMakeModules/FindCLucene.cmake +++ b/CMakeModules/FindCLucene.cmake @@ -12,7 +12,15 @@ INCLUDE(CheckSymbolExists) INCLUDE(FindLibraryWithDebug) +# try to locate a patched unstable version (for comp's sake *sigh*) first +FIND_PACKAGE(CLuceneUnstable) +IF(CLUCENEUNSTABLE_FOUND) + SET(CLucene_FOUND TRUE) + SET(CLUCENE_INCLUDE_DIR ${CLUCENE_UNSTABLE_INCLUDE_DIRS}) + SET(CLUCENE_LIBRARIES ${CLUCENE_UNSTABLE_LIBS}) + #MESSAGE(FATAL_ERROR NARF) +ELSE(CLUCENEUNSTABLE_FOUND) IF(CLucene_FIND_VERSION) SET(CLUCENE_MIN_VERSION ${CLucene_FIND_VERSION}) ELSEIF() @@ -99,6 +107,7 @@ ENDIF (CLUCENE_LIBRARY_DIR) IF(CLUCENE_INCLUDE_DIR AND CLUCENE_LIBRARIES AND CLUCENE_LIBRARY_DIR AND CLUCENE_GOOD_VERSION) SET(CLucene_FOUND TRUE) ENDIF(CLUCENE_INCLUDE_DIR AND CLUCENE_LIBRARIES AND CLUCENE_LIBRARY_DIR AND CLUCENE_GOOD_VERSION) +ENDIF(CLUCENEUNSTABLE_FOUND) IF(CLucene_FOUND) IF(NOT CLucene_FIND_QUIETLY) diff --git a/CMakeModules/FindCLuceneUnstable.cmake b/CMakeModules/FindCLuceneUnstable.cmake new file mode 100644 index 000000000..62643aa86 --- /dev/null +++ b/CMakeModules/FindCLuceneUnstable.cmake @@ -0,0 +1,37 @@ +# - Try to find clucene-unstable +# This is a workaround for distros, that want to ship a recent enough clucene but don't want to replace the old version +# +# CLUCENEUNSTABLE_FOUND - system has clucene-unstable +# CLUCENE_UNSTABLE_INCLUDE_DIR - the clucene-unstable include directories +# CLUCENE_UNSTABLE_LIBS - link these to use clucene-unstable +# +# (c) Dominik Schmidt <dev@dominik-schmidt.de> +# + +# Include dir +find_path(CLUCENE_UNSTABLE_INCLUDE_DIR + NAMES CLucene.h + PATH_SUFFIXES clucene-unstable + PATHS ${KDE4_INCLUDE_DIR} +) + +# Finally the library itself +find_library(CLUCENE_UNSTABLE_SHARED_LIB + NAMES clucene-unstable-shared + PATHS ${KDE4_LIB_DIR} +) + +find_library(CLUCENE_UNSTABLE_CORE_LIB + NAMES clucene-unstable-core + PATHS ${KDE4_LIB_DIR} +) + + +SET( CLUCENE_UNSTABLE_LIBS ${CLUCENE_UNSTABLE_SHARED_LIB} ${CLUCENE_UNSTABLE_CORE_LIB} ) +SET( CLUCENE_UNSTABLE_INCLUDE_DIRS ${CLUCENE_UNSTABLE_INCLUDE_DIR}) +INCLUDE(FindPackageHandleStandardArgs) +FIND_PACKAGE_HANDLE_STANDARD_ARGS(CLuceneUnstable DEFAULT_MSG CLUCENE_UNSTABLE_LIBS CLUCENE_UNSTABLE_INCLUDE_DIRS) + + +MARK_AS_ADVANCED(CLUCENE_UNSTABLE_LIBS CLUCENE_UNSTABLE_INCLUDE_DIRS) + diff --git a/ChangeLog b/ChangeLog index 096ff0f30..15f431f6f 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,12 +1,72 @@ Version 0.3.0: + * Fixed bug where we would download http:// tracks twice. + * Make artist names in the album view clickable. + * Don't start playing if a tomahawk:// link was clicked while Tomahawk + is paused. + * Make artist name clickable in header of Album pages. + * Fixed adding social actions such as loved tracks from other sources. + * Added a drop shadow to cover images, and put CD placeholder in jewel + case. + * Added shuffle and repeat support to tree view. + * Draw a speaker next to the currently playing playlist. + * Refresh station previews whenever a filter is changed. + * Support and show official releases on album and track pages. + * Filter out duplicates from station previews and upcoming tracks. + * Resolve lists top-down. + * Added YouTube resolver. + * Fixed bug where going offline then online would not re-connect to many + peers. + * Added support for auto-updating live xspf playlists. + * Don't show an age of 41 for tracks that have no age information. + * Show config UI for resolvers that have them as so on as you add the resolver. + * Add support for Echo Nest Personal Catalogs and User Radio. Synchronize + your catalog with The Echo Nest and enable personal recommendations + from you and your friends. + * Added support for Grooveshark resolver (requires Grooveshark Anywhere). + * Fixed re-resolving when resolvers or sources go offline. + * Correctly sort recently played tracks on the Dashboard. + * Show a Lion full-screen toggle button if running on Lion. + * Display list of who is currently listening along to you. + * Show headphone icon in source item to allow users to listen along; paint + headphones red on a source if the user is listening along to it. + * Added new job status view in the bottom of the source list that displays + current jobs such as resolving, parsing playlists, and loading from + database. + * Parse and convert a Spotify playlist url when dropped anywhere on the + sidebar + * Convert resolvers to use asynchronous calls to avoid blocking Tomahawk's + UI, greatly increasing responsiveness of Tomahawk while resolving. + * Fixed no playlists overlay not disappearing when playlists were added. + * Add support for parsing itunes track, artist and album links. + * Fixed crash when syncing playlists with peers. + * Add support for browsing, downloading and rating resolvers + from inside Tomahawk directly. + * Support multi-folder selection and scanning. + * Actually remove deleted files from the collection. + * Fixed handling of special characters in tomahawk:// links + * Improve sidebar performance by caching pixmaps and shrinking them. + * Send updated playlists to peers when tracks are moved/copied. + * Remove splitter handles in sidebar * Fixed Tomahawk preventing system shutdown / logut. * Ignore leading 'The' when sorting artists. * Added Charts page, which shows various sources' top hits & artists. * The Collection tree-views can now be filtered. + * Fixed crash when pressing enter in an empty playlist. * Moved the song queue below to the left, below the sidebar. * Added Footnotes, a contextual view that you can slide it. * Show recently added playlists in dashboard rather than recently opened playlists. + * Fixed seek slider and give it some smooth animation between ticks. + * Fixed Twitter issue where it would repeatedly send DMs to friends. + * Add a new drag and drop menu when dropping items onto playlists, + allowing users to drop the dragged tracks, the whole album, or + the whole artists's tracks. + * Bring Tomahawk window to the front when clicking a Tomahawk link. + * Fixed crash in source list when initially syncing with remote sources. + * Open temporary artist, album, and search playlists as temporary items + in the sidebar. + * Fixed sorting of playlists and items in the artist view. + * Allow dragging and dropping albums and artists to playlists. * Added MPRIS 2.1 support. Version 0.2.3: diff --git a/README b/README index d035b51a2..1c1b8701f 100644 --- a/README +++ b/README @@ -34,7 +34,7 @@ Dependencies TagLib 1.6.2 - http://developer.kde.org/~wheeler/taglib.html Boost 1.3 - http://www.boost.org/ CLucene 0.9.23 (0.9.21 will fail) - http://clucene.sourceforge.net/download.shtml - libechonest 1.1.9 - http://projects.kde.org/projects/playground/libs/libechonest/ + libechonest 1.2.0 - http://projects.kde.org/projects/playground/libs/libechonest/ The following dependencies are optional, but recommended: diff --git a/data/images/add.png b/data/images/add.png index 14e3c7e84..59fa7fb1d 100644 Binary files a/data/images/add.png and b/data/images/add.png differ diff --git a/data/images/album-shadow.png b/data/images/album-shadow.png deleted file mode 100644 index a053655e2..000000000 Binary files a/data/images/album-shadow.png and /dev/null differ diff --git a/data/images/back.png b/data/images/back.png index 699eed76c..0c18ccf38 100644 Binary files a/data/images/back.png and b/data/images/back.png differ diff --git a/data/images/charts.png b/data/images/charts.png index f0e2f9472..6e56456a2 100644 Binary files a/data/images/charts.png and b/data/images/charts.png differ diff --git a/data/images/close.png b/data/images/close.png index 36da18e7d..5e3eaffc2 100644 Binary files a/data/images/close.png and b/data/images/close.png differ diff --git a/data/images/collapse.png b/data/images/collapse.png index 66a7e8618..b92bc558f 100644 Binary files a/data/images/collapse.png and b/data/images/collapse.png differ diff --git a/data/images/cover-shadow.png b/data/images/cover-shadow.png deleted file mode 100644 index e362118c6..000000000 Binary files a/data/images/cover-shadow.png and /dev/null differ diff --git a/data/images/downloading.png b/data/images/downloading.png index dd983f655..1856dec22 100644 Binary files a/data/images/downloading.png and b/data/images/downloading.png differ diff --git a/data/images/drop-album.png b/data/images/drop-album.png index bd4ff88e5..5e0568ea5 100644 Binary files a/data/images/drop-album.png and b/data/images/drop-album.png differ diff --git a/data/images/drop-all-songs.png b/data/images/drop-all-songs.png index 5e743fa5b..d8d511399 100644 Binary files a/data/images/drop-all-songs.png and b/data/images/drop-all-songs.png differ diff --git a/data/images/drop-local-songs.png b/data/images/drop-local-songs.png index 587cbffe5..61c34f22e 100644 Binary files a/data/images/drop-local-songs.png and b/data/images/drop-local-songs.png differ diff --git a/data/images/drop-song.png b/data/images/drop-song.png index 582ded327..7e25ab572 100644 Binary files a/data/images/drop-song.png and b/data/images/drop-song.png differ diff --git a/data/images/drop-top-songs.png b/data/images/drop-top-songs.png index e12f91052..22fff4da6 100644 Binary files a/data/images/drop-top-songs.png and b/data/images/drop-top-songs.png differ diff --git a/data/images/filter.png b/data/images/filter.png index ed48da97b..d2f1c2e35 100644 Binary files a/data/images/filter.png and b/data/images/filter.png differ diff --git a/data/images/forward.png b/data/images/forward.png index 72ad320ef..9b49c6687 100644 Binary files a/data/images/forward.png and b/data/images/forward.png differ diff --git a/data/images/loved_playlist.png b/data/images/loved_playlist.png index 7e2f4fd64..79c488e8e 100644 Binary files a/data/images/loved_playlist.png and b/data/images/loved_playlist.png differ diff --git a/data/images/no-album-art-placeholder.png b/data/images/no-album-art-placeholder.png index 3db3a8dd3..a117306e2 100644 Binary files a/data/images/no-album-art-placeholder.png and b/data/images/no-album-art-placeholder.png differ diff --git a/data/images/no-album-no-case.png b/data/images/no-album-no-case.png new file mode 100644 index 000000000..1fbbbb8bf Binary files /dev/null and b/data/images/no-album-no-case.png differ diff --git a/data/images/now-playing-speaker-dark.png b/data/images/now-playing-speaker-dark.png index 6aeb9dae6..fd0fec99b 100644 Binary files a/data/images/now-playing-speaker-dark.png and b/data/images/now-playing-speaker-dark.png differ diff --git a/data/images/open.png b/data/images/open.png index 32542f05b..96bd0f982 100644 Binary files a/data/images/open.png and b/data/images/open.png differ diff --git a/data/images/rdio.png b/data/images/rdio.png new file mode 100644 index 000000000..1cb29a309 Binary files /dev/null and b/data/images/rdio.png differ diff --git a/data/images/recently-played.png b/data/images/recently-played.png index d82e7374e..956f761f2 100644 Binary files a/data/images/recently-played.png and b/data/images/recently-played.png differ diff --git a/data/images/star-hover.png b/data/images/star-hover.png index 89efe2aef..fcf452a46 100644 Binary files a/data/images/star-hover.png and b/data/images/star-hover.png differ diff --git a/data/images/starred.png b/data/images/starred.png index a93e9f6da..247c1308d 100644 Binary files a/data/images/starred.png and b/data/images/starred.png differ diff --git a/data/images/station.png b/data/images/station.png index d08a96397..9680fe8a1 100644 Binary files a/data/images/station.png and b/data/images/station.png differ diff --git a/data/images/track-placeholder.png b/data/images/track-placeholder.png index fbc578124..2976d36d7 100644 Binary files a/data/images/track-placeholder.png and b/data/images/track-placeholder.png differ diff --git a/data/images/uploading.png b/data/images/uploading.png index 9b4455138..45cea76a7 100644 Binary files a/data/images/uploading.png and b/data/images/uploading.png differ diff --git a/data/images/user-avatar.png b/data/images/user-avatar.png index a82081b31..75059d551 100644 Binary files a/data/images/user-avatar.png and b/data/images/user-avatar.png differ diff --git a/lang/tomahawk_de.ts b/lang/tomahawk_de.ts index 9a4aebce4..558d60af2 100644 --- a/lang/tomahawk_de.ts +++ b/lang/tomahawk_de.ts @@ -6,13 +6,13 @@ <message> <location filename="src/libtomahawk/playlist/albummodel.cpp" line="145"/> <source>Album</source> - <translation type="unfinished">Album</translation> + <translation>Album</translation> </message> <message> <location filename="src/libtomahawk/playlist/albummodel.cpp" line="247"/> <location filename="src/libtomahawk/playlist/albummodel.cpp" line="269"/> <source>All albums from %1</source> - <translation type="unfinished">Alle Alben von %1</translation> + <translation>Alle Alben von %1</translation> </message> </context> <context> @@ -20,27 +20,27 @@ <message> <location filename="src/audiocontrols.ui" line="32"/> <source>Form</source> - <translation type="unfinished"></translation> + <translation></translation> </message> <message> <location filename="src/audiocontrols.ui" line="78"/> <source>Prev</source> - <translation type="unfinished"></translation> + <translation>Zurück</translation> </message> <message> <location filename="src/audiocontrols.ui" line="85"/> <source>Play</source> - <translation type="unfinished"></translation> + <translation>Abspielen</translation> </message> <message> <location filename="src/audiocontrols.ui" line="92"/> <source>Pause</source> - <translation type="unfinished"></translation> + <translation>Pause</translation> </message> <message> <location filename="src/audiocontrols.ui" line="99"/> <source>Next</source> - <translation type="unfinished"></translation> + <translation>Weiter</translation> </message> <message> <location filename="src/audiocontrols.ui" line="161"/> @@ -50,47 +50,47 @@ <message> <location filename="src/audiocontrols.ui" line="213"/> <source>Artist</source> - <translation type="unfinished"></translation> + <translation>Künstler</translation> </message> <message> <location filename="src/audiocontrols.ui" line="235"/> <source>Album</source> - <translation type="unfinished"></translation> + <translation>Album</translation> </message> <message> <location filename="src/audiocontrols.ui" line="262"/> <source>Owner</source> - <translation type="unfinished"></translation> + <translation>Eigentümer</translation> </message> <message> <location filename="src/audiocontrols.ui" line="295"/> <source>Time</source> - <translation type="unfinished"></translation> + <translation>Zeit</translation> </message> <message> <location filename="src/audiocontrols.ui" line="321"/> <source>Time Left</source> - <translation type="unfinished"></translation> + <translation>Zeit verbleibend</translation> </message> <message> <location filename="src/audiocontrols.ui" line="396"/> <source>Shuffle</source> - <translation type="unfinished"></translation> + <translation>Zufall</translation> </message> <message> <location filename="src/audiocontrols.ui" line="409"/> <source>Repeat</source> - <translation type="unfinished"></translation> + <translation>Wiederholen</translation> </message> <message> <location filename="src/audiocontrols.ui" line="447"/> <source>Low</source> - <translation type="unfinished"></translation> + <translation>Niedrig</translation> </message> <message> <location filename="src/audiocontrols.ui" line="479"/> <source>High</source> - <translation type="unfinished"></translation> + <translation>Hoch</translation> </message> </context> <context> @@ -98,7 +98,7 @@ <message> <location filename="src/libtomahawk/playlist/topbar/clearbutton.cpp" line="38"/> <source>Clear</source> - <translation type="unfinished">Leeren</translation> + <translation>Leeren</translation> </message> </context> <context> @@ -106,12 +106,12 @@ <message> <location filename="src/libtomahawk/playlist/collectionflatmodel.cpp" line="86"/> <source>Your Collection</source> - <translation type="unfinished">Deine Sammlung</translation> + <translation>Deine Sammlung</translation> </message> <message> <location filename="src/libtomahawk/playlist/collectionflatmodel.cpp" line="88"/> <source>Collection of %1</source> - <translation type="unfinished">Deine Sammlung von %1</translation> + <translation>Deine Sammlung von %1</translation> </message> </context> <context> @@ -160,7 +160,7 @@ <message> <location filename="src/libtomahawk/playlist/infobar/infobar.ui" line="26"/> <source>InfoBar</source> - <translation type="unfinished"></translation> + <translation>Infoleiste</translation> </message> <message> <location filename="src/libtomahawk/playlist/infobar/infobar.ui" line="54"/> @@ -195,12 +195,12 @@ <message> <location filename="src/sip/jabber/jabber_p.cpp" line="641"/> <source>Authorize User</source> - <translation type="unfinished">Benutzer authorisieren</translation> + <translation>Benutzer authorisieren</translation> </message> <message> <location filename="src/sip/jabber/jabber_p.cpp" line="642"/> <source>Do you want to grant <b>%1</b> access to your Collection?</source> - <translation type="unfinished">Willst du <b>%1</b> wirklich den Zugriff auf deine Sammlung erlauben?</translation> + <translation>Willst du <b>%1</b> wirklich den Zugriff auf deine Sammlung erlauben?</translation> </message> </context> <context> @@ -236,7 +236,7 @@ <message> <location filename="src/libtomahawk/widgets/welcomewidget.cpp" line="203"/> <source>%1 tracks</source> - <translation type="unfinished">%1 Stücke</translation> + <translation>%1 Stücke</translation> </message> </context> <context> @@ -244,12 +244,12 @@ <message> <location filename="src/libtomahawk/playlist/playlistmanager.cpp" line="363"/> <source>All available tracks</source> - <translation type="unfinished">Alle verfügbaren Stücke</translation> + <translation>Alle verfügbaren Stücke</translation> </message> <message> <location filename="src/libtomahawk/playlist/playlistmanager.cpp" line="364"/> <source>All available albums</source> - <translation type="unfinished">Alle verfügbaren Alben</translation> + <translation>Alle verfügbaren Alben</translation> </message> </context> <context> @@ -257,12 +257,12 @@ <message> <location filename="src/libtomahawk/playlist/playlistmodel.cpp" line="86"/> <source>A playlist by %1</source> - <translation type="unfinished">Eine Playliste von %1</translation> + <translation>Eine Playliste von %1</translation> </message> <message> <location filename="src/libtomahawk/playlist/playlistmodel.cpp" line="86"/> <source>you</source> - <translation type="unfinished">dir</translation> + <translation>dir</translation> </message> </context> <context> @@ -290,7 +290,7 @@ <message> <location filename="src/libtomahawk/playlist/playlistview.cpp" line="143"/> <source>This playlist is currently empty. Add some tracks to it and enjoy the music!</source> - <translation>Die Playliste ist derzeit leer. Füge einige Stücke hinzu und genieße die Musik!</translation> + <translation>Diese Playliste ist derzeit leer. Füge einige Stücke hinzu und genieße die Musik!</translation> </message> </context> <context> @@ -298,32 +298,32 @@ <message> <location filename="src/proxydialog.ui" line="17"/> <source>Proxy Settings</source> - <translation type="unfinished">Proxy-Einstellungen</translation> + <translation>Proxy-Einstellungen</translation> </message> <message> <location filename="src/proxydialog.ui" line="91"/> <source>Host</source> - <translation type="unfinished">Rechnername</translation> + <translation>Rechnername</translation> </message> <message> <location filename="src/proxydialog.ui" line="98"/> <source>Port</source> - <translation type="unfinished">Port</translation> + <translation>Port</translation> </message> <message> <location filename="src/proxydialog.ui" line="111"/> <source>User</source> - <translation type="unfinished">Benutzer</translation> + <translation>Benutzer</translation> </message> <message> <location filename="src/proxydialog.ui" line="118"/> <source>Password</source> - <translation type="unfinished">Passwort</translation> + <translation>Passwort</translation> </message> <message> <location filename="src/proxydialog.ui" line="132"/> <source>Type</source> - <translation type="unfinished">Typ</translation> + <translation>Typ</translation> </message> </context> <context> @@ -332,12 +332,12 @@ <location filename="src/libtomahawk/playlist/queueview.cpp" line="51"/> <location filename="src/libtomahawk/playlist/queueview.cpp" line="90"/> <source>Click to show queue</source> - <translation type="unfinished">Klicke hier, um die Warteschlange anzuzeigen</translation> + <translation>Klicke hier, um die Warteschlange anzuzeigen</translation> </message> <message> <location filename="src/libtomahawk/playlist/queueview.cpp" line="75"/> <source>Click to hide queue</source> - <translation type="unfinished">Klicke hier, um die Warteschlange auszublenden</translation> + <translation>Klicke hier, um die Warteschlange auszublenden</translation> </message> </context> <context> @@ -345,7 +345,7 @@ <message> <location filename="src/libtomahawk/playlist/topbar/searchlineedit.cpp" line="56"/> <source>Search</source> - <translation type="unfinished">Suchen</translation> + <translation>Suchen</translation> </message> </context> <context> @@ -354,7 +354,7 @@ <location filename="src/settingsdialog.ui" line="20"/> <source>Music Player Settings</source> <translatorcomment>Übersetzung eher dürftig</translatorcomment> - <translation type="unfinished">Einstellungen für das Musikabspielprogramm</translation> + <translation>Einstellungen für das Musikabspielprogramm</translation> </message> <message> <location filename="src/settingsdialog.ui" line="30"/> @@ -375,7 +375,7 @@ <message> <location filename="src/settingsdialog.ui" line="132"/> <source>Advanced Jabber Settings</source> - <translation type="unfinished">Erweiterte Einstellungen für Jabber</translation> + <translation>Erweiterte Einstellungen für Jabber</translation> </message> <message> <location filename="src/settingsdialog.ui" line="152"/> @@ -565,7 +565,7 @@ <message> <location filename="src/sourcetree/sourcetreeitem.cpp" line="48"/> <source>Super Collection</source> - <translation type="unfinished">Komplettsammlung</translation> + <translation>Komplettsammlung</translation> </message> </context> <context> @@ -573,7 +573,7 @@ <message> <location filename="src/sourcetree/sourcetreeitemwidget.ui" line="20"/> <source>Form</source> - <translation type="unfinished"></translation> + <translation>Form</translation> </message> <message> <location filename="src/sourcetree/sourcetreeitemwidget.ui" line="69"/> @@ -586,67 +586,67 @@ <message> <location filename="src/sourcetree/sourcetreeitemwidget.ui" line="147"/> <source>Off</source> - <translation type="unfinished"></translation> + <translation>Aus</translation> </message> <message> <location filename="src/sourcetree/sourcetreeitemwidget.ui" line="175"/> <source>Info</source> - <translation type="unfinished"></translation> + <translation>Info</translation> </message> <message> <location filename="src/sourcetree/sourcetreeitemwidget.cpp" line="57"/> <source>Super Collection</source> - <translation type="unfinished">Komplettsammlung</translation> + <translation>Komplettsammlung</translation> </message> <message> <location filename="src/sourcetree/sourcetreeitemwidget.cpp" line="58"/> <source>All available tracks</source> - <translation type="unfinished">Alle verfügbaren Stücke</translation> + <translation>Alle verfügbaren Stücke</translation> </message> <message> <location filename="src/sourcetree/sourcetreeitemwidget.cpp" line="85"/> <source>Idle</source> - <translation type="unfinished"></translation> + <translation>Warte</translation> </message> <message> <location filename="src/sourcetree/sourcetreeitemwidget.cpp" line="128"/> <source>%L1 tracks</source> - <translation type="unfinished">%L1 Stücke</translation> + <translation>%L1 Stücke</translation> </message> <message> <location filename="src/sourcetree/sourcetreeitemwidget.cpp" line="139"/> <source>Checking</source> - <translation type="unfinished">Teste</translation> + <translation>Überprüfe</translation> </message> <message> <location filename="src/sourcetree/sourcetreeitemwidget.cpp" line="142"/> <source>Fetching</source> - <translation type="unfinished">Hole</translation> + <translation>Sammle</translation> </message> <message> <location filename="src/sourcetree/sourcetreeitemwidget.cpp" line="145"/> <source>Parsing</source> - <translation type="unfinished">Parse</translation> + <translation>Parse</translation> </message> <message> <location filename="src/sourcetree/sourcetreeitemwidget.cpp" line="148"/> <source>Saving</source> - <translation type="unfinished">Speichere</translation> + <translation>Speichere</translation> </message> <message> <location filename="src/sourcetree/sourcetreeitemwidget.cpp" line="151"/> <source>Synced</source> - <translation type="unfinished">Synchronisiert</translation> + <translation>Synchronisiert</translation> </message> <message> <location filename="src/sourcetree/sourcetreeitemwidget.cpp" line="154"/> <source>Scanning (%L1 tracks)</source> - <translation type="unfinished">Durchsuche (%L1 Stücke)</translation> + <translation>Durchsuche (%L1 Stücke)</translation> </message> <message> <location filename="src/sourcetree/sourcetreeitemwidget.cpp" line="183"/> <source>Offline</source> - <translation type="unfinished">Nicht Verbunden</translation> + <translation>Nicht verbunden</translation> </message> </context> <context> @@ -672,7 +672,7 @@ <message> <location filename="src/libtomahawk/playlist/dynamic/widgets/DynamicControlList.cpp" line="73"/> <source>Click to collapse</source> - <translation type="unfinished">Klicken zum Zusammenfalten</translation> + <translation>Klicken um einzuklappen</translation> </message> </context> <context> @@ -727,7 +727,7 @@ Bitte ändere den Filter oder versuche es erneut.</translation> <location filename="src/libtomahawk/playlist/dynamic/echonest/EchonestControl.cpp" line="240"/> <location filename="src/libtomahawk/playlist/dynamic/echonest/EchonestControl.cpp" line="259"/> <source>is</source> - <translation type="unfinished">ist</translation> + <translation>ist</translation> </message> <message> <location filename="src/libtomahawk/playlist/dynamic/echonest/EchonestControl.cpp" line="178"/> @@ -737,7 +737,7 @@ Bitte ändere den Filter oder versuche es erneut.</translation> <location filename="src/libtomahawk/playlist/dynamic/echonest/EchonestControl.cpp" line="222"/> <location filename="src/libtomahawk/playlist/dynamic/echonest/EchonestControl.cpp" line="226"/> <source>Less</source> - <translation type="unfinished">Kleiner</translation> + <translation>Kleiner</translation> </message> <message> <location filename="src/libtomahawk/playlist/dynamic/echonest/EchonestControl.cpp" line="178"/> @@ -747,178 +747,180 @@ Bitte ändere den Filter oder versuche es erneut.</translation> <location filename="src/libtomahawk/playlist/dynamic/echonest/EchonestControl.cpp" line="222"/> <location filename="src/libtomahawk/playlist/dynamic/echonest/EchonestControl.cpp" line="226"/> <source>More</source> - <translation type="unfinished">Größer</translation> + <translation>Größer</translation> </message> <message> <location filename="src/libtomahawk/playlist/dynamic/echonest/EchonestControl.cpp" line="197"/> <source>0 BPM</source> - <translation type="unfinished"></translation> + <translation>0 BPM</translation> </message> <message> <location filename="src/libtomahawk/playlist/dynamic/echonest/EchonestControl.cpp" line="197"/> <source>500 BPM</source> - <translation type="unfinished"></translation> + <translation>500 BPM</translation> </message> <message> <location filename="src/libtomahawk/playlist/dynamic/echonest/EchonestControl.cpp" line="201"/> <source>0 secs</source> - <translation type="unfinished">0 s</translation> + <translation>0 sec</translation> </message> <message> <location filename="src/libtomahawk/playlist/dynamic/echonest/EchonestControl.cpp" line="201"/> <source>3600 secs</source> - <translation type="unfinished">3600 s</translation> + <translation>3600 sec</translation> </message> <message> <location filename="src/libtomahawk/playlist/dynamic/echonest/EchonestControl.cpp" line="205"/> <source>-100 dB</source> - <translation type="unfinished"></translation> + <translation>-100 dB</translation> </message> <message> <location filename="src/libtomahawk/playlist/dynamic/echonest/EchonestControl.cpp" line="205"/> <source>100 dB</source> - <translation type="unfinished"></translation> + <translation>100 dB</translation> </message> <message> <location filename="src/libtomahawk/playlist/dynamic/echonest/EchonestControl.cpp" line="242"/> <source>Major</source> - <translation type="unfinished">Dur</translation> + <translation>Dur</translation> </message> <message> <location filename="src/libtomahawk/playlist/dynamic/echonest/EchonestControl.cpp" line="243"/> <source>Minor</source> - <translation type="unfinished">Moll</translation> + <translation>Moll</translation> </message> <message> <location filename="src/libtomahawk/playlist/dynamic/echonest/EchonestControl.cpp" line="261"/> <source>C</source> - <translation type="unfinished">C</translation> + <translation>C</translation> </message> <message> <location filename="src/libtomahawk/playlist/dynamic/echonest/EchonestControl.cpp" line="262"/> <source>C Sharp</source> - <translation type="unfinished">Cis</translation> + <translation>Cis</translation> </message> <message> <location filename="src/libtomahawk/playlist/dynamic/echonest/EchonestControl.cpp" line="263"/> <source>D</source> - <translation type="unfinished">D</translation> + <translation>D</translation> </message> <message> <location filename="src/libtomahawk/playlist/dynamic/echonest/EchonestControl.cpp" line="264"/> <source>E Flat</source> - <translation type="unfinished">Es</translation> + <translation>Es</translation> </message> <message> <location filename="src/libtomahawk/playlist/dynamic/echonest/EchonestControl.cpp" line="265"/> <source>E</source> - <translation type="unfinished">E</translation> + <translation>E</translation> </message> <message> <location filename="src/libtomahawk/playlist/dynamic/echonest/EchonestControl.cpp" line="266"/> <source>F</source> - <translation type="unfinished">F</translation> + <translation>F</translation> </message> <message> <location filename="src/libtomahawk/playlist/dynamic/echonest/EchonestControl.cpp" line="267"/> <source>F Sharp</source> - <translation type="unfinished">Fis</translation> + <translation>Fis</translation> </message> <message> <location filename="src/libtomahawk/playlist/dynamic/echonest/EchonestControl.cpp" line="268"/> <source>G</source> - <translation type="unfinished">G</translation> + <translation>G</translation> </message> <message> <location filename="src/libtomahawk/playlist/dynamic/echonest/EchonestControl.cpp" line="269"/> <source>A Flat</source> - <translation type="unfinished">As</translation> + <translation>As</translation> </message> <message> <location filename="src/libtomahawk/playlist/dynamic/echonest/EchonestControl.cpp" line="270"/> <source>A</source> - <translation type="unfinished">A</translation> + <translation>A</translation> </message> <message> <location filename="src/libtomahawk/playlist/dynamic/echonest/EchonestControl.cpp" line="271"/> <source>B Flat</source> - <translatorcomment>stimmt das?</translatorcomment> - <translation type="unfinished">B</translation> + <translation>B</translation> </message> <message> <location filename="src/libtomahawk/playlist/dynamic/echonest/EchonestControl.cpp" line="272"/> <source>B</source> - <translation type="unfinished">H</translation> + <translation>H</translation> </message> <message> <location filename="src/libtomahawk/playlist/dynamic/echonest/EchonestControl.cpp" line="289"/> <source>Ascending</source> - <translation type="unfinished">Aufsteigend</translation> + <translation>Aufsteigend</translation> </message> <message> <location filename="src/libtomahawk/playlist/dynamic/echonest/EchonestControl.cpp" line="290"/> <source>Descending</source> - <translation type="unfinished">Absteigend</translation> + <translation>Absteigend</translation> </message> <message> <location filename="src/libtomahawk/playlist/dynamic/echonest/EchonestControl.cpp" line="293"/> <source>Tempo</source> - <translation type="unfinished">Tempo</translation> + <translation>Tempo</translation> </message> <message> <location filename="src/libtomahawk/playlist/dynamic/echonest/EchonestControl.cpp" line="294"/> <source>Duration</source> - <translation type="unfinished">Dauer</translation> + <translation>Dauer</translation> </message> <message> <location filename="src/libtomahawk/playlist/dynamic/echonest/EchonestControl.cpp" line="295"/> <source>Loudness</source> - <translation type="unfinished">Lautstärke</translation> + <translation>Lautstärke</translation> </message> <message> <location filename="src/libtomahawk/playlist/dynamic/echonest/EchonestControl.cpp" line="296"/> <source>Artist Familiarity</source> - <translation type="unfinished"></translation> + <translatorcomment>nicht sicher ob das passt</translatorcomment> + <translation>Ähnlichkeit</translation> </message> <message> <location filename="src/libtomahawk/playlist/dynamic/echonest/EchonestControl.cpp" line="297"/> <source>Artist Hotttnesss</source> + <translatorcomment>keine ahnung</translatorcomment> <translation type="unfinished"></translation> </message> <message> <location filename="src/libtomahawk/playlist/dynamic/echonest/EchonestControl.cpp" line="298"/> <source>Song Hotttnesss</source> + <translatorcomment>keine ahnung</translatorcomment> <translation type="unfinished"></translation> </message> <message> <location filename="src/libtomahawk/playlist/dynamic/echonest/EchonestControl.cpp" line="299"/> <source>Latitude</source> - <translation type="unfinished">Breitengrad</translation> + <translation>Breitengrad</translation> </message> <message> <location filename="src/libtomahawk/playlist/dynamic/echonest/EchonestControl.cpp" line="300"/> <source>Longitude</source> - <translation type="unfinished">Längengrad</translation> + <translation>Längengrad</translation> </message> <message> <location filename="src/libtomahawk/playlist/dynamic/echonest/EchonestControl.cpp" line="301"/> <source>Mode</source> - <translation type="unfinished">Modus</translation> + <translation>Modus</translation> </message> <message> <location filename="src/libtomahawk/playlist/dynamic/echonest/EchonestControl.cpp" line="302"/> <source>Key</source> - <translation type="unfinished">Schlüssel</translation> + <translation>Schlüssel</translation> </message> <message> <location filename="src/libtomahawk/playlist/dynamic/echonest/EchonestControl.cpp" line="303"/> <source>Energy</source> - <translation type="unfinished">Energie</translation> + <translation>Energie</translation> </message> <message> <location filename="src/libtomahawk/playlist/dynamic/echonest/EchonestControl.cpp" line="304"/> <source>Danceability</source> - <translation type="unfinished">Tanzbarkeit</translation> + <translation>Tanzbarkeit</translation> </message> </context> <context> @@ -926,97 +928,100 @@ Bitte ändere den Filter oder versuche es erneut.</translation> <message> <location filename="src/libtomahawk/playlist/dynamic/echonest/EchonestSteerer.cpp" line="54"/> <source>Steer this station:</source> - <translation type="unfinished">Steuere diese Station:</translation> + <translation>Steuere diese Station:</translation> </message> <message> <location filename="src/libtomahawk/playlist/dynamic/echonest/EchonestSteerer.cpp" line="60"/> <source>Takes effect on track change</source> - <translation type="unfinished">Wird nach dem Wechsel eines Stückes aktiv</translation> + <translation>Wird nach dem Wechsel eines Stückes aktiv</translation> </message> <message> <location filename="src/libtomahawk/playlist/dynamic/echonest/EchonestSteerer.cpp" line="68"/> <source>Much less</source> - <translation type="unfinished">Viel Weniger</translation> + <translation>Viel weniger</translation> </message> <message> <location filename="src/libtomahawk/playlist/dynamic/echonest/EchonestSteerer.cpp" line="69"/> <source>Less</source> - <translation type="unfinished">Weniger</translation> + <translation>Weniger</translation> </message> <message> <location filename="src/libtomahawk/playlist/dynamic/echonest/EchonestSteerer.cpp" line="70"/> <source>A bit less</source> - <translation type="unfinished">Etwas Weniger</translation> + <translation>Etwas weniger</translation> </message> <message> <location filename="src/libtomahawk/playlist/dynamic/echonest/EchonestSteerer.cpp" line="71"/> <source>Keep at current</source> - <translation type="unfinished">So belassen</translation> + <translation>So belassen</translation> </message> <message> <location filename="src/libtomahawk/playlist/dynamic/echonest/EchonestSteerer.cpp" line="72"/> <source>A bit more</source> - <translation type="unfinished">Etwas Mehr</translation> + <translation>Etwas mehr</translation> </message> <message> <location filename="src/libtomahawk/playlist/dynamic/echonest/EchonestSteerer.cpp" line="73"/> <source>More</source> - <translation type="unfinished">Mehr</translation> + <translation>Mehr</translation> </message> <message> <location filename="src/libtomahawk/playlist/dynamic/echonest/EchonestSteerer.cpp" line="74"/> <source>Much more</source> - <translation type="unfinished">Viel Mehr</translation> + <translation>Viel mehr</translation> </message> <message> <location filename="src/libtomahawk/playlist/dynamic/echonest/EchonestSteerer.cpp" line="77"/> <source>Tempo</source> - <translation type="unfinished">Tempo</translation> + <translation>Tempo</translation> </message> <message> <location filename="src/libtomahawk/playlist/dynamic/echonest/EchonestSteerer.cpp" line="78"/> <source>Loudness</source> - <translation type="unfinished">Lautstärke</translation> + <translation>Lautstärke</translation> </message> <message> <location filename="src/libtomahawk/playlist/dynamic/echonest/EchonestSteerer.cpp" line="79"/> <source>Danceability</source> - <translation type="unfinished">Tanzbarkeit</translation> + <translation>Tanzbarkeit</translation> </message> <message> <location filename="src/libtomahawk/playlist/dynamic/echonest/EchonestSteerer.cpp" line="80"/> <source>Energy</source> - <translation type="unfinished">Energie</translation> + <translation>Energie</translation> </message> <message> <location filename="src/libtomahawk/playlist/dynamic/echonest/EchonestSteerer.cpp" line="81"/> <source>Song Hotttnesss</source> + <translatorcomment>keine ahnung</translatorcomment> <translation type="unfinished"></translation> </message> <message> <location filename="src/libtomahawk/playlist/dynamic/echonest/EchonestSteerer.cpp" line="82"/> <source>Artist Hotttnesss</source> + <translatorcomment>keine ahnung</translatorcomment> <translation type="unfinished"></translation> </message> <message> <location filename="src/libtomahawk/playlist/dynamic/echonest/EchonestSteerer.cpp" line="83"/> <source>Artist Familiarity</source> - <translation type="unfinished"></translation> + <translatorcomment>nicht sicher ob das stimmt</translatorcomment> + <translation>Ähnlichkeit Künstler</translation> </message> <message> <location filename="src/libtomahawk/playlist/dynamic/echonest/EchonestSteerer.cpp" line="84"/> <source>By Description</source> - <translation type="unfinished">Von der Beschreibung</translation> + <translation>Von der Beschreibung</translation> </message> <message> <location filename="src/libtomahawk/playlist/dynamic/echonest/EchonestSteerer.cpp" line="92"/> <source>Enter a description</source> - <translation type="unfinished">Gib eine Beschreibung ein</translation> + <translation>Gib eine Beschreibung ein</translation> </message> <message> <location filename="src/libtomahawk/playlist/dynamic/echonest/EchonestSteerer.cpp" line="99"/> <source>Reset all steering commands</source> - <translation type="unfinished">Setze alle Steuerkommandos zurück</translation> + <translation>Setze alle Steuerkommandos zurück</translation> </message> </context> <context> @@ -1025,27 +1030,27 @@ Bitte ändere den Filter oder versuche es erneut.</translation> <location filename="src/libtomahawk/source.cpp" line="176"/> <location filename="src/libtomahawk/source.cpp" line="211"/> <source>Scanning (%L1 tracks)</source> - <translation type="unfinished">Scanne (%L1 Stücke)</translation> + <translation>Scanne (%L1 Stücke)</translation> </message> <message> <location filename="src/libtomahawk/source.cpp" line="196"/> <source>Checking</source> - <translation type="unfinished">Teste</translation> + <translation>Überprüfe</translation> </message> <message> <location filename="src/libtomahawk/source.cpp" line="199"/> <source>Fetching</source> - <translation type="unfinished">Hole</translation> + <translation>Sammle</translation> </message> <message> <location filename="src/libtomahawk/source.cpp" line="202"/> <source>Parsing</source> - <translation type="unfinished">Parse</translation> + <translation>Parse</translation> </message> <message> <location filename="src/libtomahawk/source.cpp" line="205"/> <source>Saving</source> - <translation type="unfinished">Speichere</translation> + <translation>Speichere</translation> </message> </context> <context> @@ -1053,37 +1058,37 @@ Bitte ändere den Filter oder versuche es erneut.</translation> <message> <location filename="src/tomahawktrayicon.cpp" line="42"/> <source>Play</source> - <translation type="unfinished">Abspielen</translation> + <translation>Abspielen</translation> </message> <message> <location filename="src/tomahawktrayicon.cpp" line="43"/> <source>Pause</source> - <translation type="unfinished">Pause</translation> + <translation>Pause</translation> </message> <message> <location filename="src/tomahawktrayicon.cpp" line="44"/> <source>Stop</source> - <translation type="unfinished">Anhalten</translation> + <translation>Anhalten</translation> </message> <message> <location filename="src/tomahawktrayicon.cpp" line="46"/> <source>Previous Track</source> - <translation type="unfinished">Vorheriges Stück</translation> + <translation>Vorheriges Stück</translation> </message> <message> <location filename="src/tomahawktrayicon.cpp" line="47"/> <source>Next Track</source> - <translation type="unfinished">Nächstes Stück</translation> + <translation>Nächstes Stück</translation> </message> <message> <location filename="src/tomahawktrayicon.cpp" line="49"/> <source>Quit</source> - <translation type="unfinished">Verlassen</translation> + <translation>Verlassen</translation> </message> <message> <location filename="src/tomahawktrayicon.cpp" line="97"/> <source>Currently not playing.</source> - <translation type="unfinished">Derzeit wird nichts gespielt.</translation> + <translation>Derzeit wird nichts gespielt.</translation> </message> </context> <context> @@ -1142,7 +1147,7 @@ Bitte ändere den Filter oder versuche es erneut.</translation> <message> <location filename="src/tomahawkwindow.ui" line="111"/> <source>Re&scan Collection...</source> - <translation type="unfinished">Sammlung neu&laden…</translation> + <translation>Sammlung neu&laden…</translation> </message> <message> <location filename="src/tomahawkwindow.ui" line="116"/> @@ -1188,7 +1193,7 @@ Bitte ändere den Filter oder versuche es erneut.</translation> <location filename="src/tomahawkwindow.cpp" line="150"/> <location filename="src/tomahawkwindow.cpp" line="166"/> <source>Check for updates...</source> - <translation type="unfinished">Suche nach Updates…</translation> + <translation>Suche nach Updates…</translation> </message> <message> <location filename="src/tomahawkwindow.cpp" line="170"/> @@ -1203,7 +1208,7 @@ Bitte ändere den Filter oder versuche es erneut.</translation> <message> <location filename="src/tomahawkwindow.cpp" line="172"/> <source>Home</source> - <translation type="unfinished"></translation> + <translation>Anfang</translation> </message> <message> <location filename="src/tomahawkwindow.cpp" line="332"/> @@ -1245,7 +1250,7 @@ Bitte ändere den Filter oder versuche es erneut.</translation> <message> <location filename="src/tomahawkwindow.cpp" line="506"/> <source><h2><b>Tomahawk %1</h2>Copyright 2010, 2011<br/>Christian Muehlhaeuser &lt;muesli@tomahawk-player.org&gt;<br/><br/>Thanks to: Leo Franchi, Jeff Mitchell, Dominik Schmidt, Jason Herskowitz, Alejandro Wainzinger, Harald Sitter and Steve Robertson</source> - <translation type="unfinished"></translation> + <translation><h2><b>Tomahawk %1</h2>Copyright 2010, 2011<br/>Christian Muehlhaeuser &lt;muesli@tomahawk-player.org&gt;<br/><br/>Danke an: Leo Franchi, Jeff Mitchell, Dominik Schmidt, Jason Herskowitz, Alejandro Wainzinger, Harald Sitter und Steve Robertson</translation> </message> </context> <context> @@ -1253,47 +1258,47 @@ Bitte ändere den Filter oder versuche es erneut.</translation> <message> <location filename="src/libtomahawk/playlist/topbar/topbar.ui" line="26"/> <source>Form</source> - <translation type="unfinished"></translation> + <translation>Form</translation> </message> <message> <location filename="src/libtomahawk/playlist/topbar/topbar.ui" line="66"/> <source>0 Sources</source> - <translation type="unfinished"></translation> + <translation>0 Quellen</translation> </message> <message> <location filename="src/libtomahawk/playlist/topbar/topbar.ui" line="89"/> <source>0 Tracks</source> - <translation type="unfinished"></translation> + <translation>0 Titel</translation> </message> <message> <location filename="src/libtomahawk/playlist/topbar/topbar.ui" line="112"/> <source>0 Artists</source> - <translation type="unfinished"></translation> + <translation>0 Künstler</translation> </message> <message> <location filename="src/libtomahawk/playlist/topbar/topbar.ui" line="135"/> <source>0 Shown</source> - <translation type="unfinished"></translation> + <translation>0 angezeigt</translation> </message> <message> <location filename="src/libtomahawk/playlist/topbar/topbar.cpp" line="45"/> <source>Tracks</source> - <translation type="unfinished">Stücke</translation> + <translation>Stücke</translation> </message> <message> <location filename="src/libtomahawk/playlist/topbar/topbar.cpp" line="46"/> <source>Artists</source> - <translation type="unfinished">Künstler</translation> + <translation>Künstler</translation> </message> <message> <location filename="src/libtomahawk/playlist/topbar/topbar.cpp" line="182"/> <source>Sources</source> - <translation type="unfinished">Quellen</translation> + <translation>Quellen</translation> </message> <message> <location filename="src/libtomahawk/playlist/topbar/topbar.cpp" line="234"/> <source>Shown</source> - <translation type="unfinished">Angezeigt</translation> + <translation>Angezeigt</translation> </message> </context> <context> @@ -1301,47 +1306,47 @@ Bitte ändere den Filter oder versuche es erneut.</translation> <message> <location filename="src/libtomahawk/playlist/trackmodel.cpp" line="210"/> <source>Artist</source> - <translation type="unfinished">Künstler</translation> + <translation>Künstler</translation> </message> <message> <location filename="src/libtomahawk/playlist/trackmodel.cpp" line="210"/> <source>Track</source> - <translation type="unfinished">Titel</translation> + <translation>Titel</translation> </message> <message> <location filename="src/libtomahawk/playlist/trackmodel.cpp" line="210"/> <source>Album</source> - <translation type="unfinished">Album</translation> + <translation>Album</translation> </message> <message> <location filename="src/libtomahawk/playlist/trackmodel.cpp" line="210"/> <source>Duration</source> - <translation type="unfinished">Spieldauer</translation> + <translation>Spieldauer</translation> </message> <message> <location filename="src/libtomahawk/playlist/trackmodel.cpp" line="210"/> <source>Bitrate</source> - <translation type="unfinished">Bitrate</translation> + <translation>Bitrate</translation> </message> <message> <location filename="src/libtomahawk/playlist/trackmodel.cpp" line="210"/> <source>Age</source> - <translation type="unfinished">Alter</translation> + <translation>Alter</translation> </message> <message> <location filename="src/libtomahawk/playlist/trackmodel.cpp" line="210"/> <source>Year</source> - <translation type="unfinished">Jahr</translation> + <translation>Jahr</translation> </message> <message> <location filename="src/libtomahawk/playlist/trackmodel.cpp" line="210"/> <source>Size</source> - <translation type="unfinished">Größe</translation> + <translation>Größe</translation> </message> <message> <location filename="src/libtomahawk/playlist/trackmodel.cpp" line="210"/> <source>Origin</source> - <translation type="unfinished">Quelle</translation> + <translation>Quelle</translation> </message> </context> <context> @@ -1349,7 +1354,7 @@ Bitte ändere den Filter oder versuche es erneut.</translation> <message> <location filename="src/libtomahawk/playlist/trackview.cpp" line="325"/> <source>Sorry, your filter '%1' did not match any results.</source> - <translation type="unfinished">Entschuldige, dein Filter '%1' erzeugte keine Ergebnisse.</translation> + <translation>Entschuldige, dein Filter '%1' erzeugte keine Ergebnisse.</translation> </message> </context> <context> @@ -1375,59 +1380,61 @@ Bitte ändere den Filter oder versuche es erneut.</translation> <message> <location filename="src/sip/twitter/twitterconfigwidget.ui" line="25"/> <source>Authenticating with Twitter allows you to discover and play music from your Twitter friends running Tomahawk.</source> - <translation type="unfinished"></translation> + <translation>Die Authentifizierung mit Twitter ermöglicht es dir Musik von den Twitter-Freunden zu hören, die Tomahawk verwenden</translation> </message> <message> <location filename="src/sip/twitter/twitterconfigwidget.ui" line="35"/> <source>This feature works best when you have set a static host name in the "Network" settings tab under Advanced Settings, but may work even if you do not. Tomahawk uses Direct Messages and this will only work when both Twitter users have followed each other.</source> - <translation type="unfinished"></translation> + <translation>Dies funktioniert am Besten, wenn du einen statischen Hostnamen im "Netzwerk"-Tab unter Erweiterte Netzwerkeinstellungen eingetragen hast. Es kann aber auch ohne funktionieren. Tomahawk verwendet Direktnachrichten, was voraussetzt, dass sich beide Twitternutzer gegenseitig folgen.</translation> </message> <message> <location filename="src/sip/twitter/twitterconfigwidget.ui" line="47"/> <source>Status: No saved credentials</source> - <translation type="unfinished"></translation> + <translation>Status: Keine gespeicherten Zugangsdaten</translation> </message> <message> <location filename="src/sip/twitter/twitterconfigwidget.ui" line="57"/> <source>Authenticate with Twitter</source> - <translation type="unfinished"></translation> + <translation>Mit Twitter authentifizieren</translation> </message> <message> <location filename="src/sip/twitter/twitterconfigwidget.ui" line="68"/> <source>Here's how it works: just press one of the buttons below to tweet "Got Tomahawk?" and some necessary information. Then be (very) patient. Twitter is an asynchronous protocol so it can take a bit! If connections to peers seem to have been lost, just press the appropriate button again to re-post a tweet for resynchronization.</source> - <translation type="unfinished"></translation> + <translation>So geht's: klicke auf einen der Buttons unten um "Got Tomahawk?" und einige notwendige Informationen zu twittern. Dann heisst es (sehr) geduldig sein. Twitter ist ein asynchrones Protokoll, also kann es etwas dauern! + +Sollte die Verbindung zu anderen Gegenstellen verloren gehen, klicke einfach erneut auf den entsprechenden Button um einen Tweet zur Resynchronisierung zu senden.</translation> </message> <message> <location filename="src/sip/twitter/twitterconfigwidget.ui" line="104"/> <source>Select the kind of tweet you would like, then press the button to post it:</source> - <translation type="unfinished"></translation> + <translation>Wähle die gewünschte Tweet-Form aus und klicke auf den Button um ihn zu senden:</translation> </message> <message> <location filename="src/sip/twitter/twitterconfigwidget.ui" line="120"/> <source>Global Tweet</source> - <translation type="unfinished"></translation> + <translation>Globaler Tweet</translation> </message> <message> <location filename="src/sip/twitter/twitterconfigwidget.ui" line="125"/> <source>@Mention</source> - <translation type="unfinished"></translation> + <translation>@Erwähnung</translation> </message> <message> <location filename="src/sip/twitter/twitterconfigwidget.ui" line="130"/> <source>Direct Message</source> - <translation type="unfinished"></translation> + <translation>Direktnachricht</translation> </message> <message> <location filename="src/sip/twitter/twitterconfigwidget.ui" line="150"/> <source>e.g. @tomahawkplayer</source> - <translation type="unfinished"></translation> + <translation>z.B. @tomahawkplayer</translation> </message> <message> <location filename="src/sip/twitter/twitterconfigwidget.ui" line="163"/> <source>Tweet!</source> - <translation type="unfinished"></translation> + <translation>Twittern!</translation> </message> </context> <context> @@ -1458,27 +1465,27 @@ If connections to peers seem to have been lost, just press the appropriate butto <message> <location filename="src/libtomahawk/utils/xspfloader.cpp" line="118"/> <source>New Playlist</source> - <translation type="unfinished">Neue Playliste</translation> + <translation>Neue Playliste</translation> </message> <message> <location filename="src/libtomahawk/utils/xspfloader.cpp" line="143"/> <source>Failed to save tracks</source> - <translation type="unfinished">Konnte Stücke nicht abspeichern</translation> + <translation>Konnte Stücke nicht abspeichern</translation> </message> <message> <location filename="src/libtomahawk/utils/xspfloader.cpp" line="143"/> <source>Some tracks in the playlist do not contain an artist and a title. They will be ignored.</source> - <translation type="unfinished">Einige Stücke in der Playliste enthalten weder Künstler noch Titel. Diese werden ignoriert.</translation> + <translation>Einige Stücke in der Playliste enthalten weder Künstler noch Titel. Diese werden ignoriert.</translation> </message> <message> <location filename="src/libtomahawk/utils/xspfloader.cpp" line="164"/> <source>XSPF Error</source> - <translation type="unfinished">XSPF-Fehler</translation> + <translation>XSPF-Fehler</translation> </message> <message> <location filename="src/libtomahawk/utils/xspfloader.cpp" line="164"/> <source>This is not a valid XSPF playlist.</source> - <translation type="unfinished">Dies ist keine valide XSPF-Playliste.</translation> + <translation>Dies ist keine gültige XSPF-Playliste.</translation> </message> </context> -</TS> +</TS> \ No newline at end of file diff --git a/resources.qrc b/resources.qrc index 02ee5f9ab..369d4955d 100644 --- a/resources.qrc +++ b/resources.qrc @@ -4,7 +4,6 @@ <file>data/images/avatar-dude.png</file> <file>data/images/back-pressed.png</file> <file>data/images/back-rest.png</file> - <file>data/images/cover-shadow.png</file> <file>data/images/filter.png</file> <file>data/images/loved.png</file> <file>data/images/not-loved.png</file> @@ -112,7 +111,7 @@ <file>data/sql/dbmigrate-23_to_24.sql</file> <file>data/sql/dbmigrate-24_to_25.sql</file> <file>data/sql/dbmigrate-25_to_26.sql</file> - <file>data/sql/dbmigrate-26_to_27.sql</file> + <file>data/sql/dbmigrate-26_to_27.sql</file> <file>data/js/tomahawk.js</file> <file>data/images/avatar_frame.png</file> <file>data/images/drop-all-songs.png</file> @@ -120,7 +119,7 @@ <file>data/images/drop-top-songs.png</file> <file>data/images/drop-song.png</file> <file>data/images/drop-album.png</file> - <file>data/images/spotify-logo.png</file> + <file>data/images/spotify-logo.png</file> <file>data/images/itunes.png</file> <file>data/images/uploading.png</file> <file>data/images/downloading.png</file> @@ -128,5 +127,7 @@ <file>data/images/headphones-off.png</file> <file>data/images/headphones-sidebar.png</file> <file>data/images/headphones-bigger.png</file> + <file>data/images/no-album-no-case.png</file> + <file>data/images/rdio.png</file> </qresource> </RCC> diff --git a/src/CMakeLists.linux.txt b/src/CMakeLists.linux.txt index 355144407..ae108e4d5 100644 --- a/src/CMakeLists.linux.txt +++ b/src/CMakeLists.linux.txt @@ -5,6 +5,6 @@ FOREACH( _file ${_icons} ) INSTALL( FILES ${_file} RENAME tomahawk.png DESTINATION share/icons/hicolor/${_res}/apps ) ENDFOREACH( _file ) -INSTALL( FILES ${CMAKE_SOURCE_DIR}/data/icons/tomahawk-icon.svg RENAME tomahawk.svg DESTINATION share/icons/hicolor/scalable ) +INSTALL( FILES ${CMAKE_SOURCE_DIR}/data/icons/tomahawk-icon.svg RENAME tomahawk.svg DESTINATION share/icons/hicolor/scalable/apps ) INSTALL( FILES ${CMAKE_SOURCE_DIR}/admin/unix/tomahawk.desktop DESTINATION share/applications ) diff --git a/src/GetNewStuffDelegate.cpp b/src/GetNewStuffDelegate.cpp index 5329e0591..1e1c5d381 100644 --- a/src/GetNewStuffDelegate.cpp +++ b/src/GetNewStuffDelegate.cpp @@ -40,6 +40,7 @@ GetNewStuffDelegate::GetNewStuffDelegate( QObject* parent ) : QStyledItemDelegate ( parent ) , m_widestTextWidth( 0 ) + , m_hoveringOver( -1 ) { m_defaultCover.load( RESPATH "images/sipplugin-online.png" ); m_ratingStarPositive.load( RESPATH "images/starred.png" ); @@ -190,8 +191,10 @@ GetNewStuffDelegate::paint( QPainter* painter, const QStyleOptionViewItem& optio if ( i == 1 ) m_cachedStarRects[ QPair<int, int>(index.row(), index.column()) ] = r; - QPixmap pm; - if ( m_hoveringOver > -1 ) + const bool userHasRated = index.data( GetNewStuffModel::UserHasRatedRole ).toBool(); + if ( !userHasRated && // Show on-hover animation if the user hasn't rated it yet, and is hovering over it + m_hoveringOver > -1 && + m_hoveringItem == index ) { if ( i <= m_hoveringOver ) // positive star painter->drawPixmap( r, m_onHoverStar ); @@ -200,8 +203,13 @@ GetNewStuffDelegate::paint( QPainter* painter, const QStyleOptionViewItem& optio } else { - if ( i <= rating ) // positive star - painter->drawPixmap( r, m_ratingStarPositive ); + if ( i <= rating ) // positive or rated star + { + if ( userHasRated ) + painter->drawPixmap( r, m_onHoverStar ); + else + painter->drawPixmap( r, m_ratingStarPositive ); + } else painter->drawPixmap( r, m_ratingStarNegative ); } @@ -258,7 +266,6 @@ bool GetNewStuffDelegate::editorEvent( QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option, const QModelIndex& index ) { Q_UNUSED( option ); - m_hoveringOver = -1; if ( event->type() != QEvent::MouseButtonRelease && event->type() != QEvent::MouseMove ) @@ -287,25 +294,30 @@ GetNewStuffDelegate::editorEvent( QEvent* event, QAbstractItemModel* model, cons if ( fullStars.contains( me->pos() ) ) { - tDebug() << "A star was pressed...which one?"; - const int eachStar = starsWidth / 5; const int clickOffset = me->pos().x() - fullStars.x(); const int whichStar = (clickOffset / eachStar) + 1; if ( event->type() == QEvent::MouseButtonRelease ) { - tDebug() << "Clicked on:" << whichStar; model->setData( index, whichStar, GetNewStuffModel::RatingRole ); } else if ( event->type() == QEvent::MouseMove ) { // 0-indexed m_hoveringOver = whichStar; + m_hoveringItem = index; } return true; } } + + if ( m_hoveringOver > -1 ) + { + emit update( m_hoveringItem ); + m_hoveringOver = -1; + m_hoveringItem = QPersistentModelIndex(); + } return false; } diff --git a/src/GetNewStuffDelegate.h b/src/GetNewStuffDelegate.h index e2b71afaa..570210226 100644 --- a/src/GetNewStuffDelegate.h +++ b/src/GetNewStuffDelegate.h @@ -31,6 +31,9 @@ public: virtual void paint( QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index ) const; virtual QSize sizeHint( const QStyleOptionViewItem& option, const QModelIndex& index ) const; +signals: + void update( const QModelIndex& idx ); + protected: virtual bool editorEvent( QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option, const QModelIndex& index ); @@ -39,6 +42,7 @@ private: int m_widestTextWidth; int m_hoveringOver; + QPersistentModelIndex m_hoveringItem; mutable QHash< QPair<int, int>, QRect > m_cachedButtonRects; mutable QHash< QPair<int, int>, QRect > m_cachedStarRects; }; diff --git a/src/GetNewStuffDialog.cpp b/src/GetNewStuffDialog.cpp index a0102c648..3a5997b26 100644 --- a/src/GetNewStuffDialog.cpp +++ b/src/GetNewStuffDialog.cpp @@ -30,7 +30,9 @@ GetNewStuffDialog::GetNewStuffDialog( QWidget *parent, Qt::WindowFlags f ) ui->setupUi( this ); ui->listView->setModel( m_model ); - ui->listView->setItemDelegate( new GetNewStuffDelegate( ui->listView ) ); + GetNewStuffDelegate* del = new GetNewStuffDelegate( ui->listView ); + connect( del, SIGNAL( update( QModelIndex ) ), ui->listView, SLOT( update( QModelIndex ) ) ); + ui->listView->setItemDelegate( del ); ui->listView->setVerticalScrollMode( QAbstractItemView::ScrollPerPixel ); ui->listView->setMouseTracking( true ); diff --git a/src/GetNewStuffModel.cpp b/src/GetNewStuffModel.cpp index cb2aa99cc..405ca968b 100644 --- a/src/GetNewStuffModel.cpp +++ b/src/GetNewStuffModel.cpp @@ -94,6 +94,8 @@ GetNewStuffModel::data( const QModelIndex& index, int role ) const return resolver.author(); case StateRole: return (int)AtticaManager::instance()->resolverState( resolver ); + case UserHasRatedRole: + return AtticaManager::instance()->userHasRated( resolver ); } return QVariant(); } diff --git a/src/GetNewStuffModel.h b/src/GetNewStuffModel.h index 60e2e9104..378057e1e 100644 --- a/src/GetNewStuffModel.h +++ b/src/GetNewStuffModel.h @@ -38,7 +38,8 @@ public: DescriptionRole = Qt::UserRole + 5, TypeRole = Qt::UserRole + 6, // Category in attica-speak. What sort of item this is (resolver, etc). AuthorRole = Qt::UserRole + 7, - StateRole = Qt::UserRole + 8 + StateRole = Qt::UserRole + 8, + UserHasRatedRole = Qt::UserRole + 9 }; enum Types { diff --git a/src/audiocontrols.cpp b/src/audiocontrols.cpp index 0139b3145..42f8d7ab0 100644 --- a/src/audiocontrols.cpp +++ b/src/audiocontrols.cpp @@ -128,7 +128,7 @@ AudioControls::AudioControls( QWidget* parent ) connect( AudioEngine::instance(), SIGNAL( timerMilliSeconds( qint64 ) ), SLOT( onPlaybackTimer( qint64 ) ) ); connect( AudioEngine::instance(), SIGNAL( volumeChanged( int ) ), SLOT( onVolumeChanged( int ) ) ); - m_defaultCover = QPixmap( RESPATH "images/no-album-art-placeholder.png" ) + m_defaultCover = QPixmap( RESPATH "images/no-album-no-case.png" ) .scaled( ui->coverImage->size(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation ); connect( Tomahawk::InfoSystem::InfoSystem::instance(), diff --git a/src/libtomahawk/AtticaManager.cpp b/src/libtomahawk/AtticaManager.cpp index 55b5e11b3..5303fcd17 100644 --- a/src/libtomahawk/AtticaManager.cpp +++ b/src/libtomahawk/AtticaManager.cpp @@ -45,7 +45,6 @@ AtticaManager::AtticaManager( QObject* parent ) // resolvers m_manager.addProviderFile( QUrl( "http://bakery.tomahawk-player.org:10480/resolvers/providers.xml" ) ); - QTimer::singleShot( 0, this, SLOT( loadPixmapsFromCache() ) ); } @@ -62,6 +61,8 @@ AtticaManager::loadPixmapsFromCache() if ( !cacheDir.cd( "atticacache" ) ) // doesn't exist, no cache return; + qDebug() << "Loading resolvers from cache dir:" << cacheDir.absolutePath(); + qDebug() << "Currently we know about these resolvers:" << m_resolverStates.keys(); foreach ( const QString& file, cacheDir.entryList( QStringList() << "*.png", QDir::Files | QDir::NoSymLinks ) ) { // load all the pixmaps @@ -204,6 +205,8 @@ AtticaManager::resolversList( BaseJob* j ) m_resolverStates = TomahawkSettings::instance()->atticaResolverStates(); // load icon cache from disk, and fetch any we are missing + loadPixmapsFromCache(); + foreach ( Content resolver, m_resolvers ) { if ( !m_resolverStates.contains( resolver.id() ) ) diff --git a/src/libtomahawk/CMakeLists.txt b/src/libtomahawk/CMakeLists.txt index d8d0aa5f1..77d798ff0 100644 --- a/src/libtomahawk/CMakeLists.txt +++ b/src/libtomahawk/CMakeLists.txt @@ -113,8 +113,11 @@ set( libSources infosystem/infoplugins/generic/echonestplugin.cpp infosystem/infoplugins/generic/lastfmplugin.cpp infosystem/infoplugins/generic/chartsplugin.cpp + infosystem/infoplugins/generic/spotifyPlugin.cpp + infosystem/infoplugins/generic/hypemPlugin.cpp infosystem/infoplugins/generic/musixmatchplugin.cpp infosystem/infoplugins/generic/musicbrainzPlugin.cpp + infosystem/infoplugins/generic/RoviPlugin.cpp playlist/treemodel.cpp playlist/treeproxymodel.cpp @@ -224,11 +227,8 @@ set( libSources widgets/infowidgets/sourceinfowidget.cpp widgets/infowidgets/ArtistInfoWidget.cpp widgets/infowidgets/AlbumInfoWidget.cpp - widgets/kbreadcrumbselectionmodel.cpp - widgets/breadcrumbbar.cpp - widgets/breadcrumbbuttonbase.cpp - widgets/headerbreadcrumb.cpp - widgets/siblingcrumbbutton.cpp + widgets/Breadcrumb.cpp + widgets/BreadcrumbButton.cpp jobview/JobStatusView.cpp jobview/JobStatusModel.cpp @@ -345,8 +345,11 @@ set( libHeaders infosystem/infoplugins/generic/echonestplugin.h infosystem/infoplugins/generic/lastfmplugin.h infosystem/infoplugins/generic/chartsplugin.h + infosystem/infoplugins/generic/spotifyPlugin.h + infosystem/infoplugins/generic/hypemPlugin.h infosystem/infoplugins/generic/musixmatchplugin.h infosystem/infoplugins/generic/musicbrainzPlugin.h + infosystem/infoplugins/generic/RoviPlugin.h network/bufferiodevice.h network/msgprocessor.h @@ -451,13 +454,10 @@ set( libHeaders widgets/SocialPlaylistWidget.h widgets/infowidgets/sourceinfowidget.h widgets/infowidgets/ArtistInfoWidget.h + widgets/infowidgets/ArtistInfoWidget_p.h widgets/infowidgets/AlbumInfoWidget.h - widgets/kbreadcrumbselectionmodel.h - widgets/kbreadcrumbselectionmodel_p.h - widgets/breadcrumbbar.h - widgets/breadcrumbbuttonbase.h - widgets/headerbreadcrumb.h - widgets/siblingcrumbbutton.h + widgets/Breadcrumb.h + widgets/BreadcrumbButton.h jobview/JobStatusView.h jobview/JobStatusModel.h @@ -507,7 +507,6 @@ include_directories( . ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_BINARY_DIR}/. ${LIBECHONEST_INCLUDE_DIR} ${LIBECHONEST_INCLUDE_DIR}/.. ${CLUCENE_INCLUDE_DIR} - ${CLUCENE_LIBRARY_DIR} ${PHONON_INCLUDES} ${CMAKE_BINARY_DIR}/thirdparty/liblastfm2/src diff --git a/src/libtomahawk/audio/audioengine.cpp b/src/libtomahawk/audio/audioengine.cpp index 2398749f2..c92be1f37 100644 --- a/src/libtomahawk/audio/audioengine.cpp +++ b/src/libtomahawk/audio/audioengine.cpp @@ -424,6 +424,9 @@ AudioEngine::loadTrack( const Tomahawk::result_ptr& result ) tLog() << "Starting new song:" << m_currentTrack->url(); emit loading( m_currentTrack ); + if ( QNetworkReply* qnr_io = qobject_cast< QNetworkReply* >( io.data() ) ) + connect( qnr_io, SIGNAL( error( QNetworkReply::NetworkError ) ), this, SLOT( ioStreamError( QNetworkReply::NetworkError ) ) ); + if ( !isHttpResult( m_currentTrack->url() ) && !isLocalResult( m_currentTrack->url() ) ) { if ( QNetworkReply* qnr_io = qobject_cast< QNetworkReply* >( io.data() ) ) @@ -573,6 +576,16 @@ AudioEngine::playItem( Tomahawk::PlaylistInterface* playlist, const Tomahawk::re } } +void +AudioEngine::ioStreamError( QNetworkReply::NetworkError error ) +{ + if ( error != QNetworkReply::NoError ) + { + if ( canGoNext() ) + loadNextTrack(); + } +} + void AudioEngine::playlistNextTrackReady() @@ -682,7 +695,10 @@ AudioEngine::setPlaylist( PlaylistInterface* playlist ) } if ( !playlist ) + { + m_playlist.clear(); return; + } m_playlist = playlist->getSharedPointer(); diff --git a/src/libtomahawk/audio/audioengine.h b/src/libtomahawk/audio/audioengine.h index 038579d9c..f888ce42c 100644 --- a/src/libtomahawk/audio/audioengine.h +++ b/src/libtomahawk/audio/audioengine.h @@ -21,6 +21,7 @@ #include <QObject> #include <QTimer> +#include <QNetworkReply> #include <phonon/MediaObject> #include <phonon/AudioOutput> @@ -134,6 +135,7 @@ private slots: void setCurrentTrack( const Tomahawk::result_ptr& result ); + void ioStreamError( QNetworkReply::NetworkError ); private: void setState( AudioState state ); diff --git a/src/libtomahawk/context/ContextWidget.cpp b/src/libtomahawk/context/ContextWidget.cpp index 747dbac87..f2006e31c 100644 --- a/src/libtomahawk/context/ContextWidget.cpp +++ b/src/libtomahawk/context/ContextWidget.cpp @@ -276,10 +276,14 @@ ContextWidget::onAnimationFinished() m_scene->setSceneRect( ui->contextView->viewport()->rect() ); layoutViews( false ); setQuery( m_query, true ); + + ui->toggleButton->setText( tr( "Hide Footnotes" ) ); } else { setFixedHeight( m_minHeight ); + + ui->toggleButton->setText( tr( "Show Footnotes" ) ); } } diff --git a/src/libtomahawk/context/ContextWidget.ui b/src/libtomahawk/context/ContextWidget.ui index 336fbb413..77a9f2fe8 100644 --- a/src/libtomahawk/context/ContextWidget.ui +++ b/src/libtomahawk/context/ContextWidget.ui @@ -35,7 +35,7 @@ </sizepolicy> </property> <property name="text"> - <string>Footnotes</string> + <string>Show Footnotes</string> </property> <property name="alignment"> <set>Qt::AlignCenter</set> diff --git a/src/libtomahawk/database/databasecommand_loadsocialactions.cpp b/src/libtomahawk/database/databasecommand_loadsocialactions.cpp index cf0dfa7e7..8f2b51e2f 100644 --- a/src/libtomahawk/database/databasecommand_loadsocialactions.cpp +++ b/src/libtomahawk/database/databasecommand_loadsocialactions.cpp @@ -50,7 +50,7 @@ DatabaseCommand_LoadSocialActions::exec( DatabaseImpl* dbi ) return; QString whereToken; - whereToken = QString( "WHERE id IS %1" ).arg( trkid ); + whereToken = QString( "WHERE id IS %1" ).arg( trkid ); QString sql = QString( "SELECT k, v, timestamp, source " @@ -66,7 +66,7 @@ DatabaseCommand_LoadSocialActions::exec( DatabaseImpl* dbi ) Tomahawk::SocialAction action; action.action = query.value( 0 ); // action action.value = query.value( 1 ); // comment - action.timestamp = query.value( 2 ); // timestamp + action.timestamp = query.value( 2 ); // timestamp action.source = query.value( 3 ); // source allSocialActions.append( action ); diff --git a/src/libtomahawk/database/databasecommand_socialaction.cpp b/src/libtomahawk/database/databasecommand_socialaction.cpp index 0f30c3ca4..2b137b9da 100644 --- a/src/libtomahawk/database/databasecommand_socialaction.cpp +++ b/src/libtomahawk/database/databasecommand_socialaction.cpp @@ -49,22 +49,17 @@ DatabaseCommand_SocialAction::exec( DatabaseImpl* dbi ) TomahawkSqlQuery query = dbi->newquery(); - QVariant srcid = source()->isLocal() ? QVariant( QVariant::Int ) : source()->id(); - int trkid = -2; - if ( !m_result.isNull() && !m_artist.isNull() && !m_track.isEmpty() ) - { - bool autoCreate = true; - int artid = dbi->artistId( m_artist, autoCreate ); - if ( artid < 1 ) - return; + if ( m_artist.isNull() || m_track.isEmpty() ) + return; - autoCreate = true; // artistId overwrites autoCreate (reference) - trkid = dbi->trackId( artid, m_track, autoCreate ); - if ( trkid < 1 ) - return; - } + int artid = dbi->artistId( m_artist, true ); + if ( artid < 1 ) + return; + int trkid = dbi->trackId( artid, m_track, true ); + if ( trkid < 1 ) + return; // update if it already exists TomahawkSqlQuery find = dbi->newquery(); @@ -80,12 +75,13 @@ DatabaseCommand_SocialAction::exec( DatabaseImpl* dbi ) .arg( trkid ) .arg( source()->isLocal() ? "IS NULL" : QString( "=%1" ).arg( source()->id() ) ) .arg( m_action ) ); - } else + } + else { - query.prepare( "INSERT INTO social_attributes(id, source, k, v, timestamp) " + query.prepare( "INSERT INTO social_attributes(id, source, k, v, timestamp) " "VALUES (?, ?, ?, ?, ?)" ); - query.bindValue( 0, trkid >= -1 ? trkid : QVariant() ); + query.bindValue( 0, trkid ); query.bindValue( 1, srcid ); query.bindValue( 2, m_action ); query.bindValue( 3, m_comment ); diff --git a/src/libtomahawk/dropjob.cpp b/src/libtomahawk/dropjob.cpp index 48f9d212b..12432e682 100644 --- a/src/libtomahawk/dropjob.cpp +++ b/src/libtomahawk/dropjob.cpp @@ -112,7 +112,8 @@ DropJob::acceptsMimeData( const QMimeData* data, DropJob::DropTypes acceptedType if ( url.contains( "spotify" ) && url.contains( "track" ) ) return true; - if ( url.contains( "rdio.com" ) && url.contains( "track" ) ) + if ( url.contains( "rdio.com" ) && ( ( ( url.contains( "track" ) && url.contains( "artist" ) && url.contains( "album" ) ) + || url.contains( "playlists" ) ) ) ) return true; } @@ -122,6 +123,8 @@ DropJob::acceptsMimeData( const QMimeData* data, DropJob::DropTypes acceptedType return true; if ( url.contains( "spotify" ) && url.contains( "album" ) ) return true; + if ( url.contains( "rdio.com" ) && ( url.contains( "artist" ) && url.contains( "album" ) && !url.contains( "track" ) ) ) + return true; } if ( acceptedType.testFlag( Artist ) ) @@ -130,6 +133,8 @@ DropJob::acceptsMimeData( const QMimeData* data, DropJob::DropTypes acceptedType return true; if ( url.contains( "spotify" ) && url.contains( "artist" ) ) return true; + if ( url.contains( "rdio.com" ) && ( url.contains( "artist" ) && !url.contains( "album" ) && !url.contains( "track" ) ) ) + return true; } // We whitelist t.co and bit.ly (and j.mp) since they do some link checking. Often playable (e.g. spotify..) links hide behind them, @@ -152,6 +157,16 @@ DropJob::isDropType( DropJob::DropType desired, const QMimeData* data ) // Not the most elegant if ( url.contains( "spotify" ) && url.contains( "playlist" ) && s_canParseSpotifyPlaylists ) return true; + + if ( url.contains( "rdio.com" ) && url.contains( "people" ) && url.contains( "playlist" ) ) + return true; + + // we don't know about these.. gotta say yes for now + if ( url.contains( "bit.ly" ) || + url.contains( "j.mp" ) || + url.contains( "t.co" ) || + url.contains( "rd.io" ) ) + return true; } return false; @@ -443,6 +458,25 @@ DropJob::handleSpotifyUrls( const QString& urlsRaw ) m_queryCount++; } +void +DropJob::handleRdioUrls( const QString& urlsRaw ) +{ + QStringList urls = urlsRaw.split( QRegExp( "\\s+" ), QString::SkipEmptyParts ); + qDebug() << "Got Rdio urls!!" << urls; + + if ( dropAction() == Default ) + setDropAction( Create ); + + RdioParser* rdio = new RdioParser( this ); + connect( rdio, SIGNAL( tracks( QList<Tomahawk::query_ptr> ) ), this, SLOT( onTracksAdded( QList< Tomahawk::query_ptr > ) ) ); + + rdio->setCreatePlaylist( dropAction() == Create ); + rdio->parse( urls ); + + m_queryCount++; +} + + void DropJob::handleAllUrls( const QString& urls ) { @@ -452,6 +486,8 @@ DropJob::handleAllUrls( const QString& urls ) && ( urls.contains( "playlist" ) || urls.contains( "artist" ) || urls.contains( "album" ) || urls.contains( "track" ) ) && s_canParseSpotifyPlaylists ) handleSpotifyUrls( urls ); + else if ( urls.contains( "rdio.com" ) ) + handleRdioUrls( urls ); else handleTrackUrls ( urls ); } diff --git a/src/libtomahawk/dropjob.h b/src/libtomahawk/dropjob.h index 0142ec5bb..369899e11 100644 --- a/src/libtomahawk/dropjob.h +++ b/src/libtomahawk/dropjob.h @@ -102,7 +102,9 @@ public: void setGetWholeAlbums( bool getWholeAlbums ); void tracksFromMimeData( const QMimeData* data, bool allowDuplicates = false, bool onlyLocal = false, bool top10 = false ); void handleXspfs( const QString& files ); + void handleSpotifyUrls( const QString& urls ); + void handleRdioUrls( const QString& urls ); static bool canParseSpotifyPlaylists() { return s_canParseSpotifyPlaylists; } static void setCanParseSpotifyPlaylists( bool parseable ) { s_canParseSpotifyPlaylists = parseable; } diff --git a/src/libtomahawk/globalactionmanager.cpp b/src/libtomahawk/globalactionmanager.cpp index 1e969d2a1..b0bbe57fd 100644 --- a/src/libtomahawk/globalactionmanager.cpp +++ b/src/libtomahawk/globalactionmanager.cpp @@ -50,6 +50,7 @@ #include "utils/spotifyparser.h" #include "utils/shortenedlinkparser.h" #include "utils/rdioparser.h" +#include "widgets/searchwidget.h" GlobalActionManager* GlobalActionManager::s_instance = 0; @@ -269,6 +270,8 @@ GlobalActionManager::parseTomahawkLink( const QString& urlIn ) return handlePlayCommand( u ); } else if( cmdType == "open" ) { return handleOpenCommand( u ); + } else if( cmdType == "view" ) { + return handleViewCommand( u ); } else { tLog() << "Tomahawk link not supported, command not known!" << cmdType << u.path(); return false; @@ -357,7 +360,7 @@ GlobalActionManager::handleOpenTrack ( const query_ptr& q ) ViewManager::instance()->queue()->model()->append( q ); ViewManager::instance()->showQueue(); - if( !AudioEngine::instance()->isPlaying() ) { + if( !AudioEngine::instance()->isPlaying() && !AudioEngine::instance()->isPaused() ) { connect( q.data(), SIGNAL( resolvingFinished( bool ) ), this, SLOT( waitingForResolved( bool ) ) ); m_waitingToPlay = q; } @@ -498,23 +501,71 @@ 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( "title" ) ) - query << url.queryItemValue( "title" ); - QString queryStr = query.join( " " ); + QString queryStr; + if ( url.hasQueryItem( "query" ) ) + queryStr = url.queryItemValue( "query" ); + else + { + QStringList query; + if( url.hasQueryItem( "artist" ) ) + query << url.queryItemValue( "artist" ); + if( url.hasQueryItem( "album" ) ) + query << url.queryItemValue( "album" ); + if( url.hasQueryItem( "title" ) ) + query << url.queryItemValue( "title" ); + queryStr = query.join( " " ); + } - if( queryStr.isEmpty() ) + if( queryStr.trimmed().isEmpty() ) return false; - ViewManager::instance()->showSuperCollection(); -// ViewManager::instance()->topbar()->setFilter( queryStr ); + ViewManager::instance()->show( new SearchWidget( queryStr.trimmed() ) ); + return true; } +bool +GlobalActionManager::handleViewCommand( const QUrl& url ) +{ + QStringList parts = url.path().split( "/" ).mid( 1 ); // get the rest of the command + if( parts.isEmpty() ) { + tLog() << "No specific view command:" << url.toString(); + return false; + } + + if ( parts[ 0 ] == "artist" ) + { + const QString artist = url.queryItemValue( "name" ); + if ( artist.isEmpty() ) + { + tLog() << "Not artist supplied for view/artist command."; + return false; + } + artist_ptr artistPtr = Artist::get( artist ); + if ( !artistPtr.isNull() ) + ViewManager::instance()->show( artistPtr ); + + return true; + } + else if ( parts[ 0 ] == "album" ) + { + const QString artist = url.queryItemValue( "artist" ); + const QString album = url.queryItemValue( "name" ); + if ( artist.isEmpty() || album.isEmpty() ) + { + tLog() << "Not artist or album supplied for view/artist command:" << url; + return false; + } + album_ptr albumPtr = Album::get( Artist::get( artist, false ), album, false ); + if ( !albumPtr.isNull() ) + ViewManager::instance()->show( albumPtr ); + + return true; + } + + return false; +} + bool GlobalActionManager::handleAutoPlaylistCommand( const QUrl& url ) diff --git a/src/libtomahawk/globalactionmanager.h b/src/libtomahawk/globalactionmanager.h index e3d85eb14..3a0ad3685 100644 --- a/src/libtomahawk/globalactionmanager.h +++ b/src/libtomahawk/globalactionmanager.h @@ -95,6 +95,7 @@ private: bool handlePlayCommand(const QUrl& url ); bool handleBookmarkCommand(const QUrl& url ); bool handleOpenCommand(const QUrl& url ); + bool handleViewCommand(const QUrl& url ); bool doQueueAdd( const QStringList& parts, const QList< QPair< QString, QString > >& queryItems ); bool playSpotify( const QUrl& url ); diff --git a/src/libtomahawk/infobar/infobar.cpp b/src/libtomahawk/infobar/infobar.cpp index c441292cf..4296b75ae 100644 --- a/src/libtomahawk/infobar/infobar.cpp +++ b/src/libtomahawk/infobar/infobar.cpp @@ -27,6 +27,7 @@ #include "utils/tomahawkutils.h" #include "utils/logger.h" #include <QCheckBox> +#include <widgets/querylabel.h> #define ANIMATION_TIME 400 #define IMAGE_HEIGHT 64 @@ -37,6 +38,7 @@ using namespace Tomahawk; InfoBar::InfoBar( QWidget* parent ) : QWidget( parent ) , ui( new Ui::InfoBar ) + , m_queryLabel( 0 ) { ui->setupUi( this ); TomahawkUtils::unmarginLayout( layout() ); @@ -71,6 +73,14 @@ InfoBar::InfoBar( QWidget* parent ) ui->longDescriptionLabel->setText( QString() ); ui->imageLabel->setText( QString() ); + m_queryLabel = new QueryLabel( this ); + m_queryLabel->setType( QueryLabel::Artist ); + m_queryLabel->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Preferred ); + m_queryLabel->setTextPen( palette().brightText().color() ); + m_queryLabel->setFont( boldFont ); + m_queryLabel->hide(); + connect( m_queryLabel, SIGNAL( clickedArtist() ), this, SLOT( artistClicked() ) ); + m_autoUpdate = new QCheckBox( this ); m_autoUpdate->setText( tr( "Automatically update" ) ); m_autoUpdate->setLayoutDirection( Qt::RightToLeft ); @@ -95,6 +105,8 @@ InfoBar::InfoBar( QWidget* parent ) setPalette( p ); setAutoFillBackground( true ); + setMinimumHeight( geometry().height() ); + setMaximumHeight( geometry().height() ); connect( ViewManager::instance(), SIGNAL( filterAvailable( bool ) ), SLOT( setFilterAvailable( bool ) ) ); connect( ViewManager::instance(), SIGNAL( autoUpdateAvailable( bool ) ), SLOT( setAutoUpdateAvailable( bool ) ) ); } @@ -116,9 +128,49 @@ InfoBar::setCaption( const QString& s ) void InfoBar::setDescription( const QString& s ) { + if ( m_queryLabel->isVisible() ) + { + ui->verticalLayout->removeWidget( m_queryLabel ); + m_queryLabel->hide(); + + ui->verticalLayout->addWidget( ui->descriptionLabel ); + ui->verticalLayout->setContentsMargins( 0, 0, 0, 0 ); + ui->descriptionLabel->show(); + } ui->descriptionLabel->setText( s ); } +void +InfoBar::setDescription( const artist_ptr& artist ) +{ + m_queryLabel->setQuery( Query::get( artist->name(), QString(), QString() ) ); + m_queryLabel->setExtraContentsMargins( 4, 0, 0, 0 ); + + if ( !m_queryLabel->isVisible() ) + { + ui->verticalLayout->removeWidget( ui->descriptionLabel ); + ui->descriptionLabel->hide(); + + m_queryLabel->show(); + ui->verticalLayout->addWidget( m_queryLabel ); + ui->verticalLayout->setContentsMargins( 0, 0, 0, 15 ); + } + +} + +void +InfoBar::setDescription( const album_ptr& ) +{ + // TODO +} + +void +InfoBar::artistClicked() +{ + if ( m_queryLabel && !m_queryLabel->query().isNull() ) + ViewManager::instance()->show( Artist::get( m_queryLabel->artist() ) ); +} + void InfoBar::setLongDescription( const QString& s ) diff --git a/src/libtomahawk/infobar/infobar.h b/src/libtomahawk/infobar/infobar.h index a718a32a3..f16339d52 100644 --- a/src/libtomahawk/infobar/infobar.h +++ b/src/libtomahawk/infobar/infobar.h @@ -22,7 +22,9 @@ #include <QWidget> #include "dllmacro.h" +#include "artist.h" +class QueryLabel; class QCheckBox; class QTimeLine; class QSearchField; @@ -43,7 +45,12 @@ public: public slots: void setCaption( const QString& s ); + void setDescription( const QString& s ); + // If you want a querylabel instead of an ElidedLabel + void setDescription( const Tomahawk::artist_ptr& artist ); + void setDescription( const Tomahawk::album_ptr& album_ptr ); + void setLongDescription( const QString& s ); void setPixmap( const QPixmap& p ); @@ -60,12 +67,14 @@ protected: private slots: void onFilterEdited(); + void artistClicked(); private: Ui::InfoBar* ui; QSearchField* m_searchWidget; QCheckBox* m_autoUpdate; + QueryLabel* m_queryLabel; }; #endif // INFOBAR_H diff --git a/src/libtomahawk/infobar/infobar.ui b/src/libtomahawk/infobar/infobar.ui index 7379ef909..a0f3a9fe9 100644 --- a/src/libtomahawk/infobar/infobar.ui +++ b/src/libtomahawk/infobar/infobar.ui @@ -65,10 +65,13 @@ <property name="sizeConstraint"> <enum>QLayout::SetDefaultConstraint</enum> </property> + <property name="bottomMargin"> + <number>0</number> + </property> <item> <widget class="ElidedLabel" name="captionLabel"> <property name="sizePolicy"> - <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> + <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> diff --git a/src/libtomahawk/infosystem/infoplugins/generic/RoviPlugin.cpp b/src/libtomahawk/infosystem/infoplugins/generic/RoviPlugin.cpp new file mode 100644 index 000000000..c5810440e --- /dev/null +++ b/src/libtomahawk/infosystem/infoplugins/generic/RoviPlugin.cpp @@ -0,0 +1,207 @@ +/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> === + * + * Copyright 2010-2011, Leo Franchi <lfranchi@kde.org> + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include "RoviPlugin.h" + +#include "utils/logger.h" + +#include <QDateTime> +#include <QNetworkReply> +#include <parser.h> + +using namespace Tomahawk::InfoSystem; + +static QString +md5( const QByteArray& src ) +{ + QByteArray const digest = QCryptographicHash::hash( src, QCryptographicHash::Md5 ); + return QString::fromLatin1( digest.toHex() ).rightJustified( 32, '0' ); +} + +RoviPlugin::RoviPlugin() + : InfoPlugin() +{ + m_supportedGetTypes << InfoAlbumSongs; + + /* + * Your API Key is 7jxr9zggt45h6rg2n4ss3mrj + * Your secret is XUnYutaAW6 + */ + m_apiKey = "7jxr9zggt45h6rg2n4ss3mrj"; + m_secret = "XUnYutaAW6"; +} + +RoviPlugin::~RoviPlugin() +{ + +} + + + +void +RoviPlugin::namChangedSlot( QNetworkAccessManager* nam ) +{ + if ( !nam ) + return; + + m_nam = nam; +} + + +void +RoviPlugin::getInfo( Tomahawk::InfoSystem::InfoRequestData requestData ) +{ + if ( !requestData.input.canConvert< Tomahawk::InfoSystem::InfoStringHash >() ) + { + emit info( requestData, QVariant() ); + return; + } + InfoStringHash hash = requestData.input.value< Tomahawk::InfoSystem::InfoStringHash >(); + if ( !hash.contains( "artist" ) || !hash.contains( "album" ) ) + { + emit info( requestData, QVariant() ); + return; + } + + Tomahawk::InfoSystem::InfoStringHash criteria; + criteria["artist"] = hash["artist"]; + criteria["album"] = hash["album"]; + + emit getCachedInfo( criteria, 0, requestData ); +} + +void +RoviPlugin::notInCacheSlot( Tomahawk::InfoSystem::InfoStringHash criteria, Tomahawk::InfoSystem::InfoRequestData requestData ) +{ + switch ( requestData.type ) + { + case InfoAlbumSongs: + { + QUrl baseUrl = QUrl( "http://api.rovicorp.com/search/v2/music/search" ); + baseUrl.addQueryItem( "query", QString( "%1 %2" ).arg( criteria[ "artist" ] ).arg( criteria[ "album" ] ) ); + baseUrl.addQueryItem( "entitytype", "album" ); + baseUrl.addQueryItem( "include", "album:tracks" ); + + QNetworkReply* reply = makeRequest( baseUrl ); + + reply->setProperty( "requestData", QVariant::fromValue< Tomahawk::InfoSystem::InfoRequestData >( requestData ) ); + connect( reply, SIGNAL( finished() ), this, SLOT( albumLookupFinished() ) ); + connect( reply, SIGNAL( error( QNetworkReply::NetworkError ) ), this, SLOT( albumLookupError( QNetworkReply::NetworkError ) ) ); + break; + } + default: + { + Q_ASSERT( false ); + break; + } + } +} + +void +RoviPlugin::albumLookupError( QNetworkReply::NetworkError error ) +{ + if ( error == QNetworkReply::NoError ) + return; + + QNetworkReply* reply = qobject_cast<QNetworkReply*>( sender() ); + Q_ASSERT( reply ); + + Tomahawk::InfoSystem::InfoRequestData requestData = reply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >(); + + emit info( requestData, QVariant() ); + +} + +void +RoviPlugin::albumLookupFinished() +{ + QNetworkReply* reply = qobject_cast<QNetworkReply*>( sender() ); + Q_ASSERT( reply ); + + if ( reply->error() != QNetworkReply::NoError ) + return; + + Tomahawk::InfoSystem::InfoRequestData requestData = reply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >(); + + QJson::Parser p; + bool ok; + QVariantMap response = p.parse( reply, &ok ).toMap(); + + if ( !ok || response.isEmpty() || !response.contains( "searchResponse" ) ) + { + tLog() << "Error parsing JSON from Rovi!" << p.errorString() << response; + emit info( requestData, QVariant() ); + return; + } + + QVariantList resultList = response[ "searchResponse" ].toMap().value( "results" ).toList(); + if ( resultList.size() == 0 ) + { + emit info( requestData, QVariant() ); + return; + } + + QVariantMap results = resultList.first().toMap(); + QVariantList tracks = results[ "album" ].toMap()[ "tracks" ].toList(); + + if ( tracks.isEmpty() ) + { + tLog() << "Error parsing JSON from Rovi!" << p.errorString() << response; + emit info( requestData, QVariant() ); + } + + + QStringList trackNameList; + foreach ( const QVariant& track, tracks ) + { + const QVariantMap trackData = track.toMap(); + if ( trackData.contains( "title" ) ) + trackNameList << trackData[ "title" ].toString(); + } + + QVariantMap returnedData; + returnedData["tracks"] = trackNameList; + + emit info( requestData, returnedData ); + + Tomahawk::InfoSystem::InfoStringHash criteria; + criteria["artist"] = requestData.input.value< Tomahawk::InfoSystem::InfoStringHash>()["artist"]; + criteria["album"] = requestData.input.value< Tomahawk::InfoSystem::InfoStringHash>()["album"]; + + emit updateCache( criteria, 0, requestData.type, returnedData ); +} + + +QNetworkReply* +RoviPlugin::makeRequest( QUrl url ) +{ + url.addQueryItem( "apikey", m_apiKey ); + url.addEncodedQueryItem( "sig", generateSig() ); + + qDebug() << "Rovi request url:" << url.toString(); + return m_nam->get( QNetworkRequest( url ) ); +} + + +QByteArray +RoviPlugin::generateSig() const +{ + QByteArray raw = m_apiKey + m_secret + QString::number( QDateTime::currentMSecsSinceEpoch() / 1000 ).toLatin1(); + return md5( raw ).toLatin1(); + +} diff --git a/src/libtomahawk/infosystem/infoplugins/generic/RoviPlugin.h b/src/libtomahawk/infosystem/infoplugins/generic/RoviPlugin.h new file mode 100644 index 000000000..21c81c5a9 --- /dev/null +++ b/src/libtomahawk/infosystem/infoplugins/generic/RoviPlugin.h @@ -0,0 +1,67 @@ +/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> === + * + * Copyright 2010-2011, Leo Franchi <lfranchi@kde.org> + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#ifndef ROVIPLUGIN_H +#define ROVIPLUGIN_H + +#include "infosystem/infosystem.h" + +#include <QNetworkReply> + +class QNetworkAccessManager; + +namespace Tomahawk +{ + +namespace InfoSystem +{ + +class RoviPlugin : public InfoPlugin +{ + Q_OBJECT +public: + RoviPlugin(); + virtual ~RoviPlugin(); + +protected: + virtual void namChangedSlot( QNetworkAccessManager* nam ); + virtual void notInCacheSlot( Tomahawk::InfoSystem::InfoStringHash criteria, Tomahawk::InfoSystem::InfoRequestData requestData ); + + virtual void pushInfo( QString, Tomahawk::InfoSystem::InfoType, QVariant ) + {} + + virtual void getInfo( Tomahawk::InfoSystem::InfoRequestData requestData ); + +private slots: + void albumLookupFinished(); + void albumLookupError( QNetworkReply::NetworkError ); +private: + QNetworkReply* makeRequest( QUrl url ); + QByteArray generateSig() const; + + QNetworkAccessManager* m_nam; + + QByteArray m_apiKey; + QByteArray m_secret; +}; + +} + +} + +#endif // ROVIPLUGIN_H diff --git a/src/libtomahawk/infosystem/infoplugins/generic/chartsplugin.cpp b/src/libtomahawk/infosystem/infoplugins/generic/chartsplugin.cpp index 50bd25124..a0b3f9475 100644 --- a/src/libtomahawk/infosystem/infoplugins/generic/chartsplugin.cpp +++ b/src/libtomahawk/infosystem/infoplugins/generic/chartsplugin.cpp @@ -49,7 +49,7 @@ ChartsPlugin::ChartsPlugin() /// Add resources here - m_chartResources << "billboard" << "itunes"; + m_chartResources << "billboard" << "itunes" << "rdio" << "wearehunted"; m_supportedGetTypes << InfoChart << InfoChartCapabilities; } @@ -73,9 +73,10 @@ ChartsPlugin::namChangedSlot( QNetworkAccessManager *nam ) m_nam = QWeakPointer< QNetworkAccessManager >( nam ); /// Then get each chart from resource - /// We need to fetch them before they are asked for + /// We want to prepopulate the breadcrumb to fetch them before they are asked for - if( !m_chartResources.isEmpty() && m_nam ){ + if ( !m_chartResources.isEmpty() && m_nam && m_allChartsMap.isEmpty() ) + { tDebug() << "ChartsPlugin: InfoChart fetching possible resources"; foreach ( QVariant resource, m_chartResources ) @@ -93,21 +94,21 @@ ChartsPlugin::namChangedSlot( QNetworkAccessManager *nam ) void -ChartsPlugin::dataError( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData ) +ChartsPlugin::dataError( Tomahawk::InfoSystem::InfoRequestData requestData ) { - emit info( requestId, requestData, QVariant() ); + emit info( requestData, QVariant() ); return; } void -ChartsPlugin::getInfo( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData ) +ChartsPlugin::getInfo( Tomahawk::InfoSystem::InfoRequestData requestData ) { qDebug() << Q_FUNC_INFO << requestData.caller; qDebug() << Q_FUNC_INFO << requestData.customData; InfoStringHash hash = requestData.input.value< Tomahawk::InfoSystem::InfoStringHash >(); - bool foundSource; + bool foundSource = false; switch ( requestData.type ) { @@ -116,7 +117,7 @@ ChartsPlugin::getInfo( uint requestId, Tomahawk::InfoSystem::InfoRequestData req /// We need something to check if the request is actually ment to go to this plugin if ( !hash.contains( "chart_source" ) ) { - dataError( requestId, requestData ); + dataError( requestData ); break; } else @@ -131,19 +132,19 @@ ChartsPlugin::getInfo( uint requestId, Tomahawk::InfoSystem::InfoRequestData req if( !foundSource ) { - dataError( requestId, requestData ); + dataError( requestData ); break; } } - fetchChart( requestId, requestData ); + fetchChart( requestData ); break; case InfoChartCapabilities: - fetchChartCapabilities( requestId, requestData ); + fetchChartCapabilities( requestData ); break; default: - dataError( requestId, requestData ); + dataError( requestData ); } } @@ -158,12 +159,12 @@ ChartsPlugin::pushInfo( const QString caller, const Tomahawk::InfoSystem::InfoTy void -ChartsPlugin::fetchChart( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData ) +ChartsPlugin::fetchChart( Tomahawk::InfoSystem::InfoRequestData requestData ) { if ( !requestData.input.canConvert< Tomahawk::InfoSystem::InfoStringHash >() ) { - dataError( requestId, requestData ); + dataError( requestData ); return; } @@ -173,7 +174,7 @@ ChartsPlugin::fetchChart( uint requestId, Tomahawk::InfoSystem::InfoRequestData /// Each request needs to contain both a id and source if ( !hash.contains( "chart_id" ) && !hash.contains( "chart_source" ) ) { - dataError( requestId, requestData ); + dataError( requestData ); return; } @@ -181,29 +182,30 @@ ChartsPlugin::fetchChart( uint requestId, Tomahawk::InfoSystem::InfoRequestData criteria["chart_id"] = hash["chart_id"]; criteria["chart_source"] = hash["chart_source"]; - emit getCachedInfo( requestId, criteria, 0, requestData ); + emit getCachedInfo( criteria, 0, requestData ); } void -ChartsPlugin::fetchChartCapabilities( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData ) +ChartsPlugin::fetchChartCapabilities( Tomahawk::InfoSystem::InfoRequestData requestData ) { if ( !requestData.input.canConvert< Tomahawk::InfoSystem::InfoStringHash >() ) { - dataError( requestId, requestData ); + dataError( requestData ); return; } Tomahawk::InfoSystem::InfoStringHash criteria; - emit getCachedInfo( requestId, criteria, 0, requestData ); + + emit getCachedInfo( criteria, 0, requestData ); } void -ChartsPlugin::notInCacheSlot( uint requestId, QHash<QString, QString> criteria, Tomahawk::InfoSystem::InfoRequestData requestData ) +ChartsPlugin::notInCacheSlot( QHash<QString, QString> criteria, Tomahawk::InfoSystem::InfoRequestData requestData ) { if ( !m_nam.data() ) { tLog() << "Have a null QNAM, uh oh"; - emit info( requestId, requestData, QVariant() ); + emit info( requestData, QVariant() ); return; } @@ -217,7 +219,6 @@ ChartsPlugin::notInCacheSlot( uint requestId, QHash<QString, QString> criteria, qDebug() << Q_FUNC_INFO << "Getting chart url" << url; QNetworkReply* reply = m_nam.data()->get( QNetworkRequest( url ) ); - reply->setProperty( "requestId", requestId ); reply->setProperty( "requestData", QVariant::fromValue< Tomahawk::InfoSystem::InfoRequestData >( requestData ) ); connect( reply, SIGNAL( finished() ), SLOT( chartReturned() ) ); @@ -230,22 +231,18 @@ ChartsPlugin::notInCacheSlot( uint requestId, QHash<QString, QString> criteria, if ( m_chartsFetchJobs > 0 ) { qDebug() << Q_FUNC_INFO << "InfoChartCapabilities still fetching!"; - m_cachedRequests.append( QPair< uint, InfoRequestData >( requestId, requestData ) ); + m_cachedRequests.append( requestData ); return; } - emit info( - requestId, - requestData, - m_allChartsMap - ); + emit info( requestData, m_allChartsMap ); return; } default: { tLog() << Q_FUNC_INFO << "Couldn't figure out what to do with this type of request after cache miss"; - emit info( requestId, requestData, QVariant() ); + emit info( requestData, QVariant() ); return; } } @@ -279,96 +276,152 @@ ChartsPlugin::chartTypes() // We'll populate charts with the data from the server QVariantMap charts; QString chartName; - if ( source == "itunes" ) + QStringList defaultChain; + if ( source == "wearehunted" || source == "itunes" ) { + // Some charts can have an extra param, itunes has geo, WAH has emerging/mainstream // Itunes has geographic-area based charts. So we build a breadcrumb of // ITunes - Country - Albums - Top Chart Type // - Tracks - Top Chart Type - QHash< QString, QVariantMap > countries; + // WeAreHunted has Mainstream/Emerging + // WeAreHunted - Type - Artists - Chart Type + // - Tracks - Chart Type + QHash< QString, QVariantMap > extraType; foreach( const QVariant& chartObj, chartObjs.values() ) { - const QVariantMap chart = chartObj.toMap(); - const QString id = chart.value( "id" ).toString(); - const QString geo = chart.value( "geo" ).toString(); - QString name = chart.value( "name" ).toString(); - const QString type = chart.value( "type" ).toString(); + if( !chartObj.toMap().isEmpty() ){ + const QVariantMap chart = chartObj.toMap(); + const QString id = chart.value( "id" ).toString(); + const QString geo = chart.value( "geo" ).toString(); + QString name = chart.value( "genre" ).toString(); + const QString type = chart.value( "type" ).toString(); + const bool isDefault = ( chart.contains( "default" ) && chart[ "default" ].toInt() == 1 ); - QString country; - if ( !m_cachedCountries.contains( geo ) ) - { - QLocale l( QString( "en_%1" ).arg( geo ) ); - country = Tomahawk::CountryUtils::fullCountryFromCode( geo ); + QString extra; + if( !geo.isEmpty() ){ - for ( int i = 1; i < country.size(); i++ ) - { - if ( country.at( i ).isUpper() ) + if ( !m_cachedCountries.contains( geo ) ) { - country.insert( i, " " ); - i++; + QLocale l( QString( "en_%1" ).arg( geo ) ); + extra = Tomahawk::CountryUtils::fullCountryFromCode( geo ); + + for ( int i = 1; i < extra.size(); i++ ) + { + if ( extra.at( i ).isUpper() ) + { + extra.insert( i, " " ); + i++; + } + } + m_cachedCountries[ geo ] = extra; } + else + { + extra = m_cachedCountries[ geo ]; + } + }else extra = chart.value( "extra" ).toString(); + + if ( name.isEmpty() ) // not a specific chart, an all chart + name = tr( "Top Overall" ); + + InfoStringHash c; + c[ "id" ] = id; + c[ "label" ] = name; + c[ "type" ] = "album"; + if ( isDefault ) + c[ "default" ] = "true"; + + QList<InfoStringHash> extraTypeData = extraType[ extra ][ type ].value< QList< InfoStringHash > >(); + extraTypeData.append( c ); + + extraType[ extra ].insert( type, QVariant::fromValue< QList< InfoStringHash > >( extraTypeData ) ); + if ( isDefault ) + { + defaultChain.clear(); + defaultChain.append( extra ); + defaultChain.append( type ); + defaultChain.append( name ); } - m_cachedCountries[ geo ] = country; } - else + foreach( const QString& c, extraType.keys() ) { - country = m_cachedCountries[ geo ]; + charts[ c ] = extraType[ c ]; +// qDebug() << "extraType has types:" << c; + } + if( source == "itunes" ){ + chartName = "iTunes"; + } + if( source == "wearehunted" ){ + chartName = "WeAreHunted"; } - - if ( name.startsWith( "iTunes Store:" ) ) // truncate - name = name.mid( 13 ); - - InfoStringHash c; - c[ "id" ] = id; - c[ "label" ] = name; - c[ "type" ] = "album"; - QList<InfoStringHash> countryTypeData = countries[ country ][ type ].value< QList< InfoStringHash > >(); - countryTypeData.append( c ); - - countries[ country ].insert( type, QVariant::fromValue< QList< InfoStringHash > >( countryTypeData ) ); } - foreach( const QString& c, countries.keys() ) - { - charts[ c ] = countries[ c ]; -// qDebug() << "Country has types:" << countries[ c ]; - } - chartName = "iTunes"; - } else + }else { // We'll just build: // [Source] - Album - Chart Type // [Source] - Track - Chart Type QList< InfoStringHash > albumCharts; QList< InfoStringHash > trackCharts; + QList< InfoStringHash > artistCharts; + foreach( const QVariant& chartObj, chartObjs.values() ) { - const QVariantMap chart = chartObj.toMap(); - const QString type = chart.value( "type" ).toString(); - InfoStringHash c; - c[ "id" ] = chart.value( "id" ).toString(); - c[ "label" ] = chart.value( "name" ).toString(); - if ( type == "Album" ) - { - c[ "type" ] = "album"; - albumCharts.append( c ); - } - else if ( type == "Track" ) - { - c[ "type" ] = "tracks"; - trackCharts.append( c ); - } - } - charts.insert( tr( "Albums" ), QVariant::fromValue< QList< InfoStringHash > >( albumCharts ) ); - charts.insert( tr( "Tracks" ), QVariant::fromValue< QList< InfoStringHash > >( trackCharts ) ); + if( !chartObj.toMap().isEmpty() ){ + const QVariantMap chart = chartObj.toMap(); + const QString type = chart.value( "type" ).toString(); + const bool isDefault = ( chart.contains( "default" ) && chart[ "default" ].toInt() == 1 ); - /// @note For displaying purposes, upper the first letter - /// @note Remeber to lower it when fetching this! - chartName = source; - chartName[0] = chartName[0].toUpper(); + InfoStringHash c; + c[ "id" ] = chart.value( "id" ).toString(); + c[ "label" ] = chart.value( "name" ).toString(); + if ( isDefault ) + c[ "default" ] = "true"; + + if ( type == "Album" ) + { + c[ "type" ] = "album"; + albumCharts.append( c ); + } + else if ( type == "Track" ) + { + c[ "type" ] = "tracks"; + trackCharts.append( c ); + + }else if ( type == "Artist" ) + { + c[ "type" ] = "artists"; + artistCharts.append( c ); + + } + + if ( isDefault ) + { + defaultChain.clear(); + defaultChain.append( type + "s" ); //UGLY but it's plural to the user, see below + defaultChain.append( c[ "label" ] ); + } + } + if( !artistCharts.isEmpty() ) + charts.insert( tr( "Artists" ), QVariant::fromValue< QList< InfoStringHash > >( artistCharts ) ); + if( !albumCharts.isEmpty() ) + charts.insert( tr( "Albums" ), QVariant::fromValue< QList< InfoStringHash > >( albumCharts ) ); + if( !trackCharts.isEmpty() ) + charts.insert( tr( "Tracks" ), QVariant::fromValue< QList< InfoStringHash > >( trackCharts ) ); + + /// @note For displaying purposes, upper the first letter + /// @note Remeber to lower it when fetching this! + chartName = source; + chartName[0] = chartName[0].toUpper(); + } } /// Add the possible charts and its types to breadcrumb // qDebug() << "ADDING CHART TYPE TO CHARTS:" << chartName; + QVariantMap defaultMap = m_allChartsMap.value( "defaults" ).value< QVariantMap >(); + defaultMap[ source ] = defaultChain; + m_allChartsMap[ "defaults" ] = defaultMap; + m_allChartsMap[ "defaultSource" ] = "itunes"; m_allChartsMap.insert( chartName , QVariant::fromValue< QVariantMap >( charts ) ); } @@ -380,10 +433,9 @@ ChartsPlugin::chartTypes() m_chartsFetchJobs--; if ( !m_cachedRequests.isEmpty() && m_chartsFetchJobs == 0 ) { - QPair< uint, InfoRequestData > request; - foreach ( request, m_cachedRequests ) + foreach ( InfoRequestData request, m_cachedRequests ) { - emit info( request.first, request.second, m_allChartsMap ); + emit info( request, m_allChartsMap ); } m_cachedRequests.clear(); } @@ -414,6 +466,7 @@ ChartsPlugin::chartReturned() QVariantList chartResponse = res.value( "list" ).toList(); QList< InfoStringHash > top_tracks; QList< InfoStringHash > top_albums; + QStringList top_artists; /// Deside what type, we need to handle it differently /// @todo: We allready know the type, append it to breadcrumb hash @@ -422,6 +475,8 @@ ChartsPlugin::chartReturned() setChartType( Album ); else if( res.value( "type" ).toString() == "Track" ) setChartType( Track ); + else if( res.value( "type" ).toString() == "Artist" ) + setChartType( Artist ); else setChartType( None ); @@ -477,10 +532,29 @@ ChartsPlugin::chartReturned() top_tracks << pair; } + }else if( chartType() == Artist ) + { + if ( artist.isEmpty() ) // don't have enough... + { + tLog() << "Didn't get an artist from charts, not enough to build a query on. Aborting" << artist; + + } + else + { + top_artists << artist; + } + } } } + if( chartType() == Artist ) + { + tDebug() << "ChartsPlugin:" << "\tgot " << top_artists.size() << " artists"; + returnedData["artists"] = QVariant::fromValue( top_artists ); + returnedData["type"] = "artists"; + } + if( chartType() == Track ) { tDebug() << "ChartsPlugin:" << "\tgot " << top_tracks.size() << " tracks"; @@ -498,11 +572,7 @@ ChartsPlugin::chartReturned() Tomahawk::InfoSystem::InfoRequestData requestData = reply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >(); - emit info( - reply->property( "requestId" ).toUInt(), - requestData, - returnedData - ); + emit info( requestData, returnedData ); // TODO update cache } else diff --git a/src/libtomahawk/infosystem/infoplugins/generic/chartsplugin.h b/src/libtomahawk/infosystem/infoplugins/generic/chartsplugin.h index 3d35b39cf..c44b88ccf 100644 --- a/src/libtomahawk/infosystem/infoplugins/generic/chartsplugin.h +++ b/src/libtomahawk/infosystem/infoplugins/generic/chartsplugin.h @@ -56,15 +56,15 @@ public slots: void namChangedSlot( QNetworkAccessManager *nam ); protected slots: - virtual void getInfo( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData ); - virtual void notInCacheSlot( uint requestId, Tomahawk::InfoSystem::InfoStringHash criteria, Tomahawk::InfoSystem::InfoRequestData requestData ); + virtual void getInfo( Tomahawk::InfoSystem::InfoRequestData requestData ); + virtual void notInCacheSlot( Tomahawk::InfoSystem::InfoStringHash criteria, Tomahawk::InfoSystem::InfoRequestData requestData ); virtual void pushInfo( QString caller, Tomahawk::InfoSystem::InfoType type, QVariant data ); private: - void fetchChart( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData ); - void fetchChartCapabilities( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData ); - void dataError( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData ); + void fetchChart( Tomahawk::InfoSystem::InfoRequestData requestData ); + void fetchChartCapabilities( Tomahawk::InfoSystem::InfoRequestData requestData ); + void dataError( Tomahawk::InfoSystem::InfoRequestData requestData ); QVariantList m_chartResources; QList<InfoStringHash> m_charts; @@ -73,7 +73,7 @@ private: QVariantMap m_allChartsMap; uint m_chartsFetchJobs; - QList< QPair< uint, InfoRequestData > > m_cachedRequests; + QList< InfoRequestData > m_cachedRequests; QHash< QString, QString > m_cachedCountries; diff --git a/src/libtomahawk/infosystem/infoplugins/generic/echonestplugin.cpp b/src/libtomahawk/infosystem/infoplugins/generic/echonestplugin.cpp index a975dbe08..3d4b1e02b 100644 --- a/src/libtomahawk/infosystem/infoplugins/generic/echonestplugin.cpp +++ b/src/libtomahawk/infosystem/infoplugins/generic/echonestplugin.cpp @@ -68,37 +68,37 @@ EchoNestPlugin::namChangedSlot( QNetworkAccessManager *nam ) } void -EchoNestPlugin::getInfo( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData ) +EchoNestPlugin::getInfo( Tomahawk::InfoSystem::InfoRequestData requestData ) { switch ( requestData.type ) { case Tomahawk::InfoSystem::InfoArtistBiography: - return getArtistBiography( requestId, requestData ); + return getArtistBiography( requestData ); case Tomahawk::InfoSystem::InfoArtistFamiliarity: - return getArtistFamiliarity( requestId, requestData ); + return getArtistFamiliarity( requestData ); case Tomahawk::InfoSystem::InfoArtistHotttness: - return getArtistHotttnesss( requestId, requestData ); + return getArtistHotttnesss( requestData ); case Tomahawk::InfoSystem::InfoArtistTerms: - return getArtistTerms( requestId, requestData ); + return getArtistTerms( requestData ); case Tomahawk::InfoSystem::InfoTrackEnergy: - return getSongProfile( requestId, requestData, "energy" ); + return getSongProfile( requestData, "energy" ); case Tomahawk::InfoSystem::InfoMiscTopTerms: - return getMiscTopTerms( requestId, requestData ); + return getMiscTopTerms( requestData ); default: { - emit info( requestId, requestData, QVariant() ); + emit info( requestData, QVariant() ); return; } } } void -EchoNestPlugin::getSongProfile( uint requestId, const Tomahawk::InfoSystem::InfoRequestData &requestData, const QString &item ) +EchoNestPlugin::getSongProfile( const Tomahawk::InfoSystem::InfoRequestData &requestData, const QString &item ) { //WARNING: Totally not implemented yet Q_UNUSED( item ); - if( !isValidTrackData( requestId, requestData ) ) + if( !isValidTrackData( requestData ) ) return; // Track track( input.toString() ); @@ -110,67 +110,62 @@ EchoNestPlugin::getSongProfile( uint requestId, const Tomahawk::InfoSystem::Info } void -EchoNestPlugin::getArtistBiography( uint requestId, const Tomahawk::InfoSystem::InfoRequestData &requestData ) +EchoNestPlugin::getArtistBiography( const Tomahawk::InfoSystem::InfoRequestData &requestData ) { - if( !isValidArtistData( requestId, requestData ) ) + if( !isValidArtistData( requestData ) ) return; Echonest::Artist artist( requestData.input.toString() ); QNetworkReply *reply = artist.fetchBiographies(); reply->setProperty( "artist", QVariant::fromValue< Echonest::Artist >( artist ) ); - reply->setProperty( "requestId", requestId ); reply->setProperty( "requestData", QVariant::fromValue< Tomahawk::InfoSystem::InfoRequestData >( requestData ) ); connect( reply, SIGNAL( finished() ), SLOT( getArtistBiographySlot() ) ); } void -EchoNestPlugin::getArtistFamiliarity( uint requestId, const Tomahawk::InfoSystem::InfoRequestData &requestData ) +EchoNestPlugin::getArtistFamiliarity( const Tomahawk::InfoSystem::InfoRequestData &requestData ) { - if( !isValidArtistData( requestId, requestData ) ) + if( !isValidArtistData( requestData ) ) return; qDebug() << "Fetching artist familiarity!" << requestData.input; Echonest::Artist artist( requestData.input.toString() ); QNetworkReply* reply = artist.fetchFamiliarity(); reply->setProperty( "artist", QVariant::fromValue< Echonest::Artist >( artist ) ); - reply->setProperty( "requestId", requestId ); reply->setProperty( "requestData", QVariant::fromValue< Tomahawk::InfoSystem::InfoRequestData >( requestData ) ); connect( reply, SIGNAL( finished() ), SLOT( getArtistFamiliaritySlot() ) ); } void -EchoNestPlugin::getArtistHotttnesss( uint requestId, const Tomahawk::InfoSystem::InfoRequestData &requestData ) +EchoNestPlugin::getArtistHotttnesss( const Tomahawk::InfoSystem::InfoRequestData &requestData ) { - if( !isValidArtistData( requestId, requestData ) ) + if( !isValidArtistData( requestData ) ) return; Echonest::Artist artist( requestData.input.toString() ); QNetworkReply* reply = artist.fetchHotttnesss(); reply->setProperty( "artist", QVariant::fromValue< Echonest::Artist >( artist ) ); - reply->setProperty( "requestId", requestId ); reply->setProperty( "requestData", QVariant::fromValue< Tomahawk::InfoSystem::InfoRequestData >( requestData ) ); connect( reply, SIGNAL( finished() ), SLOT( getArtistHotttnesssSlot() ) ); } void -EchoNestPlugin::getArtistTerms( uint requestId, const Tomahawk::InfoSystem::InfoRequestData &requestData ) +EchoNestPlugin::getArtistTerms( const Tomahawk::InfoSystem::InfoRequestData &requestData ) { - if( !isValidArtistData( requestId, requestData ) ) + if( !isValidArtistData( requestData ) ) return; Echonest::Artist artist( requestData.input.toString() ); QNetworkReply* reply = artist.fetchTerms( Echonest::Artist::Weight ); reply->setProperty( "artist", QVariant::fromValue< Echonest::Artist >( artist ) ); - reply->setProperty( "requestId", requestId ); reply->setProperty( "requestData", QVariant::fromValue< Tomahawk::InfoSystem::InfoRequestData >( requestData ) ); connect( reply, SIGNAL( finished() ), SLOT( getArtistTermsSlot() ) ); } void -EchoNestPlugin::getMiscTopTerms( uint requestId, const Tomahawk::InfoSystem::InfoRequestData &requestData ) +EchoNestPlugin::getMiscTopTerms( const Tomahawk::InfoSystem::InfoRequestData &requestData ) { QNetworkReply* reply = Echonest::Artist::topTerms( 20 ); - reply->setProperty( "requestId", requestId ); reply->setProperty( "requestData", QVariant::fromValue< Tomahawk::InfoSystem::InfoRequestData >( requestData ) ); connect( reply, SIGNAL( finished() ), SLOT( getMiscTopSlot() ) ); } @@ -195,9 +190,7 @@ EchoNestPlugin::getArtistBiographySlot() biographyMap[ biography.site() ] = siteData; } Tomahawk::InfoSystem::InfoRequestData requestData = reply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >(); - emit info( reply->property( "requestId" ).toUInt(), - requestData, - biographyMap ); + emit info( requestData, biographyMap ); reply->deleteLater(); } @@ -208,9 +201,7 @@ EchoNestPlugin::getArtistFamiliaritySlot() Echonest::Artist artist = artistFromReply( reply ); qreal familiarity = artist.familiarity(); Tomahawk::InfoSystem::InfoRequestData requestData = reply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >(); - emit info( reply->property( "requestId" ).toUInt(), - requestData, - familiarity ); + emit info( requestData, familiarity ); reply->deleteLater(); } @@ -221,9 +212,7 @@ EchoNestPlugin::getArtistHotttnesssSlot() Echonest::Artist artist = artistFromReply( reply ); qreal hotttnesss = artist.hotttnesss(); Tomahawk::InfoSystem::InfoRequestData requestData = reply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >(); - emit info( reply->property( "requestId" ).toUInt(), - requestData, - hotttnesss ); + emit info( requestData, hotttnesss ); reply->deleteLater(); } @@ -241,9 +230,7 @@ EchoNestPlugin::getArtistTermsSlot() termsMap[ term.name() ] = termHash; } Tomahawk::InfoSystem::InfoRequestData requestData = reply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >(); - emit info( reply->property( "requestId" ).toUInt(), - requestData, - termsMap ); + emit info( requestData, termsMap ); reply->deleteLater(); } @@ -260,46 +247,44 @@ EchoNestPlugin::getMiscTopSlot() termsMap[ term.name() ] = termHash; } Tomahawk::InfoSystem::InfoRequestData requestData = reply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >(); - emit info( reply->property( "requestId" ).toUInt(), - requestData, - termsMap ); + emit info( requestData, termsMap ); reply->deleteLater(); } bool -EchoNestPlugin::isValidArtistData( uint requestId, const Tomahawk::InfoSystem::InfoRequestData &requestData ) +EchoNestPlugin::isValidArtistData( const Tomahawk::InfoSystem::InfoRequestData &requestData ) { if ( requestData.input.isNull() || !requestData.input.isValid() || !requestData.input.canConvert< QString >() ) { - emit info( requestId, requestData, QVariant() ); + emit info( requestData, QVariant() ); return false; } QString artistName = requestData.input.toString(); if ( artistName.isEmpty() ) { - emit info( requestId, requestData, QVariant() ); + emit info( requestData, QVariant() ); return false; } return true; } bool -EchoNestPlugin::isValidTrackData( uint requestId, const Tomahawk::InfoSystem::InfoRequestData &requestData ) +EchoNestPlugin::isValidTrackData( const Tomahawk::InfoSystem::InfoRequestData &requestData ) { if ( requestData.input.isNull() || !requestData.input.isValid() || !requestData.input.canConvert< QString >() ) { - emit info( requestId, requestData, QVariant() ); + emit info( requestData, QVariant() ); return false; } QString trackName = requestData.input.toString(); if ( trackName.isEmpty() ) { - emit info( requestId, requestData, QVariant() ); + emit info( requestData, QVariant() ); return false; } if ( !requestData.customData.contains( "artistName" ) || requestData.customData[ "artistName" ].toString().isEmpty() ) { - emit info( requestId, requestData, QVariant() ); + emit info( requestData, QVariant() ); return false; } return true; diff --git a/src/libtomahawk/infosystem/infoplugins/generic/echonestplugin.h b/src/libtomahawk/infosystem/infoplugins/generic/echonestplugin.h index e6e0825ab..bd59193c8 100644 --- a/src/libtomahawk/infosystem/infoplugins/generic/echonestplugin.h +++ b/src/libtomahawk/infosystem/infoplugins/generic/echonestplugin.h @@ -46,7 +46,7 @@ public: virtual ~EchoNestPlugin(); protected slots: - virtual void getInfo( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData ); + virtual void getInfo( Tomahawk::InfoSystem::InfoRequestData requestData ); virtual void pushInfo( const QString caller, const Tomahawk::InfoSystem::InfoType type, const QVariant data ) { @@ -55,9 +55,8 @@ protected slots: Q_UNUSED( data ); } - virtual void notInCacheSlot( uint requestId, Tomahawk::InfoSystem::InfoStringHash criteria, Tomahawk::InfoSystem::InfoRequestData requestData ) + virtual void notInCacheSlot( Tomahawk::InfoSystem::InfoStringHash criteria, Tomahawk::InfoSystem::InfoRequestData requestData ) { - Q_UNUSED( requestId ); Q_UNUSED( criteria ); Q_UNUSED( requestData ); } @@ -66,15 +65,15 @@ public slots: void namChangedSlot( QNetworkAccessManager *nam ); private: - void getSongProfile( uint requestId, const Tomahawk::InfoSystem::InfoRequestData &requestData, const QString &item = QString() ); - void getArtistBiography( uint requestId, const Tomahawk::InfoSystem::InfoRequestData &requestData ); - void getArtistFamiliarity( uint requestId, const Tomahawk::InfoSystem::InfoRequestData &requestData ); - void getArtistHotttnesss( uint requestId, const Tomahawk::InfoSystem::InfoRequestData &requestData ); - void getArtistTerms( uint requestId, const Tomahawk::InfoSystem::InfoRequestData &requestData ); - void getMiscTopTerms( uint requestId, const Tomahawk::InfoSystem::InfoRequestData &requestData ); + void getSongProfile( const Tomahawk::InfoSystem::InfoRequestData &requestData, const QString &item = QString() ); + void getArtistBiography( const Tomahawk::InfoSystem::InfoRequestData &requestData ); + void getArtistFamiliarity( const Tomahawk::InfoSystem::InfoRequestData &requestData ); + void getArtistHotttnesss( const Tomahawk::InfoSystem::InfoRequestData &requestData ); + void getArtistTerms( const Tomahawk::InfoSystem::InfoRequestData &requestData ); + void getMiscTopTerms( const Tomahawk::InfoSystem::InfoRequestData &requestData ); - bool isValidArtistData( uint requestId, const Tomahawk::InfoSystem::InfoRequestData &requestData ); - bool isValidTrackData( uint requestId, const Tomahawk::InfoSystem::InfoRequestData &requestData ); + bool isValidArtistData( const Tomahawk::InfoSystem::InfoRequestData &requestData ); + bool isValidTrackData( const Tomahawk::InfoSystem::InfoRequestData &requestData ); Echonest::Artist artistFromReply( QNetworkReply* ); private slots: diff --git a/src/libtomahawk/infosystem/infoplugins/generic/hypemPlugin.cpp b/src/libtomahawk/infosystem/infoplugins/generic/hypemPlugin.cpp new file mode 100644 index 000000000..dd6a4b7b1 --- /dev/null +++ b/src/libtomahawk/infosystem/infoplugins/generic/hypemPlugin.cpp @@ -0,0 +1,420 @@ +/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> === + * + * Copyright 2010-2011, Hugo Lindström <hugolm84@gmail.com> + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include "hypemPlugin.h" + +#include <QDir> +#include <QSettings> +#include <QCryptographicHash> +#include <QNetworkConfiguration> +#include <QNetworkReply> +#include <QDomElement> + +#include "album.h" +#include "typedefs.h" +#include "audio/audioengine.h" +#include "tomahawksettings.h" +#include "utils/tomahawkutils.h" +#include "utils/logger.h" + +#define HYPEM_URL "http://hypem.com/playlist/" +#define HYPEM_END_URL "json/1/data.js" +#include <qjson/parser.h> +#include <qjson/serializer.h> + +using namespace Tomahawk::InfoSystem; + + +hypemPlugin::hypemPlugin() + : InfoPlugin() + , m_chartsFetchJobs( 0 ) +{ + + m_supportedGetTypes << InfoChart << InfoChartCapabilities; + m_types << "Artists" << "Tracks" << "Recent by Tag"; + + m_trackTypes << "Last 3 Days" + << "Last Week" + << "No Remixes" + << "On Twitter"; + + m_byTagTypes << "Dance" + << "Experimental" + << "Electronic" + << "Funk" + << "Hip-hop" + << "Indie" + << "Instrumental" + << "Post-punk" + << "Rock" + << "Singer-songwriter" + << "Alternative" + << "Pop" + << "Female" + << "Vocalist" + << "Folk" + << "Electro" + << "Lo-fi" + << "Psychedelic" + << "Rap" + << "British" + << "Ambient" + << "Dubstep" + << "House" + << "Chillwave" + << "Dreampop" + << "Shoegaze" + << "Chillout" + << "Soul" + << "French" + << "Acoustic" + << "Canadian" + << "60s" + << "80s" + << "Techno" + << "Punk" + << "New wave"; + chartTypes(); + +} + + + + +hypemPlugin::~hypemPlugin() +{ + qDebug() << Q_FUNC_INFO; +} + + +void +hypemPlugin::namChangedSlot( QNetworkAccessManager *nam ) +{ + tDebug() << "hypemPlugin: namChangedSLot"; + qDebug() << Q_FUNC_INFO; + if( !nam ) + return; + + m_nam = QWeakPointer< QNetworkAccessManager >( nam ); + + +} + + +void +hypemPlugin::dataError( Tomahawk::InfoSystem::InfoRequestData requestData ) +{ + emit info( requestData, QVariant() ); + return; +} + + +void +hypemPlugin::getInfo( Tomahawk::InfoSystem::InfoRequestData requestData ) +{ + qDebug() << Q_FUNC_INFO << requestData.caller; + qDebug() << Q_FUNC_INFO << requestData.customData; + + InfoStringHash hash = requestData.input.value< Tomahawk::InfoSystem::InfoStringHash >(); + + + switch ( requestData.type ) + { + + case InfoChart: + if ( !hash.contains( "chart_source" ) || hash["chart_source"].toLower() != "hype machine" ) + { + dataError( requestData ); + break; + } + qDebug() << Q_FUNC_INFO << "InfoCHart req for" << hash["chart_source"]; + fetchChart( requestData ); + break; + + case InfoChartCapabilities: + fetchChartCapabilities( requestData ); + break; + default: + dataError( requestData ); + } +} + + +void +hypemPlugin::pushInfo( const QString caller, const Tomahawk::InfoSystem::InfoType type, const QVariant input ) +{ + Q_UNUSED( caller ) + Q_UNUSED( type) + Q_UNUSED( input ) +} + + +void +hypemPlugin::fetchChart( Tomahawk::InfoSystem::InfoRequestData requestData ) +{ + + if ( !requestData.input.canConvert< Tomahawk::InfoSystem::InfoStringHash >() ) + { + dataError( requestData ); + return; + } + + InfoStringHash hash = requestData.input.value< Tomahawk::InfoSystem::InfoStringHash >(); + Tomahawk::InfoSystem::InfoStringHash criteria; + + /// Each request needs to contain both a id and source + if ( !hash.contains( "chart_id" ) && !hash.contains( "chart_source" ) ) + { + dataError( requestData ); + return; + + } + /// Set the criterias for current chart + criteria["chart_id"] = hash["chart_id"]; + criteria["chart_source"] = hash["chart_source"]; + + emit getCachedInfo( criteria, 0, requestData ); +} + +void +hypemPlugin::fetchChartCapabilities( Tomahawk::InfoSystem::InfoRequestData requestData ) +{ + if ( !requestData.input.canConvert< Tomahawk::InfoSystem::InfoStringHash >() ) + { + dataError( requestData ); + return; + } + + Tomahawk::InfoSystem::InfoStringHash criteria; + emit getCachedInfo( criteria, 0, requestData ); +} + +void +hypemPlugin::notInCacheSlot( QHash<QString, QString> criteria, Tomahawk::InfoSystem::InfoRequestData requestData ) +{ + if ( !m_nam.data() ) + { + tLog() << "Have a null QNAM, uh oh"; + emit info( requestData, QVariant() ); + return; + } + + + switch ( requestData.type ) + { + case InfoChart: + { + /// Fetch the chart, we need source and id + + QUrl url = QUrl( QString( HYPEM_URL "%1/%2" ).arg( criteria["chart_id"].toLower() ).arg(HYPEM_END_URL) ); + qDebug() << Q_FUNC_INFO << "Getting chart url" << url; + + QNetworkReply* reply = m_nam.data()->get( QNetworkRequest( url ) ); + reply->setProperty( "requestData", QVariant::fromValue< Tomahawk::InfoSystem::InfoRequestData >( requestData ) ); + connect( reply, SIGNAL( finished() ), SLOT( chartReturned() ) ); + return; + + + } + + case InfoChartCapabilities: + { + if ( m_chartsFetchJobs > 0 ) + { + qDebug() << Q_FUNC_INFO << "InfoChartCapabilities still fetching!"; + m_cachedRequests.append( requestData ); + return; + } + + emit info( requestData, m_allChartsMap ); + return; + } + + default: + { + tLog() << Q_FUNC_INFO << "Couldn't figure out what to do with this type of request after cache miss"; + emit info( requestData, QVariant() ); + return; + } + } +} + + +void +hypemPlugin::chartTypes() +{ + /// Get possible chart type for specifichypemPlugin: InfoChart types returned chart source + tDebug() << Q_FUNC_INFO << "Got hypem types"; + + QVariantMap charts; + + foreach(QVariant types, m_types ) + { + QList< InfoStringHash > chart_types; + QList< InfoStringHash > pop_charts; + InfoStringHash c; + + if(types.toString() != "Artists") + { + + if(types.toString() == "Tracks") + { + + foreach(QVariant trackType, m_trackTypes) + { + QString typeId; + if(trackType.toString() == "Last 3 Days") + typeId = "popular/3day"; + + if(trackType.toString() == "Last Week") + typeId = "popular/lastweek"; + + if(trackType.toString() == "No Remixes") + typeId = "popular/noremix"; + + if(trackType.toString() == "On Twitter") + typeId = "popular/twitter"; + + c[ "id" ] = typeId; + c[ "label" ] = trackType.toString(); + c[ "type" ] = "tracks"; + pop_charts.append( c ); + } + + chart_types.append( pop_charts ); + + } + else if(types.toString() == "Recent by Tag") + { + foreach(QVariant tagTypes, m_byTagTypes) + { + + c[ "id" ] = "tags/" + tagTypes.toString().toLower(); + c[ "label" ] = tagTypes.toString(); + c[ "type" ] = tagTypes.toString(); + chart_types.append( c ); + } + + } + + }else + { + InfoStringHash c; + c[ "id" ] = "popular/artists"; + c[ "label" ] = "Most Recent"; + c[ "type" ] = "artists"; + chart_types.append( c ); + } + + charts.insert( types.toString(), QVariant::fromValue<QList< InfoStringHash > >( chart_types ) ); + } + + + m_allChartsMap.insert( "Hype Machine", QVariant::fromValue<QVariantMap>( charts ) ); + qDebug() << "hypemPlugin:Chartstype: " << m_allChartsMap; + + +} + +void +hypemPlugin::chartReturned() +{ + + /// Chart request returned something! Woho + QNetworkReply* reply = qobject_cast<QNetworkReply*>( sender() ); + QString url = reply->url().toString(); + QVariantMap returnedData; + if ( reply->error() == QNetworkReply::NoError ) + { + QJson::Parser p; + bool ok; + QVariantMap res = p.parse( reply, &ok ).toMap(); + + if ( !ok ) + { + tLog() << "Failed to parse json from chart lookup:" << p.errorString() << "On line" << p.errorLine(); + return; + } + + /// SO we have a result, parse it! + QList< InfoStringHash > top_tracks; + QStringList top_artists; + + if( url.contains( "artists" ) ) + setChartType( Artist ); + else + setChartType( Track ); + + foreach(QVariant result, res ) + { + QString title, artist; + QVariantMap chartMap = result.toMap(); + + if ( !chartMap.isEmpty() ) + { + + title = chartMap.value( "title" ).toString(); + artist = chartMap.value( "artist" ).toString(); + + if( chartType() == Track ) + { + InfoStringHash pair; + pair["artist"] = artist; + pair["track"] = title; + top_tracks << pair; + + qDebug() << "HypemChart type is track"; + } + + + if( chartType() == Artist ) + { + + top_artists << artist; + qDebug() << "HypemChart type is artist"; + + } + } + } + + if( chartType() == Track ) + { + tDebug() << "HypemPlugin:" << "\tgot " << top_tracks.size() << " tracks"; + returnedData["tracks"] = QVariant::fromValue( top_tracks ); + returnedData["type"] = "tracks"; + } + + + + if( chartType() == Artist ) + { + tDebug() << "HypemPlugin:" << "\tgot " << top_artists.size() << " artists"; + returnedData["artists"] = top_artists; + returnedData["type"] = "artists"; + } + + Tomahawk::InfoSystem::InfoRequestData requestData = reply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >(); + + + emit info( requestData, returnedData ); + // TODO update cache + } + else + qDebug() << "Network error in fetching chart:" << reply->url().toString(); + +} diff --git a/src/libtomahawk/infosystem/infoplugins/generic/hypemPlugin.h b/src/libtomahawk/infosystem/infoplugins/generic/hypemPlugin.h new file mode 100644 index 000000000..a13c0299d --- /dev/null +++ b/src/libtomahawk/infosystem/infoplugins/generic/hypemPlugin.h @@ -0,0 +1,93 @@ +/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> === + * + * Copyright 2010-2011, Hugo Lindström <hugolm84@gmail.com> + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#ifndef hypemPlugin_H +#define hypemPlugin_H + +#include "infosystem/infosystem.h" +#include "infosystem/infosystemworker.h" +#include <QNetworkReply> +#include <QObject> + +class QNetworkReply; + +namespace Tomahawk +{ + +namespace InfoSystem +{ + +class hypemPlugin : public InfoPlugin +{ + Q_OBJECT + +public: + hypemPlugin(); + virtual ~hypemPlugin(); + + enum ChartType { + None = 0x00, + Track = 0x01, + Album = 0x02, + Artist = 0x04 + + }; + void setChartType( ChartType type ) { m_chartType = type; } + ChartType chartType() const { return m_chartType; } + +public slots: + void chartReturned(); + void chartTypes(); + void namChangedSlot( QNetworkAccessManager *nam ); + +protected slots: + virtual void getInfo( Tomahawk::InfoSystem::InfoRequestData requestData ); + virtual void notInCacheSlot( Tomahawk::InfoSystem::InfoStringHash criteria, Tomahawk::InfoSystem::InfoRequestData requestData ); + + virtual void pushInfo( QString caller, Tomahawk::InfoSystem::InfoType type, QVariant data ); + +private: + void fetchChart( Tomahawk::InfoSystem::InfoRequestData requestData ); + void fetchChartCapabilities( Tomahawk::InfoSystem::InfoRequestData requestData ); + void dataError( Tomahawk::InfoSystem::InfoRequestData requestData ); + + QVariantList m_chartResources; + QList<InfoStringHash> m_charts; + + + ChartType m_chartType; + QVariantMap m_allChartsMap; + QVariantList m_types; + QVariantList m_popularTypes; + QVariantList m_trackTypes; + QVariantList m_byTagTypes; + + + uint m_chartsFetchJobs; + QList< InfoRequestData > m_cachedRequests; + + QHash< QString, QString > m_cachedCountries; + + QWeakPointer< QNetworkAccessManager > m_nam; +}; + +} + +} + +#endif // hypemPlugin_H diff --git a/src/libtomahawk/infosystem/infoplugins/generic/lastfmplugin.cpp b/src/libtomahawk/infosystem/infoplugins/generic/lastfmplugin.cpp index ae99903ec..ed7681642 100644 --- a/src/libtomahawk/infosystem/infoplugins/generic/lastfmplugin.cpp +++ b/src/libtomahawk/infosystem/infoplugins/generic/lastfmplugin.cpp @@ -123,43 +123,43 @@ LastFmPlugin::namChangedSlot( QNetworkAccessManager *nam ) void -LastFmPlugin::dataError( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData ) +LastFmPlugin::dataError( Tomahawk::InfoSystem::InfoRequestData requestData ) { - emit info( requestId, requestData, QVariant() ); + emit info( requestData, QVariant() ); return; } void -LastFmPlugin::getInfo( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData ) +LastFmPlugin::getInfo( Tomahawk::InfoSystem::InfoRequestData requestData ) { switch ( requestData.type ) { case InfoArtistImages: - fetchArtistImages( requestId, requestData ); + fetchArtistImages( requestData ); break; case InfoAlbumCoverArt: - fetchCoverArt( requestId, requestData ); + fetchCoverArt( requestData ); break; case InfoArtistSimilars: - fetchSimilarArtists( requestId, requestData ); + fetchSimilarArtists( requestData ); break; case InfoArtistSongs: - fetchTopTracks( requestId, requestData ); + fetchTopTracks( requestData ); break; case InfoChart: - fetchChart( requestId, requestData ); + fetchChart( requestData ); break; case InfoChartCapabilities: - fetchChartCapabilities( requestId, requestData ); + fetchChartCapabilities( requestData ); break; default: - dataError( requestId, requestData ); + dataError( requestData ); } } @@ -267,95 +267,95 @@ LastFmPlugin::sendLoveSong( const InfoType type, QVariant input ) void -LastFmPlugin::fetchSimilarArtists( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData ) +LastFmPlugin::fetchSimilarArtists( Tomahawk::InfoSystem::InfoRequestData requestData ) { if ( !requestData.input.canConvert< Tomahawk::InfoSystem::InfoStringHash >() ) { - dataError( requestId, requestData ); + dataError( requestData ); return; } InfoStringHash hash = requestData.input.value< Tomahawk::InfoSystem::InfoStringHash >(); if ( !hash.contains( "artist" ) ) { - dataError( requestId, requestData ); + dataError( requestData ); return; } Tomahawk::InfoSystem::InfoStringHash criteria; criteria["artist"] = hash["artist"]; - emit getCachedInfo( requestId, criteria, 2419200000, requestData ); + emit getCachedInfo( criteria, 2419200000, requestData ); } void -LastFmPlugin::fetchTopTracks( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData ) +LastFmPlugin::fetchTopTracks( Tomahawk::InfoSystem::InfoRequestData requestData ) { if ( !requestData.input.canConvert< Tomahawk::InfoSystem::InfoStringHash >() ) { - dataError( requestId, requestData ); + dataError( requestData ); return; } InfoStringHash hash = requestData.input.value< Tomahawk::InfoSystem::InfoStringHash >(); if ( !hash.contains( "artist" ) ) { - dataError( requestId, requestData ); + dataError( requestData ); return; } Tomahawk::InfoSystem::InfoStringHash criteria; criteria["artist"] = hash["artist"]; - emit getCachedInfo( requestId, criteria, 2419200000, requestData ); + emit getCachedInfo( criteria, 2419200000, requestData ); } void -LastFmPlugin::fetchChart( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData ) +LastFmPlugin::fetchChart( Tomahawk::InfoSystem::InfoRequestData requestData ) { if ( !requestData.input.canConvert< Tomahawk::InfoSystem::InfoStringHash >() ) { - dataError( requestId, requestData ); + dataError( requestData ); return; } InfoStringHash hash = requestData.input.value< Tomahawk::InfoSystem::InfoStringHash >(); Tomahawk::InfoSystem::InfoStringHash criteria; if ( !hash.contains( "chart_id" ) ) { - dataError( requestId, requestData ); + dataError( requestData ); return; } else { criteria["chart_id"] = hash["chart_id"]; } - emit getCachedInfo( requestId, criteria, 0, requestData ); + emit getCachedInfo( criteria, 0, requestData ); } void -LastFmPlugin::fetchChartCapabilities( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData ) +LastFmPlugin::fetchChartCapabilities( Tomahawk::InfoSystem::InfoRequestData requestData ) { if ( !requestData.input.canConvert< Tomahawk::InfoSystem::InfoStringHash >() ) { - dataError( requestId, requestData ); + dataError( requestData ); return; } InfoStringHash hash = requestData.input.value< Tomahawk::InfoSystem::InfoStringHash >(); Tomahawk::InfoSystem::InfoStringHash criteria; - emit getCachedInfo( requestId, criteria, 0, requestData ); + emit getCachedInfo( criteria, 0, requestData ); } void -LastFmPlugin::fetchCoverArt( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData ) +LastFmPlugin::fetchCoverArt( Tomahawk::InfoSystem::InfoRequestData requestData ) { if ( !requestData.input.canConvert< Tomahawk::InfoSystem::InfoStringHash >() ) { - dataError( requestId, requestData ); + dataError( requestData ); return; } InfoStringHash hash = requestData.input.value< Tomahawk::InfoSystem::InfoStringHash >(); if ( !hash.contains( "artist" ) || !hash.contains( "album" ) ) { - dataError( requestId, requestData ); + dataError( requestData ); return; } @@ -363,39 +363,39 @@ LastFmPlugin::fetchCoverArt( uint requestId, Tomahawk::InfoSystem::InfoRequestDa criteria["artist"] = hash["artist"]; criteria["album"] = hash["album"]; - emit getCachedInfo( requestId, criteria, 2419200000, requestData ); + emit getCachedInfo( criteria, 2419200000, requestData ); } void -LastFmPlugin::fetchArtistImages( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData ) +LastFmPlugin::fetchArtistImages( Tomahawk::InfoSystem::InfoRequestData requestData ) { if ( !requestData.input.canConvert< Tomahawk::InfoSystem::InfoStringHash >() ) { - dataError( requestId, requestData ); + dataError( requestData ); return; } InfoStringHash hash = requestData.input.value< Tomahawk::InfoSystem::InfoStringHash >(); if ( !hash.contains( "artist" ) ) { - dataError( requestId, requestData ); + dataError( requestData ); return; } Tomahawk::InfoSystem::InfoStringHash criteria; criteria["artist"] = hash["artist"]; - emit getCachedInfo( requestId, criteria, 2419200000, requestData ); + emit getCachedInfo( criteria, 2419200000, requestData ); } void -LastFmPlugin::notInCacheSlot( uint requestId, QHash<QString, QString> criteria, Tomahawk::InfoSystem::InfoRequestData requestData ) +LastFmPlugin::notInCacheSlot( QHash<QString, QString> criteria, Tomahawk::InfoSystem::InfoRequestData requestData ) { if ( !lastfm::nam() ) { tLog() << "Have a null QNAM, uh oh"; - emit info( requestId, requestData, QVariant() ); + emit info( requestData, QVariant() ); return; } @@ -407,14 +407,14 @@ LastFmPlugin::notInCacheSlot( uint requestId, QHash<QString, QString> criteria, /// We need something to check if the request is actually ment to go to this plugin if ( !hash.contains( "chart_source" ) ) { - dataError( requestId, requestData ); + dataError( requestData ); break; } else { if( "last.fm" != hash["chart_source"] ) { - dataError( requestId, requestData ); + dataError( requestData ); break; } @@ -425,7 +425,6 @@ LastFmPlugin::notInCacheSlot( uint requestId, QHash<QString, QString> criteria, args["method"] = criteria["chart_id"]; args["limit"] = "100"; QNetworkReply* reply = lastfm::ws::get(args); - reply->setProperty( "requestId", requestId ); reply->setProperty( "requestData", QVariant::fromValue< Tomahawk::InfoSystem::InfoRequestData >( requestData ) ); connect( reply, SIGNAL( finished() ), SLOT( chartReturned() ) ); @@ -466,11 +465,7 @@ LastFmPlugin::notInCacheSlot( uint requestId, QHash<QString, QString> criteria, result.insert( "Last.fm", QVariant::fromValue<QVariantMap>( charts ) ); tDebug() << "LASTFM RETURNING CHART LIST!"; - emit info( - requestId, - requestData, - result - ); + emit info( requestData, result ); return; } @@ -478,7 +473,6 @@ LastFmPlugin::notInCacheSlot( uint requestId, QHash<QString, QString> criteria, { lastfm::Artist a( criteria["artist"] ); QNetworkReply* reply = a.getSimilar(); - reply->setProperty( "requestId", requestId ); reply->setProperty( "requestData", QVariant::fromValue< Tomahawk::InfoSystem::InfoRequestData >( requestData ) ); connect( reply, SIGNAL( finished() ), SLOT( similarArtistsReturned() ) ); @@ -489,7 +483,6 @@ LastFmPlugin::notInCacheSlot( uint requestId, QHash<QString, QString> criteria, { lastfm::Artist a( criteria["artist"] ); QNetworkReply* reply = a.getTopTracks(); - reply->setProperty( "requestId", requestId ); reply->setProperty( "requestData", QVariant::fromValue< Tomahawk::InfoSystem::InfoRequestData >( requestData ) ); connect( reply, SIGNAL( finished() ), SLOT( topTracksReturned() ) ); @@ -504,7 +497,6 @@ LastFmPlugin::notInCacheSlot( uint requestId, QHash<QString, QString> criteria, QString imgurl = "http://ws.audioscrobbler.com/2.0/?method=album.imageredirect&artist=%1&album=%2&autocorrect=1&size=large&api_key=7a90f6672a04b809ee309af169f34b8b"; QNetworkRequest req( imgurl.arg( artistName ).arg( albumName ) ); QNetworkReply* reply = lastfm::nam()->get( req ); - reply->setProperty( "requestId", requestId ); reply->setProperty( "requestData", QVariant::fromValue< Tomahawk::InfoSystem::InfoRequestData >( requestData ) ); connect( reply, SIGNAL( finished() ), SLOT( coverArtReturned() ) ); @@ -518,7 +510,6 @@ LastFmPlugin::notInCacheSlot( uint requestId, QHash<QString, QString> criteria, QString imgurl = "http://ws.audioscrobbler.com/2.0/?method=artist.imageredirect&artist=%1&autocorrect=1&size=large&api_key=7a90f6672a04b809ee309af169f34b8b"; QNetworkRequest req( imgurl.arg( artistName ) ); QNetworkReply* reply = lastfm::nam()->get( req ); - reply->setProperty( "requestId", requestId ); reply->setProperty( "requestData", QVariant::fromValue< Tomahawk::InfoSystem::InfoRequestData >( requestData ) ); connect( reply, SIGNAL( finished() ), SLOT( artistImagesReturned() ) ); @@ -528,7 +519,7 @@ LastFmPlugin::notInCacheSlot( uint requestId, QHash<QString, QString> criteria, default: { tLog() << "Couldn't figure out what to do with this type of request after cache miss"; - emit info( requestId, requestData, QVariant() ); + emit info( requestData, QVariant() ); return; } } @@ -553,11 +544,7 @@ LastFmPlugin::similarArtistsReturned() Tomahawk::InfoSystem::InfoRequestData requestData = reply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >(); - emit info( - reply->property( "requestId" ).toUInt(), - requestData, - returnedData - ); + emit info( requestData, returnedData ); Tomahawk::InfoSystem::InfoStringHash origData = requestData.input.value< Tomahawk::InfoSystem::InfoStringHash>(); Tomahawk::InfoSystem::InfoStringHash criteria; @@ -609,11 +596,7 @@ LastFmPlugin::chartReturned() Tomahawk::InfoSystem::InfoRequestData requestData = reply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >(); - emit info( - reply->property( "requestId" ).toUInt(), - requestData, - returnedData - ); + emit info( requestData, returnedData ); // TODO update cache } @@ -629,11 +612,7 @@ LastFmPlugin::topTracksReturned() Tomahawk::InfoSystem::InfoRequestData requestData = reply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >(); - emit info( - reply->property( "requestId" ).toUInt(), - requestData, - returnedData - ); + emit info( requestData, returnedData ); Tomahawk::InfoSystem::InfoStringHash origData = requestData.input.value< Tomahawk::InfoSystem::InfoStringHash>(); Tomahawk::InfoSystem::InfoStringHash criteria; @@ -653,7 +632,7 @@ LastFmPlugin::coverArtReturned() if ( ba.isNull() || !ba.length() ) { tLog() << Q_FUNC_INFO << "Uh oh, null byte array"; - emit info( reply->property( "requestId" ).toUInt(), reply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >(), QVariant() ); + emit info( reply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >(), QVariant() ); return; } foreach ( const QUrl& url, m_badUrls ) @@ -668,11 +647,7 @@ LastFmPlugin::coverArtReturned() Tomahawk::InfoSystem::InfoRequestData requestData = reply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >(); - emit info( - reply->property( "requestId" ).toUInt(), - requestData, - returnedData - ); + emit info( requestData, returnedData ); Tomahawk::InfoSystem::InfoStringHash origData = requestData.input.value< Tomahawk::InfoSystem::InfoStringHash>(); Tomahawk::InfoSystem::InfoStringHash criteria; @@ -685,13 +660,12 @@ LastFmPlugin::coverArtReturned() if ( !lastfm::nam() ) { tLog() << Q_FUNC_INFO << "Uh oh, nam is null"; - emit info( reply->property( "requestId" ).toUInt(), reply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >(), QVariant() ); + emit info( reply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >(), QVariant() ); return; } // Follow HTTP redirect QNetworkRequest req( redir ); QNetworkReply* newReply = lastfm::nam()->get( req ); - newReply->setProperty( "requestId", reply->property( "requestId" ) ); newReply->setProperty( "requestData", reply->property( "requestData" ) ); connect( newReply, SIGNAL( finished() ), SLOT( coverArtReturned() ) ); } @@ -711,7 +685,7 @@ LastFmPlugin::artistImagesReturned() if ( ba.isNull() || !ba.length() ) { tLog() << Q_FUNC_INFO << "Uh oh, null byte array"; - emit info( reply->property( "requestId" ).toUInt(), reply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >(), QVariant() ); + emit info( reply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >(), QVariant() ); return; } foreach ( const QUrl& url, m_badUrls ) @@ -725,7 +699,7 @@ LastFmPlugin::artistImagesReturned() Tomahawk::InfoSystem::InfoRequestData requestData = reply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >(); - emit info( reply->property( "requestId" ).toUInt(), requestData, returnedData ); + emit info( requestData, returnedData ); Tomahawk::InfoSystem::InfoStringHash origData = requestData.input.value< Tomahawk::InfoSystem::InfoStringHash>(); Tomahawk::InfoSystem::InfoStringHash criteria; @@ -737,13 +711,12 @@ LastFmPlugin::artistImagesReturned() if ( !lastfm::nam() ) { tLog() << Q_FUNC_INFO << "Uh oh, nam is null"; - emit info( reply->property( "requestId" ).toUInt(), reply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >(), QVariant() ); + emit info( reply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >(), QVariant() ); return; } // Follow HTTP redirect QNetworkRequest req( redir ); QNetworkReply* newReply = lastfm::nam()->get( req ); - newReply->setProperty( "requestId", reply->property( "requestId" ) ); newReply->setProperty( "requestData", reply->property( "requestData" ) ); connect( newReply, SIGNAL( finished() ), SLOT( artistImagesReturned() ) ); } diff --git a/src/libtomahawk/infosystem/infoplugins/generic/lastfmplugin.h b/src/libtomahawk/infosystem/infoplugins/generic/lastfmplugin.h index 90f117882..fbd372e02 100644 --- a/src/libtomahawk/infosystem/infoplugins/generic/lastfmplugin.h +++ b/src/libtomahawk/infosystem/infoplugins/generic/lastfmplugin.h @@ -56,25 +56,25 @@ public slots: void namChangedSlot( QNetworkAccessManager *nam ); protected slots: - virtual void getInfo( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData ); - virtual void notInCacheSlot( uint requestId, Tomahawk::InfoSystem::InfoStringHash criteria, Tomahawk::InfoSystem::InfoRequestData requestData ); + virtual void getInfo( Tomahawk::InfoSystem::InfoRequestData requestData ); + virtual void notInCacheSlot( Tomahawk::InfoSystem::InfoStringHash criteria, Tomahawk::InfoSystem::InfoRequestData requestData ); virtual void pushInfo( QString caller, Tomahawk::InfoSystem::InfoType type, QVariant data ); private: - void fetchCoverArt( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData ); - void fetchArtistImages( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData ); - void fetchSimilarArtists( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData ); - void fetchTopTracks( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData ); - void fetchChart( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData ); - void fetchChartCapabilities( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData ); + void fetchCoverArt( Tomahawk::InfoSystem::InfoRequestData requestData ); + void fetchArtistImages( Tomahawk::InfoSystem::InfoRequestData requestData ); + void fetchSimilarArtists( Tomahawk::InfoSystem::InfoRequestData requestData ); + void fetchTopTracks( Tomahawk::InfoSystem::InfoRequestData requestData ); + void fetchChart( Tomahawk::InfoSystem::InfoRequestData requestData ); + void fetchChartCapabilities( Tomahawk::InfoSystem::InfoRequestData requestData ); void createScrobbler(); void nowPlaying( const QVariant &input ); void scrobble(); void sendLoveSong( const InfoType type, QVariant input ); - void dataError( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData ); + void dataError( Tomahawk::InfoSystem::InfoRequestData requestData ); QList<lastfm::Track> parseTrackList( QNetworkReply * reply ); diff --git a/src/libtomahawk/infosystem/infoplugins/generic/musicbrainzPlugin.cpp b/src/libtomahawk/infosystem/infoplugins/generic/musicbrainzPlugin.cpp index 388eda463..8c0b3c2f4 100644 --- a/src/libtomahawk/infosystem/infoplugins/generic/musicbrainzPlugin.cpp +++ b/src/libtomahawk/infosystem/infoplugins/generic/musicbrainzPlugin.cpp @@ -50,31 +50,64 @@ MusicBrainzPlugin::namChangedSlot( QNetworkAccessManager *nam ) m_nam = QWeakPointer< QNetworkAccessManager >( nam ); } - void -MusicBrainzPlugin::getInfo( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData ) +MusicBrainzPlugin::getInfo( Tomahawk::InfoSystem::InfoRequestData requestData ) { if ( !requestData.input.canConvert< Tomahawk::InfoSystem::InfoStringHash >() ) { - emit info( requestId, requestData, QVariant() ); + emit info( requestData, QVariant() ); return; } InfoStringHash hash = requestData.input.value< Tomahawk::InfoSystem::InfoStringHash >(); if ( !hash.contains( "artist" ) ) { - emit info( requestId, requestData, QVariant() ); + emit info( requestData, QVariant() ); return; } + switch ( requestData.type ) + { + case InfoArtistReleases: + { + + Tomahawk::InfoSystem::InfoStringHash criteria; + criteria["artist"] = hash["artist"]; + + emit getCachedInfo( criteria, 2419200000, requestData ); + break; + } + + case InfoAlbumSongs: + { + + Tomahawk::InfoSystem::InfoStringHash criteria; + criteria["artist"] = hash["artist"]; + criteria["album"] = hash["album"]; + + emit getCachedInfo( criteria, 2419200000, requestData ); + + break; + } + + default: + { + Q_ASSERT( false ); + break; + } + } +} + +void +MusicBrainzPlugin::notInCacheSlot( InfoStringHash criteria, InfoRequestData requestData ) +{ switch ( requestData.type ) { case InfoArtistReleases: { QString requestString( "http://musicbrainz.org/ws/2/artist" ); QUrl url( requestString ); - url.addQueryItem( "query", hash["artist"] ); + url.addQueryItem( "query", criteria["artist"] ); QNetworkReply* reply = m_nam.data()->get( QNetworkRequest( url ) ); - reply->setProperty( "requestId", requestId ); reply->setProperty( "requestData", QVariant::fromValue< Tomahawk::InfoSystem::InfoRequestData >( requestData ) ); connect( reply, SIGNAL( finished() ), SLOT( artistSearchSlot() ) ); @@ -85,9 +118,8 @@ MusicBrainzPlugin::getInfo( uint requestId, Tomahawk::InfoSystem::InfoRequestDat { QString requestString( "http://musicbrainz.org/ws/2/artist" ); QUrl url( requestString ); - url.addQueryItem( "query", hash["artist"] ); + url.addQueryItem( "query", criteria["artist"] ); QNetworkReply* reply = m_nam.data()->get( QNetworkRequest( url ) ); - reply->setProperty( "requestId", requestId ); reply->setProperty( "requestData", QVariant::fromValue< Tomahawk::InfoSystem::InfoRequestData >( requestData ) ); connect( reply, SIGNAL( finished() ), SLOT( albumSearchSlot() ) ); @@ -104,24 +136,24 @@ MusicBrainzPlugin::getInfo( uint requestId, Tomahawk::InfoSystem::InfoRequestDat bool -MusicBrainzPlugin::isValidTrackData( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData ) +MusicBrainzPlugin::isValidTrackData( Tomahawk::InfoSystem::InfoRequestData requestData ) { if ( requestData.input.isNull() || !requestData.input.isValid() || !requestData.input.canConvert< QVariantMap >() ) { - emit info( requestId, requestData, QVariant() ); + emit info( requestData, QVariant() ); qDebug() << Q_FUNC_INFO << "Data null, invalid, or can't convert"; return false; } QVariantMap hash = requestData.input.value< QVariantMap >(); if ( hash[ "trackName" ].toString().isEmpty() ) { - emit info( requestId, requestData, QVariant() ); + emit info( requestData, QVariant() ); qDebug() << Q_FUNC_INFO << "Track name is empty"; return false; } if ( hash[ "artistName" ].toString().isEmpty() ) { - emit info( requestId, requestData, QVariant() ); + emit info( requestData, QVariant() ); qDebug() << Q_FUNC_INFO << "No artist name found"; return false; } @@ -141,7 +173,7 @@ MusicBrainzPlugin::artistSearchSlot() QDomNodeList domNodeList = doc.elementsByTagName( "artist" ); if ( domNodeList.isEmpty() ) { - emit info( oldReply->property( "requestId" ).toUInt(), oldReply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >(), QVariant() ); + emit info( oldReply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >(), QVariant() ); return; } @@ -151,7 +183,6 @@ MusicBrainzPlugin::artistSearchSlot() url.addQueryItem( "artist", artist_id ); QNetworkReply* newReply = m_nam.data()->get( QNetworkRequest( url ) ); - newReply->setProperty( "requestId", oldReply->property( "requestId" ) ); newReply->setProperty( "requestData", oldReply->property( "requestData" ) ); connect( newReply, SIGNAL( finished() ), SLOT( albumFoundSlot() ) ); } @@ -169,7 +200,7 @@ MusicBrainzPlugin::albumSearchSlot() QDomNodeList domNodeList = doc.elementsByTagName( "artist" ); if ( domNodeList.isEmpty() ) { - emit info( oldReply->property( "requestId" ).toUInt(), oldReply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >(), QVariant() ); + emit info( oldReply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >(), QVariant() ); return; } @@ -179,7 +210,6 @@ MusicBrainzPlugin::albumSearchSlot() url.addQueryItem( "artist", artist_id ); QNetworkReply* newReply = m_nam.data()->get( QNetworkRequest( url ) ); - newReply->setProperty( "requestId", oldReply->property( "requestId" ) ); newReply->setProperty( "requestData", oldReply->property( "requestData" ) ); connect( newReply, SIGNAL( finished() ), SLOT( tracksSearchSlot() ) ); } @@ -197,7 +227,7 @@ MusicBrainzPlugin::tracksSearchSlot() QDomNodeList domNodeList = doc.elementsByTagName( "release" ); if ( domNodeList.isEmpty() ) { - emit info( oldReply->property( "requestId" ).toUInt(), oldReply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >(), QVariant() ); + emit info( oldReply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >(), QVariant() ); return; } @@ -214,7 +244,7 @@ MusicBrainzPlugin::tracksSearchSlot() if ( element.isNull() ) { - emit info( oldReply->property( "requestId" ).toUInt(), oldReply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >(), QVariant() ); + emit info( oldReply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >(), QVariant() ); return; } @@ -223,7 +253,6 @@ MusicBrainzPlugin::tracksSearchSlot() QUrl url( requestString ); QNetworkReply* newReply = m_nam.data()->get( QNetworkRequest( url ) ); - newReply->setProperty( "requestId", oldReply->property( "requestId" ) ); newReply->setProperty( "requestData", oldReply->property( "requestData" ) ); connect( newReply, SIGNAL( finished() ), SLOT( tracksFoundSlot() ) ); } @@ -241,7 +270,7 @@ MusicBrainzPlugin::albumFoundSlot() QDomNodeList domNodeList = doc.elementsByTagName( "title" ); if ( domNodeList.isEmpty() ) { - emit info( reply->property( "requestId" ).toUInt(), reply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >(), QVariant() ); + emit info( reply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >(), QVariant() ); return; } @@ -256,7 +285,7 @@ MusicBrainzPlugin::albumFoundSlot() Tomahawk::InfoSystem::InfoRequestData requestData = reply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >(); QVariantMap returnedData; returnedData["albums"] = albums; - emit info( reply->property( "requestId" ).toUInt(), requestData, returnedData ); + emit info( requestData, returnedData ); Tomahawk::InfoSystem::InfoStringHash origData = requestData.input.value< Tomahawk::InfoSystem::InfoStringHash>(); Tomahawk::InfoSystem::InfoStringHash criteria; @@ -277,7 +306,7 @@ MusicBrainzPlugin::tracksFoundSlot() QDomNodeList domNodeList = doc.elementsByTagName( "recording" ); if ( domNodeList.isEmpty() ) { - emit info( reply->property( "requestId" ).toUInt(), reply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >(), QVariant() ); + emit info( reply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >(), QVariant() ); return; } @@ -297,7 +326,7 @@ MusicBrainzPlugin::tracksFoundSlot() Tomahawk::InfoSystem::InfoRequestData requestData = reply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >(); QVariantMap returnedData; returnedData["tracks"] = tracks; - emit info( reply->property( "requestId" ).toUInt(), requestData, returnedData ); + emit info( requestData, returnedData ); Tomahawk::InfoSystem::InfoStringHash origData = requestData.input.value< Tomahawk::InfoSystem::InfoStringHash>(); Tomahawk::InfoSystem::InfoStringHash criteria; diff --git a/src/libtomahawk/infosystem/infoplugins/generic/musicbrainzPlugin.h b/src/libtomahawk/infosystem/infoplugins/generic/musicbrainzPlugin.h index 8e943bae3..444b30809 100644 --- a/src/libtomahawk/infosystem/infoplugins/generic/musicbrainzPlugin.h +++ b/src/libtomahawk/infosystem/infoplugins/generic/musicbrainzPlugin.h @@ -42,7 +42,8 @@ public slots: void namChangedSlot( QNetworkAccessManager *nam ); protected slots: - virtual void getInfo( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData ); + virtual void getInfo( Tomahawk::InfoSystem::InfoRequestData requestData ); + virtual void notInCacheSlot( InfoStringHash criteria, InfoRequestData requestData ); virtual void pushInfo( QString caller, Tomahawk::InfoSystem::InfoType type, QVariant data ) { @@ -51,12 +52,6 @@ protected slots: Q_UNUSED( data ); } -virtual void notInCacheSlot( uint requestId, Tomahawk::InfoSystem::InfoStringHash criteria, Tomahawk::InfoSystem::InfoRequestData requestData ) - { - Q_UNUSED( requestId ); - Q_UNUSED( criteria ); - Q_UNUSED( requestData ); - } private slots: void artistSearchSlot(); @@ -67,7 +62,7 @@ private slots: void tracksFoundSlot(); private: - bool isValidTrackData( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData ); + bool isValidTrackData( Tomahawk::InfoSystem::InfoRequestData requestData ); QWeakPointer< QNetworkAccessManager > m_nam; }; diff --git a/src/libtomahawk/infosystem/infoplugins/generic/musixmatchplugin.cpp b/src/libtomahawk/infosystem/infoplugins/generic/musixmatchplugin.cpp index ca2b96858..05811765a 100644 --- a/src/libtomahawk/infosystem/infoplugins/generic/musixmatchplugin.cpp +++ b/src/libtomahawk/infosystem/infoplugins/generic/musixmatchplugin.cpp @@ -53,17 +53,17 @@ MusixMatchPlugin::namChangedSlot( QNetworkAccessManager *nam ) } void -MusixMatchPlugin::getInfo( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData ) +MusixMatchPlugin::getInfo( Tomahawk::InfoSystem::InfoRequestData requestData ) { qDebug() << Q_FUNC_INFO; - if( !isValidTrackData( requestId, requestData ) || !requestData.input.canConvert< QVariantMap >() || m_nam.isNull() || requestData.type != Tomahawk::InfoSystem::InfoTrackLyrics ) + if( !isValidTrackData( requestData ) || !requestData.input.canConvert< QVariantMap >() || m_nam.isNull() || requestData.type != Tomahawk::InfoSystem::InfoTrackLyrics ) return; QVariantMap hash = requestData.input.value< QVariantMap >(); QString artist = hash["artistName"].toString(); QString track = hash["trackName"].toString(); if( artist.isEmpty() || track.isEmpty() ) { - emit info( requestId, requestData, QVariant() ); + emit info( requestData, QVariant() ); return; } qDebug() << "artist is " << artist << ", track is " << track; @@ -73,32 +73,31 @@ MusixMatchPlugin::getInfo( uint requestId, Tomahawk::InfoSystem::InfoRequestData url.addQueryItem( "q_artist", artist ); url.addQueryItem( "q_track", track ); QNetworkReply* reply = m_nam.data()->get( QNetworkRequest( url ) ); - reply->setProperty( "requestId", requestId ); reply->setProperty( "requestData", QVariant::fromValue< Tomahawk::InfoSystem::InfoRequestData >( requestData ) ); connect( reply, SIGNAL( finished() ), SLOT( trackSearchSlot() ) ); } bool -MusixMatchPlugin::isValidTrackData( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData ) +MusixMatchPlugin::isValidTrackData( Tomahawk::InfoSystem::InfoRequestData requestData ) { qDebug() << Q_FUNC_INFO; if ( requestData.input.isNull() || !requestData.input.isValid() || !requestData.input.canConvert< QVariantMap >() ) { - emit info( requestId, requestData, QVariant() ); + emit info( requestData, QVariant() ); qDebug() << "MusixMatchPlugin::isValidTrackData: Data null, invalid, or can't convert"; return false; } QVariantMap hash = requestData.input.value< QVariantMap >(); if ( hash[ "trackName" ].toString().isEmpty() ) { - emit info( requestId, requestData, QVariant() ); + emit info( requestData, QVariant() ); qDebug() << "MusixMatchPlugin::isValidTrackData: Track name is empty"; return false; } if ( hash[ "artistName" ].toString().isEmpty() ) { - emit info( requestId, requestData, QVariant() ); + emit info( requestData, QVariant() ); qDebug() << "MusixMatchPlugin::isValidTrackData: No artist name found"; return false; } @@ -119,7 +118,7 @@ MusixMatchPlugin::trackSearchSlot() QDomNodeList domNodeList = doc.elementsByTagName("track_id"); if ( domNodeList.isEmpty() ) { - emit info( oldReply->property( "requestId" ).toUInt(), oldReply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >(), QVariant() ); + emit info( oldReply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >(), QVariant() ); return; } QString track_id = domNodeList.at(0).toElement().text(); @@ -128,7 +127,6 @@ MusixMatchPlugin::trackSearchSlot() url.addQueryItem( "apikey", m_apiKey ); url.addQueryItem( "track_id", track_id ); QNetworkReply* newReply = m_nam.data()->get( QNetworkRequest( url ) ); - newReply->setProperty( "requestId", oldReply->property( "requestId" ) ); newReply->setProperty( "requestData", oldReply->property( "requestData" ) ); connect( newReply, SIGNAL( finished() ), SLOT( trackLyricsSlot() ) ); } @@ -146,10 +144,10 @@ MusixMatchPlugin::trackLyricsSlot() QDomNodeList domNodeList = doc.elementsByTagName( "lyrics_body" ); if ( domNodeList.isEmpty() ) { - emit info( reply->property( "requestId" ).toUInt(), reply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >(), QVariant() ); + emit info( reply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >(), QVariant() ); return; } QString lyrics = domNodeList.at(0).toElement().text(); qDebug() << "Emitting lyrics: " << lyrics; - emit info( reply->property( "requestId" ).toUInt(), reply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >(), QVariant( lyrics ) ); + emit info( reply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >(), QVariant( lyrics ) ); } diff --git a/src/libtomahawk/infosystem/infoplugins/generic/musixmatchplugin.h b/src/libtomahawk/infosystem/infoplugins/generic/musixmatchplugin.h index a6ef3fbe3..40b1cc536 100644 --- a/src/libtomahawk/infosystem/infoplugins/generic/musixmatchplugin.h +++ b/src/libtomahawk/infosystem/infoplugins/generic/musixmatchplugin.h @@ -45,7 +45,7 @@ public slots: void namChangedSlot( QNetworkAccessManager *nam ); protected slots: - virtual void getInfo( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData ); + virtual void getInfo( Tomahawk::InfoSystem::InfoRequestData requestData ); virtual void pushInfo( QString caller, Tomahawk::InfoSystem::InfoType type, QVariant data ) { @@ -54,15 +54,14 @@ protected slots: Q_UNUSED( data ); } -virtual void notInCacheSlot( uint requestId, Tomahawk::InfoSystem::InfoStringHash criteria, Tomahawk::InfoSystem::InfoRequestData requestData ) +virtual void notInCacheSlot( Tomahawk::InfoSystem::InfoStringHash criteria, Tomahawk::InfoSystem::InfoRequestData requestData ) { - Q_UNUSED( requestId ); Q_UNUSED( criteria ); Q_UNUSED( requestData ); } private: - bool isValidTrackData( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData ); + bool isValidTrackData( Tomahawk::InfoSystem::InfoRequestData requestData ); QString m_apiKey; diff --git a/src/libtomahawk/infosystem/infoplugins/generic/spotifyPlugin.cpp b/src/libtomahawk/infosystem/infoplugins/generic/spotifyPlugin.cpp new file mode 100644 index 000000000..245ceb593 --- /dev/null +++ b/src/libtomahawk/infosystem/infoplugins/generic/spotifyPlugin.cpp @@ -0,0 +1,410 @@ +/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> === + * + * Copyright 2010-2011, Hugo Lindström <hugolm84@gmail.com> + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include "spotifyPlugin.h" + +#include <QDir> +#include <QSettings> +#include <QCryptographicHash> +#include <QNetworkConfiguration> +#include <QNetworkReply> +#include <QDomElement> + +#include "album.h" +#include "typedefs.h" +#include "audio/audioengine.h" +#include "tomahawksettings.h" +#include "utils/tomahawkutils.h" +#include "utils/logger.h" +#include "chartsplugin_data_p.h" + +#define SPOTIFY_API_URL "http://spotikea.tomahawk-player.org:10380/" +#include <qjson/parser.h> +#include <qjson/serializer.h> + +using namespace Tomahawk::InfoSystem; + + +SpotifyPlugin::SpotifyPlugin() + : InfoPlugin() + , m_chartsFetchJobs( 0 ) +{ + + m_supportedGetTypes << InfoChart << InfoChartCapabilities; + +} + + +SpotifyPlugin::~SpotifyPlugin() +{ + qDebug() << Q_FUNC_INFO; +} + + +void +SpotifyPlugin::namChangedSlot( QNetworkAccessManager *nam ) +{ + tDebug() << "SpotifyPlugin: namChangedSLot"; + qDebug() << Q_FUNC_INFO; + if( !nam ) + return; + + m_nam = QWeakPointer< QNetworkAccessManager >( nam ); + + // we never need to re-fetch + if ( !m_allChartsMap.isEmpty() ) + return; + + /// We need to fetch possible types before they are asked for + tDebug() << "SpotifyPlugin: InfoChart fetching possible resources"; + + QUrl url = QUrl( QString( SPOTIFY_API_URL "toplist/charts" ) ); + QNetworkReply* reply = m_nam.data()->get( QNetworkRequest( url ) ); + tDebug() << Q_FUNC_INFO << "fetching:" << url; + connect( reply, SIGNAL( finished() ), SLOT( chartTypes() ) ); + m_chartsFetchJobs++; + +} + + +void +SpotifyPlugin::dataError( Tomahawk::InfoSystem::InfoRequestData requestData ) +{ + emit info( requestData, QVariant() ); + return; +} + + +void +SpotifyPlugin::getInfo( Tomahawk::InfoSystem::InfoRequestData requestData ) +{ + qDebug() << Q_FUNC_INFO << requestData.caller; + qDebug() << Q_FUNC_INFO << requestData.customData; + + InfoStringHash hash = requestData.input.value< Tomahawk::InfoSystem::InfoStringHash >(); + + + switch ( requestData.type ) + { + case InfoChart: + if ( !hash.contains( "chart_source" ) || hash["chart_source"] != "spotify" ) + { + dataError( requestData ); + break; + } + qDebug() << Q_FUNC_INFO << "InfoCHart req for" << hash["chart_source"]; + fetchChart( requestData ); + break; + + case InfoChartCapabilities: + fetchChartCapabilities( requestData ); + break; + default: + dataError( requestData ); + } +} + + +void +SpotifyPlugin::pushInfo( const QString caller, const Tomahawk::InfoSystem::InfoType type, const QVariant input ) +{ + Q_UNUSED( caller ) + Q_UNUSED( type) + Q_UNUSED( input ) +} + +void +SpotifyPlugin::fetchChart( Tomahawk::InfoSystem::InfoRequestData requestData ) +{ + if ( !requestData.input.canConvert< Tomahawk::InfoSystem::InfoStringHash >() ) + { + dataError( requestData ); + return; + } + InfoStringHash hash = requestData.input.value< Tomahawk::InfoSystem::InfoStringHash >(); + Tomahawk::InfoSystem::InfoStringHash criteria; + if ( !hash.contains( "chart_id" ) ) + { + dataError( requestData ); + return; + } else { + criteria["chart_id"] = hash["chart_id"]; + } + + emit getCachedInfo( criteria, 604800000 /* Expire chart cache in 1 week */, requestData ); +} +void +SpotifyPlugin::fetchChartCapabilities( Tomahawk::InfoSystem::InfoRequestData requestData ) +{ + if ( !requestData.input.canConvert< Tomahawk::InfoSystem::InfoStringHash >() ) + { + dataError( requestData ); + return; + } + + Tomahawk::InfoSystem::InfoStringHash criteria; + emit getCachedInfo( criteria, 0, requestData ); +} + +void +SpotifyPlugin::notInCacheSlot( Tomahawk::InfoSystem::InfoStringHash criteria, Tomahawk::InfoSystem::InfoRequestData requestData ) +{ + if ( !m_nam.data() ) + { + tLog() << Q_FUNC_INFO << "Have a null QNAM, uh oh"; + emit info( requestData, QVariant() ); + return; + } + + + switch ( requestData.type ) + { + + case InfoChart: + { + /// Fetch the chart, we need source and id + QUrl url = QUrl( QString( SPOTIFY_API_URL "toplist/%1/" ).arg( criteria["chart_id"] ) ); + qDebug() << Q_FUNC_INFO << "Getting chart url" << url; + + QNetworkReply* reply = m_nam.data()->get( QNetworkRequest( url ) ); + reply->setProperty( "requestData", QVariant::fromValue< Tomahawk::InfoSystem::InfoRequestData >( requestData ) ); + connect( reply, SIGNAL( finished() ), SLOT( chartReturned() ) ); + return; + + + } + case InfoChartCapabilities: + { + if ( m_chartsFetchJobs > 0 ) + { + qDebug() << Q_FUNC_INFO << "InfoChartCapabilities still fetching!"; + m_cachedRequests.append( requestData ); + return; + } + + emit info( requestData, m_allChartsMap ); + return; + } + + default: + { + tLog() << Q_FUNC_INFO << "Couldn't figure out what to do with this type of request after cache miss"; + emit info( requestData, QVariant() ); + return; + } + } +} + + +void +SpotifyPlugin::chartTypes() +{ + /// Get possible chart type for specificSpotifyPlugin: InfoChart types returned chart source + tDebug() << Q_FUNC_INFO << "Got spotifychart type result"; + QNetworkReply* reply = qobject_cast<QNetworkReply*>( sender() ); + + if ( reply->error() == QNetworkReply::NoError ) + { + QJson::Parser p; + bool ok; + const QVariantMap res = p.parse( reply, &ok ).toMap(); + const QVariantMap chartObj = res; + + if ( !ok ) + { + tLog() << Q_FUNC_INFO << "Failed to parse resources" << p.errorString() << "On line" << p.errorLine(); + + return; + } + + QVariantMap charts; + foreach(QVariant geos, chartObj.value("Charts").toList().takeLast().toMap().value("geo").toList() ) + { + + const QString geo = geos.toMap().value( "name" ).toString(); + const QString geoId = geos.toMap().value( "id" ).toString(); + QString country; + + if( geo == "For me" ) + continue; /// country = geo; Lets use this later, when we can get the spotify username from tomahawk + else if( geo == "Everywhere" ) + country = geo; + else + { + + QLocale l( QString( "en_%1" ).arg( geo ) ); + country = Tomahawk::CountryUtils::fullCountryFromCode( geo ); + + for ( int i = 1; i < country.size(); i++ ) + { + if ( country.at( i ).isUpper() ) + { + country.insert( i, " " ); + i++; + } + } + } + + QList< InfoStringHash > chart_types; + foreach(QVariant types, chartObj.value("Charts").toList().takeFirst().toMap().value("types").toList() ) + { + QString type = types.toMap().value( "id" ).toString(); + QString label = types.toMap().value( "name" ).toString(); + + InfoStringHash c; + c[ "id" ] = type + "/" + geoId; + c[ "label" ] = label; + c[ "type" ] = type; + + chart_types.append( c ); + + } + + charts.insert( country.toUtf8(), QVariant::fromValue<QList< InfoStringHash > >( chart_types ) ); + + } + + QVariantMap defaultMap; + defaultMap[ "spotify" ] = QStringList() << "United States" << "Top Albums"; + m_allChartsMap[ "defaults" ] = defaultMap; + m_allChartsMap.insert( "Spotify", QVariant::fromValue<QVariantMap>( charts ) ); + + } + else + { + tLog() << Q_FUNC_INFO << "Error fetching charts:" << reply->errorString(); + } + + m_chartsFetchJobs--; + if ( !m_cachedRequests.isEmpty() && m_chartsFetchJobs == 0 ) + { + foreach ( InfoRequestData request, m_cachedRequests ) + { + emit info( request, m_allChartsMap ); + } + m_cachedRequests.clear(); + } + +} + +void +SpotifyPlugin::chartReturned() +{ + + /// Chart request returned something! Woho + QNetworkReply* reply = qobject_cast<QNetworkReply*>( sender() ); + QString url = reply->url().toString(); + QVariantMap returnedData; + if ( reply->error() == QNetworkReply::NoError ) + { + QJson::Parser p; + bool ok; + QVariantMap res = p.parse( reply, &ok ).toMap(); + + if ( !ok ) + { + tLog() << "Failed to parse json from chart lookup:" << p.errorString() << "On line" << p.errorLine(); + return; + } + + /// SO we have a result, parse it! + QList< InfoStringHash > top_tracks; + QList< InfoStringHash > top_albums; + QStringList top_artists; + + if( url.contains( "albums" ) ) + setChartType( Album ); + else if( url.contains( "tracks" ) ) + setChartType( Track ); + else if( url.contains( "artists" ) ) + setChartType( Artist ); + else + setChartType( None ); + + foreach(QVariant result, res.value("toplist").toMap().value("result").toList() ) + { + QString title, artist; + QVariantMap chartMap = result.toMap(); + + if ( !chartMap.isEmpty() ) + { + + title = chartMap.value( "title" ).toString(); + artist = chartMap.value( "artist" ).toString(); + + if( chartType() == Track ) + { + InfoStringHash pair; + pair["artist"] = artist; + pair["track"] = title; + top_tracks << pair; + + qDebug() << "SpotifyChart type is track"; + } + + if( chartType() == Album ) + { + + InfoStringHash pair; + pair["artist"] = artist; + pair["album"] = title; + top_albums << pair; + qDebug() << "SpotifyChart type is album"; + } + + if( chartType() == Artist ) + { + + top_artists << chartMap.value( "name" ).toString(); + qDebug() << "SpotifyChart type is artist"; + + } + } + } + + if( chartType() == Track ) + { + tDebug() << "ChartsPlugin:" << "\tgot " << top_tracks.size() << " tracks"; + returnedData["tracks"] = QVariant::fromValue( top_tracks ); + returnedData["type"] = "tracks"; + } + + if( chartType() == Album ) + { + tDebug() << "ChartsPlugin:" << "\tgot " << top_albums.size() << " albums"; + returnedData["albums"] = QVariant::fromValue( top_albums ); + returnedData["type"] = "albums"; + } + + if( chartType() == Artist ) + { + tDebug() << "ChartsPlugin:" << "\tgot " << top_artists.size() << " artists"; + returnedData["artists"] = top_artists; + returnedData["type"] = "artists"; + } + + Tomahawk::InfoSystem::InfoRequestData requestData = reply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >(); + + + emit info( requestData, returnedData ); + + } + else + qDebug() << "Network error in fetching chart:" << reply->url().toString(); + +} diff --git a/src/libtomahawk/infosystem/infoplugins/generic/spotifyPlugin.h b/src/libtomahawk/infosystem/infoplugins/generic/spotifyPlugin.h new file mode 100644 index 000000000..4a27d826c --- /dev/null +++ b/src/libtomahawk/infosystem/infoplugins/generic/spotifyPlugin.h @@ -0,0 +1,80 @@ +/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> === + * + * Copyright 2010-2011, Hugo Lindström <hugolm84@gmail.com> + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#ifndef SpotifyPlugin_H +#define SpotifyPlugin_H + +#include "infosystem/infosystem.h" +#include "infosystem/infosystemworker.h" +#include <QNetworkReply> +#include <QObject> + +class QNetworkReply; + +namespace Tomahawk +{ + +namespace InfoSystem +{ + +class SpotifyPlugin : public InfoPlugin +{ + Q_OBJECT + +public: + SpotifyPlugin(); + virtual ~SpotifyPlugin(); + + enum ChartType { + None = 0x00, + Track = 0x01, + Album = 0x02, + Artist = 0x04 + + }; + void setChartType( ChartType type ) { m_chartType = type; } + ChartType chartType() const { return m_chartType; } + +public slots: + void chartReturned(); + void chartTypes(); + void namChangedSlot( QNetworkAccessManager *nam ); + +protected slots: + virtual void getInfo( Tomahawk::InfoSystem::InfoRequestData requestData ); + virtual void notInCacheSlot( Tomahawk::InfoSystem::InfoStringHash criteria, Tomahawk::InfoSystem::InfoRequestData requestData ); + virtual void pushInfo( QString caller, Tomahawk::InfoSystem::InfoType type, QVariant data ); + +private: + void fetchChart( Tomahawk::InfoSystem::InfoRequestData requestData ); + void fetchChartCapabilities( Tomahawk::InfoSystem::InfoRequestData requestData ); + void dataError( Tomahawk::InfoSystem::InfoRequestData requestData ); + + + ChartType m_chartType; + QVariantMap m_allChartsMap; + uint m_chartsFetchJobs; + QList< InfoRequestData > m_cachedRequests; + QWeakPointer< QNetworkAccessManager > m_nam; +}; + +} + +} + +#endif // SpotifyPlugin_H diff --git a/src/libtomahawk/infosystem/infoplugins/mac/adiumplugin.h b/src/libtomahawk/infosystem/infoplugins/mac/adiumplugin.h index c602d80fc..5f12a510f 100644 --- a/src/libtomahawk/infosystem/infoplugins/mac/adiumplugin.h +++ b/src/libtomahawk/infosystem/infoplugins/mac/adiumplugin.h @@ -41,9 +41,8 @@ public: virtual ~AdiumPlugin(); protected slots: - virtual void getInfo( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData ) + virtual void getInfo( Tomahawk::InfoSystem::InfoRequestData requestData ) { - Q_UNUSED( requestId ); Q_UNUSED( requestData ); } @@ -52,9 +51,8 @@ protected slots: public slots: void namChangedSlot( QNetworkAccessManager* nam ); - virtual void notInCacheSlot( uint requestId, const Tomahawk::InfoSystem::InfoStringHash criteria, Tomahawk::InfoSystem::InfoRequestData requestData ) + virtual void notInCacheSlot( const Tomahawk::InfoSystem::InfoStringHash criteria, Tomahawk::InfoSystem::InfoRequestData requestData ) { - Q_UNUSED( requestId ); Q_UNUSED( criteria ); Q_UNUSED( requestData ); } diff --git a/src/libtomahawk/infosystem/infoplugins/unix/fdonotifyplugin.h b/src/libtomahawk/infosystem/infoplugins/unix/fdonotifyplugin.h index ed5dfabf8..d5fbc4759 100644 --- a/src/libtomahawk/infosystem/infoplugins/unix/fdonotifyplugin.h +++ b/src/libtomahawk/infosystem/infoplugins/unix/fdonotifyplugin.h @@ -38,17 +38,15 @@ public: virtual void namChangedSlot( QNetworkAccessManager* ) {} protected slots: - virtual void getInfo( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData ) + virtual void getInfo( Tomahawk::InfoSystem::InfoRequestData requestData ) { - Q_UNUSED( requestId ); Q_UNUSED( requestData ); } virtual void pushInfo( QString caller, Tomahawk::InfoSystem::InfoType type, QVariant pushData ); - virtual void notInCacheSlot( uint requestId, Tomahawk::InfoSystem::InfoStringHash criteria, Tomahawk::InfoSystem::InfoRequestData requestData ) + virtual void notInCacheSlot( Tomahawk::InfoSystem::InfoStringHash criteria, Tomahawk::InfoSystem::InfoRequestData requestData ) { - Q_UNUSED( requestId ); Q_UNUSED( criteria ); Q_UNUSED( requestData ); } diff --git a/src/libtomahawk/infosystem/infoplugins/unix/mprisplugin.cpp b/src/libtomahawk/infosystem/infoplugins/unix/mprisplugin.cpp index 3e1fa3466..7725b68f2 100644 --- a/src/libtomahawk/infosystem/infoplugins/unix/mprisplugin.cpp +++ b/src/libtomahawk/infosystem/infoplugins/unix/mprisplugin.cpp @@ -432,9 +432,8 @@ MprisPlugin::Stop() // InfoPlugin Methods void -MprisPlugin::getInfo( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData ) +MprisPlugin::getInfo( Tomahawk::InfoSystem::InfoRequestData requestData ) { - Q_UNUSED( requestId ); Q_UNUSED( requestData ); qDebug() << Q_FUNC_INFO; diff --git a/src/libtomahawk/infosystem/infoplugins/unix/mprisplugin.h b/src/libtomahawk/infosystem/infoplugins/unix/mprisplugin.h index c708e2a23..81d8d1507 100644 --- a/src/libtomahawk/infosystem/infoplugins/unix/mprisplugin.h +++ b/src/libtomahawk/infosystem/infoplugins/unix/mprisplugin.h @@ -119,9 +119,8 @@ public: public slots: void namChangedSlot( QNetworkAccessManager* /*nam*/ ) {} // unused - virtual void notInCacheSlot( uint requestId, const Tomahawk::InfoSystem::InfoStringHash criteria, Tomahawk::InfoSystem::InfoRequestData requestData ) + virtual void notInCacheSlot( const Tomahawk::InfoSystem::InfoStringHash criteria, Tomahawk::InfoSystem::InfoRequestData requestData ) { - Q_UNUSED( requestId ); Q_UNUSED( criteria ); Q_UNUSED( requestData ); } @@ -143,7 +142,7 @@ public slots: protected slots: - void getInfo( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData ); + void getInfo( Tomahawk::InfoSystem::InfoRequestData requestData ); void pushInfo( QString caller, Tomahawk::InfoSystem::InfoType type, QVariant input ); private slots: diff --git a/src/libtomahawk/infosystem/infosystem.cpp b/src/libtomahawk/infosystem/infosystem.cpp index 5fa2325bb..505aa2119 100644 --- a/src/libtomahawk/infosystem/infosystem.cpp +++ b/src/libtomahawk/infosystem/infosystem.cpp @@ -74,12 +74,16 @@ InfoSystem::InfoSystem( QObject *parent ) connect( TomahawkSettings::instance(), SIGNAL( changed() ), SLOT( newNam() ) ); - connect( m_cache.data(), SIGNAL( info( uint, Tomahawk::InfoSystem::InfoRequestData, QVariant ) ), - m_worker.data(), SLOT( infoSlot( uint, Tomahawk::InfoSystem::InfoRequestData, QVariant ) ), Qt::UniqueConnection ); + connect( m_cache.data(), SIGNAL( info( Tomahawk::InfoSystem::InfoRequestData, QVariant ) ), + m_worker.data(), SLOT( infoSlot( Tomahawk::InfoSystem::InfoRequestData, QVariant ) ), Qt::UniqueConnection ); connect( m_worker.data(), SIGNAL( info( Tomahawk::InfoSystem::InfoRequestData, QVariant ) ), this, SIGNAL( info( Tomahawk::InfoSystem::InfoRequestData, QVariant ) ), Qt::UniqueConnection ); + connect( m_worker.data(), SIGNAL( finished( QString ) ), this, SIGNAL( finished( QString ) ), Qt::UniqueConnection ); + + connect( m_worker.data(), SIGNAL( finished( QString, Tomahawk::InfoSystem::InfoType ) ), + this, SIGNAL( finished( QString, Tomahawk::InfoSystem::InfoType ) ), Qt::UniqueConnection ); } InfoSystem::~InfoSystem() @@ -120,10 +124,10 @@ InfoSystem::newNam() const void -InfoSystem::getInfo( const InfoRequestData &requestData, uint timeoutMillis, bool allSources ) +InfoSystem::getInfo( const InfoRequestData &requestData ) { qDebug() << Q_FUNC_INFO; - QMetaObject::invokeMethod( m_worker.data(), "getInfo", Qt::QueuedConnection, Q_ARG( Tomahawk::InfoSystem::InfoRequestData, requestData ), Q_ARG( uint, timeoutMillis ), Q_ARG( bool, allSources ) ); + QMetaObject::invokeMethod( m_worker.data(), "getInfo", Qt::QueuedConnection, Q_ARG( Tomahawk::InfoSystem::InfoRequestData, requestData ) ); } @@ -133,11 +137,13 @@ InfoSystem::getInfo( const QString &caller, const QVariantMap &customData, const InfoRequestData requestData; requestData.caller = caller; requestData.customData = customData; + requestData.allSources = allSources; Q_FOREACH( InfoType type, inputMap.keys() ) { requestData.type = type; requestData.input = inputMap[ type ]; - QMetaObject::invokeMethod( m_worker.data(), "getInfo", Qt::QueuedConnection, Q_ARG( Tomahawk::InfoSystem::InfoRequestData, requestData ), Q_ARG( uint, ( timeoutMap.contains( type ) ? timeoutMap[ type ] : 0 ) ), Q_ARG( bool, allSources ) ); + requestData.timeoutMillis = timeoutMap.contains( type ) ? timeoutMap[ type ] : 10000; + QMetaObject::invokeMethod( m_worker.data(), "getInfo", Qt::QueuedConnection, Q_ARG( Tomahawk::InfoSystem::InfoRequestData, requestData ) ); } } diff --git a/src/libtomahawk/infosystem/infosystem.h b/src/libtomahawk/infosystem/infosystem.h index ebef34a42..01be362e5 100644 --- a/src/libtomahawk/infosystem/infosystem.h +++ b/src/libtomahawk/infosystem/infosystem.h @@ -32,6 +32,7 @@ #include <QtCore/QStringList> #include "dllmacro.h" +#include "utils/tomahawkutils.h" class QNetworkAccessManager; @@ -43,28 +44,29 @@ class InfoSystemCache; class InfoSystemWorker; enum InfoType { // as items are saved in cache, mark them here to not change them - InfoTrackID = 0, - 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, + InfoNoInfo = 0, //WARNING: *ALWAYS* keep this first! + InfoTrackID = 1, + InfoTrackArtist = 2, + InfoTrackAlbum = 3, + InfoTrackGenre = 4, + InfoTrackComposer = 5, + InfoTrackDate = 6, + InfoTrackNumber = 7, + InfoTrackDiscNumber = 8, + InfoTrackBitRate = 9, + InfoTrackLength = 10, + InfoTrackSampleRate = 11, + InfoTrackFileSize = 12, + InfoTrackBPM = 13, + InfoTrackReplayGain = 14, + InfoTrackReplayPeakGain = 15, + InfoTrackLyrics = 16, + InfoTrackLocation = 17, + InfoTrackProfile = 18, + InfoTrackEnergy = 19, + InfoTrackDanceability = 20, + InfoTrackTempo = 21, + InfoTrackLoudness = 22, InfoArtistID = 25, InfoArtistName = 26, @@ -120,14 +122,40 @@ enum InfoType { // as items are saved in cache, mark them here to not change the InfoNotifyUser = 100, - InfoNoInfo = 101 //WARNING: *ALWAYS* keep this last! + InfoLastInfo = 101 //WARNING: *ALWAYS* keep this last! }; struct InfoRequestData { + quint64 requestId; + quint64 internalId; //do not assign to this; it may get overwritten by the InfoSystem QString caller; Tomahawk::InfoSystem::InfoType type; QVariant input; QVariantMap customData; + uint timeoutMillis; + bool allSources; + + InfoRequestData() + : requestId( TomahawkUtils::infosystemRequestId() ) + , internalId( TomahawkUtils::infosystemRequestId() ) + , caller( QString() ) + , type( Tomahawk::InfoSystem::InfoNoInfo ) + , input( QVariant() ) + , customData( QVariantMap() ) + , timeoutMillis( 10000 ) + , allSources( false ) + {} + + InfoRequestData( const quint64 rId, const QString &callr, const Tomahawk::InfoSystem::InfoType typ, const QVariant &inputvar, const QVariantMap &custom ) + : requestId( rId ) + , internalId( TomahawkUtils::infosystemRequestId() ) + , caller( callr ) + , type( typ ) + , input( inputvar ) + , customData( custom ) + , timeoutMillis( 10000 ) + , allSources( false ) + {} }; typedef QMap< InfoType, QVariant > InfoTypeMap; @@ -147,15 +175,15 @@ public: QSet< InfoType > supportedPushTypes() const { return m_supportedPushTypes; } signals: - void getCachedInfo( uint requestId, Tomahawk::InfoSystem::InfoStringHash criteria, qint64 newMaxAge, Tomahawk::InfoSystem::InfoRequestData requestData ); - void info( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData, QVariant output ); + void getCachedInfo( Tomahawk::InfoSystem::InfoStringHash criteria, qint64 newMaxAge, Tomahawk::InfoSystem::InfoRequestData requestData ); + void info( Tomahawk::InfoSystem::InfoRequestData requestData, QVariant output ); void updateCache( Tomahawk::InfoSystem::InfoStringHash criteria, qint64 maxAge, Tomahawk::InfoSystem::InfoType type, QVariant output ); protected slots: - virtual void getInfo( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData ) = 0; + virtual void getInfo( Tomahawk::InfoSystem::InfoRequestData requestData ) = 0; virtual void pushInfo( QString caller, Tomahawk::InfoSystem::InfoType type, QVariant data ) = 0; - virtual void notInCacheSlot( uint requestId, Tomahawk::InfoSystem::InfoStringHash criteria, Tomahawk::InfoSystem::InfoRequestData requestData ) = 0; + virtual void notInCacheSlot( Tomahawk::InfoSystem::InfoStringHash criteria, Tomahawk::InfoSystem::InfoRequestData requestData ) = 0; virtual void namChangedSlot( QNetworkAccessManager *nam ) = 0; @@ -212,7 +240,7 @@ public: InfoSystem( QObject *parent ); ~InfoSystem(); - void getInfo( const InfoRequestData &requestData, uint timeoutMillis = 0, bool allSources = false ); + void getInfo( const InfoRequestData &requestData ); //WARNING: if changing timeoutMillis above, also change in below function in .cpp file void getInfo( const QString &caller, const QVariantMap &customData, const InfoTypeMap &inputMap, const InfoTimeoutMap &timeoutMap = InfoTimeoutMap(), bool allSources = false ); void pushInfo( const QString &caller, const InfoType type, const QVariant &input ); @@ -221,6 +249,7 @@ public: signals: void info( Tomahawk::InfoSystem::InfoRequestData requestData, QVariant output ); void finished( QString target ); + void finished( QString target, Tomahawk::InfoSystem::InfoType type ); public slots: void newNam() const; diff --git a/src/libtomahawk/infosystem/infosystemcache.cpp b/src/libtomahawk/infosystem/infosystemcache.cpp index 7ded3a9a0..7ee11a4cf 100644 --- a/src/libtomahawk/infosystem/infosystemcache.cpp +++ b/src/libtomahawk/infosystem/infosystemcache.cpp @@ -75,7 +75,7 @@ InfoSystemCache::doUpgrade( uint oldVersion, uint newVersion ) { qDebug() << Q_FUNC_INFO << "Wiping cache"; - for ( int i = 0; i <= InfoNoInfo; i++ ) + for ( int i = InfoNoInfo; i <= InfoLastInfo; i++ ) { InfoType type = (InfoType)(i); const QString cacheDirName = m_cacheBaseDir + QString::number( (int)type ); @@ -96,7 +96,7 @@ InfoSystemCache::pruneTimerFired() qDebug() << Q_FUNC_INFO << "Pruning infosystemcache"; qlonglong currentMSecsSinceEpoch = QDateTime::currentMSecsSinceEpoch(); - for ( int i = 0; i <= InfoNoInfo; i++ ) + for ( int i = InfoNoInfo; i <= InfoLastInfo; i++ ) { InfoType type = (InfoType)(i); QHash< QString, QString > fileLocationHash = m_fileLocationCache[type]; @@ -121,7 +121,7 @@ InfoSystemCache::pruneTimerFired() void -InfoSystemCache::getCachedInfoSlot( uint requestId, Tomahawk::InfoSystem::InfoStringHash criteria, qint64 newMaxAge, Tomahawk::InfoSystem::InfoRequestData requestData ) +InfoSystemCache::getCachedInfoSlot( Tomahawk::InfoSystem::InfoStringHash criteria, qint64 newMaxAge, Tomahawk::InfoSystem::InfoRequestData requestData ) { QObject* sendingObj = sender(); const QString criteriaHashVal = criteriaMd5( criteria ); @@ -133,7 +133,7 @@ InfoSystemCache::getCachedInfoSlot( uint requestId, Tomahawk::InfoSystem::InfoSt { //We already know of some values, so no need to re-read the directory again as it's already happened qDebug() << Q_FUNC_INFO << "notInCache -- filelocationhash empty"; - notInCache( sendingObj, requestId, criteria, requestData ); + notInCache( sendingObj, criteria, requestData ); return; } @@ -143,7 +143,7 @@ InfoSystemCache::getCachedInfoSlot( uint requestId, Tomahawk::InfoSystem::InfoSt { //Dir doesn't exist so clearly not in cache qDebug() << Q_FUNC_INFO << "notInCache -- dir doesn't exist"; - notInCache( sendingObj, requestId, criteria, requestData ); + notInCache( sendingObj, criteria, requestData ); return; } @@ -160,7 +160,7 @@ InfoSystemCache::getCachedInfoSlot( uint requestId, Tomahawk::InfoSystem::InfoSt { //Still didn't find it? It's really not in the cache then qDebug() << Q_FUNC_INFO << "notInCache -- filelocationhash doesn't contain criteria val"; - notInCache( sendingObj, requestId, criteria, requestData ); + notInCache( sendingObj, criteria, requestData ); return; } } @@ -180,7 +180,7 @@ InfoSystemCache::getCachedInfoSlot( uint requestId, Tomahawk::InfoSystem::InfoSt m_dataCache.remove( criteriaHashValWithType ); qDebug() << Q_FUNC_INFO << "notInCache -- file was stale"; - notInCache( sendingObj, requestId, criteria, requestData ); + notInCache( sendingObj, criteria, requestData ); return; } else if ( newMaxAge > 0 ) @@ -190,7 +190,7 @@ InfoSystemCache::getCachedInfoSlot( uint requestId, Tomahawk::InfoSystem::InfoSt if ( !QFile::rename( file.canonicalFilePath(), newFilePath ) ) { qDebug() << Q_FUNC_INFO << "notInCache -- failed to move old cache file to new location"; - notInCache( sendingObj, requestId, criteria, requestData ); + notInCache( sendingObj, criteria, requestData ); return; } @@ -204,25 +204,26 @@ InfoSystemCache::getCachedInfoSlot( uint requestId, Tomahawk::InfoSystem::InfoSt QVariant output = cachedSettings.value( "data" ); m_dataCache.insert( criteriaHashValWithType, new QVariant( output ) ); - emit info( requestId, requestData, output ); + emit info( requestData, output ); } else { - emit info( requestId, requestData, QVariant( *( m_dataCache[ criteriaHashValWithType ] ) ) ); + emit info( requestData, QVariant( *( m_dataCache[ criteriaHashValWithType ] ) ) ); } } void -InfoSystemCache::notInCache( QObject *receiver, uint requestId, Tomahawk::InfoSystem::InfoStringHash criteria, Tomahawk::InfoSystem::InfoRequestData requestData ) +InfoSystemCache::notInCache( QObject *receiver, Tomahawk::InfoSystem::InfoStringHash criteria, Tomahawk::InfoSystem::InfoRequestData requestData ) { - QMetaObject::invokeMethod( receiver, "notInCacheSlot", Q_ARG( uint, requestId ), Q_ARG( Tomahawk::InfoSystem::InfoStringHash, criteria ), Q_ARG( Tomahawk::InfoSystem::InfoRequestData, requestData ) ); + QMetaObject::invokeMethod( receiver, "notInCacheSlot", Q_ARG( Tomahawk::InfoSystem::InfoStringHash, criteria ), Q_ARG( Tomahawk::InfoSystem::InfoRequestData, requestData ) ); } void InfoSystemCache::updateCacheSlot( Tomahawk::InfoSystem::InfoStringHash criteria, qint64 maxAge, Tomahawk::InfoSystem::InfoType type, QVariant output ) { + qDebug() << Q_FUNC_INFO; const QString criteriaHashVal = criteriaMd5( criteria ); const QString criteriaHashValWithType = criteriaMd5( criteria, type ); const QString cacheDir = m_cacheBaseDir + QString::number( (int)type ); @@ -283,7 +284,7 @@ InfoSystemCache::criteriaMd5( const Tomahawk::InfoSystem::InfoStringHash &criter md5.addData( key.toUtf8() ); md5.addData( criteria[key].toUtf8() ); } - if ( type != Tomahawk::InfoSystem::InfoNoInfo ) + if ( type != Tomahawk::InfoSystem::InfoNoInfo && type != Tomahawk::InfoSystem::InfoLastInfo ) md5.addData( QString::number( (int)type ).toUtf8() ); return md5.result().toHex(); } diff --git a/src/libtomahawk/infosystem/infosystemcache.h b/src/libtomahawk/infosystem/infosystemcache.h index 5a013d663..8910a6209 100644 --- a/src/libtomahawk/infosystem/infosystemcache.h +++ b/src/libtomahawk/infosystem/infosystemcache.h @@ -43,17 +43,17 @@ public: virtual ~InfoSystemCache(); signals: - void info( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData, QVariant output ); + void info( Tomahawk::InfoSystem::InfoRequestData requestData, QVariant output ); public slots: - void getCachedInfoSlot( uint requestId, Tomahawk::InfoSystem::InfoStringHash criteria, qint64 newMaxAge, Tomahawk::InfoSystem::InfoRequestData requestData ); + void getCachedInfoSlot( Tomahawk::InfoSystem::InfoStringHash criteria, qint64 newMaxAge, Tomahawk::InfoSystem::InfoRequestData requestData ); void updateCacheSlot( Tomahawk::InfoSystem::InfoStringHash criteria, qint64 maxAge, Tomahawk::InfoSystem::InfoType type, QVariant output ); private slots: void pruneTimerFired(); private: - void notInCache( QObject *receiver, uint requestId, Tomahawk::InfoSystem::InfoStringHash criteria, Tomahawk::InfoSystem::InfoRequestData requestData ); + void notInCache( QObject *receiver, Tomahawk::InfoSystem::InfoStringHash criteria, Tomahawk::InfoSystem::InfoRequestData requestData ); void doUpgrade( uint oldVersion, uint newVersion ); const QString criteriaMd5( const Tomahawk::InfoSystem::InfoStringHash &criteria, Tomahawk::InfoSystem::InfoType type = Tomahawk::InfoSystem::InfoNoInfo ) const; diff --git a/src/libtomahawk/infosystem/infosystemworker.cpp b/src/libtomahawk/infosystem/infosystemworker.cpp index 9ae11951c..b58394130 100644 --- a/src/libtomahawk/infosystem/infosystemworker.cpp +++ b/src/libtomahawk/infosystem/infosystemworker.cpp @@ -26,8 +26,10 @@ #include "infoplugins/generic/echonestplugin.h" #include "infoplugins/generic/musixmatchplugin.h" #include "infoplugins/generic/chartsplugin.h" +#include "infoplugins/generic/spotifyPlugin.h" #include "infoplugins/generic/lastfmplugin.h" #include "infoplugins/generic/musicbrainzPlugin.h" +#include "infoplugins/generic/hypemPlugin.h" #include "utils/tomahawkutils.h" #include "utils/logger.h" @@ -40,6 +42,7 @@ #endif #include "lastfm/NetworkAccessManager" +#include "infoplugins/generic/RoviPlugin.h" namespace Tomahawk { @@ -49,7 +52,6 @@ namespace InfoSystem InfoSystemWorker::InfoSystemWorker() : QObject() - , m_nextRequest( 0 ) { // qDebug() << Q_FUNC_INFO; @@ -92,6 +94,15 @@ InfoSystemWorker::init( QWeakPointer< Tomahawk::InfoSystem::InfoSystemCache> cac InfoPluginPtr sptr( new ChartsPlugin() ); m_plugins.append( sptr ); registerInfoTypes( sptr, sptr.data()->supportedGetTypes(), sptr.data()->supportedPushTypes() ); + InfoPluginPtr roviptr( new RoviPlugin() ); + m_plugins.append( roviptr ); + registerInfoTypes( roviptr, roviptr.data()->supportedGetTypes(), roviptr.data()->supportedPushTypes() ); + InfoPluginPtr spotptr( new SpotifyPlugin() ); + m_plugins.append( spotptr ); + registerInfoTypes( spotptr, spotptr.data()->supportedGetTypes(), spotptr.data()->supportedPushTypes() ); + InfoPluginPtr hypeptr( new hypemPlugin() ); + m_plugins.append( hypeptr ); + registerInfoTypes( hypeptr, hypeptr.data()->supportedGetTypes(), hypeptr.data()->supportedPushTypes() ); #ifdef Q_WS_MAC @@ -105,24 +116,24 @@ InfoSystemWorker::init( QWeakPointer< Tomahawk::InfoSystem::InfoSystemCache> cac registerInfoTypes( fdonotifyptr, fdonotifyptr.data()->supportedGetTypes(), fdonotifyptr.data()->supportedPushTypes() ); InfoPluginPtr mprisptr( new MprisPlugin() ); m_plugins.append( mprisptr ); - registerInfoTypes( mprisptr, mprisptr.data()->supportedGetTypes(), mprisptr.data()->supportedPushTypes() ); + registerInfoTypes( mprisptr, mprisptr.data()->supportedGetTypes(), mprisptr.data()->supportedPushTypes() ); #endif Q_FOREACH( InfoPluginPtr plugin, m_plugins ) { connect( plugin.data(), - SIGNAL( info( uint, Tomahawk::InfoSystem::InfoRequestData, QVariant ) ), + SIGNAL( info( Tomahawk::InfoSystem::InfoRequestData, QVariant ) ), this, - SLOT( infoSlot( uint, Tomahawk::InfoSystem::InfoRequestData, QVariant ) ), + SLOT( infoSlot( Tomahawk::InfoSystem::InfoRequestData, QVariant ) ), Qt::UniqueConnection ); connect( plugin.data(), - SIGNAL( getCachedInfo( uint, Tomahawk::InfoSystem::InfoStringHash, qint64, Tomahawk::InfoSystem::InfoRequestData ) ), + SIGNAL( getCachedInfo( Tomahawk::InfoSystem::InfoStringHash, qint64, Tomahawk::InfoSystem::InfoRequestData ) ), cache.data(), - SLOT( getCachedInfoSlot( uint, Tomahawk::InfoSystem::InfoStringHash, qint64, Tomahawk::InfoSystem::InfoRequestData ) ) + SLOT( getCachedInfoSlot( Tomahawk::InfoSystem::InfoStringHash, qint64, Tomahawk::InfoSystem::InfoRequestData ) ) ); connect( plugin.data(), @@ -165,19 +176,19 @@ InfoSystemWorker::determineOrderedMatches( const InfoType type ) const void -InfoSystemWorker::getInfo( Tomahawk::InfoSystem::InfoRequestData requestData, uint timeoutMillis, bool allSources ) +InfoSystemWorker::getInfo( Tomahawk::InfoSystem::InfoRequestData requestData ) { -// qDebug() << Q_FUNC_INFO; + //qDebug() << Q_FUNC_INFO << "type is " << requestData.type << " and allSources = " << (allSources ? "true" : "false" ); QList< InfoPluginPtr > providers = determineOrderedMatches( requestData.type ); if ( providers.isEmpty() ) { emit info( requestData, QVariant() ); - checkFinished( requestData.caller ); + checkFinished( requestData ); return; } - if ( !allSources ) + if ( !requestData.allSources ) providers = QList< InfoPluginPtr >( providers.mid( 0, 1 ) ); bool foundOne = false; @@ -187,12 +198,22 @@ InfoSystemWorker::getInfo( Tomahawk::InfoSystem::InfoRequestData requestData, ui continue; foundOne = true; - uint requestId = ++m_nextRequest; + + if ( requestData.allSources || m_savedRequestMap.contains( requestData.requestId ) ) + { + if ( m_savedRequestMap.contains( requestData.requestId ) ) + tDebug() << Q_FUNC_INFO << "Warning: reassigning requestId because it already exists"; + requestData.internalId = TomahawkUtils::infosystemRequestId(); + } + else + requestData.internalId = requestData.requestId; + + quint64 requestId = requestData.internalId; m_requestSatisfiedMap[ requestId ] = false; - if ( timeoutMillis != 0 ) + if ( requestData.timeoutMillis != 0 ) { qint64 currMs = QDateTime::currentMSecsSinceEpoch(); - m_timeRequestMapper.insert( currMs + timeoutMillis, requestId ); + m_timeRequestMapper.insert( currMs + requestData.timeoutMillis, requestId ); } // qDebug() << "Assigning request with requestId" << requestId << "and type" << requestData.type; m_dataTracker[ requestData.caller ][ requestData.type ] = m_dataTracker[ requestData.caller ][ requestData.type ] + 1; @@ -205,19 +226,19 @@ InfoSystemWorker::getInfo( Tomahawk::InfoSystem::InfoRequestData requestData, ui data->customData = requestData.customData; m_savedRequestMap[ requestId ] = data; - QMetaObject::invokeMethod( ptr.data(), "getInfo", Qt::QueuedConnection, Q_ARG( uint, requestId ), Q_ARG( Tomahawk::InfoSystem::InfoRequestData, requestData ) ); + QMetaObject::invokeMethod( ptr.data(), "getInfo", Qt::QueuedConnection, Q_ARG( Tomahawk::InfoSystem::InfoRequestData, requestData ) ); } if ( !foundOne ) { emit info( requestData, QVariant() ); - checkFinished( requestData.caller ); + checkFinished( requestData ); } } void -InfoSystemWorker::pushInfo( QString caller, InfoType type, QVariant input ) +InfoSystemWorker::pushInfo( QString caller, Tomahawk::InfoSystem::InfoType type, QVariant input ) { // qDebug() << Q_FUNC_INFO; @@ -230,10 +251,12 @@ InfoSystemWorker::pushInfo( QString caller, InfoType type, QVariant input ) void -InfoSystemWorker::infoSlot( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData, QVariant output ) +InfoSystemWorker::infoSlot( Tomahawk::InfoSystem::InfoRequestData requestData, QVariant output ) { // qDebug() << Q_FUNC_INFO << "with requestId" << requestId; + quint64 requestId = requestData.internalId; + if ( m_dataTracker[ requestData.caller ][ requestData.type ] == 0 ) { // qDebug() << Q_FUNC_INFO << "Caller was not waiting for that type of data!"; @@ -252,23 +275,23 @@ InfoSystemWorker::infoSlot( uint requestId, Tomahawk::InfoSystem::InfoRequestDat // qDebug() << "Current count in dataTracker for target" << requestData.caller << "and type" << requestData.type << "is" << m_dataTracker[ requestData.caller ][ requestData.type ]; delete m_savedRequestMap[ requestId ]; m_savedRequestMap.remove( requestId ); - checkFinished( requestData.caller ); + checkFinished( requestData ); } void -InfoSystemWorker::checkFinished( const QString &target ) +InfoSystemWorker::checkFinished( const Tomahawk::InfoSystem::InfoRequestData &requestData ) { - Q_FOREACH( InfoType testtype, m_dataTracker[ target ].keys() ) + if ( m_dataTracker[ requestData.caller ][ requestData.type ] == 0 ) + emit finished( requestData.caller, requestData.type ); + + Q_FOREACH( InfoType testtype, m_dataTracker[ requestData.caller ].keys() ) { - if ( m_dataTracker[ target ][ testtype ] != 0) - { -// qDebug() << "Found outstanding request of type" << testtype; + if ( m_dataTracker[ requestData.caller ][ testtype ] != 0 ) return; - } } // qDebug() << "Emitting finished with target" << target; - emit finished( target ); + emit finished( requestData.caller ); } @@ -278,7 +301,7 @@ InfoSystemWorker::checkTimeoutsTimerFired() qint64 currTime = QDateTime::currentMSecsSinceEpoch(); Q_FOREACH( qint64 time, m_timeRequestMapper.keys() ) { - Q_FOREACH( uint requestId, m_timeRequestMapper.values( time ) ) + Q_FOREACH( quint64 requestId, m_timeRequestMapper.values( time ) ) { if ( time < currTime ) { @@ -313,7 +336,7 @@ InfoSystemWorker::checkTimeoutsTimerFired() if ( !m_timeRequestMapper.count( time ) ) m_timeRequestMapper.remove( time ); - checkFinished( returnData.caller ); + checkFinished( returnData ); } else { @@ -338,18 +361,31 @@ InfoSystemWorker::nam() const void InfoSystemWorker::newNam() { -// qDebug() << Q_FUNC_INFO << " begin"; - QNetworkAccessManager *oldNam = TomahawkUtils::nam(); if ( oldNam && oldNam->thread() == thread() ) { -// qDebug() << Q_FUNC_INFO << "Using old nam as it's the same thread (GUI) as me"; - m_nam = QWeakPointer< QNetworkAccessManager >( oldNam ); - emit namChanged( m_nam.data() ); + if ( m_nam.data() != oldNam ) + { + m_nam = QWeakPointer< QNetworkAccessManager >( oldNam ); + emit namChanged( m_nam.data() ); + } return; } -// qDebug() << Q_FUNC_INFO << "No nam exists, or it's a different thread, creating a new one"; + if + ( + oldNam && + !m_nam.isNull() && + oldNam->configuration() == m_nam.data()->configuration() && + oldNam->networkAccessible() == m_nam.data()->networkAccessible() + ) + { + TomahawkUtils::NetworkProxyFactory fac1 = *( dynamic_cast< TomahawkUtils::NetworkProxyFactory * >( oldNam->proxyFactory() ) ); + TomahawkUtils::NetworkProxyFactory fac2 = *( dynamic_cast< TomahawkUtils::NetworkProxyFactory * >( m_nam.data()->proxyFactory() ) ); + if ( fac1 == fac2 ) + return; + } + QNetworkAccessManager* newNam; #ifdef LIBLASTFM_FOUND newNam = new lastfm::NetworkAccessManager( this ); diff --git a/src/libtomahawk/infosystem/infosystemworker.h b/src/libtomahawk/infosystem/infosystemworker.h index 347861dd9..3bcdd1b62 100644 --- a/src/libtomahawk/infosystem/infosystemworker.h +++ b/src/libtomahawk/infosystem/infosystemworker.h @@ -52,15 +52,16 @@ public: signals: void info( Tomahawk::InfoSystem::InfoRequestData requestData, QVariant output ); void finished( QString target ); + void finished( QString target, Tomahawk::InfoSystem::InfoType type ); void namChanged( QNetworkAccessManager* ); public slots: void init( QWeakPointer< Tomahawk::InfoSystem::InfoSystemCache > cache ); - void getInfo( Tomahawk::InfoSystem::InfoRequestData requestData, uint timeoutMillis, bool allSources ); + void getInfo( Tomahawk::InfoSystem::InfoRequestData requestData ); void pushInfo( QString caller, Tomahawk::InfoSystem::InfoType type, QVariant input ); - void infoSlot( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData, QVariant output ); + void infoSlot( Tomahawk::InfoSystem::InfoRequestData requestData, QVariant output ); void newNam(); @@ -69,11 +70,11 @@ private slots: private: - void checkFinished( const QString &target ); + void checkFinished( const Tomahawk::InfoSystem::InfoRequestData &target ); QList< InfoPluginPtr > determineOrderedMatches( const InfoType type ) const; QHash< QString, QHash< InfoType, int > > m_dataTracker; - QMultiMap< qint64, uint > m_timeRequestMapper; + QMultiMap< qint64, quint64 > m_timeRequestMapper; QHash< uint, bool > m_requestSatisfiedMap; QHash< uint, InfoRequestData* > m_savedRequestMap; @@ -85,8 +86,6 @@ private: QWeakPointer< QNetworkAccessManager> m_nam; - uint m_nextRequest; - QTimer m_checkTimeoutsTimer; }; diff --git a/src/libtomahawk/network/portfwdthread.cpp b/src/libtomahawk/network/portfwdthread.cpp index 78af1c27c..b0cb98e12 100644 --- a/src/libtomahawk/network/portfwdthread.cpp +++ b/src/libtomahawk/network/portfwdthread.cpp @@ -81,7 +81,7 @@ PortFwdThread::work() qDebug() << "Trying to setup portfwd on" << tryport; if ( m_portfwd->add( tryport, m_port ) ) { - QString pubip = QString( m_portfwd->external_ip().c_str() ); + QString pubip = QString( m_portfwd->external_ip().c_str() ).trimmed(); m_externalAddress = QHostAddress( pubip ); m_externalPort = tryport; tDebug() << "External servent address detected as" << pubip << ":" << m_externalPort; diff --git a/src/libtomahawk/playlist/albumitemdelegate.cpp b/src/libtomahawk/playlist/albumitemdelegate.cpp index 4758e3c1f..377c0eddd 100644 --- a/src/libtomahawk/playlist/albumitemdelegate.cpp +++ b/src/libtomahawk/playlist/albumitemdelegate.cpp @@ -31,6 +31,8 @@ #include "playlist/albumitem.h" #include "playlist/albumproxymodel.h" +#include <QMouseEvent> +#include <viewmanager.h> AlbumItemDelegate::AlbumItemDelegate( QAbstractItemView* parent, AlbumProxyModel* proxy ) @@ -38,7 +40,6 @@ AlbumItemDelegate::AlbumItemDelegate( QAbstractItemView* parent, AlbumProxyModel , m_view( parent ) , m_model( proxy ) { - m_shadowPixmap = QPixmap( RESPATH "images/cover-shadow.png" ); m_defaultCover = QPixmap( RESPATH "images/no-album-art-placeholder.png" ); } @@ -63,9 +64,30 @@ AlbumItemDelegate::paint( QPainter* painter, const QStyleOptionViewItem& option, qApp->style()->drawControl( QStyle::CE_ItemViewItem, &opt, painter ); painter->save(); + painter->setRenderHint( QPainter::Antialiasing ); -// painter->setRenderHint( QPainter::Antialiasing ); -// painter->drawPixmap( option.rect.adjusted( 4, 4, -4, -38 ), m_shadowPixmap ); + if ( !( option.state & QStyle::State_Selected ) ) + { + QRect shadowRect = option.rect.adjusted( 5, 4, -5, -40 ); + painter->setPen( QColor( 90, 90, 90 ) ); + painter->drawRoundedRect( shadowRect, 0.5, 0.5 ); + + QPen shadowPen( QColor( 30, 30, 30 ) ); + shadowPen.setWidth( 0.4 ); + painter->drawLine( shadowRect.bottomLeft() + QPoint( -1, 2 ), shadowRect.bottomRight() + QPoint( 1, 2 ) ); + + shadowPen.setColor( QColor( 160, 160, 160 ) ); + painter->setPen( shadowPen ); + painter->drawLine( shadowRect.topLeft() + QPoint( -1, 2 ), shadowRect.bottomLeft() + QPoint( -1, 2 ) ); + painter->drawLine( shadowRect.topRight() + QPoint( 2, 2 ), shadowRect.bottomRight() + QPoint( 2, 2 ) ); + painter->drawLine( shadowRect.bottomLeft() + QPoint( 0, 3 ), shadowRect.bottomRight() + QPoint( 0, 3 ) ); + + shadowPen.setColor( QColor( 180, 180, 180 ) ); + painter->setPen( shadowPen ); + painter->drawLine( shadowRect.topLeft() + QPoint( -2, 3 ), shadowRect.bottomLeft() + QPoint( -2, 1 ) ); + painter->drawLine( shadowRect.topRight() + QPoint( 3, 3 ), shadowRect.bottomRight() + QPoint( 3, 1 ) ); + painter->drawLine( shadowRect.bottomLeft() + QPoint( 0, 4 ), shadowRect.bottomRight() + QPoint( 0, 4 ) ); + } QPixmap cover = item->cover.isNull() ? m_defaultCover : item->cover; QRect r = option.rect.adjusted( 6, 5, -6, -41 ); @@ -117,7 +139,7 @@ AlbumItemDelegate::paint( QPainter* painter, const QStyleOptionViewItem& option, oneLiner = true; else oneLiner = ( textRect.height() / 2 < painter->fontMetrics().boundingRect( item->album()->name() ).height() || - textRect.height() / 2 < painter->fontMetrics().boundingRect( item->album()->artist()->name() ).height() ); + textRect.height() / 2 < painter->fontMetrics().boundingRect( item->album()->artist()->name() ).height() ); if ( oneLiner ) { @@ -127,15 +149,95 @@ AlbumItemDelegate::paint( QPainter* painter, const QStyleOptionViewItem& option, } else { + painter->setFont( boldFont ); to.setAlignment( Qt::AlignHCenter | Qt::AlignTop ); text = painter->fontMetrics().elidedText( item->album()->name(), Qt::ElideRight, textRect.width() - 3 ); painter->drawText( textRect, text, to ); - painter->setFont( boldFont ); + // If the user is hovering over an artist rect, draw a background so she knows it's clickable + QRect r = textRect; + r.setTop( r.bottom() - painter->fontMetrics().height() ); + r.adjust( 4, 0, -4, -1 ); + if ( m_hoveringOver == index ) + TomahawkUtils::drawQueryBackground( painter, opt.palette, r, 1.5 ); + +#ifdef Q_WS_MAC + painter->setPen( opt.palette.color( QPalette::Dark ).darker( 200 ) ); +#else + painter->setPen( opt.palette.color( QPalette::Dark ) ); +#endif to.setAlignment( Qt::AlignHCenter | Qt::AlignBottom ); text = painter->fontMetrics().elidedText( item->album()->artist()->name(), Qt::ElideRight, textRect.width() - 3 ); painter->drawText( textRect, text, to ); + // Calculate rect of artist on-hover button click area + + m_artistNameRects[ index ] = r; } painter->restore(); } + +bool +AlbumItemDelegate::editorEvent( QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option, const QModelIndex& index ) +{ + Q_UNUSED( option ); + + if ( event->type() != QEvent::MouseButtonRelease && + event->type() != QEvent::MouseMove && + event->type() != QEvent::MouseButtonPress && + event->type() != QEvent::Leave ) + return false; + + if ( m_artistNameRects.contains( index ) ) + { + QMouseEvent* ev = static_cast< QMouseEvent* >( event ); + QRect artistNameRect = m_artistNameRects[ index ]; + if ( artistNameRect.contains( ev->pos() ) ) + { + if ( event->type() == QEvent::MouseMove ) + { + if ( m_hoveringOver != index ) + { + QModelIndex old = m_hoveringOver; + m_hoveringOver = index; + emit updateIndex( old ); + emit updateIndex( index ); + } + + event->accept(); + return true; + } + else if ( event->type() == QEvent::MouseButtonRelease ) + { + AlbumItem* item = m_model->sourceModel()->itemFromIndex( m_model->mapToSource( index ) ); + if ( !item || item->album().isNull() || item->album()->artist().isNull() ) + return false; + + ViewManager::instance()->show( item->album()->artist() ); + + event->accept(); + return true; + } else if ( event->type() == QEvent::MouseButtonPress ) + { + // Stop the whole album from having a down click action as we just want the artist name to be clicked + event->accept(); + return true; + } + } + } + + whitespaceMouseEvent(); + + return false; +} + +void +AlbumItemDelegate::whitespaceMouseEvent() +{ + if ( m_hoveringOver.isValid() ) + { + QModelIndex old = m_hoveringOver; + m_hoveringOver = QPersistentModelIndex(); + emit updateIndex( old ); + } +} diff --git a/src/libtomahawk/playlist/albumitemdelegate.h b/src/libtomahawk/playlist/albumitemdelegate.h index a5bb3a27a..86b31bdf5 100644 --- a/src/libtomahawk/playlist/albumitemdelegate.h +++ b/src/libtomahawk/playlist/albumitemdelegate.h @@ -23,6 +23,7 @@ #include "dllmacro.h" +class QEvent; class AlbumProxyModel; class DLLEXPORT AlbumItemDelegate : public QStyledItemDelegate @@ -32,17 +33,26 @@ Q_OBJECT public: AlbumItemDelegate( QAbstractItemView* parent = 0, AlbumProxyModel* proxy = 0 ); + void whitespaceMouseEvent(); + protected: void paint( QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index ) const; QSize sizeHint( const QStyleOptionViewItem& option, const QModelIndex& index ) const; + bool editorEvent( QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option, const QModelIndex& index ); // QWidget* createEditor( QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index ) const; +signals: + void updateIndex( const QModelIndex& idx ); + private: QAbstractItemView* m_view; AlbumProxyModel* m_model; mutable QHash< qint64, QPixmap > m_cache; + mutable QHash< QPersistentModelIndex, QRect > m_artistNameRects; + QPersistentModelIndex m_hoveringOver; + QPixmap m_shadowPixmap; QPixmap m_defaultCover; }; diff --git a/src/libtomahawk/playlist/albummodel.cpp b/src/libtomahawk/playlist/albummodel.cpp index 7c629777d..5662d5036 100644 --- a/src/libtomahawk/playlist/albummodel.cpp +++ b/src/libtomahawk/playlist/albummodel.cpp @@ -254,6 +254,8 @@ AlbumModel::addCollection( const collection_ptr& collection, bool overwrite ) Database::instance()->enqueue( QSharedPointer<DatabaseCommand>( cmd ) ); m_title = tr( "All albums from %1" ).arg( collection->source()->friendlyName() ); + + emit loadingStarted(); } @@ -280,6 +282,8 @@ AlbumModel::addFilteredCollection( const collection_ptr& collection, unsigned in m_title = tr( "All albums from %1" ).arg( collection->source()->friendlyName() ); else m_title = tr( "All albums" ); + + emit loadingStarted(); } @@ -292,6 +296,8 @@ AlbumModel::addAlbums( const QList<Tomahawk::album_ptr>& albums ) if ( m_overwriteOnAdd ) clear(); + emit loadingFinished(); + int c = rowCount( QModelIndex() ); QPair< int, int > crows; crows.first = c; diff --git a/src/libtomahawk/playlist/albummodel.h b/src/libtomahawk/playlist/albummodel.h index 596bf5f42..6a73124ac 100644 --- a/src/libtomahawk/playlist/albummodel.h +++ b/src/libtomahawk/playlist/albummodel.h @@ -94,6 +94,8 @@ signals: void trackCountChanged( unsigned int tracks ); + void loadingStarted(); + void loadingFinished(); private slots: void onDataChanged(); diff --git a/src/libtomahawk/playlist/albumview.cpp b/src/libtomahawk/playlist/albumview.cpp index 45099d6e8..d0a117a95 100644 --- a/src/libtomahawk/playlist/albumview.cpp +++ b/src/libtomahawk/playlist/albumview.cpp @@ -22,6 +22,7 @@ #include <QKeyEvent> #include <QPainter> #include <QScrollBar> +#include <qmath.h> #include "audio/audioengine.h" #include "tomahawksettings.h" @@ -31,6 +32,7 @@ #include "albummodel.h" #include "viewmanager.h" #include "utils/logger.h" +#include "dynamic/widgets/LoadingSpinner.h" #define SCROLL_TIMEOUT 280 @@ -41,13 +43,16 @@ AlbumView::AlbumView( QWidget* parent ) : QListView( parent ) , m_model( 0 ) , m_proxyModel( 0 ) -// , m_delegate( 0 ) + , m_delegate( 0 ) + , m_loadingSpinner( new LoadingSpinner( this ) ) { setDragEnabled( true ); setDropIndicatorShown( false ); setDragDropOverwriteMode( false ); setUniformItemSizes( true ); - setSpacing( 20 ); + setSpacing( 16 ); + setContentsMargins( 0, 0, 0, 0 ); + setMouseTracking( true ); setResizeMode( Adjust ); setViewMode( IconMode ); @@ -75,7 +80,9 @@ void AlbumView::setProxyModel( AlbumProxyModel* model ) { m_proxyModel = model; - setItemDelegate( new AlbumItemDelegate( this, m_proxyModel ) ); + m_delegate = new AlbumItemDelegate( this, m_proxyModel ); + connect( m_delegate, SIGNAL( updateIndex( QModelIndex ) ), this, SLOT( update( QModelIndex ) ) ); + setItemDelegate( m_delegate ); QListView::setModel( m_proxyModel ); } @@ -104,6 +111,9 @@ AlbumView::setAlbumModel( AlbumModel* model ) connect( m_proxyModel, SIGNAL( filterChanged( QString ) ), SLOT( onFilterChanged( QString ) ) ); connect( m_proxyModel, SIGNAL( rowsInserted( QModelIndex, int, int ) ), SLOT( onViewChanged() ) ); + connect( m_model, SIGNAL( loadingStarted() ), m_loadingSpinner, SLOT( fadeIn() ) ); + connect( m_model, SIGNAL( loadingFinished() ), m_loadingSpinner, SLOT( fadeOut() ) ); + setAcceptDrops( false ); onViewChanged(); // Fetch covers if albums were added to model before model was attached to view } @@ -179,6 +189,30 @@ AlbumView::paintEvent( QPaintEvent* event ) } +void +AlbumView::resizeEvent( QResizeEvent* event ) +{ + QListView::resizeEvent( event ); + +#ifdef Q_WS_X11 + int scrollbar = !verticalScrollBar()->isVisible() ? verticalScrollBar()->rect().width() : 0; +#else + int scrollbar = verticalScrollBar()->rect().width(); +#endif + int rectWidth = contentsRect().width() - scrollbar - 16 - 3; + QSize itemSize = m_proxyModel->data( QModelIndex(), Qt::SizeHintRole ).toSize(); + + int itemsPerRow = qFloor( rectWidth / ( itemSize.width() + 16 ) ); + int rightSpacing = rectWidth - ( itemsPerRow * ( itemSize.width() + 16 ) ); + int newSpacing = 16 + floor( rightSpacing / ( itemsPerRow + 1 ) ); + + if ( itemsPerRow < 1 ) + setSpacing( 16 ); + else + setSpacing( newSpacing ); +} + + void AlbumView::onFilterChanged( const QString& ) { diff --git a/src/libtomahawk/playlist/albumview.h b/src/libtomahawk/playlist/albumview.h index 52dfecee3..b16f9e898 100644 --- a/src/libtomahawk/playlist/albumview.h +++ b/src/libtomahawk/playlist/albumview.h @@ -28,6 +28,8 @@ #include "albumproxymodel.h" class AlbumModel; +class LoadingSpinner; +class AlbumItemDelegate; class DLLEXPORT AlbumView : public QListView, public Tomahawk::ViewPage { @@ -63,6 +65,7 @@ protected: virtual void startDrag( Qt::DropActions supportedActions ); void paintEvent( QPaintEvent* event ); + void resizeEvent( QResizeEvent* event ); private slots: void onFilterChanged( const QString& filter ); @@ -73,7 +76,8 @@ private slots: private: AlbumModel* m_model; AlbumProxyModel* m_proxyModel; -// PlaylistItemDelegate* m_delegate; + AlbumItemDelegate* m_delegate; + LoadingSpinner* m_loadingSpinner; QTimer m_timer; }; diff --git a/src/libtomahawk/playlist/artistview.cpp b/src/libtomahawk/playlist/artistview.cpp index 61851b692..59ce86097 100644 --- a/src/libtomahawk/playlist/artistview.cpp +++ b/src/libtomahawk/playlist/artistview.cpp @@ -158,7 +158,7 @@ ArtistView::onItemActivated( const QModelIndex& index ) if ( !item->artist().isNull() ) ViewManager::instance()->show( item->artist() ); else if ( !item->album().isNull() ) - ViewManager::instance()->show( item->album() ); + ViewManager::instance()->show( item->album(), m_model->mode() ); else if ( !item->result().isNull() && item->result()->isOnline() ) { m_model->setCurrentItem( item->index ); diff --git a/src/libtomahawk/playlist/customplaylistview.cpp b/src/libtomahawk/playlist/customplaylistview.cpp index 218b6ffb4..7273dcee3 100644 --- a/src/libtomahawk/playlist/customplaylistview.cpp +++ b/src/libtomahawk/playlist/customplaylistview.cpp @@ -52,8 +52,11 @@ CustomPlaylistView::CustomPlaylistView( CustomPlaylistView::PlaylistType type, c } } + CustomPlaylistView::~CustomPlaylistView() -{} +{ +} + bool CustomPlaylistView::isBeingPlayed() const @@ -61,6 +64,7 @@ CustomPlaylistView::isBeingPlayed() const return AudioEngine::instance()->currentTrackPlaylist() == playlistInterface(); } + bool CustomPlaylistView::jumpToCurrentTrack() { @@ -80,7 +84,7 @@ CustomPlaylistView::generateTracks() "FROM social_attributes, track, artist " "WHERE social_attributes.id = track.id AND artist.id = track.artist AND social_attributes.k = 'Love' AND social_attributes.v = 'true' AND social_attributes.source %1 " "GROUP BY track.id " - "ORDER BY counter DESC, social_attributes.timestamp DESC " ).arg( m_source->isLocal() ? "IS NULL" : QString( "=%1" ).arg( m_source->id() ) ); + "ORDER BY counter DESC, social_attributes.timestamp DESC " ).arg( m_source->isLocal() ? "IS NULL" : QString( "= %1" ).arg( m_source->id() ) ); break; case AllLovedTracks: sql = QString( "SELECT track.name, artist.name, source, COUNT(*) as counter " @@ -96,6 +100,7 @@ CustomPlaylistView::generateTracks() Database::instance()->enqueue( QSharedPointer<DatabaseCommand>( cmd ) ); } + void CustomPlaylistView::tracksGenerated( QList< query_ptr > tracks ) { @@ -103,6 +108,7 @@ CustomPlaylistView::tracksGenerated( QList< query_ptr > tracks ) m_model->append( q ); } + QString CustomPlaylistView::title() const { @@ -132,18 +138,21 @@ CustomPlaylistView::description() const } } + QString CustomPlaylistView::longDescription() const { return QString(); } + QPixmap CustomPlaylistView::pixmap() const { return QPixmap( RESPATH "images/loved_playlist.png" ); } + void CustomPlaylistView::reload() { @@ -153,7 +162,7 @@ CustomPlaylistView::reload() void -CustomPlaylistView::sourceAdded( const source_ptr& s) +CustomPlaylistView::sourceAdded( const source_ptr& s ) { connect( s.data(), SIGNAL( socialAttributesChanged() ), this, SLOT( reload() ) ); } diff --git a/src/libtomahawk/playlist/dynamic/widgets/LoadingSpinner.cpp b/src/libtomahawk/playlist/dynamic/widgets/LoadingSpinner.cpp index d7f12f7f3..19a74a2eb 100644 --- a/src/libtomahawk/playlist/dynamic/widgets/LoadingSpinner.cpp +++ b/src/libtomahawk/playlist/dynamic/widgets/LoadingSpinner.cpp @@ -1,6 +1,6 @@ /* === This file is part of Tomahawk Player - <http://tomahawk-player.org> === * - * Copyright 2010-2011, Christian Muehlhaeuser <muesli@tomahawk-player.org> + * Copyright 2010 Leo Franchi <lfranchi@kde.org> * * Tomahawk is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -58,6 +58,9 @@ LoadingSpinner::~LoadingSpinner() void LoadingSpinner::fadeIn() { + if ( isVisible() ) + return; + show(); m_anim->start(); diff --git a/src/libtomahawk/playlist/dynamic/widgets/LoadingSpinner.h b/src/libtomahawk/playlist/dynamic/widgets/LoadingSpinner.h index 5720df34c..556dcd11c 100644 --- a/src/libtomahawk/playlist/dynamic/widgets/LoadingSpinner.h +++ b/src/libtomahawk/playlist/dynamic/widgets/LoadingSpinner.h @@ -1,6 +1,6 @@ /* === This file is part of Tomahawk Player - <http://tomahawk-player.org> === * - * Copyright 2010-2011, Christian Muehlhaeuser <muesli@tomahawk-player.org> + * Copyright 2010 Leo Franchi <lfranchi@kde.org> * * Tomahawk is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -54,4 +54,4 @@ private: #endif -class QPaintEvent; \ No newline at end of file +class QPaintEvent; diff --git a/src/libtomahawk/playlist/playlistmodel.cpp b/src/libtomahawk/playlist/playlistmodel.cpp index c8a2b61dc..14bf12bae 100644 --- a/src/libtomahawk/playlist/playlistmodel.cpp +++ b/src/libtomahawk/playlist/playlistmodel.cpp @@ -384,7 +384,7 @@ PlaylistModel::dropMimeData( const QMimeData* data, Qt::DropAction action, int r // so check if the drag originated in this playlist to determine whether or not to copy #ifdef Q_WS_MAC if ( !data->hasFormat( "application/tomahawk.playlist.id" ) || - data->data( "application/tomahawk.playlist.id" ) != m_playlist->guid() ) + ( !m_playlist.isNull() && data->data( "application/tomahawk.playlist.id" ) != m_playlist->guid() ) ) dj->setDropAction( DropJob::Append ); #else if ( action & Qt::CopyAction ) diff --git a/src/libtomahawk/playlist/trackview.cpp b/src/libtomahawk/playlist/trackview.cpp index ca7e89e25..4d1ceabc4 100644 --- a/src/libtomahawk/playlist/trackview.cpp +++ b/src/libtomahawk/playlist/trackview.cpp @@ -556,7 +556,8 @@ TrackView::mousePressEvent( QMouseEvent* event ) } else { - //TODO + artist_ptr artist = Artist::get( item->query()->artist() ); + ViewManager::instance()->show( Album::get( artist, item->query()->album() ) ); } break; } diff --git a/src/libtomahawk/playlist/treeitemdelegate.cpp b/src/libtomahawk/playlist/treeitemdelegate.cpp index 1328b15cd..dee8934c9 100644 --- a/src/libtomahawk/playlist/treeitemdelegate.cpp +++ b/src/libtomahawk/playlist/treeitemdelegate.cpp @@ -40,7 +40,7 @@ TreeItemDelegate::TreeItemDelegate( ArtistView* parent, TreeProxyModel* proxy ) , m_model( proxy ) { m_nowPlayingIcon = QPixmap( RESPATH "images/now-playing-speaker.png" ); - m_defaultAlbumCover = QPixmap( RESPATH "images/no-album-art-placeholder.png" ); + m_defaultAlbumCover = QPixmap( RESPATH "images/no-album-no-case.png" ); m_defaultArtistImage = QPixmap( RESPATH "images/no-artist-image-placeholder.png" ); } diff --git a/src/libtomahawk/playlist/treemodel.cpp b/src/libtomahawk/playlist/treemodel.cpp index 8dd336ea5..a9c4a0b1d 100644 --- a/src/libtomahawk/playlist/treemodel.cpp +++ b/src/libtomahawk/playlist/treemodel.cpp @@ -38,7 +38,7 @@ TreeModel::TreeModel( QObject* parent ) , m_rootItem( new TreeModelItem( 0, this ) ) , m_infoId( uuid() ) , m_columnStyle( AllColumns ) - , m_mode( Database ) + , m_mode( DatabaseMode ) { setIcon( QPixmap( RESPATH "images/music-icon.png" ) ); @@ -576,7 +576,7 @@ TreeModel::addAlbums( const artist_ptr& artist, const QModelIndex& parent ) { emit loadingStarted(); - if ( m_mode == Database ) + if ( m_mode == DatabaseMode ) { DatabaseCommand_AllAlbums* cmd = new DatabaseCommand_AllAlbums( m_collection, artist ); cmd->setData( parent.row() ); @@ -586,7 +586,7 @@ TreeModel::addAlbums( const artist_ptr& artist, const QModelIndex& parent ) Database::instance()->enqueue( QSharedPointer<DatabaseCommand>( cmd ) ); } - else if ( m_mode == InfoSystem ) + else if ( m_mode == InfoSystemMode ) { Tomahawk::InfoSystem::InfoStringHash artistInfo; artistInfo["artist"] = artist->name(); @@ -612,7 +612,7 @@ TreeModel::addTracks( const album_ptr& album, const QModelIndex& parent ) rows << parent.row(); rows << parent.parent().row(); - if ( m_mode == Database ) + if ( m_mode == DatabaseMode ) { DatabaseCommand_AllTracks* cmd = new DatabaseCommand_AllTracks( m_collection ); cmd->setAlbum( album.data() ); @@ -623,17 +623,20 @@ TreeModel::addTracks( const album_ptr& album, const QModelIndex& parent ) Database::instance()->enqueue( QSharedPointer<DatabaseCommand>( cmd ) ); } - else if ( m_mode == InfoSystem ) + else if ( m_mode == InfoSystemMode ) { Tomahawk::InfoSystem::InfoStringHash artistInfo; artistInfo["artist"] = album->artist()->name(); artistInfo["album"] = album->name(); + m_receivedInfoData.remove( artistInfo ); Tomahawk::InfoSystem::InfoRequestData requestData; requestData.caller = m_infoId; requestData.customData["rows"] = QVariant( rows ); requestData.input = QVariant::fromValue< Tomahawk::InfoSystem::InfoStringHash >( artistInfo ); requestData.type = Tomahawk::InfoSystem::InfoAlbumSongs; + requestData.timeoutMillis = 0; + requestData.allSources = true; Tomahawk::InfoSystem::InfoSystem::instance()->getInfo( requestData ); } else @@ -826,7 +829,7 @@ TreeModel::infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData requestData, QV QStringList albums = returnedData[ "albums" ].toStringList(); QList<album_ptr> al; - InfoSystem::InfoStringHash inputInfo; + Tomahawk::InfoSystem::InfoStringHash inputInfo; inputInfo = requestData.input.value< InfoSystem::InfoStringHash >(); artist_ptr artist = Artist::get( inputInfo[ "artist" ], false ); @@ -845,12 +848,23 @@ TreeModel::infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData requestData, QV case Tomahawk::InfoSystem::InfoAlbumSongs: { + if ( m_receivedInfoData.contains( requestData.input.value< Tomahawk::InfoSystem::InfoStringHash >() ) ) + break; + + emit loadingFinished(); + QVariantMap returnedData = output.value< QVariantMap >(); + if ( returnedData.isEmpty() ) + break; + + m_receivedInfoData.insert( requestData.input.value< Tomahawk::InfoSystem::InfoStringHash >() ); + + QStringList tracks = returnedData[ "tracks" ].toStringList(); QList<query_ptr> ql; - InfoSystem::InfoStringHash inputInfo; - inputInfo = requestData.input.value< InfoSystem::InfoStringHash >(); + Tomahawk::InfoSystem::InfoStringHash inputInfo; + inputInfo = requestData.input.value< Tomahawk::InfoSystem::InfoStringHash >(); foreach ( const QString& trackName, tracks ) { diff --git a/src/libtomahawk/playlist/treemodel.h b/src/libtomahawk/playlist/treemodel.h index af4aa6bf0..3973b396f 100644 --- a/src/libtomahawk/playlist/treemodel.h +++ b/src/libtomahawk/playlist/treemodel.h @@ -33,6 +33,7 @@ #include "infosystem/infosystem.h" #include "dllmacro.h" +#include "typedefs.h" class QMetaData; @@ -55,9 +56,6 @@ public: enum ColumnStyle { AllColumns = 0, TrackOnly }; - enum ModelMode - { Database = 0, InfoSystem }; - explicit TreeModel( QObject* parent = 0 ); virtual ~TreeModel(); @@ -73,8 +71,8 @@ public: virtual int rowCount( const QModelIndex& parent = QModelIndex() ) const; virtual int columnCount( const QModelIndex& parent = QModelIndex() ) const; - virtual ModelMode mode() const { return m_mode; } - virtual void setMode( ModelMode mode ) { m_mode = mode; } + virtual Tomahawk::ModelMode mode() const { return m_mode; } + virtual void setMode( Tomahawk::ModelMode mode ) { m_mode = mode; } virtual QVariant data( const QModelIndex& index, int role = Qt::DisplayRole ) const; virtual QVariant headerData( int section, Qt::Orientation orientation, int role ) const; @@ -165,12 +163,13 @@ private: QString m_description; QPixmap m_icon; ColumnStyle m_columnStyle; - ModelMode m_mode; + Tomahawk::ModelMode m_mode; QList<Tomahawk::artist_ptr> m_artistsFilter; Tomahawk::collection_ptr m_collection; QHash<qlonglong, QPersistentModelIndex> m_coverHash; + QSet<Tomahawk::InfoSystem::InfoStringHash> m_receivedInfoData; }; #endif // ALBUMMODEL_H diff --git a/src/libtomahawk/playlist/treeproxymodel.cpp b/src/libtomahawk/playlist/treeproxymodel.cpp index 5dc0e0cfa..206bbd1b7 100644 --- a/src/libtomahawk/playlist/treeproxymodel.cpp +++ b/src/libtomahawk/playlist/treeproxymodel.cpp @@ -195,7 +195,7 @@ TreeProxyModel::filterAcceptsRow( int sourceRow, const QModelIndex& sourceParent TreeModelItem* pi = sourceModel()->itemFromIndex( sourceModel()->index( sourceRow, 0, sourceParent ) ); Q_ASSERT( pi ); - if ( m_model->mode() == TreeModel::Database && !pi->result().isNull() ) + if ( m_model->mode() == Tomahawk::DatabaseMode && !pi->result().isNull() ) { QList< Tomahawk::result_ptr > rl = m_cache.values( sourceParent ); foreach ( const Tomahawk::result_ptr& result, rl ) @@ -329,14 +329,34 @@ TreeProxyModel::siblingItem( int itemsAway ) Tomahawk::result_ptr TreeProxyModel::siblingItem( int itemsAway, bool readOnly ) { - qDebug() << Q_FUNC_INFO; - QModelIndex idx = currentIndex(); + if ( !idx.isValid() ) + return Tomahawk::result_ptr(); + + if ( m_shuffled ) + idx = index( qrand() % rowCount( idx.parent() ), 0, idx.parent() ); + else if ( m_repeatMode == PlaylistInterface::RepeatOne ) + idx = index( idx.row(), 0, idx.parent() ); + else + idx = index( idx.row() + ( itemsAway > 0 ? 1 : -1 ), 0, idx.parent() ); + + if ( !idx.isValid() && m_repeatMode == PlaylistInterface::RepeatAll ) + { + if ( itemsAway > 0 ) + { + // reset to first item + idx = index( 0, 0, currentIndex().parent() ); + } + else + { + // reset to last item + idx = index( rowCount( currentIndex().parent() ) - 1, 0, currentIndex().parent() ); + } + } // Try to find the next available PlaylistItem (with results) if ( idx.isValid() ) do { - idx = index( idx.row() + ( itemsAway > 0 ? 1 : -1 ), 0, idx.parent() ); if ( !idx.isValid() ) break; diff --git a/src/libtomahawk/resolvers/qtscriptresolver.cpp b/src/libtomahawk/resolvers/qtscriptresolver.cpp index f0b760d82..0d4bd5978 100644 --- a/src/libtomahawk/resolvers/qtscriptresolver.cpp +++ b/src/libtomahawk/resolvers/qtscriptresolver.cpp @@ -31,6 +31,7 @@ #include <network/servent.h> #include <QNetworkRequest> #include <QNetworkReply> +#include <QMessageBox> // FIXME: bloody hack, remove this for 0.3 // this one adds new functionality to old resolvers @@ -186,9 +187,9 @@ ScriptEngine::javaScriptConsoleMessage( const QString& message, int lineNumber, tLog() << "JAVASCRIPT:" << m_scriptPath << message << lineNumber << sourceID; /// I guess there is somereason for a assert in here, maybe fatal js errors, but /// undefined is not so fatal - if(sourceID != "undefined") - Q_ASSERT( false ); - +#ifdef QT_DEBUG + QMessageBox::critical( 0, "Script Resolver Error", QString( "%1 %2 %3 %4" ).arg( m_scriptPath ).arg( message ).arg( lineNumber ).arg( sourceID ) ); +#endif } diff --git a/src/libtomahawk/typedefs.h b/src/libtomahawk/typedefs.h index b3b98232e..b972009ba 100644 --- a/src/libtomahawk/typedefs.h +++ b/src/libtomahawk/typedefs.h @@ -63,6 +63,12 @@ namespace Tomahawk Static }; + enum ModelMode + { + DatabaseMode = 0, + InfoSystemMode + }; + }; // ns typedef int AudioErrorCode; diff --git a/src/libtomahawk/utils/dropjobnotifier.cpp b/src/libtomahawk/utils/dropjobnotifier.cpp index c104ada55..219f51215 100644 --- a/src/libtomahawk/utils/dropjobnotifier.cpp +++ b/src/libtomahawk/utils/dropjobnotifier.cpp @@ -51,8 +51,8 @@ DropJobNotifier::DropJobNotifier( QPixmap servicePixmap, QString service, DropJo DropJobNotifier::DropJobNotifier( QPixmap pixmap, DropJob::DropType type ) : JobStatusItem() - , m_pixmap( pixmap ) , m_job( 0 ) + , m_pixmap( pixmap ) { init( type ); } diff --git a/src/libtomahawk/utils/rdioparser.cpp b/src/libtomahawk/utils/rdioparser.cpp index 8be1a40d2..7e7d2e28f 100644 --- a/src/libtomahawk/utils/rdioparser.cpp +++ b/src/libtomahawk/utils/rdioparser.cpp @@ -1,6 +1,7 @@ /* === This file is part of Tomahawk Player - <http://tomahawk-player.org> === * * Copyright 2010-2011, Leo Franchi <lfranchi@kde.org> + * Copyright 2010-2011, Hugo Lindström <hugolm84@gmail.com> * * Tomahawk is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -18,15 +19,39 @@ #include "rdioparser.h" +#include "shortenedlinkparser.h" +#include "config.h" +#include "utils/tomahawkutils.h" +#include "utils/logger.h" +#include "dropjob.h" +#include "jobview/JobStatusView.h" +#include "jobview/JobStatusModel.h" +#include "dropjobnotifier.h" +#include "viewmanager.h" +#include "sourcelist.h" + +#include <qjson/parser.h> +#include <QDateTime> +#include <QtNetwork/QNetworkAccessManager> +#include <QtNetwork/QNetworkReply> #include <QUrl> #include <QStringList> -#include "shortenedlinkparser.h" + +#include <QtCore/QCryptographicHash> using namespace Tomahawk; +QPixmap* RdioParser::s_pixmap = 0; + +#ifdef QCA2_FOUND +QCA::Initializer RdioParser::m_qcaInit = QCA::Initializer(); +#endif + RdioParser::RdioParser( QObject* parent ) : QObject( parent ) , m_count( 0 ) + , m_browseJob( 0 ) + , m_createPlaylist( false ) { } @@ -63,50 +88,276 @@ RdioParser::parseUrl( const QString& url ) return; } - query_ptr query; - m_count++; - - if ( url.contains( "artist" ) && url.contains( "album" ) ) + if ( url.contains( "artist" ) && url.contains( "album" ) && url.contains( "track" ) ) + parseTrack( url ); + else { - // this is a "full" url, no redirection needed - QString realUrl = QUrl::fromUserInput( url ).toString().replace( "_", " " ); - - QString artist, trk, album; - QString matchStr = "/%1/([^/]*)/"; - QRegExp r( QString( matchStr ).arg( "artist" ) ); - - int loc = r.indexIn( realUrl ); - if ( loc >= 0 ) - artist = r.cap( 1 ); - - r = QRegExp( QString( matchStr ).arg( "album" ) ); - loc = r.indexIn( realUrl ); - if ( loc >= 0 ) - album = r.cap( 1 ); - - r = QRegExp( QString( matchStr ).arg( "track" ) ); - loc = r.indexIn( realUrl ); - if ( loc >= 0 ) - trk = r.cap( 1 ); - - if ( !trk.isEmpty() && !artist.isEmpty() ) + DropJob::DropType type = DropJob::None; + if ( url.contains( "artist" ) && url.contains( "album" ) ) + type = DropJob::Album; + else if ( url.contains( "artist" ) ) + type = DropJob::Artist; + else if ( url.contains( "people" ) && url.contains( "playlist" ) ) + type = DropJob::Playlist; + else { - query = Query::get( artist, trk, album, uuid(), true ); + tLog() << "Got Rdio URL I can't parse!" << url; + return; } + + // artist, album, or playlist link requre fetching + fetchObjectsFromUrl( url, type ); } - if ( m_multi ) - { - if ( !query.isNull() ) - m_queries << query; - - if ( m_count == m_total ) - emit tracks( m_queries ); - } - if ( !m_multi && !query.isNull() ) - emit track( query ); } +void +RdioParser::fetchObjectsFromUrl( const QString& url, DropJob::DropType type ) +{ + QList< QPair< QByteArray, QByteArray > > params; + params.append( QPair<QByteArray, QByteArray>( "extras", "tracks" ) ); + + QString cleanedUrl = url; + cleanedUrl.replace("#/", ""); + + QByteArray data; + QNetworkRequest request = generateRequest( "getObjectFromUrl", cleanedUrl, params, &data ); + + request.setHeader( QNetworkRequest::ContentTypeHeader, QLatin1String( "application/x-www-form-urlencoded" ) ); + QNetworkReply* reply = TomahawkUtils::nam()->post( request, data ); + connect( reply, SIGNAL( finished() ), this, SLOT( rdioReturned() ) ); + + m_browseJob = new DropJobNotifier( pixmap(), QString( "Rdio" ), type, reply ); + JobStatusView::instance()->model()->addJob( m_browseJob ); + + m_reqQueries.insert( reply ); +} + +void +RdioParser::rdioReturned() +{ + + QNetworkReply* r = qobject_cast< QNetworkReply* >( sender() ); + Q_ASSERT( r ); + m_reqQueries.remove( r ); + m_count++; + r->deleteLater(); + + if ( r->error() == QNetworkReply::NoError ) + { + QJson::Parser p; + bool ok; + QVariantMap res = p.parse( r, &ok ).toMap(); + QVariantMap result = res.value( "result" ).toMap(); + + if ( !ok || result.isEmpty() ) + { + tLog() << "Failed to parse json from Rdio browse item :" << p.errorString() << "On line" << p.errorLine() << "With data:" << res; + + return; + } + + QVariantList tracks = result.value( "tracks" ).toList(); + if ( tracks.isEmpty() ) + { + tLog() << "Got no tracks in result, ignoring!" << result; + return; + } + + // Playlists will have these + m_title = result[ "name" ].toString(); + m_creator = result[ "owner" ].toString(); + + foreach( QVariant track, tracks ) + { + QVariantMap rdioResult = track.toMap(); + QString title, artist, album; + + title = rdioResult.value( "name", QString() ).toString(); + artist = rdioResult.value( "artist", QString() ).toString(); + album = rdioResult.value( "album", QString() ).toString(); + + if ( title.isEmpty() && artist.isEmpty() ) // don't have enough... + { + tLog() << "Didn't get an artist and track name from Rdio, not enough to build a query on. Aborting" << title << artist << album; + return; + } + + Tomahawk::query_ptr q = Tomahawk::Query::get( artist, title, album, uuid(), !m_createPlaylist ); + m_tracks << q; + } + + } else + { + tLog() << "Error in network request to Rdio for track decoding:" << r->errorString(); + } + + + checkFinished(); + + +} + +void +RdioParser::parseTrack( const QString& origUrl ) +{ + QString url = origUrl; + QString artist, trk, album, playlist; + QString realUrl = url.replace( "_", " " ); + QString matchStr = "/%1/([^/]*)/"; + QString matchPlStr = "/%1/(?:[^/]*)/([^/]*)/"; + + QRegExp r( QString( matchStr ).arg( "artist" ) ); + + int loc = r.indexIn( realUrl ); + if ( loc >= 0 ) + artist = r.cap( 1 ); + + r = QRegExp( QString( matchStr ).arg( "album" ) ); + loc = r.indexIn( realUrl ); + if ( loc >= 0 ) + album = r.cap( 1 ); + + r = QRegExp( QString( matchStr ).arg( "track" ) ); + loc = r.indexIn( realUrl ); + if ( loc >= 0 ) + trk = r.cap( 1 ); + + r = QRegExp( QString( matchPlStr ).arg( "playlists" ) ); + loc = r.indexIn( realUrl ); + if ( loc >= 0 ) + playlist = r.cap( 1 ); + + if ( trk.isEmpty() || artist.isEmpty() ) + { + tLog() << "Parsed Rdio track url but it's missing artist or track!" << url; + return; + } + + query_ptr q = Query::get( artist, trk, album, uuid(), !m_createPlaylist ); + m_count++; + m_tracks << q; + + checkFinished(); +} + + +QNetworkRequest +RdioParser::generateRequest( const QString& method, const QString& url, const QList< QPair< QByteArray, QByteArray > >& extraParams, QByteArray* data ) +{ + QUrl fetchUrl( "http://api.rdio.com/1/" ); + QUrl toSignUrl = fetchUrl; + + QPair<QByteArray, QByteArray> param; + foreach( param, extraParams ) + { + toSignUrl.addEncodedQueryItem( param.first, param.second ); + } + toSignUrl.addQueryItem( "method", method ); + toSignUrl.addEncodedQueryItem("oauth_consumer_key", "gk8zmyzj5xztt8aj48csaart" ); + QString nonce; + for ( int i = 0; i < 8; i++ ) + nonce += QString::number( qrand() % 10 ); + toSignUrl.addQueryItem("oauth_nonce", nonce ); + toSignUrl.addEncodedQueryItem("oauth_signature_method", "HMAC-SHA1"); + toSignUrl.addQueryItem("oauth_timestamp", QString::number(QDateTime::currentMSecsSinceEpoch() / 1000 ) ); + toSignUrl.addEncodedQueryItem("oauth_version", "1.0"); + toSignUrl.addEncodedQueryItem( "url", QUrl::toPercentEncoding( url ) ); + int size = toSignUrl.encodedQueryItems().size(); + for( int i = 0; i < size; i++ ) { + const QPair< QByteArray, QByteArray > item = toSignUrl.encodedQueryItems().at( i ); + data->append( item.first + "=" + item.second + "&" ); + } + data->truncate( data->size() - 1 ); // remove extra & + + QByteArray toSign = "POST&" + QUrl::toPercentEncoding( fetchUrl.toEncoded() ) + '&' + QUrl::toPercentEncoding( *data ); + qDebug() << "Rdio" << toSign; + + toSignUrl.addEncodedQueryItem( "oauth_signature", QUrl::toPercentEncoding( hmacSha1("yt35kakDyW&", toSign ) ) ); + + data->clear(); + size = toSignUrl.encodedQueryItems().size(); + for( int i = 0; i < size; i++ ) { + const QPair< QByteArray, QByteArray > item = toSignUrl.encodedQueryItems().at( i ); + data->append( item.first + "=" + item.second + "&" ); + } + data->truncate( data->size() - 1 ); // remove extra & + + qDebug() << "POST data:" << *data; + + QNetworkRequest request = QNetworkRequest( fetchUrl ); + request.setHeader( QNetworkRequest::ContentTypeHeader, QLatin1String( "application/x-www-form-urlencoded" ) ); + + return request; +} + + +QByteArray +RdioParser::hmacSha1(QByteArray key, QByteArray baseString) +{ +#ifdef QCA2_FOUND + QCA::MessageAuthenticationCode hmacsha1( "hmac(sha1)", QCA::SecureArray() ); + QCA::SymmetricKey keyObject( key ); + hmacsha1.setup( keyObject ); + + hmacsha1.update( QCA::SecureArray( baseString ) ); + QCA::SecureArray resultArray = hmacsha1.final(); + + QByteArray result = resultArray.toByteArray().toBase64(); + return result; +#else + tLog() << "Tomahawk compiled without QCA support, cannot generate HMAC signature"; + return QByteArray(); +#endif +} + +void +RdioParser::checkFinished() +{ + tDebug() << "Checking for Rdio batch playlist job finished" << m_reqQueries.isEmpty(); + if ( m_reqQueries.isEmpty() ) // we're done + { + if ( m_browseJob ) + m_browseJob->setFinished(); + + if ( m_tracks.isEmpty() ) + return; + + if( m_createPlaylist ) + { + m_playlist = Playlist::create( SourceList::instance()->getLocal(), + uuid(), + m_title, + "", + m_creator, + false, + m_tracks ); + + connect( m_playlist.data(), SIGNAL( revisionLoaded( Tomahawk::PlaylistRevision ) ), this, SLOT( playlistCreated() ) ); + + return; + } + else + { + if ( !m_multi ) + emit track( m_tracks.first() ); + else if ( m_multi && m_count == m_total ) + emit tracks( m_tracks ); + + m_tracks.clear(); + } + + deleteLater(); + } +} + +void +RdioParser::playlistCreated( Tomahawk::PlaylistRevision ) +{ + ViewManager::instance()->show( m_playlist ); +} + + void RdioParser::expandedLinks( const QStringList& urls ) { @@ -117,3 +368,13 @@ RdioParser::expandedLinks( const QStringList& urls ) } } +QPixmap +RdioParser::pixmap() const +{ + if ( !s_pixmap ) + s_pixmap = new QPixmap( RESPATH "images/rdio.png" ); + + + return *s_pixmap; +} + diff --git a/src/libtomahawk/utils/rdioparser.h b/src/libtomahawk/utils/rdioparser.h index 9bcf53a1e..3302b4715 100644 --- a/src/libtomahawk/utils/rdioparser.h +++ b/src/libtomahawk/utils/rdioparser.h @@ -1,6 +1,7 @@ /* === This file is part of Tomahawk Player - <http://tomahawk-player.org> === * * Copyright 2010-2011, Leo Franchi <lfranchi@kde.org> + * Copyright 2010-2011, Hugo Lindström <hugolm84@gmail.com> * * Tomahawk is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -19,15 +20,28 @@ #ifndef RDIOPARSER_H #define RDIOPARSER_H +#include "jobview/JobStatusItem.h" +#include "query.h" +#include "config.h" +#include "dropjob.h" +#include "typedefs.h" +#include "playlist.h" + #include <QtCore/QObject> #include <QStringList> +#include <QSet> -#include "query.h" +#include <QNetworkRequest> + +#ifdef QCA2_FOUND +#include <QtCrypto> +#endif class QNetworkReply; namespace Tomahawk { +class DropJobNotifier; /** * Small class to parse spotify links into query_ptrs * @@ -38,25 +52,50 @@ class RdioParser : public QObject { Q_OBJECT public: + explicit RdioParser( QObject* parent = 0 ); virtual ~RdioParser(); void parse( const QString& url ); void parse( const QStringList& urls ); + void setCreatePlaylist( bool createPlaylist ) { m_createPlaylist = createPlaylist; } + signals: void track( const Tomahawk::query_ptr& track ); void tracks( const QList< Tomahawk::query_ptr > tracks ); private slots: void expandedLinks( const QStringList& ); + void rdioReturned(); + void playlistCreated( Tomahawk::PlaylistRevision ); private: + void parseTrack( const QString& url ); + void fetchObjectsFromUrl( const QString& url, DropJob::DropType type ); + + QByteArray hmacSha1(QByteArray key, QByteArray baseString); + QNetworkRequest generateRequest( const QString& method, const QString& url, const QList< QPair< QByteArray, QByteArray > >& extraParams, QByteArray* postData ); + QPixmap pixmap() const; + void checkFinished(); void parseUrl( const QString& url ); bool m_multi; int m_count, m_total; - QList< query_ptr > m_queries; + QSet< QNetworkReply* > m_reqQueries; + DropJobNotifier* m_browseJob; + + QString m_title, m_creator; + playlist_ptr m_playlist; + + static QPixmap* s_pixmap; + + bool m_createPlaylist; + QList< query_ptr > m_tracks; + +#ifdef QCA2_FOUND + static QCA::Initializer m_qcaInit; +#endif }; } diff --git a/src/libtomahawk/utils/tomahawkutils.cpp b/src/libtomahawk/utils/tomahawkutils.cpp index e6a2ae830..3797557da 100644 --- a/src/libtomahawk/utils/tomahawkutils.cpp +++ b/src/libtomahawk/utils/tomahawkutils.cpp @@ -24,6 +24,7 @@ #include <QtGui/QColor> #include <QtCore/QDateTime> #include <QtCore/QDir> +#include <QtCore/QMutex> #include <QtGui/QLayout> #include <QtGui/QPainter> #include <QtGui/QPixmap> @@ -64,6 +65,8 @@ namespace TomahawkUtils static int s_headerHeight = 0; +static quint64 s_infosystemRequestId = 0; +static QMutex s_infosystemRequestIdMutex; #ifdef Q_WS_MAC QString @@ -431,6 +434,14 @@ drawBackgroundAndNumbers( QPainter* painter, const QString& text, const QRect& f painter->drawText( figRect.adjusted( -5, 0, 6, 0 ), text, to ); } +void +drawQueryBackground( QPainter* p, const QPalette& palette, const QRect& r, qreal lightnessFactor ) +{ + p->setPen( palette.mid().color().lighter( lightnessFactor * 100 ) ); + p->setBrush( palette.highlight().color().lighter( lightnessFactor * 100 ) ); + p->drawRoundedRect( r, 4.0, 4.0 ); +} + void unmarginLayout( QLayout* layout ) @@ -502,6 +513,15 @@ NetworkProxyFactory::setProxy( const QNetworkProxy& proxy ) } +bool NetworkProxyFactory::operator==( const NetworkProxyFactory& other ) +{ + if ( m_noProxyHosts != other.m_noProxyHosts or m_proxy != other.m_proxy ) + return false; + + return true; +} + + NetworkProxyFactory* proxyFactory() { @@ -690,4 +710,13 @@ removeDirectory( const QString& dir ) } +quint64 infosystemRequestId() +{ + QMutexLocker locker( &s_infosystemRequestIdMutex ); + quint64 result = s_infosystemRequestId; + s_infosystemRequestId++; + return result; +} + + } // ns diff --git a/src/libtomahawk/utils/tomahawkutils.h b/src/libtomahawk/utils/tomahawkutils.h index 30656d01c..924045a1d 100644 --- a/src/libtomahawk/utils/tomahawkutils.h +++ b/src/libtomahawk/utils/tomahawkutils.h @@ -26,6 +26,7 @@ #include <QtNetwork/QNetworkProxy> #include <QtCore/QStringList> #include <QtCore/QRect> +#include <QPalette> #define RESPATH ":/data/" @@ -64,6 +65,8 @@ namespace TomahawkUtils void setProxy( const QNetworkProxy &proxy ); QNetworkProxy proxy() { return m_proxy; } + bool operator==( const NetworkProxyFactory &other ); + private: QStringList m_noProxyHosts; QNetworkProxy m_proxy; @@ -85,6 +88,7 @@ namespace TomahawkUtils DLLEXPORT QPixmap createDragPixmap( MediaType type, int itemCount = 1 ); DLLEXPORT void drawBackgroundAndNumbers( QPainter* p, const QString& text, const QRect& rect ); + DLLEXPORT void drawQueryBackground( QPainter* p, const QPalette& palette, const QRect& r, qreal lightnessFactor = 1 ); DLLEXPORT void unmarginLayout( QLayout* layout ); @@ -106,6 +110,8 @@ namespace TomahawkUtils DLLEXPORT void setHeaderHeight( int height ); DLLEXPORT bool removeDirectory( const QString& dir ); + + DLLEXPORT quint64 infosystemRequestId(); } #endif // TOMAHAWKUTILS_H diff --git a/src/libtomahawk/viewmanager.cpp b/src/libtomahawk/viewmanager.cpp index 594abc40f..b7cd72b08 100644 --- a/src/libtomahawk/viewmanager.cpp +++ b/src/libtomahawk/viewmanager.cpp @@ -217,12 +217,12 @@ ViewManager::show( const Tomahawk::artist_ptr& artist ) Tomahawk::ViewPage* -ViewManager::show( const Tomahawk::album_ptr& album ) +ViewManager::show( const Tomahawk::album_ptr& album, Tomahawk::ModelMode initialMode ) { AlbumInfoWidget* swidget; if ( !m_albumViews.contains( album ) || m_albumViews.value( album ).isNull() ) { - swidget = new AlbumInfoWidget( album ); + swidget = new AlbumInfoWidget( album, initialMode ); m_albumViews.insert( album, swidget ); } else @@ -555,6 +555,12 @@ ViewManager::setPage( ViewPage* page, bool trackHistory ) if( obj->metaObject()->indexOfSignal( "descriptionChanged(QString)" ) > -1 ) connect( obj, SIGNAL( descriptionChanged( QString ) ), m_infobar, SLOT( setDescription( QString ) ), Qt::UniqueConnection ); + if( obj->metaObject()->indexOfSignal( "descriptionChanged(Tomahawk::artist_ptr)" ) > -1 ) + connect( obj, SIGNAL( descriptionChanged( Tomahawk::artist_ptr ) ), m_infobar, SLOT( setDescription( Tomahawk::artist_ptr ) ), Qt::UniqueConnection ); + + if( obj->metaObject()->indexOfSignal( "descriptionChanged(Tomahawk::album_ptr)" ) > -1 ) + connect( obj, SIGNAL( descriptionChanged( Tomahawk::album_ptr ) ), m_infobar, SLOT( setDescription( Tomahawk::album_ptr ) ), Qt::UniqueConnection ); + if( obj->metaObject()->indexOfSignal( "longDescriptionChanged(QString)" ) > -1 ) connect( obj, SIGNAL( longDescriptionChanged( QString ) ), m_infobar, SLOT( setLongDescription( QString ) ), Qt::UniqueConnection ); @@ -672,7 +678,19 @@ ViewManager::updateView() m_infobar->setVisible( currentPage()->showInfoBar() ); m_infobar->setCaption( currentPage()->title() ); - m_infobar->setDescription( currentPage()->description() ); + switch( currentPage()->descriptionType() ) + { + case ViewPage::TextType: + m_infobar->setDescription( currentPage()->description() ); + break; + case ViewPage::ArtistType: + m_infobar->setDescription( currentPage()->descriptionArtist() ); + break; + case ViewPage::AlbumType: + m_infobar->setDescription( currentPage()->descriptionAlbum() ); + break; + + } m_infobar->setLongDescription( currentPage()->longDescription() ); m_infobar->setPixmap( currentPage()->pixmap() ); diff --git a/src/libtomahawk/viewmanager.h b/src/libtomahawk/viewmanager.h index 38820b63b..3c2bed9a6 100644 --- a/src/libtomahawk/viewmanager.h +++ b/src/libtomahawk/viewmanager.h @@ -139,7 +139,7 @@ public slots: 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::album_ptr& album, Tomahawk::ModelMode withInitialMode = Tomahawk::InfoSystemMode ); Tomahawk::ViewPage* show( const Tomahawk::collection_ptr& collection ); Tomahawk::ViewPage* show( const Tomahawk::source_ptr& source ); diff --git a/src/libtomahawk/viewpage.h b/src/libtomahawk/viewpage.h index 6e32bcef9..2810830d1 100644 --- a/src/libtomahawk/viewpage.h +++ b/src/libtomahawk/viewpage.h @@ -23,6 +23,8 @@ #include "typedefs.h" #include "playlistinterface.h" +#include "artist.h" +#include "album.h" #include "utils/tomahawkutils.h" #include "dllmacro.h" @@ -33,6 +35,12 @@ namespace Tomahawk class DLLEXPORT ViewPage { public: + enum DescriptionType { + TextType = 0, + ArtistType = 1, + AlbumType = 2 + }; + ViewPage() {} virtual ~ViewPage() {} @@ -40,7 +48,12 @@ public: virtual Tomahawk::PlaylistInterface* playlistInterface() const = 0; virtual QString title() const = 0; + + virtual DescriptionType descriptionType() { return TextType; } virtual QString description() const = 0; + virtual Tomahawk::artist_ptr descriptionArtist() const { return Tomahawk::artist_ptr(); } + virtual Tomahawk::album_ptr descriptionAlbum() const { return Tomahawk::album_ptr(); } + virtual QString longDescription() const { return QString(); } virtual QPixmap pixmap() const { return QPixmap( RESPATH "icons/tomahawk-icon-128x128.png" ); } @@ -62,6 +75,8 @@ public: /** subclasses implementing ViewPage can emit the following signals: * nameChanged( const QString& ) * descriptionChanged( const QString& ) + * descriptionChanged( const Tomahawk::artist_ptr& artist ) + * descriptionChanged( const Tomahawk::album_ptr& album ) * longDescriptionChanged( const QString& ) * pixmapChanged( const QPixmap& ) * destroyed( QWidget* widget ); diff --git a/src/libtomahawk/widgets/Breadcrumb.cpp b/src/libtomahawk/widgets/Breadcrumb.cpp new file mode 100644 index 000000000..8409283ff --- /dev/null +++ b/src/libtomahawk/widgets/Breadcrumb.cpp @@ -0,0 +1,171 @@ +/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> === + * + * Copyright 2010-2011, Leo Franchi <lfranchi@kde.org> + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include "Breadcrumb.h" + +#include "BreadcrumbButton.h" +#include "utils/stylehelper.h" +#include "utils/logger.h" + +#include <QStylePainter> +#include <QPushButton> +#include <QHBoxLayout> +#include <QPropertyAnimation> +#include <QDebug> + +using namespace Tomahawk; + +Breadcrumb::Breadcrumb( QWidget* parent, Qt::WindowFlags f ) + : QWidget( parent, f ) + , m_model( 0 ) + + , m_buttonlayout( new QHBoxLayout( this ) ) +{ + m_buttonlayout->setSpacing( 0 ); + m_buttonlayout->setMargin( 0 ); + m_buttonlayout->setAlignment( Qt::AlignLeft ); + + setAutoFillBackground( true ); + setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Fixed ); + + setLayoutDirection( Qt::LeftToRight ); + setLayout( m_buttonlayout ); + show(); +} + +Breadcrumb::~Breadcrumb() +{ + +} + +void +Breadcrumb::setModel( QAbstractItemModel* model ) +{ + foreach ( BreadcrumbButton* b, m_buttons ) + b->deleteLater();; + m_buttons.clear(); + + m_model = model; + updateButtons( QModelIndex() ); +} + +void +Breadcrumb::setRootIcon( const QPixmap& pm ) +{ + m_rootIcon = pm; + + QPushButton* button = new QPushButton( QIcon( m_rootIcon ), "", this ); + button->setFlat( true ); + button->setStyleSheet( "QPushButton{ background-color: transparent; border: none; width:16px; height:16px;}" ); + m_buttonlayout->insertWidget( 0, button ); + m_buttonlayout->insertSpacing( 0,5 ); + m_buttonlayout->insertSpacing( 2,5 ); +} + + +void +Breadcrumb::paintEvent( QPaintEvent* ) +{ + QStylePainter p( this ); + StyleHelper::horizontalHeader( &p, rect() ); +} + +// updateFrom is the item that has changed---all children must be recomputed +// if invalid, redo the whole breadcrumb +void +Breadcrumb::updateButtons( const QModelIndex& updateFrom ) +{ + qDebug() << "Updating buttons:" << updateFrom.data(); + int cur = 0; + QModelIndex idx = updateFrom; + for ( int i = 0; i < m_buttons.count(); i++ ) + { + qDebug() << "Checking if this breadcrumb item changed:" << m_buttons[ i ]->currentIndex().data() << updateFrom.data() << ( m_buttons[ i ]->currentIndex() != updateFrom); + if ( m_buttons[ i ]->currentIndex() == updateFrom ) + { + cur = i; + break; + } + } + + // We set the parent index, so go up one + idx = idx.parent(); + + // Ok, changed all indices that are at cur or past it. lets update them + // When we get to the "end" of the tree, the leaf node is the chart itself + qDebug() << "DONE and beginning iteration:" << idx.data(); + while ( m_model->rowCount( idx ) > 0 ) + { + qDebug() << "CHANGED AND iterating:" << idx.data(); + BreadcrumbButton* btn = 0; + if ( m_buttons.size() <= cur ) + { + // We have to create a new button, doesn't exist yet + btn = new BreadcrumbButton( this, m_model ); + connect( btn, SIGNAL( currentIndexChanged( QModelIndex ) ), this, SLOT( breadcrumbComboChanged( QModelIndex ) ) ); + + m_buttonlayout->addWidget( btn ); + btn->show(); + + // Animate all buttons except the first + if ( m_buttons.count() > 0 && isVisible() ) + { + QPropertyAnimation* animation = new QPropertyAnimation( btn, "pos" ); + animation->setDuration( 300 ); + animation->setStartValue( m_buttons.last()->pos()); + animation->setEndValue( btn->pos() ); + animation->start( QAbstractAnimation::DeleteWhenStopped ); + } + + m_buttons.append( btn ); + } + else + { + // Got a button already, we just want to change the contents + btn = m_buttons[ cur ]; + } + + // The children of idx are what populates this combobox. + // It takes care of setting the default/user-populated value. + btn->setParentIndex( idx ); + + // Repeat with children + idx = btn->currentIndex(); + + cur++; + } + + // extra buttons to delete! (cur is 0-indexed) + while ( m_buttons.size() > cur ) + { + BreadcrumbButton* b = m_buttons.takeLast(); + m_buttonlayout->removeWidget( b ); + b->deleteLater(); + } + + // Now we're at the leaf, lets activate the chart + emit activateIndex( idx ); +} + +void +Breadcrumb::breadcrumbComboChanged( const QModelIndex& childSelected ) +{ + // Some breadcrumb buttons' combobox changed. lets update the child breadcrumbs + tDebug() << "Combo changed:" << childSelected.data(); + updateButtons( childSelected ); +} diff --git a/src/libtomahawk/widgets/Breadcrumb.h b/src/libtomahawk/widgets/Breadcrumb.h new file mode 100644 index 000000000..74adabf1a --- /dev/null +++ b/src/libtomahawk/widgets/Breadcrumb.h @@ -0,0 +1,83 @@ +/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> === + * + * Copyright 2010-2011, Leo Franchi <lfranchi@kde.org> + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#ifndef TOMAHAWK_BREADCRUMB_H +#define TOMAHAWK_BREADCRUMB_H + +#include <QWidget> +#include <QModelIndex> + +class QHBoxLayout; +class QAbstractItemModel; + +namespace Tomahawk { + +class BreadcrumbButton; + +/** + * A breadcrumb widget for Tomahawk's needs. + * + * This breadcrumb operates on a QAIM. It uses a KBreadcrumbSelectionModel to manage the visible + * breadcrumb selection. + * This breadcrumb always expands fully to the deepest child. + * Selections are remembered when switching to/from in parent nodes + * Items that have a DefaultRole set will automatically select the default unless the user has + * made a previous selection, which is saved in the UserSelection role + */ +class Breadcrumb : public QWidget +{ + Q_OBJECT +public: + enum ExtraRoles { + DefaultRole = Qt::UserRole + 1, + UserSelectedRole = Qt::UserRole + 2, + ChartIdRole = Qt::UserRole + 3 + }; + + explicit Breadcrumb( QWidget* parent = 0, Qt::WindowFlags f = 0 ); + virtual ~Breadcrumb(); + + void setModel( QAbstractItemModel* model ); + QAbstractItemModel* model() const { return m_model; } + + void setRootIcon( const QPixmap& pm ); + +protected: + virtual void paintEvent( QPaintEvent* ); + +signals: + void activateIndex( const QModelIndex& idx ); + +private slots: + void breadcrumbComboChanged( const QModelIndex& ); + +private: + // Takes an index in the selection model to update from (updates from that to all children) + void updateButtons( const QModelIndex& fromIndex ); + + QAbstractItemModel* m_model; + QPixmap m_rootIcon; + + QHBoxLayout* m_buttonlayout; + QList<BreadcrumbButton*> m_buttons; +}; + +} + +#endif // TOMAHAWK_BREADCRUMB_H + diff --git a/src/libtomahawk/widgets/siblingcrumbbutton.cpp b/src/libtomahawk/widgets/BreadcrumbButton.cpp similarity index 51% rename from src/libtomahawk/widgets/siblingcrumbbutton.cpp rename to src/libtomahawk/widgets/BreadcrumbButton.cpp index f11704aac..4a94b469a 100644 --- a/src/libtomahawk/widgets/siblingcrumbbutton.cpp +++ b/src/libtomahawk/widgets/BreadcrumbButton.cpp @@ -1,6 +1,7 @@ /* === This file is part of Tomahawk Player - <http://tomahawk-player.org> === * * Copyright 2011, Casey Link <unnamedrambler@gmail.com> + * Copyright 2010-2011, Leo Franchi <lfranchi@kde.org> * * Tomahawk is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -16,76 +17,45 @@ * along with Tomahawk. If not, see <http://www.gnu.org/licenses/>. */ -#include "siblingcrumbbutton.h" +#include "BreadcrumbButton.h" +#include "Breadcrumb.h" #include "combobox.h" #include "utils/stylehelper.h" #include "utils/tomahawkutils.h" -#include <QTimer> -#include <QDebug> +#include <QPaintEvent> #include <QPainter> -#include <QStyle> -#include <QStyleOption> +#include <QHBoxLayout> -BreadcrumbButtonBase* SiblingCrumbButtonFactory::newButton(QModelIndex index, BreadcrumbBar *parent) -{ - return new SiblingCrumbButton(index, parent); -} +using namespace Tomahawk; -SiblingCrumbButton::SiblingCrumbButton( - QModelIndex index, BreadcrumbBar *parent) - : BreadcrumbButtonBase(parent), - m_index(index), m_combo( new ComboBox(this) ) +BreadcrumbButton::BreadcrumbButton( Breadcrumb* parent, QAbstractItemModel* model ) + : QWidget( parent ) + , m_breadcrumb( parent ) + , m_model( model ) + , m_combo( new ComboBox( this ) ) { + setLayout( new QHBoxLayout ); + layout()->setContentsMargins( 0, 0, 0, 0 ); + layout()->setSpacing( 0 ); + layout()->addWidget( m_combo ); + setFixedHeight( TomahawkUtils::headerHeight() ); m_combo->setSizeAdjustPolicy( QComboBox::AdjustToContents ); - setIndex(index); - connect(m_combo, SIGNAL(activated(int)), SLOT(comboboxActivated(int))); + + setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Expanding ); + + connect( m_combo, SIGNAL( activated( int ) ), SLOT( comboboxActivated( int ) ) ); } -void SiblingCrumbButton::setIndex( QModelIndex index ) +void +BreadcrumbButton::paintEvent( QPaintEvent* ) { - if ( !(m_index == index && text() == index.data().toString()) ) - { - m_index = index; - setText( index.data().toString() ); - } - fillCombo(); -} - -QModelIndex SiblingCrumbButton::index() const -{ - return m_index; -} - -void SiblingCrumbButton::setActive( bool active ) -{ - Q_UNUSED( active ); - if ( active ) - QTimer::singleShot( 0, this, SLOT( activateSelf() ) ); -} - -bool SiblingCrumbButton::isActive() const -{ - return false; -} - -QSize SiblingCrumbButton::sizeHint() const -{ - // our width = width of combo + 20px for right-arrow and spacing - const int padding = hasChildren() ? 20 : 5; - return m_combo->sizeHint() + QSize( padding, 0 ); -} - -void SiblingCrumbButton::paintEvent( QPaintEvent *event ) -{ - Q_UNUSED( event ); - QPainter p( this ); QStyleOption opt; opt.initFrom( this ); - QRect r = opt.rect; + QRect r = rect(); StyleHelper::horizontalHeader( &p, r ); // draw the background @@ -97,13 +67,12 @@ void SiblingCrumbButton::paintEvent( QPaintEvent *event ) int rightSpacing = 10; int left = !reverse ? r.right()-rightSpacing - menuButtonWidth : r.left(); int right = !reverse ? r.right()-rightSpacing : r.left() + menuButtonWidth; - int height = sizeHint().height(); + int height = r.height(); QRect arrowRect( ( left + right ) / 2 + ( reverse ? 6 : -6 ), 0, height, height ); QStyleOption arrowOpt = opt; arrowOpt.rect = arrowRect; - QLine l1( left, 0, right, height/2 ); QLine l2( left, height, right, height/2 ); @@ -125,16 +94,38 @@ void SiblingCrumbButton::paintEvent( QPaintEvent *event ) p.drawLine( l2 ); } -void SiblingCrumbButton::fillCombo() +QSize +BreadcrumbButton::sizeHint() const { + // our width = width of combo + 20px for right-arrow and spacing + const int padding = hasChildren() ? 20 : 8; + return m_combo->sizeHint() + QSize( padding, 0 ); +} + + +void +BreadcrumbButton::setParentIndex( const QModelIndex& idx ) +{ + m_parentIndex = idx; + // Populate listview with list of children. + // Then try to find a default QStringList list; - int count = breadcrumbBar()->model()->rowCount(m_index.parent()); - for(int i = 0; i < count; ++i) { - QModelIndex sibling = m_index.sibling(i,0); - if( sibling.isValid() ) - list << sibling.data().toString(); + int count = m_model->rowCount( m_parentIndex ); + int defaultIndex = -1, userSelected = -1; + for ( int i = 0; i < count; ++i ) + { + QModelIndex idx = m_model->index( i, 0, m_parentIndex ); + if ( idx.isValid() ) + { + list << idx.data().toString(); + if ( idx.data( Breadcrumb::DefaultRole ).toBool() ) + defaultIndex = i; + if ( idx.data( Breadcrumb::UserSelectedRole ).toBool() ) + userSelected = i; + } } + if ( m_combo->count() && list.count() ) { // Check if it's the same, Don't change if it is, as it'll cause flickering @@ -146,31 +137,42 @@ void SiblingCrumbButton::fillCombo() return; } + m_combo->hide(); m_combo->clear(); - m_combo->addItems(list); - m_combo->setCurrentIndex( m_combo->findText(text())); + m_combo->addItems( list ); + + if ( userSelected > -1 ) + m_combo->setCurrentIndex( userSelected ); + else if ( defaultIndex > -1 ) + m_combo->setCurrentIndex( defaultIndex ); + + m_curIndex = m_model->index( m_combo->currentIndex(), 0, m_parentIndex ); + + m_combo->show(); m_combo->adjustSize(); } -void SiblingCrumbButton::comboboxActivated(int i) +void +BreadcrumbButton::comboboxActivated( int idx ) { - QModelIndex activated = m_index.sibling(i,0); - int count = breadcrumbBar()->model()->rowCount(activated); - if( count > 0 ) { -// qDebug() << "activated crumb with children:" << activated.child(0,0).data().toString(); - breadcrumbBar()->currentChangedTriggered(activated.child(0,0)); - } else { - // if it has no children, then emit itself - breadcrumbBar()->currentChangedTriggered(activated); - } + m_model->setData( m_curIndex, false, Breadcrumb::UserSelectedRole ); + + QModelIndex selected = m_model->index( idx, 0, m_parentIndex ); + m_curIndex = selected; + m_model->setData( selected, true, Breadcrumb::UserSelectedRole ); + + emit currentIndexChanged( selected ); } -void SiblingCrumbButton::activateSelf() + +bool +BreadcrumbButton::hasChildren() const { - comboboxActivated(m_index.row()); + return m_model->rowCount( m_model->index( m_combo->currentIndex(), 0, m_parentIndex ) ) > 0; } -bool SiblingCrumbButton::hasChildren() const +QModelIndex +BreadcrumbButton::currentIndex() const { - return m_index.model()->hasChildren(m_index); + return m_model->index( m_combo->currentIndex(), 0, m_parentIndex ); } diff --git a/src/libtomahawk/widgets/BreadcrumbButton.h b/src/libtomahawk/widgets/BreadcrumbButton.h new file mode 100644 index 000000000..d7efaa96d --- /dev/null +++ b/src/libtomahawk/widgets/BreadcrumbButton.h @@ -0,0 +1,69 @@ +/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> === + * + * Copyright 2010-2011, Leo Franchi <lfranchi@kde.org> + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#ifndef TOMAHAWK_BREADCRUMBBUTTON_H +#define TOMAHAWK_BREADCRUMBBUTTON_H + +#include <QWidget> +#include <QModelIndex> + +class ComboBox; +class QPaintEvent; + +namespace Tomahawk { + +class Breadcrumb; +class BreadcrumbModel; + +class BreadcrumbButton : public QWidget +{ + Q_OBJECT +public: + explicit BreadcrumbButton( Breadcrumb* parent, QAbstractItemModel* model ); + + void setParentIndex( const QModelIndex& idx ); + + // Which index is currently visible. This is the first, default or last selected + // calculated immediately after loading, or what the user has selected if he has made + // a manua;l selection + QModelIndex currentIndex() const; +protected: + virtual void paintEvent( QPaintEvent* ); + virtual QSize sizeHint() const; + +signals: + // Some combobox value is changed + void currentIndexChanged( const QModelIndex& childSelected ); + +private slots: + void comboboxActivated( int ); + +private: + bool hasChildren() const; + + Breadcrumb* m_breadcrumb; + QAbstractItemModel* m_model; + + QPersistentModelIndex m_parentIndex; + QPersistentModelIndex m_curIndex; + ComboBox* m_combo; +}; + +} + +#endif // TOMAHAWK_BREADCRUMBBUTTON_H diff --git a/src/libtomahawk/widgets/breadcrumbbar.cpp b/src/libtomahawk/widgets/breadcrumbbar.cpp deleted file mode 100644 index dea514c0e..000000000 --- a/src/libtomahawk/widgets/breadcrumbbar.cpp +++ /dev/null @@ -1,345 +0,0 @@ -/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> === - * - * Copyright 2011, Casey Link <unnamedrambler@gmail.com> - * Copyright (C) 2004-2011 Glenn Van Loon, glenn@startupmanager.org - * - * 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 <http://www.gnu.org/licenses/>. - */ - -#include "breadcrumbbar.h" -#include "breadcrumbbuttonbase.h" - -#include <QAbstractItemModel> -#include <QAbstractProxyModel> -#include <QHBoxLayout> -#include <QItemSelectionModel> -#include <QLabel> -#include <QPropertyAnimation> -#include <QResizeEvent> -#include <QPushButton> - -#include "utils/logger.h" - - -BreadcrumbBar::BreadcrumbBar( BreadcrumbButtonFactory *buttonFactory, QWidget *parent ) - : QWidget( parent ) - , m_buttonFactory( buttonFactory ) - , m_model( 0 ) - , m_selectionModel( 0 ) - , m_layout( new QHBoxLayout( this ) ) - , m_useAnimation( false ) - -{ - m_layout->setSpacing( 0 ); - m_layout->setMargin( 0 ); - m_layout->setAlignment( Qt::AlignLeft ); - - setAutoFillBackground( false ); - setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Fixed ); - - setLayoutDirection( Qt::LeftToRight ); - setLayout( m_layout ); - setMinimumWidth( 100 ); - show(); -} - - -BreadcrumbBar::BreadcrumbBar( QWidget *parent ) - : QWidget( parent ) - , m_buttonFactory( 0 ) - , m_model( 0 ) - , m_selectionModel( 0 ) - , m_layout( new QHBoxLayout( this ) ) - , m_useAnimation( false ) - -{ - m_layout->setSpacing( 0 ); - m_layout->setMargin( 0 ); - m_layout->setAlignment( Qt::AlignLeft ); - - setAutoFillBackground( false ); - setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Fixed ); - - setLayoutDirection( Qt::LeftToRight ); - setLayout( m_layout ); - setMinimumWidth( 100 ); - show(); -} - - -BreadcrumbBar::~BreadcrumbBar() -{ -} - - -void BreadcrumbBar::setButtonFactory(BreadcrumbButtonFactory *buttonFactory) -{ - tDebug( LOGVERBOSE ) << "Breadcrumbbar:: got me some button factory!"; - m_buttonFactory = buttonFactory; -} - - -BreadcrumbButtonFactory* BreadcrumbBar::buttonFactory() const -{ - return m_buttonFactory; -} - - -void BreadcrumbBar::appendButton(BreadcrumbButtonBase *widget, int stretch) -{ - m_layout->insertWidget(m_layout->count(), widget, stretch); - if( !m_useAnimation ) - return; //we're done here. - - // A nifty trick to force the widget to calculate its position and geometry - //widget->setAttribute(Qt::WA_DontShowOnScreen); - widget->show(); - //widget->setAttribute(Qt::WA_DontShowOnScreen, false); - - if( m_navButtons.size() > 0 ) { - QWidget* neighbor = m_layout->itemAt(m_layout->count()-2)->widget(); - QPropertyAnimation *animation = new QPropertyAnimation(widget,"pos"); - animation->setDuration(300); - animation->setStartValue(neighbor->pos()); - animation->setEndValue(widget->pos()); - animation->start(QAbstractAnimation::DeleteWhenStopped); - } -} - - -void BreadcrumbBar::deleteAnimationFinished() -{ - QPropertyAnimation *anim = qobject_cast<QPropertyAnimation*>(sender()); - - if( !anim ) - return; - QObject *obj = anim->targetObject(); - obj->deleteLater(); - anim->deleteLater(); -} - - -void BreadcrumbBar::deleteButton(BreadcrumbButtonBase *widget) -{ - widget->hide(); - widget->deleteLater(); - return; // all done here - - // Don't animate on delete. We expand a child recursively until it has no more children---this makes - // the deleting and creating animations overlap. - -// int index = m_layout->indexOf(widget); -// if( index != 0 && m_navButtons.size() > 0 ) { -// QWidget* neighbor = m_layout->itemAt(index-1)->widget(); -// QPropertyAnimation *animation = new QPropertyAnimation(widget,"pos"); -// m_layout->removeWidget(widget); -// connect(animation, SIGNAL(finished()), SLOT(deleteAnimationFinished())); -// animation->setDuration(300); -// animation->setStartValue(widget->pos()); -// animation->setEndValue(neighbor->pos()); -// animation->start(); -// } else { -// widget->hide(); -// widget->deleteLater(); -// } -} - - -void BreadcrumbBar::updateButtons() -{ - tDebug( LOGVERBOSE ) << "Breadcrumbbar:: updateButtons" << m_buttonFactory << m_selectionModel ; - if ( m_selectionModel ) - tDebug( LOGVERBOSE ) <<"Breadcrumbbar:: update buttoms current index"<< m_selectionModel->currentIndex().isValid(); - if ( !m_buttonFactory || !m_selectionModel || !m_selectionModel->currentIndex().isValid() ) - { - tDebug( LOGVERBOSE ) << "Breadcrumb:: updatebuttons failed!"; - return; - } - - QLinkedList<BreadcrumbButtonBase*>::iterator it = m_navButtons.begin(); - QLinkedList<BreadcrumbButtonBase*>::const_iterator const itEnd = m_navButtons.end(); - bool createButton = false; - - QModelIndex index = m_selectionModel->currentIndex(); - QList<QModelIndex> indexes; - while (index.parent().isValid()) - { - indexes.prepend(index); - index = index.parent(); - } - tDebug( LOGVERBOSE ) << "BreadcrumbBar::updateButtons:: " << index.data().toString(); - indexes.prepend(index); - - int count = indexes.size(), i = 0; - foreach (index, indexes) - { - createButton = (it == itEnd); - bool isLastButton = (++i == count); - - QString const dirName = index.data().toString(); - BreadcrumbButtonBase *button = 0; - if (createButton) - { - button = m_buttonFactory->newButton(index, this); - appendButton(button); - button->setActive(isLastButton); - m_navButtons.append(button); - } - else - { - button = *it; - button->setIndex(index); - button->setActive(isLastButton); - ++it; - } - } - - QLinkedList<BreadcrumbButtonBase*>::Iterator itBegin = it; - while (it != itEnd) - { - deleteButton(*it); - ++it; - } - m_navButtons.erase(itBegin, m_navButtons.end()); - - collapseButtons(); - - adjustSize(); -} - - -void BreadcrumbBar::collapseButtons() -{ - foreach (BreadcrumbButtonBase *button, m_navButtons) { - button->show(); - } - - const int desired_width = size().width(); - int current_width = sizeHint().width(); - - QLinkedList<BreadcrumbButtonBase*>::iterator it = m_navButtons.begin(); - QLinkedList<BreadcrumbButtonBase*>::const_iterator const itEnd = m_navButtons.end(); - it = m_navButtons.begin(); - while( current_width > desired_width && it != itEnd ) { - (*it)->hide(); - ++it; - current_width = sizeHint().width(); - } -} - - -void BreadcrumbBar::clearButtons() -{ - foreach (BreadcrumbButtonBase *button, m_navButtons) - { - button->hide(); - button->deleteLater(); - } - m_navButtons.clear(); -} - - -void BreadcrumbBar::currentIndexChanged() -{ - updateButtons(); -} - - -void BreadcrumbBar::setRootIcon(const QIcon &icon) -{ - - m_rootIcon = icon; - QPushButton* button = new QPushButton(icon, "", this); - button->setFlat(true); - button->setStyleSheet( "QPushButton{ background-color: transparent; border: none; width:16px; height:16px;}" ); - m_layout->insertWidget(0, button); - m_layout->insertSpacing(0,5); - m_layout->insertSpacing(2,5); - connect(button, SIGNAL(clicked()), this, SIGNAL(rootClicked())); -} - - -void BreadcrumbBar::setRootText(const QString &text) -{ - //TODO: implement this - m_rootText = text; - /*QLabel *label= new QLabel(this); - label->setPixmap(icon.pixmap(16,16)); - m_layout->insertWidget(0, label); - m_layout->insertSpacing(0,5); - m_layout->insertSpacing(2,5);*/ -} - - -void BreadcrumbBar::setUseAnimation(bool use) -{ - m_useAnimation = use; -} - - -bool BreadcrumbBar::useAnimation() const -{ - return m_useAnimation; -} - - -void BreadcrumbBar::setModel(QAbstractItemModel *model) -{ - m_model = model; - updateButtons(); -} - - -QAbstractItemModel* BreadcrumbBar::model() -{ - return m_model; -} - - -void BreadcrumbBar::setSelectionModel(QItemSelectionModel *selectionModel) -{ - m_selectionModel = selectionModel; - m_selectionModel->setCurrentIndex(m_model->index(0,0), QItemSelectionModel::SelectCurrent); - connect(m_selectionModel, - SIGNAL(currentChanged(QModelIndex const&, QModelIndex const&)), - this, SLOT(currentIndexChanged())); - updateButtons(); -} - - -QItemSelectionModel* BreadcrumbBar::selectionModel() -{ - return m_selectionModel; -} - - -QModelIndex BreadcrumbBar::currentIndex() -{ - return m_selectionModel->currentIndex(); -} - - -void BreadcrumbBar::currentChangedTriggered(QModelIndex const& index) -{ - Q_ASSERT(m_selectionModel); - m_selectionModel->setCurrentIndex( index, QItemSelectionModel::SelectCurrent); - emit currentIndexChanged(index); -} - - -void BreadcrumbBar::resizeEvent ( QResizeEvent * event ) -{ - Q_UNUSED( event ); - collapseButtons(); -} diff --git a/src/libtomahawk/widgets/breadcrumbbar.h b/src/libtomahawk/widgets/breadcrumbbar.h deleted file mode 100644 index 0ebe68df0..000000000 --- a/src/libtomahawk/widgets/breadcrumbbar.h +++ /dev/null @@ -1,198 +0,0 @@ -/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> === - * - * Copyright 2011, Casey Link <unnamedrambler@gmail.com> - * Copyright (C) 2004-2011 Glenn Van Loon, glenn@startupmanager.org - * - * 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 <http://www.gnu.org/licenses/>. - */ - -#ifndef BREADCRUMBBAR_H -#define BREADCRUMBBAR_H - -#include "breadcrumbbuttonbase.h" - -#include <QModelIndex> -#include <QWidget> -#include <QLinkedList> -#include <QIcon> - -class QAbstractItemModel; -class QItemSelectionModel; -class QAbstractItemModel; -class QAbstractProxyModel; -class QHBoxLayout; -class QItemSelectionModel; -class QResizeEvent; - -/** - * \brief A breadcrumb view for a QAbstractItemModel - * - * This a lean Breadcrumb navigation bar that supports the following features: - * - a QAbstractItemModel data source for MV goodness - * - client provided crumb button widgets (to use your own style and behavior) - * - client provided crumb animation [optional] - * - client provided root item (icon or text) [optional] - * - */ -class BreadcrumbBar : public QWidget -{ - Q_OBJECT - -public: - /** - * \brief breadcrumb bar constructor - * \param buttonFactory the button factory to instantiate bread crumbs from - */ - BreadcrumbBar(BreadcrumbButtonFactory *buttonFactory, QWidget *parent = 0); - /** - * \brief breadcrumb bar constructor - * You must set the button factory using BreadcrumbBar::setButtonFactory - */ - BreadcrumbBar(QWidget *parent = 0); - - virtual ~BreadcrumbBar(); - - /** - * \brief sets the button factory to use to create buttons - * \param buttonFactory the button factory - */ - void setButtonFactory(BreadcrumbButtonFactory *buttonFactory); - BreadcrumbButtonFactory* buttonFactory() const; - - /** - * \brief Set the icon that should be displayed at the root - * \param icon the icon - */ - void setRootIcon(const QIcon &icon); - /** - * \brief Set the text that should be displayed at the root - * \param test the text - */ - void setRootText(const QString &text); - - /** - * \brief Set whether the crumb animation should be used (default: false) - */ - void setUseAnimation(bool use); - bool useAnimation() const; - - /** - * \brief set the item model to use as a data source - * \param model the item model - * - * Although the item model can be of any structure, the breadcrumb bar - * works best when the model's structure is a tree. - */ - void setModel(QAbstractItemModel *model); - QAbstractItemModel *model(); - - /** - * \brief set the selection model used to determine the crumb items - * \param selectionModel the selection model - * - * The selection model is just as important as your model. In fact your - * model can be of any structure (even not-tree-like) as long as your - * selection model understands what it means for an item to be a crumb, - * and knows how to select it. - * - * If you have a standard tree model, you probably should use - * KBreadCrumbSelectionModel which encapsulates the concept of "When an - * item is selected, make a selection which includes its parent and - * ancestor items until the top is reached". - * - * \sa See Stephen Kelley's blog post here http://steveire.wordpress.com/2010/04/21/breadcrumbs-for-your-view - */ - void setSelectionModel(QItemSelectionModel *selectionModel); - QItemSelectionModel *selectionModel(); - - /** - * \brief get the index of the currently active item - * \return the active index - */ - QModelIndex currentIndex(); - - /** - * \brief used by crumbs to notify that the current index has changed - * \param index the new current index - */ - void currentChangedTriggered(QModelIndex const& index); - -signals: - void rootClicked(); - void currentIndexChanged(QModelIndex); - -protected: - /** - * \brief append a crumb widget - * \param button the crumb button to add - * \param stretch widget stretch factor - * Respects the useAnimation() setting. - */ - void appendButton(BreadcrumbButtonBase *button, int stretch = 0); - - /** - * \brief deletes a crumb from the bar - * \param button the crumb button to delete - * Respects the useAnimation() setting. - */ - void deleteButton(BreadcrumbButtonBase *button); - - /** - * \brief collapses crumbs when there isnt enough room for all of them - * Starts hiding from the left until we can fit all the buttons. - */ - void collapseButtons(); - /** - * \brief reimpl from QWidget - */ - void resizeEvent ( QResizeEvent * event ); - - -protected slots: - - /** - * \brief The current index has changed in the selection model - * When the selection model changes, we get notified here - */ - void currentIndexChanged(); - - /** - * \brief Recreate the button bar from the selection model - */ - void updateButtons(); - /** - * \brief clear breadcrumb buttons - */ - void clearButtons(); - - /** - * Called when the delete animation finishes so we can delete the button - * object. - */ - void deleteAnimationFinished(); - - -private: - BreadcrumbButtonFactory *m_buttonFactory; /*!< Factory used to create new crumbs */ - QAbstractItemModel *m_model; /*!< The source model */ - QItemSelectionModel *m_selectionModel; /*!< The selection model */ - QHBoxLayout *m_layout; /*!< The layout holding out crumb buttons */ - QLinkedList<BreadcrumbButtonBase*> m_navButtons; /*< Our list of crumbs! */ - QIcon m_rootIcon; /*!< The root icon */ - QString m_rootText; /*!< The root text */ - bool m_useAnimation; /*<!< Whether we should animate the transition or not */ -}; - - -#endif // BREADCRUMBBAR_H diff --git a/src/libtomahawk/widgets/breadcrumbbuttonbase.cpp b/src/libtomahawk/widgets/breadcrumbbuttonbase.cpp deleted file mode 100644 index 1c5fa8583..000000000 --- a/src/libtomahawk/widgets/breadcrumbbuttonbase.cpp +++ /dev/null @@ -1,39 +0,0 @@ -/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> === - * - * Copyright 2011, Casey Link <unnamedrambler@gmail.com> - * Copyright (C) 2004-2011 Glenn Van Loon, glenn@startupmanager.org - * - * 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 <http://www.gnu.org/licenses/>. - */ - -#include "breadcrumbbuttonbase.h" -#include "breadcrumbbar.h" - -BreadcrumbButtonBase::BreadcrumbButtonBase(BreadcrumbBar *parent) - : QPushButton(parent), m_breadcrumbBar(parent) -{ - setFocusPolicy(Qt::NoFocus); - setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Fixed); - setMinimumHeight(parent->minimumHeight()); -} - -BreadcrumbButtonBase::~BreadcrumbButtonBase() -{ -} - -BreadcrumbBar* BreadcrumbButtonBase::breadcrumbBar() const -{ - return m_breadcrumbBar; -} - diff --git a/src/libtomahawk/widgets/breadcrumbbuttonbase.h b/src/libtomahawk/widgets/breadcrumbbuttonbase.h deleted file mode 100644 index 7625b6bae..000000000 --- a/src/libtomahawk/widgets/breadcrumbbuttonbase.h +++ /dev/null @@ -1,94 +0,0 @@ -/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> === - * - * Copyright 2011, Casey Link <unnamedrambler@gmail.com> - * Copyright (C) 2004-2011 Glenn Van Loon, glenn@startupmanager.org - * - * 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 <http://www.gnu.org/licenses/>. - */ - -#ifndef BREADCRUMBBUTTONBASE_P_H -#define BREADCRUMBBUTTONBASE_P_H - -#include <QPushButton> -#include <QModelIndex> - -class BreadcrumbBar; -class BreadcrumbButtonBase; - -/** - * \brief an abstract factory class to create crumb buttons - * Subclass this class and make it return bread crumb buttons of your type. - */ -class BreadcrumbButtonFactory { - - public: - BreadcrumbButtonFactory(){} - - virtual ~BreadcrumbButtonFactory(){} - - /** - * \brief instantiates a new bread crumb button - * \param index the initial index this crumb should hold - * \param parent the breadcrumb bar this button will belong to - * \returns a new bread crumb button allocated on the heap. - * - * This method can be as simple as: - * \code - * return new MyBreadCrumbButton(index, parent); - * \endcode - * - */ - virtual BreadcrumbButtonBase* newButton(QModelIndex index, BreadcrumbBar *parent) = 0; - -}; - -/** - * \brief The button base class for the BreadcrumbBar - * Re-implement this to provide your own crumb buttons. Don't forget to supply - * a BreadcrumbButtonFactory as well. - */ -class BreadcrumbButtonBase : public QPushButton -{ - Q_OBJECT - -public: - explicit BreadcrumbButtonBase(BreadcrumbBar *parent); - virtual ~BreadcrumbButtonBase(); - - /** - * \brief retrieve the breadcrumb bar that this button belongs to - * \return the parent breadcrumb bar - */ - BreadcrumbBar* breadcrumbBar() const; - - /** - * \brief set the model item that this button represents - * \param index the index of the model items to display - */ - virtual void setIndex(QModelIndex index) = 0; - virtual QModelIndex index() const = 0; - - /** - * \brief sets whether this button is active or not - * \param active true for active, false for inactive - * You could, for example, make the active button bold. - */ - virtual void setActive(bool active) = 0; - virtual bool isActive() const = 0; - -private: - BreadcrumbBar *m_breadcrumbBar; -}; - -#endif // BREADCRUMBBUTTONBASE_P_H diff --git a/src/libtomahawk/widgets/infowidgets/AlbumInfoWidget.cpp b/src/libtomahawk/widgets/infowidgets/AlbumInfoWidget.cpp index 975e2f605..2f0f8415a 100644 --- a/src/libtomahawk/widgets/infowidgets/AlbumInfoWidget.cpp +++ b/src/libtomahawk/widgets/infowidgets/AlbumInfoWidget.cpp @@ -39,7 +39,7 @@ static QString s_aiInfoIdentifier = QString( "AlbumInfoWidget" ); using namespace Tomahawk; -AlbumInfoWidget::AlbumInfoWidget( const Tomahawk::album_ptr& album, QWidget* parent ) +AlbumInfoWidget::AlbumInfoWidget( const Tomahawk::album_ptr& album, ModelMode startingMode, QWidget* parent ) : QWidget( parent ) , ui( new Ui::AlbumInfoWidget ) { @@ -58,16 +58,19 @@ AlbumInfoWidget::AlbumInfoWidget( const Tomahawk::album_ptr& album, QWidget* par ui->albumsView->setAlbumModel( m_albumsModel ); m_tracksModel = new TreeModel( ui->tracksView ); - m_tracksModel->setMode( TreeModel::InfoSystem ); + m_tracksModel->setMode( startingMode ); ui->tracksView->setTreeModel( m_tracksModel ); ui->tracksView->setRootIsDecorated( false ); m_pixmap = QPixmap( RESPATH "images/no-album-art-placeholder.png" ).scaledToWidth( 48, Qt::SmoothTransformation ); m_button = new OverlayButton( ui->tracksView ); - m_button->setText( tr( "Click to show Super Collection Tracks" ) ); m_button->setCheckable( true ); - m_button->setChecked( true ); + m_button->setChecked( m_tracksModel->mode() == InfoSystemMode ); + if ( m_button->isChecked() ) + m_button->setText( tr( "Click to show Super Collection Tracks" ) ); + else + m_button->setText( tr( "Click to show Official Tracks" ) ); connect( m_button, SIGNAL( clicked() ), SLOT( onModeToggle() ) ); connect( m_tracksModel, SIGNAL( loadingStarted() ), SLOT( onLoadingStarted() ) ); @@ -88,11 +91,24 @@ AlbumInfoWidget::~AlbumInfoWidget() delete ui; } +PlaylistInterface* +AlbumInfoWidget::playlistInterface() const +{ + return ui->tracksView->playlistInterface(); +} + +void +AlbumInfoWidget::setMode( ModelMode mode ) +{ + if ( m_tracksModel->mode() != mode ) + onModeToggle(); +} + void AlbumInfoWidget::onModeToggle() { - m_tracksModel->setMode( m_button->isChecked() ? TreeModel::InfoSystem : TreeModel::Database ); + m_tracksModel->setMode( m_button->isChecked() ? InfoSystemMode : DatabaseMode ); m_tracksModel->clear(); m_tracksModel->addTracks( m_album, QModelIndex() ); @@ -130,6 +146,23 @@ AlbumInfoWidget::isBeingPlayed() const return false; } +artist_ptr AlbumInfoWidget::descriptionArtist() const +{ + if ( !m_album.isNull() && !m_album->artist().isNull() ) + return m_album->artist(); + + return artist_ptr(); +} + +ViewPage::DescriptionType +AlbumInfoWidget::descriptionType() +{ + if ( !m_album.isNull() && !m_album->artist().isNull() ) + return ViewPage::ArtistType; + + return ViewPage::TextType; +} + void AlbumInfoWidget::load( const album_ptr& album ) @@ -137,6 +170,7 @@ AlbumInfoWidget::load( const album_ptr& album ) m_album = album; m_title = album->name(); m_description = album->artist()->name(); + ui->albumsLabel->setText( tr( "Other Albums by %1" ).arg( album->artist()->name() ) ); m_tracksModel->addTracks( album, QModelIndex() ); diff --git a/src/libtomahawk/widgets/infowidgets/AlbumInfoWidget.h b/src/libtomahawk/widgets/infowidgets/AlbumInfoWidget.h index bb5c521d0..9542ab679 100644 --- a/src/libtomahawk/widgets/infowidgets/AlbumInfoWidget.h +++ b/src/libtomahawk/widgets/infowidgets/AlbumInfoWidget.h @@ -35,6 +35,7 @@ #include "infosystem/infosystem.h" #include "dllmacro.h" +#include "typedefs.h" class AlbumModel; class TreeModel; @@ -50,9 +51,29 @@ class DLLEXPORT AlbumInfoWidget : public QWidget, public Tomahawk::ViewPage Q_OBJECT public: - AlbumInfoWidget( const Tomahawk::album_ptr& album, QWidget* parent = 0 ); + AlbumInfoWidget( const Tomahawk::album_ptr& album, Tomahawk::ModelMode startingMode = Tomahawk::InfoSystemMode, QWidget* parent = 0 ); ~AlbumInfoWidget(); + virtual QWidget* widget() { return this; } + virtual Tomahawk::PlaylistInterface* playlistInterface() const; + + virtual QString title() const { return m_title; } + virtual DescriptionType descriptionType(); + virtual QString description() const { return m_description; } + virtual Tomahawk::artist_ptr descriptionArtist() const; + virtual QString longDescription() const { return m_longDescription; } + virtual QPixmap pixmap() const { if ( m_pixmap.isNull() ) return Tomahawk::ViewPage::pixmap(); else return m_pixmap; } + + void setMode( Tomahawk::ModelMode mode ); + + virtual bool isTemporaryPage() const { return true; } + virtual bool showStatsBar() const { return false; } + + virtual bool jumpToCurrentTrack() { return false; } + virtual bool isBeingPlayed() const; + +public slots: + /** \brief Loads information for a given album. * \param album The album that you want to load information for. * @@ -63,23 +84,9 @@ public: */ void load( const Tomahawk::album_ptr& album ); - virtual QWidget* widget() { return this; } - virtual Tomahawk::PlaylistInterface* playlistInterface() const { return 0; } - - virtual QString title() const { return m_title; } - virtual QString description() const { return m_description; } - virtual QString longDescription() const { return m_longDescription; } - virtual QPixmap pixmap() const { if ( m_pixmap.isNull() ) return Tomahawk::ViewPage::pixmap(); else return m_pixmap; } - - virtual bool isTemporaryPage() const { return true; } - virtual bool showStatsBar() const { return false; } - - virtual bool jumpToCurrentTrack() { return false; } - virtual bool isBeingPlayed() const; - signals: void longDescriptionChanged( const QString& description ); - void descriptionChanged( const QString& description ); + void descriptionChanged( const Tomahawk::artist_ptr& artist ); void pixmapChanged( const QPixmap& pixmap ); protected: diff --git a/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp b/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp index 32a32b083..3ead1a03c 100644 --- a/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp +++ b/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp @@ -17,12 +17,13 @@ */ #include "ArtistInfoWidget.h" +#include "ArtistInfoWidget_p.h" #include "ui_ArtistInfoWidget.h" #include "audio/audioengine.h" -#include "viewmanager.h" #include "playlist/treemodel.h" #include "playlist/playlistmodel.h" +#include "playlist/treeproxymodel.h" #include "database/databasecommand_alltracks.h" #include "database/databasecommand_allalbums.h" @@ -45,6 +46,8 @@ ArtistInfoWidget::ArtistInfoWidget( const Tomahawk::artist_ptr& artist, QWidget* { ui->setupUi( this ); + m_plInterface = new MetaPlaylistInterface( this ); + ui->albums->setFrameShape( QFrame::NoFrame ); ui->albums->setAttribute( Qt::WA_MacShowFocusRect, 0 ); ui->relatedArtists->setFrameShape( QFrame::NoFrame ); @@ -59,7 +62,7 @@ ArtistInfoWidget::ArtistInfoWidget( const Tomahawk::artist_ptr& artist, QWidget* TomahawkUtils::unmarginLayout( ui->albumHeader->layout() ); m_albumsModel = new TreeModel( ui->albums ); - m_albumsModel->setMode( TreeModel::InfoSystem ); + m_albumsModel->setMode( InfoSystemMode ); ui->albums->setTreeModel( m_albumsModel ); m_relatedModel = new TreeModel( ui->relatedArtists ); @@ -70,7 +73,7 @@ ArtistInfoWidget::ArtistInfoWidget( const Tomahawk::artist_ptr& artist, QWidget* m_topHitsModel->setStyle( TrackModel::Short ); ui->topHits->setTrackModel( m_topHitsModel ); - m_pixmap = QPixmap( RESPATH "images/no-album-art-placeholder.png" ).scaledToWidth( 48, Qt::SmoothTransformation ); + m_pixmap = QPixmap( RESPATH "images/no-album-no-case.png" ).scaledToWidth( 48, Qt::SmoothTransformation ); m_button = new OverlayButton( ui->albums ); m_button->setText( tr( "Click to show All Releases" ) ); @@ -93,14 +96,21 @@ ArtistInfoWidget::ArtistInfoWidget( const Tomahawk::artist_ptr& artist, QWidget* ArtistInfoWidget::~ArtistInfoWidget() { + delete m_plInterface; delete ui; } +PlaylistInterface* +ArtistInfoWidget::playlistInterface() const +{ + return m_plInterface; +} + void ArtistInfoWidget::onModeToggle() { - m_albumsModel->setMode( m_button->isChecked() ? TreeModel::InfoSystem : TreeModel::Database ); + m_albumsModel->setMode( m_button->isChecked() ? InfoSystemMode : DatabaseMode ); m_albumsModel->clear(); m_albumsModel->addAlbums( m_artist, QModelIndex() ); @@ -178,12 +188,15 @@ ArtistInfoWidget::load( const artist_ptr& artist ) requestData.input = QVariant::fromValue< Tomahawk::InfoSystem::InfoStringHash >( artistInfo ); requestData.type = Tomahawk::InfoSystem::InfoArtistImages; + requestData.requestId = TomahawkUtils::infosystemRequestId(); Tomahawk::InfoSystem::InfoSystem::instance()->getInfo( requestData ); requestData.type = Tomahawk::InfoSystem::InfoArtistSimilars; + requestData.requestId = TomahawkUtils::infosystemRequestId(); Tomahawk::InfoSystem::InfoSystem::instance()->getInfo( requestData ); requestData.type = Tomahawk::InfoSystem::InfoArtistSongs; + requestData.requestId = TomahawkUtils::infosystemRequestId(); Tomahawk::InfoSystem::InfoSystem::instance()->getInfo( requestData ); } diff --git a/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.h b/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.h index 65ef84f29..754578bef 100644 --- a/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.h +++ b/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.h @@ -47,6 +47,8 @@ namespace Ui class ArtistInfoWidget; } +class MetaPlaylistInterface; + class DLLEXPORT ArtistInfoWidget : public QWidget, public Tomahawk::ViewPage { Q_OBJECT @@ -66,7 +68,7 @@ public: void load( const Tomahawk::artist_ptr& artist ); virtual QWidget* widget() { return this; } - virtual Tomahawk::PlaylistInterface* playlistInterface() const { return 0; } + virtual Tomahawk::PlaylistInterface* playlistInterface() const; virtual QString title() const { return m_title; } virtual QString description() const { return m_description; } @@ -103,6 +105,7 @@ private: TreeModel* m_relatedModel; TreeModel* m_albumsModel; PlaylistModel* m_topHitsModel; + MetaPlaylistInterface* m_plInterface; OverlayButton* m_button; @@ -111,6 +114,8 @@ private: QString m_longDescription; QString m_infoId; QPixmap m_pixmap; + + friend class MetaPlaylistInterface; }; #endif // ARTISTINFOWIDGET_H diff --git a/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget_p.h b/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget_p.h new file mode 100644 index 000000000..4a3b6b50a --- /dev/null +++ b/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget_p.h @@ -0,0 +1,106 @@ +/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> === + * + * Copyright 2011, Leo Franchi <lfranchi@kde.org> + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#ifndef ARTISTINFOWIDGET_P_H +#define ARTISTINFOWIDGET_P_H + +#include "ArtistInfoWidget.h" +#include "ui_ArtistInfoWidget.h" +#include "playlistinterface.h" +#include "treeproxymodel.h" +#include "result.h" + +#include <QObject> + +class MetaPlaylistInterface : public QObject, public Tomahawk::PlaylistInterface +{ + Q_OBJECT +public: + explicit MetaPlaylistInterface( ArtistInfoWidget* w ) + : PlaylistInterface( this ) + , m_w( w ) + { + connect( m_w->ui->albums->proxyModel(), SIGNAL( repeatModeChanged( Tomahawk::PlaylistInterface::RepeatMode ) ), + SLOT( anyRepeatModeChanged( Tomahawk::PlaylistInterface::RepeatMode ) ) ); + connect( m_w->ui->relatedArtists->proxyModel(), SIGNAL( repeatModeChanged( Tomahawk::PlaylistInterface::RepeatMode ) ), + SLOT( anyRepeatModeChanged( Tomahawk::PlaylistInterface::RepeatMode ) ) ); + connect( m_w->ui->topHits->proxyModel(), SIGNAL( repeatModeChanged( Tomahawk::PlaylistInterface::RepeatMode ) ), + SLOT( anyRepeatModeChanged( Tomahawk::PlaylistInterface::RepeatMode ) ) ); + + connect( m_w->ui->albums->proxyModel(), SIGNAL( shuffleModeChanged( bool ) ), + SLOT( anyShuffleChanged( bool ) ) ); + connect( m_w->ui->relatedArtists->proxyModel(), SIGNAL( shuffleModeChanged( bool ) ), + SLOT( anyShuffleChanged( bool ) ) ); + connect( m_w->ui->topHits->proxyModel(), SIGNAL( shuffleModeChanged( bool ) ), + SLOT( anyShuffleChanged( bool ) ) ); + } + virtual ~MetaPlaylistInterface() {} + + + // Any one is fine, we keep them all synched + virtual RepeatMode repeatMode() const { return m_w->ui->albums->proxyModel()->repeatMode(); } + + virtual bool shuffled() const { return m_w->ui->albums->proxyModel()->shuffled(); } + + // Do nothing + virtual Tomahawk::result_ptr currentItem() const { return Tomahawk::result_ptr(); } + virtual Tomahawk::result_ptr siblingItem( int ) { return Tomahawk::result_ptr(); } + virtual int trackCount() const { return 0; } + virtual QList< Tomahawk::query_ptr > tracks() { return QList< Tomahawk::query_ptr >(); } + virtual int unfilteredTrackCount() const { return 0; } + +public slots: + virtual void setRepeatMode( RepeatMode mode ) + { + m_w->ui->albums->proxyModel()->setRepeatMode( mode ); + m_w->ui->relatedArtists->proxyModel()->setRepeatMode( mode ); + m_w->ui->topHits->proxyModel()->setRepeatMode( mode ); + } + + virtual void setShuffled( bool enabled ) + { + m_w->ui->albums->proxyModel()->setShuffled( enabled ); + m_w->ui->relatedArtists->proxyModel()->setShuffled( enabled ); + m_w->ui->topHits->proxyModel()->setShuffled( enabled ); + } + +signals: + void repeatModeChanged( Tomahawk::PlaylistInterface::RepeatMode mode ); + void shuffleModeChanged( bool enabled ); + + void trackCountChanged( unsigned int tracks ); + void sourceTrackCountChanged( unsigned int tracks ); + void nextTrackReady(); + +private slots: + void anyRepeatModeChanged( Tomahawk::PlaylistInterface::RepeatMode mode ) + { + emit repeatModeChanged( mode ); + } + + void anyShuffleChanged( bool enabled ) + { + emit shuffleModeChanged( enabled ); + } + +private: + ArtistInfoWidget* m_w; + +}; + +#endif diff --git a/src/libtomahawk/widgets/kbreadcrumbselectionmodel.cpp b/src/libtomahawk/widgets/kbreadcrumbselectionmodel.cpp deleted file mode 100644 index 92af04c96..000000000 --- a/src/libtomahawk/widgets/kbreadcrumbselectionmodel.cpp +++ /dev/null @@ -1,202 +0,0 @@ -/* - Copyright (C) 2010 Klarälvdalens Datakonsult AB, - a KDAB Group company, info@kdab.net, - author Stephen Kelly <stephen@kdab.com> - - This library is free software; you can redistribute it and/or modify it - under the terms of the GNU Library General Public License as published by - the Free Software Foundation; either version 2 of the License, or (at your - option) any later version. - - This library 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 Library General Public - License for more details. - - You should have received a copy of the GNU Library General Public License - along with this library; see the file COPYING.LIB. If not, write to the - Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - 02110-1301, USA. -*/ - - -#include "kbreadcrumbselectionmodel.h" -#include "kbreadcrumbselectionmodel_p.h" - -#include <QDebug> - -KBreadcrumbSelectionModel::KBreadcrumbSelectionModel(QItemSelectionModel *selectionModel, QObject* parent) - : QItemSelectionModel(const_cast<QAbstractItemModel *>(selectionModel->model()), parent), - d_ptr(new KBreadcrumbSelectionModelPrivate(this, selectionModel, MakeBreadcrumbSelectionInSelf)) -{ - d_ptr->init(); -} - -KBreadcrumbSelectionModel::KBreadcrumbSelectionModel(QItemSelectionModel *selectionModel, BreadcrumbTarget direction, QObject* parent) - : QItemSelectionModel(const_cast<QAbstractItemModel *>(selectionModel->model()), parent), - d_ptr(new KBreadcrumbSelectionModelPrivate(this, selectionModel, direction)) -{ - if ( direction != MakeBreadcrumbSelectionInSelf) - connect(selectionModel, SIGNAL(selectionChanged(const QItemSelection&,const QItemSelection&)), - this, SLOT(sourceSelectionChanged(const QItemSelection&,const QItemSelection&))); - - d_ptr->init(); -} - -KBreadcrumbSelectionModel::~KBreadcrumbSelectionModel() -{ - delete d_ptr; -} - -bool KBreadcrumbSelectionModel::isActualSelectionIncluded() const -{ - Q_D(const KBreadcrumbSelectionModel); - return d->m_includeActualSelection; -} - -void KBreadcrumbSelectionModel::setActualSelectionIncluded(bool includeActualSelection) -{ - Q_D(KBreadcrumbSelectionModel); - d->m_includeActualSelection = includeActualSelection; -} - -int KBreadcrumbSelectionModel::breadcrumbLength() const -{ - Q_D(const KBreadcrumbSelectionModel); - return d->m_selectionDepth; -} - -void KBreadcrumbSelectionModel::setBreadcrumbLength(int breadcrumbLength) -{ - Q_D(KBreadcrumbSelectionModel); - d->m_selectionDepth = breadcrumbLength; -} - -QItemSelection KBreadcrumbSelectionModelPrivate::getBreadcrumbSelection(const QModelIndex& index) -{ - QItemSelection breadcrumbSelection; - - if (m_includeActualSelection) - breadcrumbSelection.append(QItemSelectionRange(index)); - - QModelIndex parent = index.parent(); - int sumBreadcrumbs = 0; - bool includeAll = m_selectionDepth < 0; - while (parent.isValid() && (includeAll || sumBreadcrumbs < m_selectionDepth)) { - breadcrumbSelection.append(QItemSelectionRange(parent)); - parent = parent.parent(); - } - return breadcrumbSelection; -} - -QItemSelection KBreadcrumbSelectionModelPrivate::getBreadcrumbSelection(const QItemSelection& selection) -{ - QItemSelection breadcrumbSelection; - - if (m_includeActualSelection) - breadcrumbSelection = selection; - - QItemSelection::const_iterator it = selection.constBegin(); - const QItemSelection::const_iterator end = selection.constEnd(); - - for ( ; it != end; ++it) - { - QModelIndex parent = it->parent(); - - if (breadcrumbSelection.contains(parent)) - continue; - - int sumBreadcrumbs = 0; - bool includeAll = m_selectionDepth < 0; - - while (parent.isValid() && (includeAll || sumBreadcrumbs < m_selectionDepth)) - { - breadcrumbSelection.append(QItemSelectionRange(parent)); - parent = parent.parent(); - - if (breadcrumbSelection.contains(parent)) - break; - - ++sumBreadcrumbs; - } - } - return breadcrumbSelection; -} - -void KBreadcrumbSelectionModelPrivate::sourceSelectionChanged(const QItemSelection& selected, const QItemSelection& deselected) -{ - Q_Q(KBreadcrumbSelectionModel); - QItemSelection deselectedCrumbs = getBreadcrumbSelection(deselected); - QItemSelection selectedCrumbs = getBreadcrumbSelection(selected); - - QItemSelection removed = deselectedCrumbs; - foreach(const QItemSelectionRange &range, selectedCrumbs) - { - removed.removeAll(range); - } - - QItemSelection added = selectedCrumbs; - foreach(const QItemSelectionRange &range, deselectedCrumbs) - { - added.removeAll(range); - } - - if (!removed.isEmpty()) - { - q->QItemSelectionModel::select(removed, QItemSelectionModel::Deselect); - } - if (!added.isEmpty()) - { - q->QItemSelectionModel::select(added, QItemSelectionModel::Select); - } -} - -void KBreadcrumbSelectionModel::select(const QModelIndex &index, QItemSelectionModel::SelectionFlags command) -{ - Q_D(KBreadcrumbSelectionModel); - // When an item is removed, the current index is set to the top index in the model. - // That causes a selectionChanged signal with a selection which we do not want. - if ( d->m_ignoreCurrentChanged ) - { - d->m_ignoreCurrentChanged = false; - return; - } - if ( d->m_direction == MakeBreadcrumbSelectionInOther ) - { - d->m_selectionModel->select(d->getBreadcrumbSelection(index), command); - QItemSelectionModel::select(index, command); - } else { - d->m_selectionModel->select(index, command); - QItemSelectionModel::select(d->getBreadcrumbSelection(index), command); - } -} - -void KBreadcrumbSelectionModel::select(const QItemSelection &selection, QItemSelectionModel::SelectionFlags command) -{ - Q_D(KBreadcrumbSelectionModel); - QItemSelection bcc = d->getBreadcrumbSelection(selection); - if ( d->m_direction == MakeBreadcrumbSelectionInOther ) - { - d->m_selectionModel->select(selection, command); - QItemSelectionModel::select(bcc, command); - } else { - d->m_selectionModel->select(bcc, command); - QItemSelectionModel::select(selection, command); - } -} - -void KBreadcrumbSelectionModelPrivate::init() -{ - Q_Q(KBreadcrumbSelectionModel); - q->connect(m_selectionModel->model(), SIGNAL(layoutChanged()), SLOT(syncBreadcrumbs())); - q->connect(m_selectionModel->model(), SIGNAL(modelReset()), SLOT(syncBreadcrumbs())); - q->connect(m_selectionModel->model(), SIGNAL(rowsMoved(QModelIndex, int, int, QModelIndex, int)), SLOT(syncBreadcrumbs())); - // Don't need to handle insert & remove because they can't change the breadcrumbs on their own. -} - -void KBreadcrumbSelectionModelPrivate::syncBreadcrumbs() -{ - Q_Q(KBreadcrumbSelectionModel); - q->select(m_selectionModel->selection(), QItemSelectionModel::ClearAndSelect); -} - diff --git a/src/libtomahawk/widgets/kbreadcrumbselectionmodel.h b/src/libtomahawk/widgets/kbreadcrumbselectionmodel.h deleted file mode 100644 index 751be28ef..000000000 --- a/src/libtomahawk/widgets/kbreadcrumbselectionmodel.h +++ /dev/null @@ -1,164 +0,0 @@ -/* - Copyright (C) 2010 Klarälvdalens Datakonsult AB, - a KDAB Group company, info@kdab.net, - author Stephen Kelly <stephen@kdab.com> - - This library is free software; you can redistribute it and/or modify it - under the terms of the GNU Library General Public License as published by - the Free Software Foundation; either version 2 of the License, or (at your - option) any later version. - - This library 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 Library General Public - License for more details. - - You should have received a copy of the GNU Library General Public License - along with this library; see the file COPYING.LIB. If not, write to the - Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - 02110-1301, USA. -*/ - -#ifndef KBREADCRUMBSPROXYMODEL_H -#define KBREADCRUMBSPROXYMODEL_H - -#include <QtGui/QItemSelectionModel> -#include <QtCore/QAbstractItemModel> -#include <QObject> - - -class KBreadcrumbSelectionModelPrivate; - -/** - @class KBreadcrumbSelectionModel kbreadcrumbselectionmodel.h - - @brief Selects the parents of selected items to create breadcrumbs - - For example, if the tree is - @verbatim - - A - - B - - - C - - - D - - - - E - - - - - F - @endverbatim - - and E is selected, the selection can contain - - @verbatim - - B - - D - @endverbatim - - or - - @verbatim - - B - - D - - E - @endverbatim - - if isActualSelectionIncluded is true. - - The depth of the selection may also be set. For example if the breadcrumbLength is 1: - - @verbatim - - D - - E - @endverbatim - - And if breadcrumbLength is 2: - - @verbatim - - B - - D - - E - @endverbatim - - A KBreadcrumbsProxyModel with a breadcrumbLength of 0 and including the actual selection is - the same as a KSelectionProxyModel in the KSelectionProxyModel::ExactSelection configuration. - - @code - view1->setModel(rootModel); - - QItemSelectionModel *breadcrumbSelectionModel = new QItemSelectionModel(rootModel, this); - - KBreadcrumbSelectionModel *breadcrumbProxySelector = new KBreadcrumbSelectionModel(breadcrumbSelectionModel, rootModel, this); - - view1->setSelectionModel(breadcrumbProxySelector); - - KSelectionProxyModel *breadcrumbSelectionProxyModel = new KSelectionProxyModel( breadcrumbSelectionModel, this); - breadcrumbSelectionProxyModel->setSourceModel( rootModel ); - breadcrumbSelectionProxyModel->setFilterBehavior( KSelectionProxyModel::ExactSelection ); - - view2->setModel(breadcrumbSelectionProxyModel); - @endcode - - @image html kbreadcrumbselectionmodel.png "KBreadcrumbSelectionModel in several configurations" - - This can work in two directions. One option is for a single selection in the KBreadcrumbSelectionModel to invoke - the breadcrumb selection in its constructor argument. - - The other is for a selection in the itemselectionmodel in the constructor argument to cause a breadcrumb selection - in @p this. - - @since 4.5 - -*/ -class KBreadcrumbSelectionModel : public QItemSelectionModel -{ - Q_OBJECT -public: - enum BreadcrumbTarget - { - MakeBreadcrumbSelectionInOther, - MakeBreadcrumbSelectionInSelf - }; - - explicit KBreadcrumbSelectionModel(QItemSelectionModel *selectionModel, QObject* parent = 0); - KBreadcrumbSelectionModel(QItemSelectionModel *selectionModel, BreadcrumbTarget target, QObject* parent = 0); - virtual ~KBreadcrumbSelectionModel(); - - /** - Returns whether the actual selection in included in the proxy. - - The default is true. - */ - bool isActualSelectionIncluded() const; - - /** - Set whether the actual selection in included in the proxy to @p isActualSelectionIncluded. - */ - void setActualSelectionIncluded(bool isActualSelectionIncluded); - - /** - Returns the depth that the breadcrumb selection should go to. - */ - int breadcrumbLength() const; - - /** - Sets the depth that the breadcrumb selection should go to. - - If the @p breadcrumbLength is -1, all breadcrumbs are selected. - The default is -1 - */ - void setBreadcrumbLength(int breadcrumbLength); - - /* reimp */ void select(const QModelIndex &index, QItemSelectionModel::SelectionFlags command); - - /* reimp */ void select(const QItemSelection &selection, QItemSelectionModel::SelectionFlags command); - -protected: - KBreadcrumbSelectionModelPrivate * const d_ptr; -private: - //@cond PRIVATE - Q_DECLARE_PRIVATE(KBreadcrumbSelectionModel) - Q_PRIVATE_SLOT( d_func(),void sourceSelectionChanged(const QItemSelection &selected, const QItemSelection &deselected)) - Q_PRIVATE_SLOT( d_func(),void syncBreadcrumbs()) - //@cond PRIVATE -}; - - -#include "kbreadcrumbselectionmodel_p.h" //HACK ALERT - Why doesn't it compile without this?! -#endif diff --git a/src/libtomahawk/widgets/kbreadcrumbselectionmodel_p.h b/src/libtomahawk/widgets/kbreadcrumbselectionmodel_p.h deleted file mode 100644 index 93aac514c..000000000 --- a/src/libtomahawk/widgets/kbreadcrumbselectionmodel_p.h +++ /dev/null @@ -1,71 +0,0 @@ -/* - Copyright (C) 2010 Klarälvdalens Datakonsult AB, - a KDAB Group company, info@kdab.net, - author Stephen Kelly <stephen@kdab.com> - - This library is free software; you can redistribute it and/or modify it - under the terms of the GNU Library General Public License as published by - the Free Software Foundation; either version 2 of the License, or (at your - option) any later version. - - This library 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 Library General Public - License for more details. - - You should have received a copy of the GNU Library General Public License - along with this library; see the file COPYING.LIB. If not, write to the - Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - 02110-1301, USA. -*/ - -#ifndef KBREADCRUMBSPROXYMODEL_P_H -#define KBREADCRUMBSPROXYMODEL_P_H - -#include "kbreadcrumbselectionmodel.h" - -#include <QtGui/QItemSelectionModel> -#include <QtCore/QAbstractItemModel> -#include <QObject> - -class KBreadcrumbSelectionModelPrivate -{ - Q_DECLARE_PUBLIC(KBreadcrumbSelectionModel) - KBreadcrumbSelectionModel * const q_ptr; -public: - KBreadcrumbSelectionModelPrivate(KBreadcrumbSelectionModel *breadcrumbSelector, QItemSelectionModel *selectionModel, KBreadcrumbSelectionModel::BreadcrumbTarget direction) - : q_ptr(breadcrumbSelector), - m_includeActualSelection(true), - m_selectionDepth(-1), - m_showHiddenAscendantData(false), - m_selectionModel(selectionModel), - m_direction(direction), - m_ignoreCurrentChanged(false) - { - - } - - /** - Returns a selection containing the breadcrumbs for @p index - */ - QItemSelection getBreadcrumbSelection(const QModelIndex &index); - - /** - Returns a selection containing the breadcrumbs for @p selection - */ - QItemSelection getBreadcrumbSelection(const QItemSelection &selection); - - void sourceSelectionChanged(const QItemSelection &selected, const QItemSelection &deselected); - - void init(); - void syncBreadcrumbs(); - - bool m_includeActualSelection; - int m_selectionDepth; - bool m_showHiddenAscendantData; - QItemSelectionModel *m_selectionModel; - KBreadcrumbSelectionModel::BreadcrumbTarget m_direction; - bool m_ignoreCurrentChanged; -}; - -#endif diff --git a/src/libtomahawk/widgets/querylabel.cpp b/src/libtomahawk/widgets/querylabel.cpp index eee17a469..0218bfc6d 100644 --- a/src/libtomahawk/widgets/querylabel.cpp +++ b/src/libtomahawk/widgets/querylabel.cpp @@ -80,8 +80,10 @@ QueryLabel::init() setContentsMargins( 0, 0, 0, 0 ); setMouseTracking( true ); - align = Qt::AlignLeft; - mode = Qt::ElideMiddle; + m_useCustomPen = false; + m_useCustomFont = false; + m_align = Qt::AlignLeft; + m_mode = Qt::ElideMiddle; } @@ -234,38 +236,63 @@ QueryLabel::setQuery( const Tomahawk::query_ptr& query ) Qt::Alignment QueryLabel::alignment() const { - return align; + return m_align; } void QueryLabel::setAlignment( Qt::Alignment alignment ) { - if ( this->align != alignment ) + if ( m_align != alignment ) { - this->align = alignment; + m_align = alignment; update(); // no geometry change, repaint is sufficient } } +void +QueryLabel::setTextPen( const QPen & pen ) +{ + m_useCustomPen = true; + m_textPen = pen; +} + +QPen +QueryLabel::textPen() const +{ + return m_textPen; +} Qt::TextElideMode QueryLabel::elideMode() const { - return mode; + return m_mode; } void QueryLabel::setElideMode( Qt::TextElideMode mode ) { - if ( this->mode != mode ) + if ( m_mode != mode ) { - this->mode = mode; + m_mode = mode; updateLabel(); } } +QFont +QueryLabel::font() const +{ + return m_font; +} + +void +QueryLabel::setFont( const QFont& font ) +{ + m_useCustomFont = true; + m_font = font; +} + void QueryLabel::updateLabel() @@ -277,6 +304,17 @@ QueryLabel::updateLabel() update(); } +void +QueryLabel::setExtraContentsMargins( int left, int top, int right, int bottom ) +{ + QMargins margins = contentsMargins(); + margins.setLeft( margins.left() + left ); + margins.setTop( margins.top() + top ); + margins.setRight( margins.right() + right ); + margins.setBottom( margins.bottom() + bottom ); + setContentsMargins( margins ); +} + QSize QueryLabel::sizeHint() const @@ -290,7 +328,7 @@ QueryLabel::sizeHint() const QSize QueryLabel::minimumSizeHint() const { - switch ( mode ) + switch ( m_mode ) { case Qt::ElideNone: return sizeHint(); @@ -312,23 +350,28 @@ QueryLabel::paintEvent( QPaintEvent* event ) QPainter p( this ); QRect r = contentsRect(); QString s = text(); - const QString elidedText = fontMetrics().elidedText( s, mode, r.width() ); + const QString elidedText = fontMetrics().elidedText( s, m_mode, r.width() ); p.save(); p.setRenderHint( QPainter::Antialiasing ); + QFontMetrics fm = fontMetrics(); + if ( m_useCustomFont ) + { + p.setFont( m_font ); + fm = QFontMetrics( m_font ); + } + if ( m_hoverArea.width() ) { if ( elidedText != s ) { m_hoverArea.setLeft( 0 ); - m_hoverArea.setRight( fontMetrics().width( elidedText ) + contentsMargins().left() * 2 ); + m_hoverArea.setRight( fm.width( elidedText ) + contentsMargins().left() * 2 ); m_hoverType = Track; } - p.setPen( palette().mid().color() ); - p.setBrush( palette().highlight() ); - p.drawRoundedRect( m_hoverArea, 4.0, 4.0 ); + TomahawkUtils::drawQueryBackground( &p, palette(), m_hoverArea ); } if ( elidedText != s || ( m_result.isNull() && m_query.isNull() ) ) @@ -343,20 +386,23 @@ QueryLabel::paintEvent( QPaintEvent* event ) p.setBrush( palette().window() ); p.setPen( palette().color( foregroundRole() ) ); } - p.drawText( r, align, elidedText ); + p.drawText( r, m_align, elidedText ); } else { - const QFontMetrics& fm = fontMetrics(); int dashX = fm.width( DASH ); int artistX = m_type & Artist ? fm.width( artist() ) : 0; int albumX = m_type & Album ? fm.width( album() ) : 0; int trackX = m_type & Track ? fm.width( track() ) : 0; + if ( m_useCustomPen ) + p.setPen( m_textPen ); + if ( m_type & Artist ) { p.setBrush( palette().window() ); - p.setPen( palette().color( foregroundRole() ) ); + if ( !m_useCustomPen ) + p.setPen( palette().color( foregroundRole() ) ); if ( m_hoverType == Artist ) { @@ -364,17 +410,18 @@ QueryLabel::paintEvent( QPaintEvent* event ) p.setBrush( palette().highlight() ); } - p.drawText( r, align, artist() ); + p.drawText( r, m_align, artist() ); r.adjust( artistX, 0, 0, 0 ); } if ( m_type & Album ) { p.setBrush( palette().window() ); - p.setPen( palette().color( foregroundRole() ) ); + if ( !m_useCustomPen ) + p.setPen( palette().color( foregroundRole() ) ); if ( m_type & Artist ) { - p.drawText( r, align, DASH ); + p.drawText( r, m_align, DASH ); r.adjust( dashX, 0, 0, 0 ); } if ( m_hoverType == Album ) @@ -383,17 +430,18 @@ QueryLabel::paintEvent( QPaintEvent* event ) p.setBrush( palette().highlight() ); } - p.drawText( r, align, album() ); + p.drawText( r, m_align, album() ); r.adjust( albumX, 0, 0, 0 ); } if ( m_type & Track ) { p.setBrush( palette().window() ); - p.setPen( palette().color( foregroundRole() ) ); + if ( !m_useCustomPen ) + p.setPen( palette().color( foregroundRole() ) ); if ( m_type & Artist || m_type & Album ) { - p.drawText( r, align, DASH ); + p.drawText( r, m_align, DASH ); r.adjust( dashX, 0, 0, 0 ); } if ( m_hoverType == Track ) @@ -402,7 +450,7 @@ QueryLabel::paintEvent( QPaintEvent* event ) p.setBrush( palette().highlight() ); } - p.drawText( r, align, track() ); + p.drawText( r, m_align, track() ); r.adjust( trackX, 0, 0, 0 ); } } @@ -432,7 +480,8 @@ void QueryLabel::mousePressEvent( QMouseEvent* event ) { QFrame::mousePressEvent( event ); - time.start(); + m_time.restart(); + m_dragPos = event->pos(); } @@ -442,7 +491,8 @@ QueryLabel::mouseReleaseEvent( QMouseEvent* event ) QFrame::mouseReleaseEvent( event ); m_dragPos = QPoint(); - if ( time.elapsed() < qApp->doubleClickInterval() ) + qDebug() << "ELAPSED TIME" << m_time.elapsed() << "limit:" << qApp->doubleClickInterval(); + if ( m_time.elapsed() < qApp->doubleClickInterval() ) { switch( m_hoverType ) { @@ -484,7 +534,10 @@ QueryLabel::mouseMoveEvent( QMouseEvent* event ) return; } - const QFontMetrics& fm = fontMetrics(); + QFontMetrics fm = fontMetrics(); + if ( m_useCustomFont ) + fm = QFontMetrics( m_font ); + int dashX = fm.width( DASH ); int artistX = m_type & Artist ? fm.width( artist() ) : 0; int albumX = m_type & Album ? fm.width( album() ) : 0; diff --git a/src/libtomahawk/widgets/querylabel.h b/src/libtomahawk/widgets/querylabel.h index 1fed3d9c2..ef3b758f0 100644 --- a/src/libtomahawk/widgets/querylabel.h +++ b/src/libtomahawk/widgets/querylabel.h @@ -1,5 +1,5 @@ /* === This file is part of Tomahawk Player - <http://tomahawk-player.org> === - * + * * Copyright 2010-2011, Christian Muehlhaeuser <muesli@tomahawk-player.org> * * Tomahawk is free software: you can redistribute it and/or modify @@ -21,6 +21,7 @@ #include <QFrame> #include <QTime> +#include <QPen> #include "result.h" #include "query.h" @@ -67,6 +68,14 @@ public: Qt::TextElideMode elideMode() const; void setElideMode( Qt::TextElideMode mode ); + void setTextPen( const QPen& ); + QPen textPen() const; + + void setFont( const QFont& ); + QFont font() const; + + void setExtraContentsMargins( int left, int top, int right, int bottom ); + virtual QSize sizeHint() const; virtual QSize minimumSizeHint() const; @@ -98,18 +107,22 @@ protected: virtual void paintEvent( QPaintEvent* event ); virtual void startDrag(); - + private: QString smartAppend( QString& text, const QString& appendage ) const; - QTime time; + QTime m_time; DisplayType m_type; QString m_text; Tomahawk::result_ptr m_result; Tomahawk::query_ptr m_query; - Qt::Alignment align; - Qt::TextElideMode mode; + Qt::Alignment m_align; + Qt::TextElideMode m_mode; + + bool m_useCustomPen, m_useCustomFont; + QPen m_textPen; + QFont m_font; DisplayType m_hoverType; QRect m_hoverArea; diff --git a/src/libtomahawk/widgets/siblingcrumbbutton.h b/src/libtomahawk/widgets/siblingcrumbbutton.h deleted file mode 100644 index 7525f1c21..000000000 --- a/src/libtomahawk/widgets/siblingcrumbbutton.h +++ /dev/null @@ -1,80 +0,0 @@ -/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> === - * - * Copyright 2011, Casey Link <unnamedrambler@gmail.com> - * - * 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 <http://www.gnu.org/licenses/>. - */ - -#ifndef SIBLINGCRUMBBUTTON_H -#define SIBLINGCRUMBBUTTON_H - -#include "breadcrumbbuttonbase.h" -#include "breadcrumbbar.h" - -#include "combobox.h" - -#include <QList> -#include <QModelIndex> -#include <QPointer> -#include <QSize> -#include <QMenu> - -/** - * \brief A factory for sibling crumb buttons - */ -class SiblingCrumbButtonFactory : public BreadcrumbButtonFactory -{ - - public: - SiblingCrumbButtonFactory(){} - - virtual ~SiblingCrumbButtonFactory(){} - virtual BreadcrumbButtonBase* newButton(QModelIndex index, BreadcrumbBar *parent); -}; - -/** - * \brief A crumb button implementation where the dropdowns show sibling items - * Unlike most crumb buttons, this one shows a list of sibling items (as - * opposed to child items). This is desireable in certain circumstances. - */ -class SiblingCrumbButton : public BreadcrumbButtonBase -{ - Q_OBJECT - -public: - SiblingCrumbButton(QModelIndex index, BreadcrumbBar *parent); - - void setIndex(QModelIndex index); - QModelIndex index() const; - void setActive(bool active); - bool isActive() const; - virtual QSize sizeHint() const; - -protected: - virtual void paintEvent(QPaintEvent *event); - -private slots: - void fillCombo(); - void comboboxActivated(int i); - void activateSelf(); - -private: - bool hasChildren() const; - - QModelIndex m_index; /*!< our current index */ - ComboBox *m_combo; /*!< our combobox! */ - -}; - -#endif // SIBLINGCRUMBBUTTON_H diff --git a/src/libtomahawk/widgets/whatshotwidget.cpp b/src/libtomahawk/widgets/whatshotwidget.cpp index 89a113fa1..a0ac24639 100644 --- a/src/libtomahawk/widgets/whatshotwidget.cpp +++ b/src/libtomahawk/widgets/whatshotwidget.cpp @@ -34,8 +34,6 @@ #include "playlist/playlistmodel.h" #include "playlist/treeproxymodel.h" #include "widgets/overlaywidget.h" -#include "widgets/siblingcrumbbutton.h" -#include "widgets/kbreadcrumbselectionmodel.h" #include "utils/tomahawkutils.h" #include "utils/logger.h" #include <pipeline.h> @@ -52,6 +50,7 @@ static QString s_whatsHotIdentifier = QString( "WhatsHotWidget" ); WhatsHotWidget::WhatsHotWidget( QWidget* parent ) : QWidget( parent ) , ui( new Ui::WhatsHotWidget ) + , m_sortedProxy( 0 ) { ui->setupUi( this ); @@ -65,17 +64,14 @@ WhatsHotWidget::WhatsHotWidget( QWidget* parent ) TomahawkUtils::unmarginLayout( ui->breadCrumbLeft->layout() ); TomahawkUtils::unmarginLayout( ui->verticalLayout->layout() ); - //set crumb widgets - SiblingCrumbButtonFactory * crumbFactory = new SiblingCrumbButtonFactory; - m_crumbModelLeft = new QStandardItemModel( this ); + m_sortedProxy = new QSortFilterProxyModel( this ); + m_sortedProxy->setDynamicSortFilter( true ); + m_sortedProxy->setFilterCaseSensitivity( Qt::CaseInsensitive ); - ui->breadCrumbLeft->setButtonFactory( crumbFactory ); - ui->breadCrumbLeft->setModel( m_crumbModelLeft ); - ui->breadCrumbLeft->setRootIcon(QIcon( RESPATH "images/charts.png" )); - ui->breadCrumbLeft->setUseAnimation( true ); + ui->breadCrumbLeft->setRootIcon( QPixmap( RESPATH "images/charts.png" ) ); - connect( ui->breadCrumbLeft, SIGNAL( currentIndexChanged( QModelIndex ) ), SLOT( leftCrumbIndexChanged(QModelIndex) ) ); + connect( ui->breadCrumbLeft, SIGNAL( activateIndex( QModelIndex ) ), SLOT( leftCrumbIndexChanged(QModelIndex) ) ); ui->tracksViewLeft->setFrameShape( QFrame::NoFrame ); ui->tracksViewLeft->setAttribute( Qt::WA_MacShowFocusRect, 0 ); @@ -100,7 +96,6 @@ WhatsHotWidget::WhatsHotWidget( QWidget* parent ) connect( Tomahawk::InfoSystem::InfoSystem::instance(), SIGNAL( finished( QString ) ), SLOT( infoSystemFinished( QString ) ) ); - connect( AudioEngine::instance(), SIGNAL( playlistChanged( Tomahawk::PlaylistInterface* ) ), this, SLOT( playlistChanged( Tomahawk::PlaylistInterface* ) ) ); QTimer::singleShot( 0, this, SLOT( fetchData() ) ); } @@ -145,9 +140,10 @@ WhatsHotWidget::fetchData() requestData.caller = s_whatsHotIdentifier; requestData.customData = QVariantMap(); requestData.input = QVariant::fromValue< Tomahawk::InfoSystem::InfoStringHash >( artistInfo ); - requestData.type = Tomahawk::InfoSystem::InfoChartCapabilities; - Tomahawk::InfoSystem::InfoSystem::instance()->getInfo( requestData, 20000, true ); + requestData.timeoutMillis = 20000; + requestData.allSources = true; + Tomahawk::InfoSystem::InfoSystem::instance()->getInfo( requestData ); tDebug( LOGVERBOSE ) << "WhatsHot: requested InfoChartCapabilities"; } @@ -171,7 +167,12 @@ WhatsHotWidget::infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData requestDat QStandardItem *rootItem= m_crumbModelLeft->invisibleRootItem(); tDebug( LOGVERBOSE ) << "WhatsHot:: " << returnedData.keys(); - foreach( const QString label, returnedData.keys() ) + QVariantMap defaults; + if ( returnedData.contains( "defaults" ) ) + defaults = returnedData.take( "defaults" ).toMap(); + QString defaultSource = returnedData.take( "defaultSource" ).toString(); + + foreach ( const QString label, returnedData.keys() ) { tDebug( LOGVERBOSE ) << "WhatsHot:: parsing " << label; QStandardItem *childItem = parseNode( rootItem, label, returnedData[label] ); @@ -179,10 +180,42 @@ WhatsHotWidget::infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData requestDat rootItem->appendRow(childItem); } - KBreadcrumbSelectionModel *selectionModelLeft = new KBreadcrumbSelectionModel(new QItemSelectionModel(m_crumbModelLeft, this), this); - ui->breadCrumbLeft->setSelectionModel(selectionModelLeft); + // Set the default source + // Set the default chart for each source + for ( int i = 0; i < rootItem->rowCount(); i++ ) + { + QStandardItem* source = rootItem->child( i, 0 ); + if ( defaultSource.toLower() == source->text().toLower() ) + { + qDebug() << "Setting DEFAULT SOURCE:" << source->text(); + source->setData( true, Breadcrumb::DefaultRole ); + } - //ui->breadCrumbRight->setSelectionModel(selectionModelLeft); + if ( defaults.contains( source->text().toLower() ) ) + { + QStringList defaultIndices = defaults[ source->text().toLower() ].toStringList(); + QStandardItem* cur = source; + + foreach( const QString& index, defaultIndices ) + { + // Go through the children of the current item, marking the default one as default + for ( int k = 0; k < cur->rowCount(); k++ ) + { + if ( cur->child( k, 0 )->text() == index ) + { + tDebug() << "Found DEFAULT ITEM:" << index; + cur = cur->child( k, 0 ); // this is the default, drill down into the default to pick the next default + cur->setData( true, Breadcrumb::DefaultRole ); + break; + } + } + } + } + } + + m_sortedProxy->setSourceModel( m_crumbModelLeft ); + m_sortedProxy->sort( 0, Qt::AscendingOrder ); + ui->breadCrumbLeft->setModel( m_sortedProxy ); break; } case InfoSystem::InfoChart: @@ -212,7 +245,8 @@ WhatsHotWidget::infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData requestDat m_artistModels[ chartId ] = artistsModel; - setLeftViewArtists( artistsModel ); + if ( m_queueItemToShow == chartId ) + setLeftViewArtists( artistsModel ); } else if( type == "albums" ) { @@ -233,7 +267,9 @@ WhatsHotWidget::infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData requestDat albumModel->addAlbums( al ); m_albumModels[ chartId ] = albumModel; - setLeftViewAlbums( albumModel ); + + if ( m_queueItemToShow == chartId ) + setLeftViewAlbums( albumModel ); } else if( type == "tracks" ) { @@ -252,7 +288,9 @@ WhatsHotWidget::infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData requestDat trackModel->append( tracklist ); m_trackModels[ chartId ] = trackModel; - setLeftViewTracks( trackModel ); + + if ( m_queueItemToShow == chartId ) + setLeftViewTracks( trackModel ); } else { @@ -278,10 +316,10 @@ void WhatsHotWidget::leftCrumbIndexChanged( QModelIndex index ) { tDebug( LOGVERBOSE ) << "WhatsHot:: left crumb changed" << index.data(); - QStandardItem* item = m_crumbModelLeft->itemFromIndex( index ); + QStandardItem* item = m_crumbModelLeft->itemFromIndex( m_sortedProxy->mapToSource( index ) ); if( !item ) return; - if( !item->data().isValid() ) + if( !item->data( Breadcrumb::ChartIdRole ).isValid() ) return; @@ -293,7 +331,7 @@ WhatsHotWidget::leftCrumbIndexChanged( QModelIndex index ) } - const QString chartId = item->data().toString(); + const QString chartId = item->data( Breadcrumb::ChartIdRole ).toString(); if ( m_artistModels.contains( chartId ) ) { @@ -327,13 +365,15 @@ WhatsHotWidget::leftCrumbIndexChanged( QModelIndex index ) requestData.caller = s_whatsHotIdentifier; requestData.customData = customData; requestData.input = QVariant::fromValue< Tomahawk::InfoSystem::InfoStringHash >( criteria ); - requestData.type = Tomahawk::InfoSystem::InfoChart; + requestData.timeoutMillis = 20000; + requestData.allSources = true; qDebug() << "Making infosystem request for chart of type:" <<chartId; - Tomahawk::InfoSystem::InfoSystem::instance()->getInfo( requestData, 20000, true ); + Tomahawk::InfoSystem::InfoSystem::instance()->getInfo( requestData ); m_queuedFetches.insert( chartId ); + m_queueItemToShow = chartId; } @@ -367,7 +407,11 @@ WhatsHotWidget::parseNode( QStandardItem* parentItem, const QString &label, cons foreach ( Tomahawk::InfoSystem::InfoStringHash chart, charts ) { QStandardItem *childItem= new QStandardItem( chart[ "label" ] ); - childItem->setData( chart[ "id" ] ); + childItem->setData( chart[ "id" ], Breadcrumb::ChartIdRole ); + if ( chart.value( "default", "" ) == "true") + { + childItem->setData( true, Breadcrumb::DefaultRole ); + } sourceItem->appendRow( childItem ); } } @@ -386,7 +430,6 @@ WhatsHotWidget::parseNode( QStandardItem* parentItem, const QString &label, cons foreach ( const QVariant value, dataList ) { - qDebug() << "CREATED:" << value.toString(); QStandardItem *childItem= new QStandardItem(value.toString()); sourceItem->appendRow(childItem); } diff --git a/src/libtomahawk/widgets/whatshotwidget.h b/src/libtomahawk/widgets/whatshotwidget.h index e13c0421e..ecd0be8ef 100644 --- a/src/libtomahawk/widgets/whatshotwidget.h +++ b/src/libtomahawk/widgets/whatshotwidget.h @@ -31,6 +31,7 @@ #include "dllmacro.h" +class QSortFilterProxyModel; class QStandardItemModel; class QStandardItem; class TreeModel; @@ -91,11 +92,13 @@ private: Ui::WhatsHotWidget *ui; QStandardItemModel* m_crumbModelLeft; + QSortFilterProxyModel* m_sortedProxy; // Cache our model data QHash< QString, AlbumModel* > m_albumModels; QHash< QString, TreeModel* > m_artistModels; QHash< QString, PlaylistModel* > m_trackModels; + QString m_queueItemToShow; QSet< QString > m_queuedFetches; QTimer* m_timer; }; diff --git a/src/libtomahawk/widgets/whatshotwidget.ui b/src/libtomahawk/widgets/whatshotwidget.ui index 4caa5e32b..7c635acf8 100644 --- a/src/libtomahawk/widgets/whatshotwidget.ui +++ b/src/libtomahawk/widgets/whatshotwidget.ui @@ -12,7 +12,7 @@ </property> <layout class="QVBoxLayout" name="verticalLayout_2"> <item> - <widget class="HeaderBreadCrumb" name="breadCrumbLeft" native="true"/> + <widget class="Tomahawk::Breadcrumb" name="breadCrumbLeft" native="true"/> </item> <item> <widget class="QStackedWidget" name="stackLeft"> @@ -79,9 +79,10 @@ <header>playlist/playlistview.h</header> </customwidget> <customwidget> - <class>HeaderBreadCrumb</class> + <class>Tomahawk::Breadcrumb</class> <extends>QWidget</extends> - <header location="global">widgets/headerbreadcrumb.h</header> + <header>widgets/Breadcrumb.h</header> + <container>1</container> </customwidget> </customwidgets> <resources/> diff --git a/src/settingsdialog.cpp b/src/settingsdialog.cpp index ef56af80f..6914f938e 100644 --- a/src/settingsdialog.cpp +++ b/src/settingsdialog.cpp @@ -233,6 +233,7 @@ SettingsDialog::~SettingsDialog() m_resolversModel->saveScriptResolvers(); s->applyChanges(); + s->sync(); } else qDebug() << "Settings dialog cancelled, NOT saving prefs."; diff --git a/src/sourcetree/items/categoryitems.cpp b/src/sourcetree/items/categoryitems.cpp index c1237cd97..896ffdd21 100644 --- a/src/sourcetree/items/categoryitems.cpp +++ b/src/sourcetree/items/categoryitems.cpp @@ -256,7 +256,7 @@ CategoryAddItem::dropMimeData( const QMimeData* data, Qt::DropAction ) if ( data->hasFormat( "application/tomahawk.dragsource.type" ) ) dj->setProperty( "dragsource", QString::fromUtf8( data->data( "application/tomahawk.dragsource.type" ) ) ); - connect( dj, SIGNAL( tracks( QList< Tomahawk::query_ptr > ) ), this, SLOT( parsedDroppedTracks( QList< Tomahawk::query_ptr > ) ) ); + connect( dj, SIGNAL( tracks( QList< Tomahawk::query_ptr > ) ), this, SLOT( parsedDroppedTracks( QList< Tomahawk::query_ptr > ) ), Qt::QueuedConnection ); if ( dropType() == DropTypeAllFromArtist ) dj->setGetWholeArtists( true ); if ( dropType() == DropTypeThisAlbum ) diff --git a/src/sourcetree/sourcetreeview.cpp b/src/sourcetree/sourcetreeview.cpp index 59e9a4d01..39771bdc7 100644 --- a/src/sourcetree/sourcetreeview.cpp +++ b/src/sourcetree/sourcetreeview.cpp @@ -115,8 +115,11 @@ SourceTreeView::SourceTreeView( QWidget* parent ) connect( this, SIGNAL( catchUpRequest() ), m_latchManager, SLOT( catchUpRequest() ) ); } + SourceTreeView::~SourceTreeView() -{} +{ +} + void SourceTreeView::setupMenus() @@ -151,7 +154,7 @@ SourceTreeView::setupMenus() m_latchOnAction->setText( tr( "&Catch Up" ) ); m_latchMenu.addSeparator(); m_latchOffAction = m_latchMenu.addAction( tr( "&Stop Listening Along" ) ); - connect( m_latchOffAction, SIGNAL( triggered() ), SLOT( latchOff() ) ); + connect( m_latchOffAction, SIGNAL( triggered() ), SLOT( latchOff() ) ); } } } @@ -231,6 +234,7 @@ SourceTreeView::selectRequest( const QPersistentModelIndex& idx ) } } + void SourceTreeView::expandRequest( const QPersistentModelIndex &idx ) { @@ -245,6 +249,7 @@ SourceTreeView::loadPlaylist() onItemActivated( m_contextMenuIndex ); } + void SourceTreeView::deletePlaylist( const QModelIndex& idxIn ) { @@ -353,6 +358,7 @@ SourceTreeView::latchOnOrCatchUp() emit latchRequest( source ); } + void SourceTreeView::latchOff() { @@ -479,8 +485,9 @@ SourceTreeView::dragMoveEvent( QDragMoveEvent* event ) m_dropRect = QRect(); } - if ( accept ) + if ( accept || DropJob::isDropType( DropJob::Playlist, event->mimeData() ) ) { + // Playlists are accepted always since they can be dropped anywhere //tDebug() << Q_FUNC_INFO << "Accepting"; event->setDropAction( Qt::CopyAction ); event->accept(); @@ -533,6 +540,9 @@ SourceTreeView::dropEvent( QDropEvent* event ) dropThis->setDropTypes( DropJob::Playlist ); dropThis->setDropAction( DropJob::Create ); dropThis->parseMimeData( event->mimeData() ); + + // Don't add it to the playlist under drop, it's a new playlist now + return; } QTreeView::dropEvent( event ); diff --git a/src/tomahawkapp.cpp b/src/tomahawkapp.cpp index be5c83fd6..69126417a 100644 --- a/src/tomahawkapp.cpp +++ b/src/tomahawkapp.cpp @@ -182,8 +182,6 @@ TomahawkApp::init() TomahawkUtils::setProxyFactory( proxyFactory ); - Echonest::Config::instance()->setAPIKey( "JRIHWEP6GPOER2QQ6" ); - m_audioEngine = QWeakPointer<AudioEngine>( new AudioEngine ); m_scanManager = QWeakPointer<ScanManager>( new ScanManager( this ) ); new Pipeline( this ); @@ -194,6 +192,8 @@ TomahawkApp::init() tDebug() << "Init Database."; initDatabase(); + Echonest::Config::instance()->setAPIKey( "JRIHWEP6GPOER2QQ6" ); + tDebug() << "Init Echonest Factory."; GeneratorFactory::registerFactory( "echonest", new EchonestFactory ); tDebug() << "Init Database Factory."; @@ -228,9 +228,7 @@ TomahawkApp::init() tDebug() << "Init InfoSystem."; m_infoSystem = QWeakPointer<Tomahawk::InfoSystem::InfoSystem>( new Tomahawk::InfoSystem::InfoSystem( this ) ); - Echonest::Config::instance()->setAPIKey( "JRIHWEP6GPOER2QQ6" ); Echonest::Config::instance()->setNetworkAccessManager( TomahawkUtils::nam() ); - EchonestGenerator::setupCatalogs(); #ifndef TOMAHAWK_HEADLESS @@ -310,10 +308,11 @@ TomahawkApp::~TomahawkApp() delete SipHandler::instance(); + Pipeline::instance()->stop(); + if ( !m_database.isNull() ) delete m_database.data(); - Pipeline::instance()->stop(); delete Pipeline::instance(); #ifndef TOMAHAWK_HEADLESS diff --git a/src/tomahawkwindow.cpp b/src/tomahawkwindow.cpp index 9203e65ce..44931115d 100644 --- a/src/tomahawkwindow.cpp +++ b/src/tomahawkwindow.cpp @@ -180,6 +180,8 @@ TomahawkWindow::applyPlatformTweaks() setUnifiedTitleAndToolBarOnMac( true ); delete ui->hline1; delete ui->hline2; + /// Mac users allready have Tomahawk appmenu, change the name + ui->menuApp->setTitle( "&Music Player" ); #else ui->hline1->setStyleSheet( "border: 1px solid gray;" ); ui->hline2->setStyleSheet( "border: 1px solid gray;" ); @@ -708,7 +710,7 @@ TomahawkWindow::showAboutTomahawk() { QMessageBox::about( this, tr( "About Tomahawk" ), tr( "<h2><b>Tomahawk %1<br/>(%2)</h2>Copyright 2010, 2011<br/>Christian Muehlhaeuser <muesli@tomahawk-player.org><br/><br/>" - "Thanks to: Leo Franchi, Jeff Mitchell, Dominik Schmidt, Jason Herskowitz, Alejandro Wainzinger, Michael Zanetti, Harald Sitter and Steve Robertson" ) + "Thanks to: Leo Franchi, Jeff Mitchell, Dominik Schmidt, Jason Herskowitz, Alejandro Wainzinger, Hugo Lindström, Michael Zanetti, Harald Sitter and Steve Robertson" ) .arg( TomahawkUtils::appFriendlyVersion() ) .arg( qApp->applicationVersion() ) ); } diff --git a/src/tomahawkwindow.ui b/src/tomahawkwindow.ui index 57f53bbbc..97fdca390 100644 --- a/src/tomahawkwindow.ui +++ b/src/tomahawkwindow.ui @@ -67,7 +67,7 @@ <x>0</x> <y>0</y> <width>1000</width> - <height>22</height> + <height>20</height> </rect> </property> <widget class="QMenu" name="menuSettings"> @@ -78,12 +78,16 @@ </widget> <widget class="QMenu" name="menuApp"> <property name="title"> - <string>&Music Player</string> + <string>&Tomahawk</string> </property> <addaction name="actionPlay"/> <addaction name="actionPrevious"/> <addaction name="actionNext"/> <addaction name="separator"/> + <addaction name="actionCreatePlaylist"/> + <addaction name="actionCreate_New_Station"/> + <addaction name="actionLoadXSPF"/> + <addaction name="separator"/> <addaction name="actionUpdateCollection"/> <addaction name="actionRescanCollection"/> <addaction name="separator"/> @@ -112,17 +116,7 @@ <addaction name="actionDiagnostics"/> <addaction name="actionAboutTomahawk"/> </widget> - <widget class="QMenu" name="menuPlaylist"> - <property name="title"> - <string>&Playlist</string> - </property> - <addaction name="actionCreatePlaylist"/> - <addaction name="actionCreate_New_Station"/> - <addaction name="separator"/> - <addaction name="actionLoadXSPF"/> - </widget> <addaction name="menuApp"/> - <addaction name="menuPlaylist"/> <addaction name="menuNetwork"/> <addaction name="menuSettings"/> <addaction name="menuWindow"/>