diff --git a/CMakeLists.txt b/CMakeLists.txt index e0c066dd1..83c574412 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -25,6 +25,7 @@ option(BUILD_GUI "Build Tomahawk with GUI" ON) option(BUILD_RELEASE "Generate TOMAHAWK_VERSION without GIT info" OFF) option(WITH_BREAKPAD "Build with breakpad integration" ON) option(WITH_CRASHREPORTER "Build with CrashReporter" ON) +option(WITH_BINARY_ATTICA "Enable support for downloading binary resolvers automatically" ON) option(LEGACY_KDE_INTEGRATION "Install tomahawk.protocol file, deprecated since 4.6.0" OFF) IF( CMAKE_SYSTEM_PROCESSOR MATCHES "arm" ) @@ -120,8 +121,10 @@ macro_log_feature(Boost_FOUND "Boost" "Provides free peer-reviewed portable C++ macro_optional_find_package(QCA2) macro_log_feature(QCA2_FOUND "QCA2" "Provides encryption and signing functions required for Grooveshark resolver" "http://delta.affinix.com/qca/" FALSE "" "") +if( NOT QTKEYCHAIN_INCLUDE_DIRS OR NOT QTKEYCHAIN_LIBRARIES ) macro_optional_find_package(QtKeychain) macro_log_feature(QTKEYCHAIN_FOUND "QtKeychain" "Provides support for secure password storage" "https://github.com/frankosterfeld/qtkeychain" TRUE "" "") +endif() macro_optional_find_package(LibAttica) macro_log_feature(LIBATTICA_FOUND "libattica" "Provides support for automatic fetching and managing of resolvers from the tomahawk website" "https://projects.kde.org/projects/kdesupport/attica" TRUE "" "") diff --git a/CMakeModules/AddTomahawkPlugin.cmake b/CMakeModules/AddTomahawkPlugin.cmake new file mode 100644 index 000000000..f4cd96fa1 --- /dev/null +++ b/CMakeModules/AddTomahawkPlugin.cmake @@ -0,0 +1,99 @@ +MACRO(PARSE_ARGUMENTS prefix arg_names option_names) + SET(DEFAULT_ARGS) + FOREACH(arg_name ${arg_names}) + SET(${prefix}_${arg_name}) + ENDFOREACH(arg_name) + FOREACH(option ${option_names}) + SET(${prefix}_${option} FALSE) + ENDFOREACH(option) + + SET(current_arg_name DEFAULT_ARGS) + SET(current_arg_list) + FOREACH(arg ${ARGN}) + SET(larg_names ${arg_names}) + LIST(FIND larg_names "${arg}" is_arg_name) + IF (is_arg_name GREATER -1) + SET(${prefix}_${current_arg_name} ${current_arg_list}) + SET(current_arg_name ${arg}) + SET(current_arg_list) + ELSE (is_arg_name GREATER -1) + SET(loption_names ${option_names}) + LIST(FIND loption_names "${arg}" is_option) + IF (is_option GREATER -1) + SET(${prefix}_${arg} TRUE) + ELSE (is_option GREATER -1) + SET(current_arg_list ${current_arg_list} ${arg}) + ENDIF (is_option GREATER -1) + ENDIF (is_arg_name GREATER -1) + ENDFOREACH(arg) + SET(${prefix}_${current_arg_name} ${current_arg_list}) +ENDMACRO(PARSE_ARGUMENTS) + +MACRO(CAR var) + SET(${var} ${ARGV1}) +ENDMACRO(CAR) + +MACRO(CDR var junk) + SET(${var} ${ARGN}) +ENDMACRO(CDR) + + +macro(add_tomahawk_plugin) + parse_arguments(PLUGIN + "SOURCES;UI;LINK_LIBRARIES;TYPE;EXPORT_MACRO;COMPILE_DEFINITIONS" + "NO_INSTALL" + ${ARGN} + ) + car(PLUGIN_NAME ${PLUGIN_DEFAULT_ARGS}) + +# message("*** Arguments for ${PLUGIN_NAME}") +# message("Sources: ${PLUGIN_SOURCES}") +# message("Link libraries: ${PLUGIN_LINK_LIBRARIES}") +# message("UI: ${PLUGIN_UI}") +# message("TYPE: ${PLUGIN_TYPE}") +# message("EXPORT_MACRO: ${PLUGIN_EXPORT_MACRO}") + + # create target name once for convenience + set(target "tomahawk_${PLUGIN_TYPE}_${PLUGIN_NAME}") + + # qt stuff + include_directories(${CMAKE_CURRENT_BINARY_DIR}) + if(PLUGIN_UI) + qt4_wrap_ui(PLUGIN_UI_SOURCES ${PLUGIN_UI}) + list(APPEND PLUGIN_SOURCES ${PLUGIN_UI_SOURCES}) + endif() + + if(EXISTS "${CMAKE_CURRENT_LIST_DIR}/resources.qrc") + qt4_add_resources(PLUGIN_RC_SOURCES "resources.qrc") + list(APPEND PLUGIN_SOURCES ${PLUGIN_RC_SOURCES}) + unset(PLUGIN_RC_SOURCES) + endif() + + # add target + add_library(${target} MODULE ${PLUGIN_SOURCES}) + + # definitions - can this be moved into set_target_properties below? + add_definitions(${QT_DEFINITIONS}) + set_target_properties(${target} PROPERTIES AUTOMOC TRUE COMPILE_DEFINITIONS ${PLUGIN_EXPORT_MACRO}) + if(PLUGIN_COMPILE_DEFINITIONS) + # Dear CMake, i hate you! Sincerely, domme + # At least in CMake 2.8.8, you CANNOT set more than one COMPILE_DEFINITIONS value + # only takes the first one if called multiple times or bails out with wrong number of arguments + # when passing in a list, thus i redefine the export macro here in hope it won't mess up other targets + add_definitions( "-D${PLUGIN_EXPORT_MACRO}" ) + + set_target_properties(${target} PROPERTIES COMPILE_DEFINITIONS ${PLUGIN_COMPILE_DEFINITIONS}) + endif() + + # add link targets + target_link_libraries(${target} tomahawklib) + if(PLUGIN_LINK_LIBRARIES) + target_link_libraries(${target} ${PLUGIN_LINK_LIBRARIES}) + endif() + + # make installation optional, maybe useful for dummy plugins one day + if(NOT PLUGIN_NO_INSTALL) + include(GNUInstallDirs) + install(TARGETS ${target} DESTINATION ${CMAKE_INSTALL_LIBDIR}) + endif() +endmacro() diff --git a/CMakeModules/FindGloox.cmake b/CMakeModules/FindGloox.cmake deleted file mode 100644 index ccdbdcfbf..000000000 --- a/CMakeModules/FindGloox.cmake +++ /dev/null @@ -1,23 +0,0 @@ -# - Try to find GLOOX -# Find GLOOX headers, libraries and the answer to all questions. -# -# GLOOX_FOUND True if gloox got found -# GLOOX_INCLUDE_DIR Location of gloox headers -# GLOOX_LIBRARIES List of libaries to use gloox -# -# Copyright (c) 2009 Nigmatullin Ruslan -# -# Redistribution and use is allowed according to the terms of the New -# BSD license. -# For details see the accompanying COPYING-CMAKE-SCRIPTS file. -# - -FIND_PATH( GLOOX_INCLUDE_DIR "gloox/gloox.h" ) -FIND_LIBRARY( GLOOX_LIBRARIES gloox ) - -if( GLOOX_LIBRARIES AND GLOOX_INCLUDE_DIR ) - message( STATUS "Found gloox: ${GLOOX_LIBRARIES}" ) - set( GLOOX_FOUND 1 ) -else( GLOOX_LIBRARIES AND GLOOX_INCLUDE_DIR ) - message( STATUS "Could NOT find gloox" ) -endif( GLOOX_LIBRARIES AND GLOOX_INCLUDE_DIR ) diff --git a/CMakeModules/FindJreen.cmake b/CMakeModules/FindJreen.cmake index 3788e9f23..e1f97d16c 100644 --- a/CMakeModules/FindJreen.cmake +++ b/CMakeModules/FindJreen.cmake @@ -30,7 +30,6 @@ ENDIF() INCLUDE(FindPackageHandleStandardArgs) FIND_PACKAGE_HANDLE_STANDARD_ARGS(Jreen - REQUIRED_VARS JREEN_LIBRARIES JREEN_INCLUDE_DIR - VERSION_VAR PC_JREEN_VERSION) + REQUIRED_VARS JREEN_LIBRARIES JREEN_INCLUDE_DIR) MARK_AS_ADVANCED(JREEN_INCLUDE_DIR JREEN_LIBRARIES) diff --git a/CMakeModules/NSIS.template.in b/CMakeModules/NSIS.template.in index 03122c6fa..2e1aa7c93 100644 --- a/CMakeModules/NSIS.template.in +++ b/CMakeModules/NSIS.template.in @@ -280,7 +280,8 @@ Section "Tomahawk Player" SEC_TOMAHAWK_PLAYER File "${INSTALL_PATH}\bin\libtomahawk_portfwd.dll" File "${INSTALL_PATH}\bin\libtomahawk_lastfm2.dll" File "${INSTALL_PATH}\bin\libtomahawklib.dll" - File "${INSTALL_PATH}\lib\libtomahawk_account_*.dll" + ; plugins + File "${INSTALL_PATH}\lib\libtomahawk_*_*.dll" !endif !ifndef INSTALL_PATH ;Main executable. @@ -293,7 +294,8 @@ Section "Tomahawk Player" SEC_TOMAHAWK_PLAYER File "${BUILD_PATH}\libqxtweb-standalone.dll" File "${BUILD_PATH}\libtomahawk_portfwd.dll" File "${BUILD_PATH}\libtomahawk_lastfm2.dll" - File "${BUILD_PATH}\libtomahawk_account_*.dll" + ; plugins + File "${BUILD_PATH}\libtomahawk_*_*.dll" !endif ;License & release notes. diff --git a/ChangeLog b/ChangeLog index faa496d26..0fa6c3c4b 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,6 +1,35 @@ +Version 0.5.0: + * Priortize resolution of a track on double-click + * New Tomahawk logo and icon + * Spotify Resolver shipped with Tomahawk and installable via new Preferences interface + * Sync select playlists (and updates) with Spotify + * Support .aiff (AIFF mimetype) files + * Cleaned up Diagnostics window + * Don't show non-resolved tracks as Recently Played + * Added "Stop playback after this track" context menu items + * Double-clicking on a playlist name (in the sidebar) will play the playlist + * You can now import your entire Last.fm playback history into Tomahawk + * Related Artists now sorted by relatedness + * Support for multimedia keys (Play, Pause, Next etc.) on Windows & Linux + * When listening privately scrobbling to Last.fm and Adium is now disabled + * Added a toolbar with page back / forward buttons and the global search + * New grid view with direct playback controls (Charts, Album Page, Artist Page, Track Page) + * New Releases (by Genre) pages added + * Add social sharing widget in Now Playing Controls + * Added a track page showing a song's lyrics and other similar tracks + * Selecting Track name in Now Playing now brings you to Track page + * Separate Loved Tracks and Recently Played views per source. + * Added animated spinner where needed + * Some new sidebar icons + * Support Transifex for localization and add translations for Arabic, French, Bulgarian, Catalan, Spanish (Castillian), and more. + * New Context Menu items (Love, Add to Queue, etc.) + * Fading album art + * New Account and Plug-in Preferences/Management Interface + * Fix XSPF auto-updating + + Version 0.4.2: * Fix ZeroConf protocol showing IP addresses instead of host names. - Sometimes. Full fix coming later. * Updated translations for various languages. * Resuming playback restores correct volume settings. * Reduced CPU usage during playback. @@ -22,7 +51,7 @@ Version 0.4.0: * Fixed icons not appearing in resolvers list. * Fixed various UI glitches and stray error messages in stations. * Fixed bug where album page would resolve bottom-to-top. - * Fixed bug where Footnotes would not update when changing selected album in Album View. + * Fixed bug where Footnotes would not update when changing selected album. * Fixed dragging albums and artists from charts, album, and artist views. * Fixed bug where filter text would be one step behind filter value. * Fixed bug where resolvers would enable themselves after auto-updating. diff --git a/admin/mac/macdeploy.py b/admin/mac/macdeploy.py index 71bdefb77..6d7987982 100755 --- a/admin/mac/macdeploy.py +++ b/admin/mac/macdeploy.py @@ -199,10 +199,10 @@ QT_PLUGINS = [ ] TOMAHAWK_PLUGINS = [ - 'libtomahawk_account_xmpp.dylib', - 'libtomahawk_account_google.dylib', - 'libtomahawk_account_twitter.dylib', - 'libtomahawk_account_zeroconf.dylib', + 'libtomahawk_account_xmpp.so', + 'libtomahawk_account_google.so', + 'libtomahawk_account_twitter.so', + 'libtomahawk_account_zeroconf.so', ] QT_PLUGINS_SEARCH_PATH=[ @@ -495,11 +495,6 @@ for plugin in VLC_PLUGINS: for plugin in TOMAHAWK_PLUGINS: FixPlugin(plugin, '../MacOS') -try: - FixPlugin('spotify_tomahawkresolver', '../MacOS') -except: - print 'Failed to find spotify resolver' - try: FixPlugin('tomahawk_crash_reporter', '../MacOS') except: diff --git a/data/icons/tomahawk-icon-128x128-grayscale.png b/data/icons/tomahawk-icon-128x128-grayscale.png index 550781596..f9daadb33 100644 Binary files a/data/icons/tomahawk-icon-128x128-grayscale.png and b/data/icons/tomahawk-icon-128x128-grayscale.png differ diff --git a/data/icons/tomahawk-icon-128x128.png b/data/icons/tomahawk-icon-128x128.png index bbee6eb80..8df40cb4a 100644 Binary files a/data/icons/tomahawk-icon-128x128.png and b/data/icons/tomahawk-icon-128x128.png differ diff --git a/data/icons/tomahawk-icon-16x16.png b/data/icons/tomahawk-icon-16x16.png index cd5709e6b..8d2cd8589 100644 Binary files a/data/icons/tomahawk-icon-16x16.png and b/data/icons/tomahawk-icon-16x16.png differ diff --git a/data/icons/tomahawk-icon-256x256.png b/data/icons/tomahawk-icon-256x256.png index 227abc838..e0793782d 100644 Binary files a/data/icons/tomahawk-icon-256x256.png and b/data/icons/tomahawk-icon-256x256.png differ diff --git a/data/icons/tomahawk-icon-32x32.png b/data/icons/tomahawk-icon-32x32.png index 396e3f8fa..952151ba2 100644 Binary files a/data/icons/tomahawk-icon-32x32.png and b/data/icons/tomahawk-icon-32x32.png differ diff --git a/data/icons/tomahawk-icon-512x512.png b/data/icons/tomahawk-icon-512x512.png index e42d30e85..6c9757a3d 100644 Binary files a/data/icons/tomahawk-icon-512x512.png and b/data/icons/tomahawk-icon-512x512.png differ diff --git a/data/icons/tomahawk-icon-64x64.png b/data/icons/tomahawk-icon-64x64.png index d8b278ce5..e27712df7 100644 Binary files a/data/icons/tomahawk-icon-64x64.png and b/data/icons/tomahawk-icon-64x64.png differ diff --git a/data/icons/tomahawk-icon.svg b/data/icons/tomahawk-icon.svg index 49f1a86b5..05b80614d 100644 --- a/data/icons/tomahawk-icon.svg +++ b/data/icons/tomahawk-icon.svg @@ -1,304 +1,80 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + +image/svg+xml + + + + + + + \ No newline at end of file diff --git a/data/images/add.png b/data/images/add.png index 59fa7fb1d..1c16968fc 100644 Binary files a/data/images/add.png and b/data/images/add.png differ diff --git a/data/images/album-icon.png b/data/images/album-icon.png index 51c54e8ca..88b103f27 100644 Binary files a/data/images/album-icon.png and b/data/images/album-icon.png differ diff --git a/data/images/album-placeholder-grid.png b/data/images/album-placeholder-grid.png new file mode 100644 index 000000000..5e986a666 Binary files /dev/null and b/data/images/album-placeholder-grid.png differ diff --git a/data/images/apply-check.png b/data/images/apply-check.png index 0f3153c4b..b17894dfe 100644 Binary files a/data/images/apply-check.png and b/data/images/apply-check.png differ diff --git a/data/images/artist-icon.png b/data/images/artist-icon.png index 87d8fd48e..3a11c7085 100644 Binary files a/data/images/artist-icon.png and b/data/images/artist-icon.png differ diff --git a/data/images/artist-placeholder-grid.png b/data/images/artist-placeholder-grid.png new file mode 100644 index 000000000..c47a989ee Binary files /dev/null and b/data/images/artist-placeholder-grid.png differ diff --git a/data/images/automatic-playlist.png b/data/images/automatic-playlist.png index a33bfb9c8..0dfa254e4 100644 Binary files a/data/images/automatic-playlist.png and b/data/images/automatic-playlist.png differ diff --git a/data/images/avatar-dude-plus.png b/data/images/avatar-dude-plus.png deleted file mode 100644 index 961b05000..000000000 Binary files a/data/images/avatar-dude-plus.png and /dev/null differ diff --git a/data/images/avatar-dude.png b/data/images/avatar-dude.png deleted file mode 100644 index f1c2ebca8..000000000 Binary files a/data/images/avatar-dude.png and /dev/null differ diff --git a/data/images/charts.png b/data/images/charts.png index 6e56456a2..50c6f3dfa 100644 Binary files a/data/images/charts.png and b/data/images/charts.png differ diff --git a/data/images/collapse.png b/data/images/collapse.png deleted file mode 100644 index b92bc558f..000000000 Binary files a/data/images/collapse.png and /dev/null differ diff --git a/data/images/collection.png b/data/images/collection.png index abc20d3ca..cc5c16b87 100644 Binary files a/data/images/collection.png and b/data/images/collection.png differ diff --git a/data/images/create-playlist.png b/data/images/create-playlist.png deleted file mode 100644 index 0ea11f467..000000000 Binary files a/data/images/create-playlist.png and /dev/null differ diff --git a/data/images/dashboard.png b/data/images/dashboard.png index 7f14a454f..47c962012 100644 Binary files a/data/images/dashboard.png and b/data/images/dashboard.png differ diff --git a/data/images/home.png b/data/images/home.png deleted file mode 100644 index f6f5703b8..000000000 Binary files a/data/images/home.png and /dev/null differ diff --git a/data/images/itunes.png b/data/images/itunes.png index e0f2711da..3692962d2 100644 Binary files a/data/images/itunes.png and b/data/images/itunes.png differ diff --git a/data/images/jump-link.png b/data/images/jump-link.png index 09a0cf0fb..ca7845b47 100644 Binary files a/data/images/jump-link.png and b/data/images/jump-link.png differ diff --git a/data/images/loved_playlist.png b/data/images/loved_playlist.png index 79c488e8e..b6b9ca2b3 100644 Binary files a/data/images/loved_playlist.png and b/data/images/loved_playlist.png differ diff --git a/data/images/music-icon.png b/data/images/music-icon.png index daf8e7d13..13b3d5ecc 100644 Binary files a/data/images/music-icon.png and b/data/images/music-icon.png differ diff --git a/data/images/new-additions.png b/data/images/new-additions.png index 3b7ef64d0..772e7034c 100644 Binary files a/data/images/new-additions.png and b/data/images/new-additions.png differ diff --git a/data/images/new-releases.png b/data/images/new-releases.png index 05252d24e..36c53619b 100644 Binary files a/data/images/new-releases.png and b/data/images/new-releases.png differ diff --git a/data/images/playlist-icon.png b/data/images/playlist-icon.png index ccbbef4b5..41d0f9a59 100644 Binary files a/data/images/playlist-icon.png and b/data/images/playlist-icon.png differ diff --git a/data/images/recently-played.png b/data/images/recently-played.png index 0c1161af0..22ee8faea 100644 Binary files a/data/images/recently-played.png and b/data/images/recently-played.png differ diff --git a/data/images/search-icon.png b/data/images/search-icon.png index 05ea58db8..55770c844 100644 Binary files a/data/images/search-icon.png and b/data/images/search-icon.png differ diff --git a/data/images/share.png b/data/images/share.png index 7cd3e0023..501752c4d 100644 Binary files a/data/images/share.png and b/data/images/share.png differ diff --git a/data/images/station.png b/data/images/station.png index 9680fe8a1..a3b384e8c 100644 Binary files a/data/images/station.png and b/data/images/station.png differ diff --git a/data/images/supercollection.png b/data/images/supercollection.png index cefead535..13b3d5ecc 100644 Binary files a/data/images/supercollection.png and b/data/images/supercollection.png differ diff --git a/data/images/post.png b/data/images/track-icon-sidebar.png similarity index 59% rename from data/images/post.png rename to data/images/track-icon-sidebar.png index 808a0737d..b579f957c 100644 Binary files a/data/images/post.png and b/data/images/track-icon-sidebar.png differ diff --git a/data/images/track-icon.png b/data/images/track-icon.png new file mode 100644 index 000000000..2851e5b3f Binary files /dev/null and b/data/images/track-icon.png differ diff --git a/data/images/track-placeholder.png b/data/images/track-placeholder.png index 2976d36d7..333ea9101 100644 Binary files a/data/images/track-placeholder.png and b/data/images/track-placeholder.png differ diff --git a/data/images/user-avatar.png b/data/images/user-avatar.png index 32a4b3895..a2fc3fbd8 100644 Binary files a/data/images/user-avatar.png and b/data/images/user-avatar.png differ diff --git a/data/misc/tomahawk_pubkey.pem b/data/misc/tomahawk_pubkey.pem new file mode 100644 index 000000000..5e9606170 --- /dev/null +++ b/data/misc/tomahawk_pubkey.pem @@ -0,0 +1,20 @@ +-----BEGIN PUBLIC KEY----- +MIIDOzCCAi0GByqGSM44BAEwggIgAoIBAQDRltnNbKWFroVCsG1nTSdlTDmo7fjl +tgOuQ0YB2s0a1bcqgQ5YJRE59pFvF/z2pkHEHdyBA6USd9N7/T9lolwNcJoByJpO +MobUNs04elqZXliriaAdoSb2g6ZpxiedppbbyNP/BlK6o+zpyn0LVYXDI/OwJFzS +xjGXM+rBEWdUJnogZxV31gF9W3yD1Quz6icBulT9V/Soo6me9Mc60ooKSYj4Zgqd +3ln8tG90RFnWfbb0nbrITvR3ll6XXLfn081tjhymcXqHcgvaaqcmpKWL6ZWwX1mH +3t1pImnif/tSSZPG21KGE3FtuQ/+YFo19apQ6U6l8kaSFxqcDLAYzBy9AhUA/QfN +8WEIvzOEZ9uSWT7lYy64mUkCggEABsUmcs3kwjrmszIAAmPIowA0DBrxWZL03JBV +bDKT6tNHZaFFlCufVSjiL1EFZjRARC16OWYaDcElUsZYFMcsNIIa8LyDQaq6+SSm +quhMO5heeJiYPrutDiJzbJr0+HoY77Ll+Q4/cEkl0UAN4Ovp18WKwaq6GpHAvBnv +71LunLGAKsVb5joXBQ8In6zQkibJhgiBJwzLK90/j0OTiDaaOwM3PsAegORBVlVE +TAk4AQmawmF8nBGLzTyKXl83J571ku1Mm2JTl16jMYziKARKXYBmkcP1at0YddVK +WWpAwRKSxOucVJYfV58JqmjZqst8BBeH6esQKr5dklUvvDMaEwOCAQYAAoIBAQCw +5mo+8/R3S9cNYg9o8JNJGdSbMhSkurILHh9WNElsIC3RNtPcpijmAnWtXTVDhe6w +77wLj37tUuFGbsu2qPXtZoup35emf9DDshZ5w5UOclPaZ9HYjlC1H64c6d66Rllk +fY6FRDv9qVfjT84APbvMDrk6csJ5YHxFPDaqeQaFB0nxFiCMVwjEx+ZSvQNK1jJ2 +o2gtuOvSPVSphsMeJ72DDNxO+SRRVnOmWaxg9rlmFuGle6Z+UJ2FItfmPEvhSBMY +hzndUbC7Wi4sIpBzbm9O5MiPYMv0VmN+0t1156EiC9uR4f7AKH2S94dnQob/YeY0 +jMH+XxU/wzGUCmsOx1lx +-----END PUBLIC KEY----- diff --git a/data/www/auth.html b/data/www/auth.html index cdfba330d..d1193ee5d 100644 --- a/data/www/auth.html +++ b/data/www/auth.html @@ -43,8 +43,8 @@ - - Tomahawk - Powered by Playdar + + Tomahawk - Powered by Playdar
diff --git a/data/www/auth.na.html b/data/www/auth.na.html index d1b816572..ee4463d5b 100644 --- a/data/www/auth.na.html +++ b/data/www/auth.na.html @@ -32,8 +32,8 @@ - - Tomahawk - Powered by Playdar + + Tomahawk - Powered by Playdar
diff --git a/data/www/tomahawk_banner_small.png b/data/www/tomahawk_banner_small.png index 83a818d17..67fc8b4cb 100644 Binary files a/data/www/tomahawk_banner_small.png and b/data/www/tomahawk_banner_small.png differ diff --git a/lang/tomahawk_ar.ts b/lang/tomahawk_ar.ts new file mode 100644 index 000000000..15a2091f4 --- /dev/null +++ b/lang/tomahawk_ar.ts @@ -0,0 +1,3917 @@ + + + AccountFactoryWrapper + + + Dialog + حوار + + + + Description goes here + أضف الوصف هنا + + + + Add Account + أضف حساب + + + + AccountFactoryWrapperDelegate + + + Online + متصل + + + + Connecting... + جاري الاتصال... + + + + Offline + غير متصل + + + + AclJobDelegate + + + Error displaying ACL info + خطأ في عرض معلومات ACL + + + + + Allow %1 to +connect and stream from you? + هل تسمح ل %1 +بالربط بك ومشاركة أغانيك ؟ + + + + ActionCollection + + + &Listen Along + &استمع مع + + + + Stop &Listening Along + توقيف &الاستماع مع + + + + &Follow in real-time + &تابع بنفس الوقت + + + + + &Listen Privately + &إستمع بخصوصية + + + + + &Listen Publicly + &إستمع علنا + + + + &Load Playlist + &تحميل قائمة الأغاني + + + + &Rename Playlist + &إعادة تسمية قائمة الأغاني + + + + &Copy Playlist Link + &نسخ قائمة الأغاني + + + + &Play + &إستمع + + + + &Stop + &أوقف الإستماع + + + + &Previous Track + &الأغنية السابقة + + + + &Next Track + &الأغنية التالية + + + + &Quit + &أخرج + + + + AlbumInfoWidget + + + Form + استمارة + + + + Other Albums by Artist + ألبومات أخرى للفنان + + + + Sorry, we could not find any other albums for this artist! + نعتذر, لم نستطيع إيجاد ألبومات أخرى لهذا الفنان! + + + + Sorry, we could not find any tracks for this album! + نعتذر، لم نستطيع إيجاد أغاني أخرى لهذا الألبوم! + + + + Other Albums by %1 + ألبومات أخرى ل%1 + + + + AlbumModel + + + + All albums from %1 + جميع الألبومات من %1 + + + + All albums + جميع الألبومات + + + + ArtistInfoWidget + + + Form + استمارة + + + + Top Hits + الأكثر شهرة + + + + Related Artists + الفنانين ذات الذوق القريب + + + + Albums + ألبومات + + + + Sorry, we could not find any albums for this artist! + نعتذر, لم نستطيع إيجاد ألبومات أخرى لهذا الفنان! + + + + Sorry, we could not find any related artists! + نعتذر، لم نستطيع إيجاد فنانين! + + + + Sorry, we could not find any top hits for this artist! + نعتذر، لم نستطيع إيجاد أغاني مشهورة جدا لهذا الفنان! + + + + AudioControls + + + Prev + سابق + + + + Play + إستمع + + + + Pause + تعليق + + + + Next + التالي + + + + Artist + فنان + + + + Album + البوم + + + + Owner + المالك + + + + social + اجتماعي + + + + love + أحب + + + + Time + الوقت + + + + Time Left + الوقت المتبقي + + + + Shuffle + خلط + + + + Repeat + إعادة + + + + Low + منخفض + + + + High + مرتفع + + + + CategoryAddItem + + + Create new Playlist + إنشاء قائمة أغاني جديدة + + + + Create new Station + إنشاء إذاعة جديدة + + + + + + New Station + إنشاء إذاعة جديدة + + + + + + %1 Station + إذاعة %1 + + + + CategoryItem + + + Playlists + قائمات الأغاني + + + + Stations + الإذاعات + + + + ClearButton + + + Clear + مسح + + + + ContextWidget + + + InfoBar + InfoBar + + + + + Show Footnotes + أظهر الحاشية + + + + Hide Footnotes + إخفي الحاشية + + + + CrashReporter + + + Tomahawk Crash Reporter + مراسل توماهوك للانهيار + + + + <html><head/><body><p><span style=" font-weight:600;">Sorry!</span> Tomahawk crashed. Please tell us about it! Tomahawk has created an error report for you that can help improve the stability in the future. You can now send this report directly to the Tomahawk developers.</p></body></html> + <html><head/><body><p><span style=" font-weight:600;">نتأسف!</span>&nbsp;توماهوك تحطم. يرجى أن تخبرنا عن ذلك! لقد أنشئ توماهوك تقرير عن الخطأ نيابة عنك لمساعدتنا في تحسين استقرار البرنامج في المستقبل. يمكنك إرسال هذا التقرير الأن مباشرة لمطوري توماهوك.</p></body></html> + + + + Send this report + أرسل هذا التقرير + + + + Don't send + لا ترسل + + + + Abort + الغاء + + + + You can disable sending crash reports in the configuration dialog. + يمكنك إلغاء إرسال تقارير الانهيار في خيارات الضبط. + + + + Uploaded %L1 of %L2 KB. + تحميل %L1 من أصل %L2 كيلو بايت. + + + + + Close + اغلاق + + + + Sent! <b>Many thanks</b>. + تم الإرسال! <b>شكرا</b>. + + + + Failed to send crash info. + فشل في إرسال معلومات الانهيار. + + + + DatabaseCommand_AllAlbums + + + Unknown + مجهول + + + + DelegateConfigWrapper + + + About + عن + + + + Delete Account + حذف الحساب + + + + About this Account + عن هذا الحساب + + + + DiagnosticsDialog + + + Tomahawk Diagnostics + تشخيص توماهوك + + + + Copy to Clipboard + نسخ إلى الحافظة + + + + GlobalSearchWidget + + + Form + استمارة + + + + IndexingJobItem + + + Indexing database + فهرسة قاعدة البيانات + + + + InfoBar + + + InfoBar + InfoBar + + + + Filter... + مرشح... + + + + LastFmConfig + + + Form + استمارة + + + + Scrobble tracks to Last.fm + نقل الأغاني إلى Last.fm + + + + Username: + اسم المستخدم: + + + + Password: + كلمة السر: + + + + Test Login + اختبار الدخول + + + + Import Playback History + استيراد تاريخ إعادة الإستماع + + + + LastfmContext + + + Last.fm + Last.fm + + + + LatchedStatusItem + + + %1 is listening along to you! + %1 يستمع معك! + + + + LoadXSPF + + + Load XSPF + تحميل XSPF + + + + Playlist URL + رابط قائمة الأغاني + + + + Enter URL... + أدخل الرابط... + + + + ... + ... + + + + Automatically update + التحديث التلقائي + + + + LoadXSPFDialog + + + Load XSPF File + تحميل ملف XSPF + + + + XSPF Files (*.xspf) + XSPF (*.xspf) ملف + + + + LocalCollection + + + Bookmarks + العلامات + + + + Saved tracks + الأغاني المحفوظة + + + + NewPlaylistWidget + + + Enter a title for the new playlist: + أدخل عنوان لقائمة الأغاني الجديدة: + + + + Tomahawk offers a variety of ways to help you create playlists and find music you enjoy! + توماهوك يقدم مجموعة متنوعة من الطرق اللتي تساعدك على إنشاء قوائم أغاني و إيجاد الموسيقى اللتي تستمتع إليها! + + + + Just enter a genre or tag name and Tomahawk will suggest a few songs to get you started with your new playlist: + فقط أدخل النمط أو الإسم و تاماهاوك سيقترح بعض الأغاني لتبدأ بقائمة أغاني جديدة: + + + + &Create Playlist + &إنشاء قائمة أغاني + + + + Create a new playlist + إنشاء قائمة أغاني جديدة + + + + NewReleasesWidget + + + New Releases + جديد الاصدارات + + + + PlayableModel + + + Artist + فنان + + + + Title + عنوان + + + + Composer + مؤلف + + + + Album + البوم + + + + Track + اغنية + + + + Duration + مدة + + + + Bitrate + معدل البت + + + + Age + عمر + + + + Year + سنة + + + + Size + حجم + + + + Origin + أصل + + + + Score + علامة + + + + + Name + إسم + + + + PlaylistItemDelegate + + + played %1 by you + سمعت %1 + + + + played %1 by %2 + %2 سمع %1 + + + + PlaylistLargeItemDelegate + + + played %1 by you + سمعت %1 + + + + played %1 by %2 + %2 سمع %1 + + + + added %1 + إضافة %1 + + + + PlaylistModel + + + A playlist by %1, created %2 + قائمة أغاني من قبل %1, أنشئت %2 + + + + you + أنت + + + + All tracks by %1 on album %2 + جميع أغاني %1 في البوم %2 + + + + All tracks by %1 + جميع أغاني %1 + + + + PlaylistTypeSelectorDlg + + + New Playlist + قائمة أغاني جديدة + + + + Just a regular old playlist... Give it a name, drag in some tracks, and go! + قائمة أغاني إعتيادية قديمة...سمها ما شئت، أضف بعض الأغاني وانطلق! + + + + Don't know exactly what you want? Give Tomahawk a few pointers and let it build a playlist for you! + لا تعرف تماما ماذا تريد؟ أعط توماهوك بعض الإشارات و دعه يبني لك قائمة أغاني! + + + + Name: + إسم: + + + + New Playlist... + قائمة أغاني جديدة... + + + + Create Manual Playlist + إنشاء قائمة أغاني جديدة يدويا + + + + Create Automatic Playlist + إنشاء قائمة أغاني جديدة أوتوماتيكية + + + + PlaylistView + + + This playlist is currently empty. + هذه المجموعة فارغة حاليا. + + + + This playlist is currently empty. Add some tracks to it and enjoy the music! + هذه القائمة فارغة حاليا. أضف لها بعض الأغاني واستمتع للموسيقى! + + + + ProxyDialog + + + Proxy Settings + إعدادات الوكيل + + + + Hostname of proxy server + إسم المضيف لخادم الوسيط + + + + Host + مضيف + + + + + Port + بوابة + + + + Proxy login + تسجيل دخول الوسيط + + + + User + المستخدم + + + + Password + كلمة السر + + + + Proxy password + كلمة السر للوكيل + + + + No Proxy Hosts: +(Overrides system proxy) + لا مضيف للوكيل: +(تجاوز وكيل النظام) + + + + localhost *.example.com (space separated) + localhost *.example.com (مفصولة بفراغ) + + + + Use proxy for DNS lookups? + استخدام ألوكيل لعمليات البحث عن ألدي أن أس (DNS ) ؟ + + + + QObject + + + %n year(s) ago + منذ %n سنةمنذ سنة %nمنذ سنتين %nمنذ %n سنواتمنذ %n سنواتمنذ %n سنوات + + + + %n year(s) + منذ %n سنةمنذ سنة %nمنذ سنتين %nمنذ %n سنواتمنذ %n سنواتمنذ %n سنوات + + + + %n month(s) ago + منذ %n شهرمنذ شهر %nمنذ شهرين %nمنذ %n أشهرمنذ %n أشهرمنذ %n أشهر + + + + %n month(s) + منذ %n شهرمنذ شهر %nمنذ شهرين %nمنذ %n أشهرمنذ %n أشهرمنذ %n أشهر + + + + %n week(s) ago + منذ %n أسبوعمنذ أسبوع %nمنذ أسبوعين %nمنذ %n أسابيعمنذ %n أسابيعمنذ %n أسابيع + + + + %n week(s) + منذ %n أسبوعمنذ أسبوع %nمنذ أسبوعين %nمنذ %n أسابيعمنذ %n أسابيعمنذ %n أسابيع + + + + %n day(s) ago + منذ %n يوممنذ يوم %nمنذ يومين %nمنذ %n أياممنذ %n أياممنذ %n أيام + + + + %n day(s) + منذ %n يوممنذ يوم %nمنذ يومين %nمنذ %n أياممنذ %n أياممنذ %n أيام + + + + %n hour(s) ago + منذ %n ساعةمنذ ساعة %nمنذ ساعتين %nمنذ %n ساعاتمنذ %n ساعاتمنذ %n ساعات + + + + %n hour(s) + منذ %n ساعةمنذ ساعة %nمنذ ساعتين %nمنذ %n ساعاتمنذ %n ساعاتمنذ %n ساعات + + + + %1 minutes ago + منذ %1 دقائق + + + + %1 minutes + %1 دقائق + + + + just now + الآن + + + + Friend Finders + محرك البحث عن الأصدقاء + + + + Music Finders + مكتشفي الاغاني + + + + Status Updaters + أنظمة تحديث حالتك + + + + QuaZipFilePrivate + + + ZIP/UNZIP API error %1 + مشكلة %1 في "ZIP/UNZIP API" + + + + QueueView + + + InfoBar + InfoBar + + + + + Open Queue + إفتح قائمة الإنتظار + + + + The queue is currently empty. Drop something to enqueue it! + قائمة الانتظار هذه فارغة حاليا. إرمي شيئا لإضافته! + + + + Open Queue - %n item(s) + إفتح قائمة الإنتظار - %n بندإفتح قائمة الإنتظار - بند %nإفتح قائمة الإنتظار - بندين %nإفتح قائمة الإنتظار - %n بنودإفتح قائمة الإنتظار - %n بنودإفتح قائمة الإنتظار - %n بنود + + + + Close Queue + أغلق قائمة الإنتظار + + + + RelatedArtistsContext + + + Related Artists + الفنانين ذات الذوق القريب + + + + ResolverConfigDelegate + + + Not found: %1 + لم يوجد: %1 + + + + Failed to load: %1 + فشل في تحميل: %1 + + + + SearchLineEdit + + + Search + بحث + + + + SearchWidget + + + Search: %1 + بحث: %1 + + + + Results for '%1' + نتائج البحث عن '%1' + + + + SettingsDialog + + + Collection + مجموعة + + + + Advanced + متقدمة + + + + All + الكل + + + + Some changed settings will not take effect until Tomahawk is restarted + بعض الإعدادات التي تم تغييرها لن تصبح نافذة المفعول حتى يتم إعادة تشغيل توماهوك + + + + Services + خدمات + + + + Install resolver from file + تثبيت محلل من ملف + + + + Information + معلومات + + + + SocialPlaylistWidget + + + Popular New Albums From Your Friends + ألبومات جديدة مشهورة من أصدقائك + + + + Most Played Playlists + قوائم الأغاني التي سمعت كثيرا + + + + Most Played Tracks You Don't Have + الأغاني التي سمعت كثيرا و التي ليست موجودة لديك + + + + SocialWidget + + + Form + استمارة + + + + Facebook + فيسبوك (Facebook) + + + + Twitter + تويتر (Twitter) + + + + Cover + غلاف + + + + TextLabel + TextLabel + + + + Listening to "%1" by %2 and loving it! %3 + الإستماع إلى "%1" ل%2 ومحبتها! %3 + + + + Listening to "%1" by %2 on "%3" and loving it! %4 + إنني أستمع إلى "%1" من قبل %2 على "%3" وقد أحببتها! %4 + + + + %1 characters left + تبقى %1 حروف + + + + SourceDelegate + + + Track + اغنية + + + + Album + البوم + + + + Artist + فنان + + + + Local + محلية + + + + Top 10 + توب ١٠ + + + + All available tracks + جميع الأغاني المتاحة + + + + + Show + أظهر + + + + + Hide + إخفي + + + + SourceInfoWidget + + + Recent Albums + ألبومات جديدة + + + + Latest Additions + الإضافة الأخيرة + + + + Recently Played Tracks + الأغاني التي إستمعت إليها مؤخرا + + + + New Additions + الإضافة الجديدة + + + + My recent activity + نشاطاتي الأخيرة + + + + Recent activity from %1 + النشاطات المؤخرة ل %1 + + + + SourceItem + + + Collection + مجموعة + + + + + Latest Additions + أحدث الإضافات + + + + Recently Played + تم الاستماع لها مؤخرا + + + + Loved Tracks + الأغاني المحبوبة + + + + SuperCollection + سوبر كولكشن + + + + Sorry, we could not find any loved tracks! + نعتذر، لم نستطيع إيجاد أغاني محبوبة! + + + + Latest additions to your collection + آخر إضافات على مجموعتك + + + + Latest additions to %1's collection + آخر إضافات على مجموعة %1 + + + + Sorry, we could not find any recent additions! + نعتذر، لم نستطيع إيجاد إضافة جديدة! + + + + Recently Played Tracks + الأغاني التي إستمعت إليها مؤخرا + + + + Your recently played tracks + الأغاني التي إستمعت إليها مؤخرا + + + + %1's recently played tracks + الأغاني التي سمعها مؤخرا %1 + + + + Sorry, we could not find any recent plays! + نعتذر، لم نستطيع إيجاد أغاني مسموعة مؤخرا! + + + + SourceTreeView + + + &Copy Link + &نسخ الرابط + + + + &Delete %1 + &أحذف %1 + + + + Add to my Playlists + أضف إلى لوائحي للأغاني + + + + Add to my Automatic Playlists + أضف إلى لوائحي الأوتوماتيكية للأغاني + + + + Add to my Stations + أضف إلى إذاعاتي + + + + &Export Playlist + &تصدير قائمة الأغاني + + + + playlist + قائمة الأغاني + + + + automatic playlist + قائمة أغاني أوتوماتيكية + + + + station + إذاعة + + + + Delete %1? + playlist/station/... + أحذف %1؟ + + + + Would you like to delete the %1 <b>"%2"</b>? + e.g. Would you like to delete the playlist named Foobar? + هل ترغب في حذف %1 <b>"%2"</b>؟ + + + + Save XSPF + حفظ XSPF + + + + Playlists (*.xspf) + قوائم أغاني (*.xspf) + + + + SourcesModel + + + Group + فئة + + + + Collection + مجموعة + + + + Playlist + قائمة الأغاني + + + + Automatic Playlist + قائمة أغاني أوتوماتيكية + + + + Station + إذاعة + + + + Browse + تصفح + + + + Search History + تاريخ البحث + + + + My Music + موسيقتي الخاصة + + + + SuperCollection + سوبر كولكشن + + + + Top Loved Tracks + الأغاني المحبوبة الأكثر شهرة + + + + Dashboard + لوحة القيادة + + + + Recently Played + تم الاستماع لها مؤخرا + + + + Charts + الرسوم البيانية + + + + New Releases + جديد الاصدارات + + + + Friends + الأصدقاء + + + + SpotifyConfig + + + Form + استمارة + + + + Configure your Spotify account + تكوين حساب "Spotify" + + + + Username or Facebook Email + إسم المستخدم أو الايميل على فيسبوك (Facebook Email) + + + + Log In + تسجيل الدخول + + + + Right click on any Tomahawk playlist to sync it to Spotify. + أنقر يمينا على أية قائمة أغاني في توماهوك للمزامنة مع سبوتيفي (Spotify). + + + + High Quality Streams + جودة عالية في الأغاني المحملة + + + + Spotify playlists to keep in sync: + قوائم أغاني سبوتيفي (Spotify) التي يجب إبقائها بتزامن: + + + + Delete Tomahawk playlist when removing synchronization + حذف قوائم أغاني توماهوك عند إزالة التزام + + + + Username: + اسم المستخدم: + + + + Password: + كلمة السر: + + + + SpotifyPlaylistUpdater + + + Delete in Spotify? + أحذف في سبوتيفي (Spotify)؟ + + + + Would you like to delete the corresponding Spotify playlist as well? + هل ترغب في حذف قائمة التشغيل المطابقة على سبوتيفي (Spotify)؟ + + + + StackedSettingsDialog + + + Tomahawk Settings + إعدادات توماهوك + + + + Local Music Information + معلومات الأغاني المحلية + + + + Path to scan for music files: + ماسار لتفحص ملفات الموسيقى: + + + + The Echo Nest supports keeping track of your catalog metadata + and using it to craft personalized radios. Enabling this option + will allow you (and all your friends) to create automatic playlists + and stations based on your personal taste profile. + "The Echo Nest" يدعم إبقاء مسار فوقية فهرسك واستخدامه + لصياغة إذاعات شخصية. تمكين هذا الخيار يسمح لك (و لأصدقائك) +إنشاء قوائم أغاني أوتوماتيكيا و إذاعات حسب ذوقك الشخصي. + + + + Upload collection list to The Echo Nest to enable user radio + تحميل قائمة المجموعة إلى "The Echo" لتمكين راديو المستخدم + + + + Watch for changes + إنتبه للتغييرات + + + + Time between scans, in seconds: + الوقت بين المسوحات، بالثواني: + + + + Advanced Settings + إعدادات متقدمة + + + + Remote Peer Connection Method + طريقة الاتصال بالند البعيد + + + + None (outgoing connections only) + لا شيء (الاتصالات الصادرة فقط) + + + + Use UPnP to establish port forward (recommended) + استخدم عمل UPnP لإنشاء بوابة إعادة التوجيه (موصى به) + + + + Use static external IP address/host name and port + استخدم عنوان/مضيف ايب (IP) و بوابة ثابتين + + + + Set this to your external IP address or host name. Make sure to forward the port to this host! + عين هذا إلى عنوان الايب الخارجي (External IP) الخاص بك أو اسم المضيف. تأكد من إحالة البوابة إلى هذا المضيف! + + + + SOCKS Proxy + سوكس الوكيل + + + + Use SOCKS Proxy + استخدم سوكس الوكيل + + + + Internet Services + خدمات الانترنت + + + + Install from file... + تثبيت من ملف... + + + + Filter by capability: + ترشيح حسب القدرة: + + + + Static Host Name: + اسم مضيف ثابت: + + + + Static Port: + بوابة ثابتة: + + + + Proxy Settings... + إعدادات الوسيط... + + + + Other Settings + إعدادات أخرى + + + + Send reports after Tomahawk crashed + أرسل تقارير بعد فشل توماهوك + + + + Allow web browsers to interact with Tomahawk (recommended) + السماح لمحرك البحث بالتفاعل مع توماهوك (موصى به) + + + + Tomahawk::Accounts::AccountDelegate + + + Add Account + أضف حساب + + + + Remove Account + حذف الحساب + + + + %1 downloads + تحميل %1 + + + + Online + متصل + + + + Connecting... + جاري الاتصال... + + + + Offline + غير متصل + + + + Tomahawk::Accounts::AccountModel + + + Manual Install Required + مطلوب تثبيت يدوي + + + + Unfortunately, automatic installation of this resolver is not available or disabled for your platform.<br /><br />Please use "Install from file" above, by fetching it from your distribution or compiling it yourself. Further instructions can be found here:<br /><br />http://www.tomahawk-player.org/resolvers/%1 + لسوء الحظ، التثبيت التلقائي لهذا المحلل لم يتوفر بعد على منصتك. <br /><br /> الرجاء إستخدام " التثبيت من الملف" أعلاه، بواسطة جلبه من توزيعتك أو تجميعه بنفسك. ويمكن الاطلاع على مزيد من التعليمات هنا: <br /><br />http://www.tomahawk-player.org/resolvers/%1 + + + + Tomahawk::Accounts::GoogleWrapper + + + Configure this Google Account + تكوين حساب "Google" + + + + Google Address + الحساب الخاص بجوجل (Google) + + + + Enter your Google login to connect with your friends using Tomahawk! + أدخل معلوماتك على "Google" ليتم الربط مع أصدقائك من خلال توماهوك! + + + + username@gmail.com + username@gmail.com + + + + Tomahawk::Accounts::GoogleWrapperFactory + + + Connect to Google Talk to find your friends + تواصل مع جوجل توك (Google Talk) لإيجاد أصدقائك + + + + Tomahawk::Accounts::GoogleWrapperSip + + + Add Friend + أضف صديق + + + + Enter Google Address: + أدخل الحساب الخاص بجوجل (Google): + + + + Tomahawk::Accounts::LastFmAccountFactory + + + Scrobble your tracks to last.fm, and find freely downloadable tracks to play + إبعث أغانيك الخاصة إلى last .fm و جد أغاني جديدة مجانا للاستماع إليها + + + + Tomahawk::Accounts::LastFmConfig + + + Testing... + اختبار... + + + + Test Login + اختبار الدخول + + + + Importing %1 + e.g. Importing 2012/01/01 + استيراد %1 + + + + Importing History... + نقل التاريخ... + + + + History Incomplete. Resume + تاريخ ناقص. استأنف + + + + Playback History Imported + تم استيراد تاريخ إعادة الإستماع + + + + + Failed + فشلت + + + + Success + نجاح + + + + Could not contact server + لم أستطيع الإتصال بالخادم + + + + Tomahawk::Accounts::SpotifyAccount + + + Sync with Spotify + مزامنة مع سبوتيفي (Spotify) + + + + Re-enable syncing with Spotify + إعادة تمكين المزامنة مع سبوتيفي (Spotify) + + + + Stop syncing with Spotify + أوقف المزامنة مع سبوتيفي (Spotify) + + + + Tomahawk::Accounts::SpotifyAccountConfig + + + Logging in... + جاري تسجيل الدخول... + + + + Logged in! + مسجل! + + + + Failed: %1 + فشل: %1 + + + + Log In + تسجيل الدخول + + + + Tomahawk::Accounts::SpotifyAccountFactory + + + Play music from and sync your playlists with Spotify Premium + إسمع موسيقى و زامن قوائم تشغيلك مع سبوتيفي بريميوم (Spotify Premium) + + + + Tomahawk::Accounts::TwitterAccountFactory + + + Connect to your Twitter followers. + تواصل مع أصدقائك على تويتر (Twitter). + + + + Tomahawk::Accounts::TwitterConfigWidget + + + + + Tweet! + تويت (Tweet)! + + + + + Status: No saved credentials + الحالة: بيانات غير محفوظة + + + + + + Authenticate + صادق + + + + + Status: Credentials saved for %1 + الحالة: بيانات %1 حفظت + + + + + De-authenticate + قطع المصادقة + + + + + + + + + + Tweetin' Error + خطأ توتينغ ('Tweetin) + + + + The credentials could not be verified. +You may wish to try re-authenticating. + لم نستطيع التحقق من صحة البيانات. +قد ترغب بإعادة المصادقة. + + + + Status: Error validating credentials + الحالة: خطأ في التحقق من صحة البيانات + + + + Global Tweet + تويت شامل (Global Tweet) + + + + Direct Message + رسالة مباشرة + + + + Send Message! + إرسل رسالة! + + + + @Mention + @ذكر + + + + Send Mention! + إرسال ذكر! + + + + You must enter a user name for this type of tweet. + يجب إدخال اسم المستخدم لهذا النوع من التويت (tweet). + + + + Your saved credentials could not be loaded. +You may wish to try re-authenticating. + لم نستطيع تحميل بياناتك المحفوظة. +قد ترغب بمعاودة التصديق. + + + + Your saved credentials could not be verified. +You may wish to try re-authenticating. + لم نستطيع التحقق من بياناتك المحفوظة. +قد ترغب بمعاودة التصديق. + + + + + There was an error posting your status -- sorry! + عذراً... حصل خطأ في تسجيل رسالتك! + + + + + Tweeted! + ثبت التويت! (!Tweeted) + + + + Your tweet has been posted! + رسالتك على تويتر قد نشرت! + + + + There was an error posting your direct message -- sorry! + نعتذر، حصل خطأ في نشر رسالتك المباشرة ! + + + + Your message has been posted! + رسالتك قد نشرت! + + + + Tomahawk::Accounts::XmppAccountFactory + + + Log on to your Jabber/XMPP account to connect to your friends + أدخل إلى حسابك على Jabber/XMPP للربط بأصدقائك + + + + Tomahawk::Accounts::ZeroconfFactory + + + Automatically connect to Tomahawks on the local network + أربط نفسك بتوماهاوك على الشبكة المحلية بطريقة مباشرة أوتوماتيكيا + + + + Tomahawk::ContextMenu + + + &Play + &إستمع + + + + + + Add to &Queue + أضف إلى &قائمة الانتظار + + + + + &Love + &أحب + + + + &Copy Track Link + &إنسخ رابط الأغنية + + + + Un-&Love + لا &أحب + + + + &Delete Items + &أحذف البنود + + + + &Continue Playback after this Track + &أكمل الاستماع بعد هذه الأغنية + + + + &Stop Playback after this Track + &أوقف الاستماع بعد هذه الأغنية + + + + &Show Track Page + &أظهر صفحة الأغاني + + + + &Delete Item + &أحذف البند + + + + &Show Album Page + &أظهر صفحة الألبومات + + + + &Show Artist Page + &أظهر صفحة الفنان + + + + Tomahawk::CustomPlaylistView + + + Top Loved Tracks + الأغاني المحبوبة الأكثر شهرة + + + + Your loved tracks + الأغاني التي أحببتها + + + + %1's loved tracks + الأغاني المحبوبة ل%1 + + + + The most loved tracks from all your friends + الأغاني المحبوبة كثيرا بين أصدقائك + + + + All of your loved tracks + جميع الأغاني التي أحببتها + + + + All of %1's loved tracks + جميع الأغاني المحبوبة ل%1 + + + + Tomahawk::DropJobNotifier + + + playlist + قائمة الأغاني + + + + artist + فنان + + + + track + اغنية + + + + album + البوم + + + + Fetching %1 from database + جلب %1 من قاعدة البيانات + + + + Parsing %1 %2 + تحليل %1 %2 + + + + Tomahawk::DynamicControlList + + + Click to collapse + إضغط للتقليص + + + + Tomahawk::DynamicModel + + + + Could not find a playable track. + +Please change the filters or try again. + لم يوجد أغنية للاستماع إليها. + +غير كلمة الترشيح وأعد الكرة. + + + + Failed to generate preview with the desired filters + فشل في إيجاد عرض مسبق للبحث المطلوب + + + + Tomahawk::DynamicSetupWidget + + + Type: + نوع: + + + + Generate + توليد + + + + Tomahawk::DynamicView + + + Add some filters above to seed this station! + أدخل بعض الترشيحات أعلاه لتوزيع هذه الإذاعة! + + + + Press Generate to get started! + إضغط "توليد" لتبدأ! + + + + Add some filters above, and press Generate to get started! + أدخل بعض الترشيحات أعلاه ثم iضغط "توليد" لتبدأ! + + + + Tomahawk::DynamicWidget + + + Station ran out of tracks! + +Try tweaking the filters for a new set of songs to play. + الإذاعة فرغت من الأغاني! + +عدل في كلمة الترشيح لإيجاد مجموعة جديدة من الأغاني. + + + + Tomahawk::EchonestControl + + + + + + + + is + هو + + + + from user + من المستخدم + + + + + No users with Echo Nest Catalogs enabled. Try enabling option in Collection settings + لا يوجد مستخدمين مع فهرس "Echo Nest" ممكن. تجربة تمكين خيار المجموعة في الإعدادات يمكن أن يساعد + + + + similar to + مشابهة ل + + + + + + + + + + Less + أقل + + + + + + + + + + More + أكثر + + + + 0 BPM + ٠ نبضة في الدقيقة + + + + 500 BPM + ٥٠٠ نبضة في الدقيقة + + + + 0 secs + ٠ ثانية + + + + 3600 secs + ٣٦٠٠ ثانية + + + + -100 dB + -١٠٠ د.ب. + + + + 100 dB + ١٠٠ د.ب. + + + + Major + رئيسي + + + + Minor + ثانوي + + + + C + C + + + + C Sharp + C Sharp + + + + D + D + + + + E Flat + E Flat + + + + E + E + + + + F + F + + + + F Sharp + F Sharp + + + + G + G + + + + A Flat + A Flat + + + + A + A + + + + B Flat + B Flat + + + + B + B + + + + Ascending + تصاعدي + + + + Descending + تنازلي + + + + Tempo + سرعة إيقاع + + + + Duration + مدة + + + + Loudness + قوة الصوت + + + + Artist Familiarity + ألفة الفنان + + + + Artist Hotttnesss + شعبية الفنان + + + + Song Hotttnesss + شعبية الأغنية + + + + Latitude + مدى + + + + Longitude + طول + + + + Mode + أسلوب + + + + Key + مفتاح + + + + Energy + حيوية + + + + Danceability + القابلية للرقص + + + + only by ~%1 + فقط من جانب ~%1 + + + + similar to ~%1 + مشابهة ل%1 + + + + with genre ~%1 + مع النوع ~%1 + + + + + from no one + من لا أحد + + + + My Collection + مجموعتي الخاصة + + + + from %1 radio + من إذاعة %1 + + + + with %1 %2 + مع %1 %2 + + + + about %1 BPM + حوالي %1 نبضة في الدقيقة + + + + about %n minute(s) long + بطول حوالي %n دقائقبطول حوالي دقيقة %nبطول حوالي دقيقتين %nبطول حوالي %n دقائقبطول حوالي %n دقائقبطول حوالي %n دقائق + + + + about %1 dB + حوالي %1 د.ب. + + + + at around %1%2 %3 + في حوالي %1%2 %3 + + + + in %1 + في %1 + + + + in a %1 key + في المفتاح %1 + + + + sorted in %1 %2 order + تم فرزها بالترتيب %1 %2 + + + + with a %1 mood + في مزاج %1 + + + + in a %1 style + في أسلوب %1 + + + + Tomahawk::EchonestSteerer + + + Steer this station: + قد هذه الإذاعة: + + + + Much less + أقل بكثير + + + + Less + أقل + + + + A bit less + أقل بقليل + + + + Keep at current + أبقه على حاله + + + + A bit more + أكثر قليلا + + + + More + أكثر + + + + Much more + أكثر من ذلك بكثير + + + + Tempo + سرعة إيقاع + + + + Loudness + قوة الصوت + + + + Danceability + درجة الرقص + + + + Energy + حيوية + + + + Song Hotttnesss + شعبية الأغنية + + + + Artist Hotttnesss + شعبية الفنان + + + + Artist Familiarity + ألفة الفنان + + + + By Description + من خلال الوصف + + + + Enter a description + أدخل الوصف + + + + Apply steering command + طبق أوامر القيادة + + + + Reset all steering commands + إعادة ضبط جميع أوامر القيادة + + + + Tomahawk::GroovesharkParser + + + Error fetching Grooveshark information from the network! + مشكلة في جلب معلومات "Grooveshark" من الشبكة! + + + + Tomahawk::InfoSystem::ChartsPlugin + + + Top Overall + الأكثر شهرة بالإجمال + + + + Artists + فنانين + + + + Albums + ألبومات + + + + Tracks + أغاني + + + + Tomahawk::InfoSystem::FdoNotifyPlugin + + + Tomahawk is playing "%1" by %2%3. + توماهوك يلعب "%1" ل%2%3. + + + + on "%1" + على "%1" + + + + Tomahawk::InfoSystem::LastFmInfoPlugin + + + Top Tracks + أفضل الأغاني + + + + Loved Tracks + الأغاني المحبوبة + + + + Hyped Tracks + فقت الأغاني + + + + Top Artists + أفضل الفنانين + + + + Hyped Artists + فقت الفنانين + + + + Tomahawk::InfoSystem::NewReleasesPlugin + + + Albums + ألبومات + + + + Tomahawk::InfoSystem::TwitterInfoPlugin + + + Listening to "%1" by %2 and loving it! %3 + إنني أستمع إلى "%1" من قبل %2 وقد أحببتها! %3 + + + + Tomahawk::ItunesParser + + + Error fetching iTunes information from the network! + مشكلة في جلب معلومات "iTunes" من الشبكة! + + + + Tomahawk::JSPFLoader + + + New Playlist + قائمة أغاني جديدة + + + + Failed to save tracks + فشل في حفظ الأغاني + + + + Some tracks in the playlist do not contain an artist and a title. They will be ignored. + بعض الأغاني في قائمة التشغيل لا تحتوي على إسم الفنان أو إسم الأغنية. هذه الأغاني سوف تتجاهل. + + + + XSPF Error + خطأ XSPF + + + + This is not a valid XSPF playlist. + قائمة الأغاني XSPF هذه ليست صالحة. + + + + Tomahawk::LatchManager + + + &Catch Up + &إلحق + + + + + &Listen Along + &استمع مع + + + + Tomahawk::Query + + + and + و + + + + You + أنت + + + + you + أنت + + + + and + و + + + + %n other(s) + %n أخرين%n أخر%n أخرين%n أخرين%n أخرين%n أخرين + + + + %1 people + %1 أشخاص + + + + loved this track + أحببت هذه الأغنية + + + + Tomahawk::RdioParser + + + Error fetching Rdio information from the network! + مشكلة في جلب معلومات "Rdio" من الشبكة! + + + + Tomahawk::ShortenedLinkParser + + + Network error parsing shortened link! + خطأ شبكة في تحليل تقصير الرابط! + + + + Tomahawk::Source + + + + Scanning (%L1 tracks) + يجري مسح (%L1 أغنية) + + + + Scanning + مسح + + + + Checking + فحص + + + + Fetching + جلب + + + + Parsing + تحليل + + + + Saving (%1%) + تحفيظ(%1%) + + + + Online + متصل + + + + Offline + غير متصل + + + + Tomahawk::SpotifyParser + + + Error fetching Spotify information from the network! + مشكلة في جلب معلومات "Spotify" من الشبكة! + + + + Tomahawk::XspfUpdater + + + Automatically update from XSPF + تحديث تلقائي (أوتوماتيكيا) من XSPF + + + + TomahawkApp + + + My Collection + مجموعتي الخاصة + + + + TomahawkOAuthTwitter + + + Twitter PIN + رقم البن لتويتر (Twitter Pin) + + + + After authenticating on Twitter's web site, +enter the displayed PIN number here: + بعد المصادقة على موقع تويتر (Twitter)، +أدخل رقم البين (PIN) الموجود أمامك على الشاشة هنا: + + + + TomahawkTrayIcon + + + &Stop Playback after current Track + &أوقف الاستماع بعد الأغنية الحالية + + + + + Hide Tomahawk Window + إخفي نافذة توماهوك + + + + Show Tomahawk Window + أظهر نافذة توماهوك + + + + Currently not playing. + لا يتم الاستماع حاليا. + + + + Play + إستمع + + + + Pause + تعليق + + + + &Love + &أحب + + + + Un-&Love + لا &أحب + + + + &Continue Playback after current Track + &أكمل الاستماع بعد الأغنية الحالية + + + + TomahawkWindow + + + Tomahawk + توماهوك + + + + &Settings + &إعدادات + + + + &Controls + &ضوابط + + + + &Network + &شبكة + + + + &Window + &نافذة + + + + &Help + &مساعدة + + + + &Quit + &إنهاء + + + + Ctrl+Q + Ctrl+Q + + + + Go &Online + اصبح &متصل + + + + Add &Friend... + أضف &صديق... + + + + U&pdate Collection + &تحديث المجموعة + + + + Update Collection + تحديث المجموعة + + + + &Configure Tomahawk... + &تكوين توماهوك... + + + + Load &XSPF... + تحميل XSPF&... + + + + Create &New Playlist... + إنشاء &قائمة أغاني جديدة... + + + + About &Tomahawk... + عن &توماهوك... + + + + Create New &Automatic Playlist + إنشاء قائمة أغاني جديدة أ&وتوماتيكيا + + + + Create New &Station + إنشاء &قائمة أغاني جديدة + + + + Show Offline Sources + أظهر المصادر الغير متصلة + + + + Hide Offline Sources + إخفي المصادر الغير متصلة + + + + Minimize + خفض + + + + Ctrl+M + Ctrl+M + + + + Zoom + زوم + + + + Meta+Ctrl+Z + Meta+Ctrl+Z + + + + Diagnostics... + تشخيص... + + + + Fully &Rescan Collection + إعادة &مسح المجموعة كاملة + + + + Fully Rescan Collection + إعادة مسح المجموعة كاملة + + + + + Play + إستمع + + + + Space + مساحة + + + + Previous + سابق + + + + Next + التالي + + + + Back + إلى الوراء + + + + Go back one page + العودة صفحة واحدة إلى الوراء + + + + Forward + تقدم + + + + Go forward one page + تقدم صفحة واحدة + + + + Global Search... + بحث شامل... + + + + + Check For Updates... + التحقق من التحديثات... + + + + + + Connect To Peer + ربط بالند + + + + Enter peer address: + أدخل عنوان الند: + + + + Enter peer port: + أدخل بوابة الند: + + + + Enter peer key: + أدخل مفتاح الند: + + + + XSPF Error + خطأ XSPF + + + + This is not a valid XSPF playlist. + قائمة الأغاني XSPF هذه ليست صالحة. + + + + Failed to save tracks + فشل في حفظ الأغاني + + + + Some tracks in the playlist do not contain an artist and a title. They will be ignored. + بعض الأغاني في قائمة الأغاني لا تحتوي على إسم الفنان أو إسم الأغنية. هذه الأغاني سوف تتجاهل. + + + + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Make sure you have a suitable Phonon backend and required plugins installed. + عذرا، هناك مشكلة في الوصول إلى جهاز الصوت أو الأغنية المطلوب، سوف يتم تخطي الأغنية الحالية. تأكد أن لديك خلفية فونون المناسبة والإضافات المطلوبة مثبتة. + + + + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. + عذرا، هناك مشكلة في الوصول إلى جهاز الصوت أو الأغنية المطلوب، سوف يتم تخطي الأغنية الحالية. + + + + Station + إذاعة + + + + Create New Station + إنشاء قائمة أغاني جديدة + + + + Name: + الاسم: + + + + Playlist + قائمة الأغاني + + + + Automatic Playlist + قائمة أغاني أوتوماتيكية + + + + Pause + تعليق + + + + Go &offline + أصبح &غير متصل + + + + Go &online + اصبح &متصل + + + + Authentication Error + خطأ في المصادقة + + + + Error connecting to SIP: Authentication failed! + خطأ في الاتصال بسيب (SIP): فشلت المصادقة! + + + + %1 by %2 + track, artist name + %1 من قبل %2 + + + + %1 - %2 + current track, some window title + %1 - %2 + + + + <h2><b>Tomahawk %1<br/>(%2)</h2> + <h2><b>Tomahawk %1<br/>(%2)</h2> + + + + <h2><b>Tomahawk %1</h2> + <h2><b>Tomahawk %1</h2> + + + + Copyright 2010 - 2012 + حقوق الطبع والنشر ٢٠١٠ - ٢٠١٢ + + + + Thanks to: + شكر لكل من: + + + + About Tomahawk + عن توماهوك + + + + TopBar + + + Form + استمارة + + + + 0 Sources + ٠ مصادر + + + + 0 Tracks + لا أغاني + + + + 0 Artists + لا فنانين + + + + 0 Shown + لا ظهور + + + + Tracks + أغاني + + + + Artists + فنانين + + + + Filter + مرشح + + + + Artist View + عرض الفنان + + + + Flat View + نظرة مسطحة + + + + Sources + المصادر + + + + Shown + ظاهرة + + + + TopTracksContext + + + Top Hits + الأكثر شهرة + + + + TrackInfoWidget + + + Form + استمارة + + + + Cover + غلاف + + + + Track + اغنية + + + + by + من قبل + + + + Artist + فنان + + + + from + من + + + + Album + البوم + + + + Statistics + إحصائيات + + + + Lyrics + كلمات + + + + Similar Tracks + أغاني قريبة + + + + Sorry, but we could not find similar tracks for this song! + نعتذر، لم نستطيع إيجاد أغاني قريبة من هذه الأغنية! + + + + You've listened to this track %n time(s). + لقد استمعت إلى هذه الأغنية %n مرة.لقد استمعت إلى هذه الأغنية مرة %n.لقد استمعت إلى هذه الأغنية مرتين %n.لقد استمعت إلى هذه الأغنية %n مرات.لقد استمعت إلى هذه الأغنية %n مرات.لقد استمعت إلى هذه الأغنية %n مرات. + + + + You've never listened to this track before. + لم تستمع لهذه الأغنية من قبل. + + + + You first listened to it on %1. + استمعت إليها أولاً في %1. + + + + You've listened to %1 %n time(s). + لقد استمعت إلى %1 %n مرة.لقد استمعت إلى %1 مرة %n.لقد استمعت إلى %1 مرتين %n.لقد استمعت إلى %1 %n مرات.لقد استمعت إلى %1 %n مرات.لقد استمعت إلى %1 %n مرات. + + + + You've never listened to %1 before. + لم تستمع إلى %1 من قبل. + + + + TrackView + + + Sorry, your filter '%1' did not match any results. + عذراً، ترشيحك "%1" لم يطابق أي نتائج. + + + + TransferStatusItem + + + from + من + + + + to + إلى + + + + TreeItemDelegate + + + Unknown + مجهول + + + + TreeModel + + + All Artists + جميع الفنانين + + + + + My Collection + مجموعتي الخاصة + + + + + Collection of %1 + مجموعة %1 + + + + TreeView + + + Sorry, your filter '%1' did not match any results. + نتأسف، نظام الترشيح لم يجد شيئا تحت عنوان '%1'. + + + + TwitterConfigWidget + + + Configure this Twitter account + تكوين حساب "Twitter" + + + + The Twitter plugin allows you to discover and play music from your Twitter friends running Tomahawk and post messages to your account. + مساعد تويتر (Twitter) يسمح لك بإكتشاف والإستماع إلى الموسيقى من أصدقائك على تويتر المستخدمين لتوماهوك بالإضافة إلى نشر الرسائل إلى حسابك. + + + + Status: No saved credentials + الحالة: بيانات غير محفوظة + + + + Authenticate with Twitter + صادق مع تويتر (Twitter) + + + + Twitter Connections + إتصالات بتويتر (Twitter) + + + + +If you only want to post tweets, you're done. + +If you want to connect Tomahawk to your friends using Twitter, select the type of tweet and press the button below to send a sync message. You must both be following each other as Direct Messages are used. Then be (very) patient -- it can take several minutes! + +You can re-send a sync message at any time simply by sending another tweet using the button. + +إن كنت تريد نشر التويت (Tweet) فقط، لقد انتهيت! + +إن كنت تريد لتوماهوك الاتصال بأصدقائك من خلال تويتر (Twitter)، حدد نوع التويت (Tweet) و إضغط على الزر أدناه لإرسال رسالة مزامنة. يجب أن تكونا تابعين لبعضكما البعض على حد سواء لأن الرسائل المباشرة تستخدم. ثم يجب الإنتظار بصبر (كبير) - يمكن أن تستغرق العملية عدة دقائق! + +يمكنك إعادة إرسال رسالة المزامنة في أي وقت عن طريق إرسال تويت (Tweet) آخر بضغط هذا الزر من جديد. + + + + Select the kind of tweet you would like, then press the button to post it: + حدد نوع التويت (Tweet) اللذي ترغب به، ثم إضغط على الزر لنشره: + + + + Global Tweet + تويت شامل (Global Tweet) + + + + @Mention + @ذكر + + + + Direct Message + رسالة مباشرة + + + + e.g. @tomahawk + مثل: @tomahawk + + + + Send Message + إرسل رسالة + + + + ViewManager + + + After you have scanned your music collection you will find your tracks right here. + بعد إجراء مسح مجموعة أغانيك الخاصة ستجد أغانيك هنا. + + + + This collection is empty. + هذه المجموعة فارغة. + + + + SuperCollection + سوبر كولكشن + + + + Combined libraries of all your online friends + مكتبات مجمعة لكل اصحابك المتصلين + + + + All available albums + جميع الألبومات الجديدة + + + + Recently Played Tracks + الأغاني التي إستمعت إليها مؤخرا + + + + Recently played tracks from all your friends + جميع الأغاني التي استمع إليها أصدقائك مؤخرا + + + + WelcomeWidget + + + Recent Additions + آخر الإضافات + + + + Newest Stations & Playlists + أجدد الإذاعات و قوائم الأغاني + + + + Recently Played Tracks + الأغاني التي إستمعت إليها مؤخرا + + + + No recently created playlists in your network. + لا قوائم أغاني جديدة أنشئت مؤخرا على شبكتك. + + + + Welcome to Tomahawk + مرحبا بكم في توماهوك + + + + WhatsHotWidget + + + Charts + الرسوم البيانية + + + + WikipediaContext + + + Wikipedia + ويكيبيديا + + + + XMPPBot + + + +Terms for %1: + + شروط %1: + + + + No terms found, sorry. + لا شروط موجودة، نتأسف. + + + + +Hotttness for %1: %2 + + +شعبية %1:%2 + + + + + +Familiarity for %1: %2 + + +ألفت %1:%2 + + + + + +Lyrics for "%1" by %2: + +%3 + + +كلمات أغاني "%1" من قبل %2" + +%3 + + + + + XSPFLoader + + + Failed to parse contents of XSPF playlist + فشل في تحليل محتويات قائمة الأغاني XSPF + + + + Some playlist entries were found without artist and track name, they will be omitted + تم العثور على مداخل في قوائم الأغاني لا تحتوي على إسم فنان أو إسم أغنية، هذه المداخل سوف تحذف + + + + Failed to fetch the desired playlist from the network, or the desired file does not exist + فشل في جلب قائمة الأغاني المطلوبة من الشبكة، أو الملف المطلوب غير موجود + + + + New Playlist + قائمة أغاني جديدة + + + + XmlConsole + + + Xml stream console + Xml stream console + + + + + Filter + مرشح + + + + Save log + حفظ تسجيل الدخول + + + + Disabled + تعطيل + + + + By JID + By JID + + + + By namespace uri + By namespace uri + + + + By all attributes + By all attributes + + + + Visible stanzas + Visible stanzas + + + + Information query + معلومات الإستعلام + + + + Message + رسالة + + + + Presence + Presence + + + + Custom + تعديل + + + + Close + اغلاق + + + + Save XMPP log to file + Save XMPP log to file + + + + OpenDocument Format (*.odf);;HTML file (*.html);;Plain text (*.txt) + OpenDocument Format (*.odf);;HTML file (*.html);;Plain text (*.txt) + + + + XmppConfigWidget + + + Xmpp Configuration + تكوينات أكسمبب (XMPP) + + + + Configure this Xmpp account + تكوين حساب "XMPP" + + + + Enter your Xmpp login to connect with your friends using Tomahawk! + أدخل معلوماتك على "XMPP" ليتم الربط مع أصدقائك من خلال توماهوك! + + + + Login Information + معلومات الدخول + + + + Xmpp ID: + تعريف أكسمبب (XMPP ID): + + + + e.g. user@example.com + مثل: user@example.com + + + + Password: + كلمة السر: + + + + An account with this name already exists! + حساب بهذا الإسم موجود أصلا! + + + + Advanced Xmpp Settings + إعدادات أكسمبب (XMPP) المتقدمة + + + + Server: + خادم: + + + + Port: + بوابة: + + + + Lots of servers don't support this (e.g. GTalk, jabber.org) + الكثير من الخوادم لا تدعم هذا (e.g. GTalk, jabber.org) + + + + Publish currently playing track + أنشر الأغنية التي تسمع الآن + + + + Enforce secure connection + أفرض إتصال أمن + + + + XmppSipPlugin + + + User Interaction + تفاعل المستخدم + + + + Host is unknown + المضيف مجهول + + + + Item not found + البند غير موجودة + + + + Authorization Error + خطا في الترخيص + + + + Remote Stream Error + خطأ في التحميل (المجرى) البعيد + + + + Remote Connection failed + فشل في الاتصال البعيد + + + + Internal Server Error + خطأ داخلي في الخادم + + + + System shutdown + إغلاق النظام + + + + Conflict + تضارب + + + + Unknown + مجهول + + + + No Compression Support + لا دعم للضغط + + + + No Encryption Support + لا دعم للتشفير + + + + No Authorization Support + لا دعم للترخيص + + + + No Supported Feature + لا ميزة معتمدة + + + + Add Friend + أضف صديق + + + + Enter Xmpp ID: + أدخل تعريف أكسمبب (XMPP ID): + + + + Add Friend... + أضف صديق... + + + + XML Console... + وحدة التحكم XML... + + + + I'm sorry -- I'm just an automatic presence used by Tomahawk Player (http://gettomahawk.com). If you are getting this message, the person you are trying to reach is probably not signed on, so please try again later! + أنا أسف... أنا مجرد وجود ألي مستخدم من قبل توماهوك (http://gettomahawk.com). في حال الحصول على هذه الرسالة، فإن الشخص اللذي تحاول الوصول إليه خارج الخدمة، فنرجو المحاولة لاحقاً! + + + + Authorize User + أعطي الإذن للمستخدم + + + + Do you want to grant <b>%1</b> access to your Collection? + هل تريد أن تسمح ل <b>%1</b> بالوصول إلى مجموعتك ؟ + + + + ZeroconfConfig + + + Form + استمارة + + + + Local Network configuration + تكوين الشبكة المحلية + + + + This plugin will automatically find other users running Tomahawk on your local network + هذا المساعد سيجد مستخدمين أخرين لتوماهاوك على الشبكة المحلية أوتوماتيكيا + + + + Connect automatically when Tomahawk starts + أربط مباشرتا عند بدء توماهوك + + + \ No newline at end of file diff --git a/lang/tomahawk_bg.ts b/lang/tomahawk_bg.ts index cd1fb8e6c..eaa52a8bd 100644 --- a/lang/tomahawk_bg.ts +++ b/lang/tomahawk_bg.ts @@ -2,17 +2,17 @@ AccountFactoryWrapper - + Dialog - + Description goes here - + Add Account Добави регистрация @@ -20,17 +20,17 @@ AccountFactoryWrapperDelegate - + Online На линия - + Connecting... Свързване... - + Offline Извън линия @@ -38,13 +38,13 @@ AclJobDelegate - + Error displaying ACL info Грешка при изобразяване на ACL информация - - + + Allow %1 to connect and stream from you? Ще позволиш ли на %1 да се свърже и слуша музика от твоята колекция? @@ -53,69 +53,69 @@ connect and stream from you? ActionCollection - + &Listen Along &Слушай заедно с - + Stop &Listening Along Спри &да слушаш заедно с - + &Follow in real-time &Следвай в реално време - - + + &Listen Privately &Слушай самостоятелно - - + + &Listen Publicly &Слушай публично - + &Load Playlist &Зареди списък - + &Rename Playlist &Преименувай списък - + &Copy Playlist Link &Копирай връзка към списък - + &Play &Изпълни - + &Stop &Спри - + &Previous Track &Предходна песен - + &Next Track &Следваща песен - + &Quit &Изключи приложението @@ -133,30 +133,17 @@ connect and stream from you? Други албуми на този артист - - - Click to show Official Tracks - Покажи само официалните песни + + Sorry, we could not find any other albums for this artist! + - - - Click to show SuperCollection Tracks - Покажи песните от супер колекцията + + Sorry, we could not find any tracks for this album! + - - - Click to show SuperCollection Albums - Покажи албумите от супер колекцията - - - - Click to show Official Albums - Покажи само официалните албуми - - - + Other Albums by %1 Други албуми от %1 @@ -164,35 +151,17 @@ connect and stream from you? AlbumModel - - Album - Албум - - - - + + All albums from %1 Всички албуми на %1 - + All albums Всички албуми - - AlbumView - - - After you have scanned your music collection you will find your latest album additions right here. - След като бъде сканирана музикалната ти колекция, ще откриеш тук най-скоро добавените албуми. - - - - This collection doesn't have any recent albums. - В тази колекция все още няма добавени наскоро албуми. - - ArtistInfoWidget @@ -211,41 +180,24 @@ connect and stream from you? Свързани артисти - + Albums Албуми - - - Click to show SuperCollection Albums - Покажи албумите от супер колекцията -/Сборен излед от локалните и наличните в колекциите на приятелите ти/ + + Sorry, we could not find any albums for this artist! + - - Click to show Official Albums - Покажи само официалните албуми -/Албумите означени като "Официални" в БД на Misicbrainz, Rovi, и др.п./ - - - - ArtistView - - - After you have scanned your music collection you will find your tracks right here. - След като приключи сканирането на музикалната ти колекция, ще откриеш музиката си тук. + + Sorry, we could not find any related artists! + - - This collection is currently empty. - Базата данни с песни е празна. - - - - Sorry, your filter '%1' did not match any results. - Съжалявам. -Твоят филтър '%1' не върна никакъв резултат. + + Sorry, we could not find any top hits for this artist! + @@ -329,23 +281,26 @@ connect and stream from you? CategoryAddItem - - - New Playlist - Нов списък + + Create new Playlist + - - - - + + Create new Station + + + + + + New Station Нова станция - - - + + + %1 Station %1 Станция @@ -371,27 +326,6 @@ connect and stream from you? Изчисти - - CollectionFlatModel - - - My Collection - Моята колекция - - - - Collection of %1 - Колекцията на %1 - - - - CollectionView - - - This collection is empty. - Тази колекция е празна. - - ContextWidget @@ -401,12 +335,12 @@ connect and stream from you? - + Show Footnotes Покажи подробности - + Hide Footnotes Скрий подробности @@ -418,39 +352,52 @@ connect and stream from you? Tomahawk Crash Reporter Автоматично докладване на грешки на Tomahawk - + - <p><b>Sorry!</b>&nbsp;Tomahawk crashed. Information about the crash is now being sent to Tomahawk HQ so that we can fix the bug.</p> - <p><b>Извинявай</b>&nbsp;Tomahawk спря да работи. Информация относно проблемът се изпраща към нашата централа, за да можем да го отстраним.</p> + <html><head/><body><p><span style=" font-weight:600;">Sorry!</span> Tomahawk crashed. Please tell us about it! Tomahawk has created an error report for you that can help improve the stability in the future. You can now send this report directly to the Tomahawk developers.</p></body></html> + <html><head/><body><p><span style=" font-weight:600;">Извинявай!</span> Tomahawk заби. +Моля уведоми ни за този слуай! +Tomahaw създаде доклад относно това и изпращайки го, ще помогнеш за по-добрата работа на приложението за в бъдеще. +Изпрати директно до разработчиците на Tomahawk</p></body></html> - + + Send this report + Изпрати докладът + + + + Don't send + Не изпращай + + + Abort Откажи - + You can disable sending crash reports in the configuration dialog. Можете да спрете изпращането на информация относно проблеми в панелът с настройки. - + Uploaded %L1 of %L2 KB. Качени %L1 от %В2 КБ. - - + + Close Затвори - + Sent! <b>Many thanks</b>. Изпращането приключи. <b>Благодарим ви за отзивчивостта!</b>. - + Failed to send crash info. Изпращането на краш-данни е неуспешно. @@ -466,17 +413,17 @@ connect and stream from you? DelegateConfigWrapper - + About Относно - + Delete Account Изтрий регистрация - + About this Account Относно тази регистрация @@ -489,24 +436,11 @@ connect and stream from you? Диагностична информация относно Tomahawk - - Update - Обнови - - - + Copy to Clipboard Копирай в буферът - - DropJob - - - No tracks found for given %1 - Няма открити изпълнения на %1 - - GlobalSearchWidget @@ -531,29 +465,11 @@ connect and stream from you? Информационно поле - + Filter... Филтър... - - JobStatusView - - - Searching For - Търсене за - - - - Pending - Изчакващо - - - - Idle - Бездействам - - LastFmConfig @@ -581,6 +497,11 @@ connect and stream from you? Test Login Пробна връзка + + + Import Playback History + Импортирай история на просвирените песни + LastfmContext @@ -629,12 +550,12 @@ connect and stream from you? LoadXSPFDialog - + Load XSPF File Зареди XSPF файл - + XSPF Files (*.xspf) XSPF файлове (*.xspf) @@ -683,20 +604,89 @@ connect and stream from you? NewReleasesWidget - + New Releases Нови албуми + + PlayableModel + + + Artist + Артист + + + + Title + Име + + + + Composer + Композитор + + + + Album + Албум + + + + Track + Песен + + + + Duration + Продължителност + + + + Bitrate + Качество + + + + Age + Възраст + + + + Year + Година + + + + Size + Големина + + + + Origin + Източник + + + + Score + Резултат + + + + + Name + + + PlaylistItemDelegate - + played %1 by you изпълнена %1 от мен - + played %1 by %2 изпълнена %1 от %2 @@ -704,17 +694,17 @@ connect and stream from you? PlaylistLargeItemDelegate - + played %1 by you изпълнена %1 от мен - + played %1 by %2 изпълнена %1 от %2 - + added %1 добавен %1 @@ -722,12 +712,12 @@ connect and stream from you? PlaylistModel - + A playlist by %1, created %2 Списък - създател %1, от %2 - + you ти @@ -783,7 +773,12 @@ connect and stream from you? PlaylistView - + + This playlist is currently empty. + + + + This playlist is currently empty. Add some tracks to it and enjoy the music! Този списък в момента е празен. Добави няколко изпълнения и се наслади на музиката! @@ -850,82 +845,82 @@ connect and stream from you? QObject - + %n year(s) ago преди %n годинапреди %n години - + %n year(s) %n година%n години - + %n month(s) ago преди %n месецпреди %n месеца - + %n month(s) %n месец%n месеца - + %n week(s) ago преди %n седмицапреди %n седмици - + %n week(s) %n седмица%n седмици - + %n day(s) ago преди %n денпреди %n дена - + %n day(s) %n ден%n дена - + %n hour(s) ago преди %n часпреди %n часа - + %n hour(s) %n час %n часа - + %1 minutes ago преди %1 минути - + %1 minutes %1 минути - + just now току-що - + Friend Finders Търсене на приятели - + Music Finders Търсене на музика - + Status Updaters Обновяване на статуси @@ -947,20 +942,30 @@ connect and stream from you? - - Show Queue - Покажи опашката + + Open Queue + - - Hide Queue - Скрий опашката + + The queue is currently empty. Drop something to enqueue it! + + + + + Open Queue - %n item(s) + + + + + Close Queue + RelatedArtistsContext - + Related Artists Подобни артисти @@ -1002,37 +1007,37 @@ connect and stream from you? SettingsDialog - + Collection Колекция - + Advanced Разширени - + All Всички - + Some changed settings will not take effect until Tomahawk is restarted Някои промени няма да имат ефект, докато програмата не бъде рестартирана. - + Services Услуги - + Install resolver from file Инсталирай услиги за търсене от файл - + Information Информация @@ -1083,17 +1088,17 @@ connect and stream from you? Етикет - + Listening to "%1" by %2 and loving it! %3 ♥ "%1" от %2 %3 - + Listening to "%1" by %2 on "%3" and loving it! %4 #fb ♥ "%1" от %2 от "%3" %4 - + %1 characters left Остават още %1 символа @@ -1126,29 +1131,19 @@ connect and stream from you? Първите 10 - - Offline - Извън линия - - - + All available tracks Всички налични изпълнения - - Online - На линия - - - - + + Show Покажи - - + + Hide Скрий @@ -1171,17 +1166,17 @@ connect and stream from you? Наскоро изпълнени песни - + New Additions Нови попълнения - + My recent activity Скорошна активност - + Recent activity from %1 Скорошна активност на %1 @@ -1189,41 +1184,52 @@ connect and stream from you? SourceItem - + Collection Колекция - - + + Latest Additions Последно добавени - + Recently Played Наскоро изпълнени песни - + Loved Tracks Харесани песни - + SuperCollection - Супер колекция + Супер колекция +/Сборен излед от локалните и наличните в колекциите на приятелите ти/ - + + Sorry, we could not find any loved tracks! + + + + Latest additions to your collection Последно добавени към колекцията - + Latest additions to %1's collection Последно добавени в колекцията на %1 + + + Sorry, we could not find any recent additions! + + Recently Played Tracks @@ -1239,31 +1245,78 @@ connect and stream from you? %1's recently played tracks Наскоро изпълнените песни от %1 + + + Sorry, we could not find any recent plays! + + SourceTreeView - + &Copy Link &Копирай адресът - + &Delete %1 &Изтрий %1 + + + Add to my Playlists + + + + + Add to my Automatic Playlists + + + + + Add to my Stations + + &Export Playlist &Изнеси списък - + + playlist + + + + + automatic playlist + + + + + station + + + + + Delete %1? + playlist/station/... + + + + + Would you like to delete the %1 <b>"%2"</b>? + e.g. Would you like to delete the playlist named Foobar? + + + + Save XSPF Запази XSPF - + Playlists (*.xspf) Списъци (*.xspf) @@ -1313,7 +1366,8 @@ connect and stream from you? SuperCollection - Супер колекция + Супер колекция +/Сборен излед от локалните и наличните в колекциите на приятелите ти/ @@ -1533,7 +1587,7 @@ connect and stream from you? Send reports after Tomahawk crashed - Изпрати сервизна информация след счупването на програмата. + Изпрати сервизна информация след забиване. @@ -1544,36 +1598,52 @@ connect and stream from you? Tomahawk::Accounts::AccountDelegate - + Add Account Добави регистрация - + Remove Account Премахни регистрация - + %1 downloads %1 сваляния - + Online На линия - + Connecting... Свързване... - + Offline Извън линия + + Tomahawk::Accounts::AccountModel + + + Manual Install Required + Изисква се ръчно инсталиране + + + + Unfortunately, automatic installation of this resolver is not available or disabled for your platform.<br /><br />Please use "Install from file" above, by fetching it from your distribution or compiling it yourself. Further instructions can be found here:<br /><br />http://www.tomahawk-player.org/resolvers/%1 + За съжаление, автоматичното инсталиране на този компонен е деактивирано за твоята платформа. +<br /><br /> +Моля използвай опцията 'Инсталирай от файл', намираща се по-горе. За целта е необходимо предварително да изтеглиш необходимите файлове за твоята платформа и да ги компилираш самостоятелно. +Допълнителни инструкции могат да бъдат открити на <br /><br />http://www.tomahawk-player.org/resolvers/%1 + + Tomahawk::Accounts::GoogleWrapper @@ -1629,23 +1699,49 @@ connect and stream from you? Tomahawk::Accounts::LastFmConfig - + + Testing... + Тествам... + + + Test Login Тествай връзката - - + + Importing %1 + e.g. Importing 2012/01/01 + Импортирам %1 + + + + Importing History... + Импортирам... + + + + History Incomplete. Resume + Непълна информация. Продължи. + + + + Playback History Imported + Историята на просвирените песни е импортирана + + + + Failed Неуспешно - + Success Ура! - + Could not contact server Не мога да се свържа със сървъра @@ -1653,12 +1749,17 @@ connect and stream from you? Tomahawk::Accounts::SpotifyAccount - + Sync with Spotify Синхронизирай със Spotify - + + Re-enable syncing with Spotify + Включи отново синхронизирането със Spotify + + + Stop syncing with Spotify Спри синхронизацията със Spotify @@ -1666,22 +1767,22 @@ connect and stream from you? Tomahawk::Accounts::SpotifyAccountConfig - + Logging in... Влизам... - + Logged in! Готово! - + Failed: %1 Неуспех: %1 - + Log In Влез @@ -1689,7 +1790,7 @@ connect and stream from you? Tomahawk::Accounts::SpotifyAccountFactory - + Play music from and sync your playlists with Spotify Premium Слушай музика и синхронизирай твоите списъци със Spotify Premium @@ -1705,129 +1806,129 @@ connect and stream from you? Tomahawk::Accounts::TwitterConfigWidget - - - + + + Tweet! Чурулик! - - + + Status: No saved credentials Няма запазени данни за вход - - - + + + Authenticate Удостовери - - + + Status: Credentials saved for %1 Статус: данните са запаметени за %1 - - + + De-authenticate Премахни удостоверяване - - - - - - - + + + + + + + Tweetin' Error Грешка при изпращане на съобщение - + The credentials could not be verified. You may wish to try re-authenticating. Данните за вход не могат да бъдат потвърдени. Моля, опитай отново. - + Status: Error validating credentials Грешка при валидиране на данни за вход - + Global Tweet Общо съобщение - + Direct Message Лично съобщение - + Send Message! Изпрати съобщението! - + @Mention @Споменаване - + Send Mention! Изпрати споменаването! - + You must enter a user name for this type of tweet. Въведи име на потребител, за този вид съобщение. - + Your saved credentials could not be loaded. You may wish to try re-authenticating. Данните ти за вход не могат да бъдат заредени. Моля, оторизирай програмата отново. - + Your saved credentials could not be verified. You may wish to try re-authenticating. Запаметените данни за вход не могат да бъдат потвърдени. Моля, оторизирай програмата отново. - - + + There was an error posting your status -- sorry! Грешка при побликуване. Опитай отново. - - + + Tweeted! Изчуруликано! - + Your tweet has been posted! Съобщението ти беше побликувано. - + There was an error posting your direct message -- sorry! Грешка при изпращане на лично съобщение. Опитай отново. - + Your message has been posted! Съобщението беще побликувано! @@ -1843,7 +1944,7 @@ You may wish to try re-authenticating. Tomahawk::Accounts::ZeroconfFactory - + Automatically connect to Tomahawks on the local network Автоматично свързва Tomahawk с останалите копия на програмата, работещи в локалната мрежа. @@ -1852,20 +1953,20 @@ You may wish to try re-authenticating. Tomahawk::ContextMenu - + &Play &Изпълни - - - + + + Add to &Queue Добави към &опашката - - + + &Love &Харесай @@ -1875,40 +1976,45 @@ You may wish to try re-authenticating. &Копирай адресът на изпълнението - - Show &Album page - Покажи &страницата на албума - - - - Show &Artist page - Покажи страницата на &артиста - - - + Un-&Love Не-&харесай - + &Delete Items &Изтрий позициите - + &Continue Playback after this Track &Продължи след тази песен - + &Stop Playback after this Track &Спри след тази песен - + + &Show Track Page + &Покажи страницата на песента + + + &Delete Item &Изтрий позицията + + + &Show Album Page + &Покажи страницата на албума + + + + &Show Artist Page + &Покажи страницата на артистът + Tomahawk::CustomPlaylistView @@ -1946,12 +2052,32 @@ You may wish to try re-authenticating. Tomahawk::DropJobNotifier - + + playlist + + + + + artist + + + + + track + + + + + album + + + + Fetching %1 from database Извличане на %1 от БД - + Parsing %1 %2 Обединяване на %1 %2 @@ -1959,7 +2085,7 @@ You may wish to try re-authenticating. Tomahawk::DynamicControlList - + Click to collapse Свий @@ -1997,17 +2123,17 @@ Please change the filters or try again. Tomahawk::DynamicView - + Add some filters above to seed this station! Добави няколко критерия, за да разпространиш тази станция - + Press Generate to get started! Натисни "Генерирай" за да почне просвирване. - + Add some filters above, and press Generate to get started! Добави няколко критерия и натисни "Генерирай" за да почне просвирване. @@ -2015,7 +2141,7 @@ Please change the filters or try again. Tomahawk::DynamicWidget - + Station ran out of tracks! Try tweaking the filters for a new set of songs to play. @@ -2329,97 +2455,97 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::EchonestSteerer - + Steer this station: Настройки: - + Much less Много по-малко - + Less По-малко - + A bit less Малко по-малко - + Keep at current Запази - + A bit more Малко повече - + More Повече - + Much more Много повече - + Tempo Темпо - + Loudness Сила - + Danceability Танцувалност - + Energy Енергия - + Song Hotttnesss Популярност на изпълненията - + Artist Hotttnesss Популярност на изпълнителя - + Artist Familiarity Близост на изпълнителите - + By Description По описание - + Enter a description Въведи описание - + Apply steering command Приложи избраната команда - + Reset all steering commands Нулирай всички предишни настройки @@ -2435,22 +2561,22 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::InfoSystem::ChartsPlugin - + Top Overall Най-известни - + Artists Артисти - + Albums Албуми - + Tracks Песни @@ -2458,12 +2584,12 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::InfoSystem::FdoNotifyPlugin - + Tomahawk is playing "%1" by %2%3. Tomahawk възпроизвежда "%1" от %2%3 - + on "%1" от "%1" @@ -2471,27 +2597,27 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::InfoSystem::LastFmInfoPlugin - + Top Tracks Най-слушани песни - + Loved Tracks Харесани песни - + Hyped Tracks Песни, изпъкващи сред останалите - + Top Artists Най-слушани артисти - + Hyped Artists Изпълнители, изпъкващи сред останалите @@ -2499,7 +2625,7 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::InfoSystem::NewReleasesPlugin - + Albums Албуми @@ -2507,7 +2633,7 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::InfoSystem::TwitterInfoPlugin - + Listening to "%1" by %2 and loving it! %3 #fb ♥ "%1" от %2 %3 @@ -2565,37 +2691,37 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Query - + and и - + You Ти - + you ти - + and и - + %n other(s) %n друг%n други - + %1 people %1 хора - + loved this track хареса тази песен @@ -2611,7 +2737,7 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::ShortenedLinkParser - + Network error parsing shortened link! Мрежова грешка при извличане на съкратеният адрес. @@ -2619,36 +2745,46 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) Сканиране (%L1 песни) - + Scanning Сканирам - + Checking Проверявам - + Fetching Извличам - + Parsing Сортирам - + Saving (%1%) Запазвам (%1%) + + + Online + + + + + Offline + + Tomahawk::SpotifyParser @@ -2661,7 +2797,7 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::XspfUpdater - + Automatically update from XSPF Автоматично обнови от XSPF @@ -2669,7 +2805,7 @@ Try tweaking the filters for a new set of songs to play. TomahawkApp - + My Collection Моята колекция @@ -2692,39 +2828,48 @@ enter the displayed PIN number here: TomahawkTrayIcon - - + &Stop Playback after current Track &Спри възпроизвеждането след текущата песен - - + + Hide Tomahawk Window Скрий главният прозорец - + Show Tomahawk Window Покажи главният прозорец - + Currently not playing. В момента не се изпълнява нищо - + Play Изпълни - + Pause Пауза - + + &Love + + + + + Un-&Love + + + + &Continue Playback after current Track &Продължи възпроизвеждането след текущата песен @@ -2868,7 +3013,7 @@ enter the displayed PIN number here: - + Play Изпълни @@ -2888,140 +3033,175 @@ enter the displayed PIN number here: Следваща - + + Back + + + + + Go back one page + Една страница назад + + + + Forward + Напред + + + + Go forward one page + Една страница напред + + + Global Search... Глобално търсене... - - + + Check For Updates... Провери за обновления... - - - + + + Connect To Peer Свържи се с друг потребител - + Enter peer address: Въведи адресът на отдалеченият потребител: - + Enter peer port: Въведи порт: - + Enter peer key: Въведи ключът за удостоверяване: - + XSPF Error XSPF Грешка - + This is not a valid XSPF playlist. Това не е валиден XSPF списък - + Failed to save tracks Не мога да запазя списъкът с песни - + Some tracks in the playlist do not contain an artist and a title. They will be ignored. Някои от песните в този списък нямат артист и заглавие. Те ще бъдат игнорирани. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Make sure you have a suitable Phonon backend and required plugins installed. Съжалявам. Има проблем с достъпа до твоето аудио-устройство или до избраната песен - тя ще бъде прескочена. Моля, увери се, че са инсталирани подходящ Phonon и приставки. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Съжалявам. Има проблем с достъпа до твоето аудио устройство или избраната песен. Тя ще бъде пропусната. - + + Station + + + + Create New Station Създай нова станция - + Name: Име: - - New Station - Нова станция + + Playlist + - - New Playlist - Нов списък + + Automatic Playlist + - + Pause Пауза - + Go &offline Излез &извън линия - + Go &online Свържи &се - + Authentication Error Грешка при удостоверяване - + + Error connecting to SIP: Authentication failed! + Грешка при свързване: Неуспешно удостоверяване! + + + %1 by %2 track, artist name %1 от %2 - + %1 - %2 current track, some window title %1 - %2 - + <h2><b>Tomahawk %1<br/>(%2)</h2> <h2><b>Tomahawk %1<br/>(%2)</h2> - + <h2><b>Tomahawk %1</h2> <h2><b>Tomahawk %1</h2> - - Copyright 2010 - 2012<br/>Christian Muehlhaeuser &lt;muesli@tomahawk-player.org&gt;<br/><br/>Thanks to: Leo Franchi, Jeff Mitchell, Dominik Schmidt, Jason Herskowitz, Alejandro Wainzinger, Hugo Lindstr&ouml;m, Michael Zanetti, Harald Sitter and Steve Robertson - Всички права запазени 2010 - 2012<br/>Christian Muehlhaeuser &lt;muesli@tomahawk-player.org&gt;<br/><br/> Благодарности на: Leo Franchi, Jeff Mitchell, Dominik Schmidt, Jason Herskowitz, Alejandro Wainzinger, Hugo Lindstr&ouml;m, Michael Zanetti, Harald Sitter and Steve Robertson + + Copyright 2010 - 2012 + Всички права - запазени. 2010 - 2012 - + + Thanks to: + Благодарности на: + + + About Tomahawk Относно Tomahawk @@ -3092,7 +3272,7 @@ enter the displayed PIN number here: TopTracksContext - + Top Hits Най-слушани @@ -3140,103 +3320,50 @@ enter the displayed PIN number here: Статистика - + + Lyrics + Текст + + + Similar Tracks Подобни песни + + + Sorry, but we could not find similar tracks for this song! + + - + You've listened to this track %n time(s). Ти си слушал тази песен %n път(и)Ти си слушал тази песен %n път(и) - + You've never listened to this track before. Никога не си слушал тази песен преди - + You first listened to it on %1. Първоначално си я слушал на %1 - + You've listened to %1 %n time(s). Слушал си %1 път(и)Слушал си %1 %n път(и) - + You've never listened to %1 before. Никога не си слушал %1 преди - - TrackModel - - - Artist - Артист - - - - Title - Заглавие - - - - Album - Албум - - - - Track - Номер - - - - Duration - Продължителност - - - - Bitrate - Качество - - - - Age - Възраст - - - - Year - Година - - - - Size - Размер - - - - Origin - Произход - - - - Score - Популярност - - - - Composer - Композитор - - TrackView - + Sorry, your filter '%1' did not match any results. Съжалявам, твоят филтър %1 не върна никакъв резултат. @@ -3257,7 +3384,7 @@ enter the displayed PIN number here: TreeItemDelegate - + Unknown Неизвестно @@ -3265,63 +3392,31 @@ enter the displayed PIN number here: TreeModel - - Name - Име - - - - Duration - Продължителност - - - - Bitrate - Качество - - - - Age - Възраст - - - - Year - Година - - - - Size - Размер - - - - Origin - Произход - - - - Composer - Композитор - - - + All Artists Всички артисти - - + + My Collection Моята колекция - - + + Collection of %1 Колекцията на %1 + + TreeView + + + Sorry, your filter '%1' did not match any results. + + + TwitterConfigWidget @@ -3399,27 +3494,38 @@ You can re-send a sync message at any time simply by sending another tweet using ViewManager - + + After you have scanned your music collection you will find your tracks right here. + + + + + This collection is empty. + + + + SuperCollection - Супер колекция + Супер колекция +/Сборен излед от локалните и наличните в колекциите на приятелите ти/ - + Combined libraries of all your online friends - Комбинирани библиотеки от всичките ми приятели на линия + Обща колекция с всичките ми приятели на линия - + All available albums Всички налични албуми - + Recently Played Tracks Наскоро изпълени песни - + Recently played tracks from all your friends Наскоро изпълнени песни от всичките ти приятели @@ -3442,12 +3548,12 @@ You can re-send a sync message at any time simply by sending another tweet using Наскоро изпълнени - + No recently created playlists in your network. Не откривам наскоро създадени списъци в твоята мрежа - + Welcome to Tomahawk Здравей! @@ -3455,7 +3561,7 @@ You can re-send a sync message at any time simply by sending another tweet using WhatsHotWidget - + Charts Класации @@ -3688,107 +3794,107 @@ Lyrics for "%1" by %2: XmppSipPlugin - + User Interaction Действие от потребителя - + Host is unknown Непознат адрес - + Item not found Обектът не е открит - + Authorization Error Грешка при даване на достъп - + Remote Stream Error Грешка в стриймът от отдалечената машина - + Remote Connection failed Отдалечената връзка е неуспешна - + Internal Server Error Вътрешна грешка на сървъра - + System shutdown Изключване на системата - + Conflict Конфликт - + Unknown Неизвестно - + No Compression Support Няма поддръжка на компресия - + No Encryption Support Няма поддръжка на криптиране - + No Authorization Support Няма поддръжка на удостоверяване - + No Supported Feature Неподдържана функция - + Add Friend Добави приятел - + Enter Xmpp ID: Въведи Xmpp ID: - + Add Friend... Добави приятел... - + XML Console... XML Конзола... - + I'm sorry -- I'm just an automatic presence used by Tomahawk Player (http://gettomahawk.com). If you are getting this message, the person you are trying to reach is probably not signed on, so please try again later! Извинявай.. Аз съм режимът за автоматични отговори изпълзван от Tomahawk. ( http://gettomahawk.com ) Щом получаваш това съобщение, този с който се опитваш да се свържеш вероятно не е на линия. Моля, опитай отново по-късно. - + Authorize User Оправомощяване на потребител - + Do you want to grant <b>%1</b> access to your Collection? Искате ли да позволите на <b>%1</b> достъп до вашата колекция? diff --git a/lang/tomahawk_ca.ts b/lang/tomahawk_ca.ts new file mode 100644 index 000000000..1f7558a66 --- /dev/null +++ b/lang/tomahawk_ca.ts @@ -0,0 +1,3914 @@ + + + AccountFactoryWrapper + + + Dialog + Diàleg + + + + Description goes here + Aquí va la descripció + + + + Add Account + Afegiu un compte + + + + AccountFactoryWrapperDelegate + + + Online + En línia + + + + Connecting... + Connectant-se... + + + + Offline + Fora de línia + + + + AclJobDelegate + + + Error displaying ACL info + Error en mostrar la informació ACL + + + + + Allow %1 to +connect and stream from you? + Voleu permetre que %1 s'us connecti i transmetre-li la vostra música? + + + + ActionCollection + + + &Listen Along + &Escolta a la vegada + + + + Stop &Listening Along + Deixa d'&escoltar a la vegada + + + + &Follow in real-time + &Segueix en temps real + + + + + &Listen Privately + &Escolta privada + + + + + &Listen Publicly + &Escolta Pública + + + + &Load Playlist + &Carrega la llista de reproducció + + + + &Rename Playlist + &Reanomena la llista de reproducció + + + + &Copy Playlist Link + &Copia l'enllaç a la llista de reproducció + + + + &Play + &Reprodueix + + + + &Stop + &Atura + + + + &Previous Track + Cançó &Anterior + + + + &Next Track + Cançó &Següent + + + + &Quit + &Surt + + + + AlbumInfoWidget + + + Form + Formulari + + + + Other Albums by Artist + Altres Àlbums de l'Artista + + + + Sorry, we could not find any other albums for this artist! + + + + + Sorry, we could not find any tracks for this album! + + + + + Other Albums by %1 + Altres Àlbums de %1 + + + + AlbumModel + + + + All albums from %1 + Tots els Àlbums de %1 + + + + All albums + Tots els Àlbums + + + + ArtistInfoWidget + + + Form + Formulari + + + + Top Hits + Grans Èxits + + + + Related Artists + Artistes Relacionats + + + + Albums + Àlbums + + + + Sorry, we could not find any albums for this artist! + + + + + Sorry, we could not find any related artists! + + + + + Sorry, we could not find any top hits for this artist! + + + + + AudioControls + + + Prev + Anterior + + + + Play + Reprodueix + + + + Pause + Pausa + + + + Next + Següent + + + + Artist + Artista + + + + Album + Àlbum + + + + Owner + Propietari + + + + social + social + + + + love + M'encanta + + + + Time + Temps + + + + Time Left + Temps Restant + + + + Shuffle + Mescla + + + + Repeat + Repeteix + + + + Low + Baix + + + + High + Alt + + + + CategoryAddItem + + + Create new Playlist + + + + + Create new Station + + + + + + + New Station + Nova Emissora + + + + + + %1 Station + Emissora %1 + + + + CategoryItem + + + Playlists + Llistes de reproducció + + + + Stations + Emissores + + + + ClearButton + + + Clear + Suprimeix + + + + ContextWidget + + + InfoBar + Barra d'Informació + + + + + Show Footnotes + Mostra les Notes al peu + + + + Hide Footnotes + Amaga les Notes al peu + + + + CrashReporter + + + Tomahawk Crash Reporter + Enviament de Fallades de Tomahawk + + + + <html><head/><body><p><span style=" font-weight:600;">Sorry!</span> Tomahawk crashed. Please tell us about it! Tomahawk has created an error report for you that can help improve the stability in the future. You can now send this report directly to the Tomahawk developers.</p></body></html> + + + + + Send this report + + + + + Don't send + + + + + Abort + Interromp + + + + You can disable sending crash reports in the configuration dialog. + Podeu deshabilitar l'enviament d'informació sobre les fallades des del diàleg de configuració. + + + + Uploaded %L1 of %L2 KB. + %L1 de %L2 KB carregats. + + + + + Close + Tanca + + + + Sent! <b>Many thanks</b>. + Enviat! <b>Moltes gràcies</b>. + + + + Failed to send crash info. + S'ha produït un error en enviar la informació sobre la fallada. + + + + DatabaseCommand_AllAlbums + + + Unknown + Desconegut + + + + DelegateConfigWrapper + + + About + Quant a + + + + Delete Account + Esborra el Compte + + + + About this Account + Quant a aquest compte + + + + DiagnosticsDialog + + + Tomahawk Diagnostics + Diagnòstics de Tomahawk + + + + Copy to Clipboard + Copia al Porta-retalls + + + + GlobalSearchWidget + + + Form + Formulari + + + + IndexingJobItem + + + Indexing database + S'està indexant la base de dades + + + + InfoBar + + + InfoBar + Barra d'Informació + + + + Filter... + Filtra... + + + + LastFmConfig + + + Form + Formulari + + + + Scrobble tracks to Last.fm + Fes scrobbling de les cançons a Last.fm + + + + Username: + Nom d'usuari: + + + + Password: + Contrasenya: + + + + Test Login + Comprova l'autenticació + + + + Import Playback History + + + + + LastfmContext + + + Last.fm + Last.fm + + + + LatchedStatusItem + + + %1 is listening along to you! + %1 us està escoltant a la vegada! + + + + LoadXSPF + + + Load XSPF + Carrega una XSPF + + + + Playlist URL + URL de la llista de reproduccó + + + + Enter URL... + Introduïu l'URL... + + + + ... + ... + + + + Automatically update + Actualitza automàticament + + + + LoadXSPFDialog + + + Load XSPF File + Carrega un fitxer XSPF + + + + XSPF Files (*.xspf) + Fitxers XSPF (*.xspf) + + + + LocalCollection + + + Bookmarks + Preferits + + + + Saved tracks + Cançons desades + + + + NewPlaylistWidget + + + Enter a title for the new playlist: + Introduïu un títol per la nova llista de reproducció: + + + + Tomahawk offers a variety of ways to help you create playlists and find music you enjoy! + Tomahawk us ofereix diverses formes per ajudar-vos a crear llistes de reproducció i trobar la música que us agrada! + + + + Just enter a genre or tag name and Tomahawk will suggest a few songs to get you started with your new playlist: + Introduïu el nom d'un gènere o una etiqueta i Tomahawk us suggerirà unes quantes cançons per començar la nova llista: + + + + &Create Playlist + &Crea la Llista de Reproducció + + + + Create a new playlist + Crea una nova llista de reproducció + + + + NewReleasesWidget + + + New Releases + Nous llançaments + + + + PlayableModel + + + Artist + + + + + Title + + + + + Composer + + + + + Album + + + + + Track + + + + + Duration + + + + + Bitrate + + + + + Age + + + + + Year + + + + + Size + + + + + Origin + + + + + Score + + + + + + Name + + + + + PlaylistItemDelegate + + + played %1 by you + reproduït %1 + + + + played %1 by %2 + reproduït %1 per %2 + + + + PlaylistLargeItemDelegate + + + played %1 by you + reproduït %1 + + + + played %1 by %2 + reproduït %1 per %2 + + + + added %1 + afegit %1 + + + + PlaylistModel + + + A playlist by %1, created %2 + Una llista de reproduccó de %1, creada %2 + + + + you + + + + + All tracks by %1 on album %2 + Totes les cançons de %1 a l'àlbum %2 + + + + All tracks by %1 + Totes les cançons de %1 + + + + PlaylistTypeSelectorDlg + + + New Playlist + Nova Llista de Reproducció + + + + Just a regular old playlist... Give it a name, drag in some tracks, and go! + Una llista vella i regular... Poseu-li un nom, arrossegueu-hi algunes cançons i llestos! + + + + Don't know exactly what you want? Give Tomahawk a few pointers and let it build a playlist for you! + No sabeu exactament què voleu? Doneu-li unes indicacions a Tomahawk i deixeu que us faci la llista! + + + + Name: + Nom: + + + + New Playlist... + Nova Llista de Reproducció... + + + + Create Manual Playlist + Crea una Llista de Reproducció Manual + + + + Create Automatic Playlist + Crea una Llista de Reproducció Automàtica + + + + PlaylistView + + + This playlist is currently empty. + + + + + This playlist is currently empty. Add some tracks to it and enjoy the music! + Aquesta llista es troba buida. Afegiu-hi algunes cançons i gaudiu de la música! + + + + ProxyDialog + + + Proxy Settings + Paràmetres del proxy + + + + Hostname of proxy server + Nom del proxy + + + + Host + Ordinador central + + + + Port + Port + + + + Proxy login + Dades d'inici de sessió del proxy + + + + User + Usuari + + + + Password + Contrasenya + + + + Proxy password + Contrasenya del proxy + + + + No Proxy Hosts: +(Overrides system proxy) + Cap proxy: +(Sobreescriu el proxy del sistema) + + + + localhost *.example.com (space separated) + localhost *.exemple.com (separats per espais) + + + + Use proxy for DNS lookups? + Voleu emprar un proxy per cercar les DNS? + + + + QObject + + + %n year(s) ago + fa %n anyfa %n anys + + + + %n year(s) + %n any%n anys + + + + %n month(s) ago + fa %n mesfa %n mesos + + + + %n month(s) + %n mes%n mesos + + + + %n week(s) ago + fa %n setmanafa %n setmanes + + + + %n week(s) + %n setmana%n setmanes + + + + %n day(s) ago + fa %n diafa %n dies + + + + %n day(s) + %n dia%n dies + + + + %n hour(s) ago + fa %n horafa %n hores + + + + %n hour(s) + %n hora%n hores + + + + %1 minutes ago + fa %1 minut + + + + %1 minutes + %1 minuts + + + + just now + ara mateix + + + + Friend Finders + Cercadors d'Amics + + + + Music Finders + Cercadors de Música + + + + Status Updaters + Actualitzadors d'Estat + + + + QuaZipFilePrivate + + + ZIP/UNZIP API error %1 + Error %1 de l'API ZIP/UNZIP + + + + QueueView + + + InfoBar + Barra d'Informació + + + + + Open Queue + + + + + The queue is currently empty. Drop something to enqueue it! + + + + + Open Queue - %n item(s) + + + + + Close Queue + + + + + RelatedArtistsContext + + + Related Artists + Artistes Relacionats + + + + ResolverConfigDelegate + + + Not found: %1 + No trobades: %1 + + + + Failed to load: %1 + Fallades a carregar: %1 + + + + SearchLineEdit + + + Search + Cerca + + + + SearchWidget + + + Search: %1 + Cerca: %1 + + + + Results for '%1' + Resultats per '%1' + + + + SettingsDialog + + + Collection + Col·lecció + + + + Advanced + Avançat + + + + All + Tot + + + + Some changed settings will not take effect until Tomahawk is restarted + Alguns paràmetres no tindran efecte fins que no reinicieu Tomahawk + + + + Services + Serveis + + + + Install resolver from file + Instal·la un Resolver des d'un fitxer + + + + Information + Informació + + + + SocialPlaylistWidget + + + Popular New Albums From Your Friends + Àlbums Populars dels Vostres Amics + + + + Most Played Playlists + Llistes de Reproducció Més Escoltades + + + + Most Played Tracks You Don't Have + Cançons Més Escoltades que encara no heu Reproduït + + + + SocialWidget + + + Form + Formulari + + + + Facebook + Facebook + + + + Twitter + Twitter + + + + Cover + Cover + + + + TextLabel + TextLabel + + + + Listening to "%1" by %2 and loving it! %3 + Estic escoltant "%1" de "%2" i m'encanta!%3 + + + + Listening to "%1" by %2 on "%3" and loving it! %4 + Estic escoltant "%1" de "%2" a "%3" i m'encanta!%4 + + + + %1 characters left + %1 caràcters restants + + + + SourceDelegate + + + Track + Cançó + + + + Album + Àlbum + + + + Artist + Artista + + + + Local + Local + + + + Top 10 + Top 10 + + + + All available tracks + Totes les cançons disponibles + + + + + Show + Mostra + + + + + Hide + Amaga + + + + SourceInfoWidget + + + Recent Albums + Àlbums Recents + + + + Latest Additions + Cançons Recents + + + + Recently Played Tracks + Cançons Escoltades Recentment + + + + New Additions + Novetats + + + + My recent activity + La meva activitat recent + + + + Recent activity from %1 + Activitat recent de %1 + + + + SourceItem + + + Collection + Col·lecció + + + + + Latest Additions + Darreres Novetats + + + + Recently Played + Escoltades Recentment + + + + Loved Tracks + Cançons Preferides + + + + SuperCollection + SuperCol·lecció + + + + Sorry, we could not find any loved tracks! + + + + + Latest additions to your collection + Darreres novetats a la vostra col·lecció + + + + Latest additions to %1's collection + Darreres novetats a la col·lecció de %1 + + + + Sorry, we could not find any recent additions! + + + + + Recently Played Tracks + Cançons Escoltades Recentment + + + + Your recently played tracks + Cançons Escoltades Recentment + + + + %1's recently played tracks + Cançons Escoltades Recentment per %1 + + + + Sorry, we could not find any recent plays! + + + + + SourceTreeView + + + &Copy Link + &Copia l'Enllaç + + + + &Delete %1 + &Esborra %1 + + + + Add to my Playlists + + + + + Add to my Automatic Playlists + + + + + Add to my Stations + + + + + &Export Playlist + E&xporta la Llista de Reproducció + + + + playlist + + + + + automatic playlist + + + + + station + + + + + Delete %1? + playlist/station/... + + + + + Would you like to delete the %1 <b>"%2"</b>? + e.g. Would you like to delete the playlist named Foobar? + + + + + Save XSPF + Desa com XSPF + + + + Playlists (*.xspf) + Llistes de reproducció (*.xspf) + + + + SourcesModel + + + Group + Grup + + + + Collection + Col·lecció + + + + Playlist + Llista de Reproducció + + + + Automatic Playlist + Llista de Reproducció Automàtica + + + + Station + Emissora + + + + Browse + Cerca + + + + Search History + Historial de Cerca + + + + My Music + La Meva Música + + + + SuperCollection + SuperCol·lecció + + + + Top Loved Tracks + Top de Cançons Preferides + + + + Dashboard + Presentació + + + + Recently Played + Escoltades Recentment + + + + Charts + Llistes + + + + New Releases + Nous Llançaments + + + + Friends + Amics + + + + SpotifyConfig + + + Form + Formulari + + + + Configure your Spotify account + Configureu el compte Spotify + + + + Username or Facebook Email + Nom d'usuari o adreça de Facebook + + + + Log In + Inicia Sessió + + + + Right click on any Tomahawk playlist to sync it to Spotify. + Feu click dret a qualsevol llista de reproducció de Tomahawk per sincronitzar-la amb Spotify. + + + + High Quality Streams + Fluxos d'Alta Qualitat + + + + Spotify playlists to keep in sync: + Llistes de reproducció de Spotify per mantenir sincronitzades: + + + + Delete Tomahawk playlist when removing synchronization + Esborra les llistes de Tomahawk quan es tregui la sincronització + + + + Username: + Usuari: + + + + Password: + Contrasenya: + + + + SpotifyPlaylistUpdater + + + Delete in Spotify? + Voleu esborrar-ho de Spotify? + + + + Would you like to delete the corresponding Spotify playlist as well? + Voleu esborrar les llistes de Spotify, també? + + + + StackedSettingsDialog + + + Tomahawk Settings + Paràmetres de Tomahawk + + + + Local Music Information + Informació de la Música Local + + + + Path to scan for music files: + Camí als fitxers de música: + + + + The Echo Nest supports keeping track of your catalog metadata + and using it to craft personalized radios. Enabling this option + will allow you (and all your friends) to create automatic playlists + and stations based on your personal taste profile. + L'ajuda The Echo Nest llegeix les metadades de la vostra col·lecció +i les utilitza per fer emissores de ràdio personalitzades. Si activeu +aquesta opció podreu crear llistes de reproducció automàtiques +i emissores de ràdio basades en el vostre gust musical. + + + + Upload collection list to The Echo Nest to enable user radio + Carrega la llista de la col·lecció a The Echo Nest per activar la radio d'usuari + + + + Watch for changes + Monitoritza els canvis + + + + Time between scans, in seconds: + Temps entre escanejos, en segons: + + + + Advanced Settings + Paràmetres Avançats + + + + Remote Peer Connection Method + Mètode de connexió remota entre iguals + + + + None (outgoing connections only) + Cap (només connexions cap a l'exterior) + + + + Use UPnP to establish port forward (recommended) + Utiliza UPnP per establir el redireccionament de ports (recomanat) + + + + Use static external IP address/host name and port + Utilitza l'adreça IP estàtica externa/nom de l'ordinador i el port + + + + Set this to your external IP address or host name. Make sure to forward the port to this host! + Ajusteu aquest paràmetre amb la vostra adreça IP o el nom de l'ordinador. Assegure-vos que redireccioneu el port a aquest ordinador! + + + + SOCKS Proxy + Proxys SOCKS + + + + Use SOCKS Proxy + Utilitza un proxy SOCKS + + + + Internet Services + Serveis d'Internet + + + + Install from file... + Instal·la des d'un fitxer... + + + + Filter by capability: + Filtra per capacitats: + + + + Static Host Name: + Nom de l'ordinador estàtic: + + + + Static Port: + Port estàtic: + + + + Proxy Settings... + Paràmetres del proxy... + + + + Other Settings + Altres Paràmetres + + + + Send reports after Tomahawk crashed + Envia informació de les fallades de Tomahawk + + + + Allow web browsers to interact with Tomahawk (recommended) + Permet que els navegadors web interactuin amb Tomahawk (recomanat) + + + + Tomahawk::Accounts::AccountDelegate + + + Add Account + Afegeix un compte + + + + Remove Account + Esborra el compte + + + + %1 downloads + %1 descàrregues + + + + Online + En Línia + + + + Connecting... + Connectant-se... + + + + Offline + Fora de Línia + + + + Tomahawk::Accounts::AccountModel + + + Manual Install Required + + + + + Unfortunately, automatic installation of this resolver is not available or disabled for your platform.<br /><br />Please use "Install from file" above, by fetching it from your distribution or compiling it yourself. Further instructions can be found here:<br /><br />http://www.tomahawk-player.org/resolvers/%1 + + + + + Tomahawk::Accounts::GoogleWrapper + + + Configure this Google Account + Configura el Compte de Google + + + + Google Address + Adreça de correu de Google + + + + Enter your Google login to connect with your friends using Tomahawk! + Introduïu les dades d'inici de sessió de Google i conecteu-vos amb els amics mitjançant Tomahawk! + + + + username@gmail.com + usuari@gmail.com + + + + Tomahawk::Accounts::GoogleWrapperFactory + + + Connect to Google Talk to find your friends + Connecteu-vos a Google Talsk per trobar els amics + + + + Tomahawk::Accounts::GoogleWrapperSip + + + Add Friend + Afegeix un Amic + + + + Enter Google Address: + Introduïu l'adreça de Google: + + + + Tomahawk::Accounts::LastFmAccountFactory + + + Scrobble your tracks to last.fm, and find freely downloadable tracks to play + Fes scrobbling de les cançons a last.fm, i cerca cançons descarregables gratuïtament per escoltar-les + + + + Tomahawk::Accounts::LastFmConfig + + + Testing... + + + + + Test Login + Comprova l'inici de sessió + + + + Importing %1 + e.g. Importing 2012/01/01 + + + + + Importing History... + + + + + History Incomplete. Resume + + + + + Playback History Imported + + + + + + Failed + Error + + + + Success + Èxit + + + + Could not contact server + No s'ha pogut contactar amb el servidor + + + + Tomahawk::Accounts::SpotifyAccount + + + Sync with Spotify + Sincronitza amb Spotify + + + + Re-enable syncing with Spotify + + + + + Stop syncing with Spotify + Atura la sincronització amb Spotify + + + + Tomahawk::Accounts::SpotifyAccountConfig + + + Logging in... + Iniciant sessió... + + + + Logged in! + Sessió iniciada! + + + + Failed: %1 + Error: %1 + + + + Log In + Incia Sessió + + + + Tomahawk::Accounts::SpotifyAccountFactory + + + Play music from and sync your playlists with Spotify Premium + Reprodueix música i sincronitza les llistes de reproducció amb Spotify Premium + + + + Tomahawk::Accounts::TwitterAccountFactory + + + Connect to your Twitter followers. + Connecteu-vos als seguidors de Twitter. + + + + Tomahawk::Accounts::TwitterConfigWidget + + + + + Tweet! + Twitteja! + + + + + Status: No saved credentials + Estat: no hi ha credencials desades + + + + + + Authenticate + Autentifica + + + + + Status: Credentials saved for %1 + Estat: Credencials desades per %1 + + + + + De-authenticate + Des-autentifica + + + + + + + + + + Tweetin' Error + Error en Twittejar + + + + The credentials could not be verified. +You may wish to try re-authenticating. + Les crendencials no s'han pogut verificar +Torneu a autenticar-vos. + + + + Status: Error validating credentials + Estat: Error en validar les credencials + + + + Global Tweet + Tweet Global + + + + Direct Message + Missatge Directe + + + + Send Message! + Envia el Missatge! + + + + @Mention + @Menció + + + + Send Mention! + Envia la Menció! + + + + You must enter a user name for this type of tweet. + Heu d'introduir un nom d'usuari per aquest tipus de tweet. + + + + Your saved credentials could not be loaded. +You may wish to try re-authenticating. + Les credencials desades no s'han pogut carregar. +Torneu a autenticar-vos. + + + + Your saved credentials could not be verified. +You may wish to try re-authenticating. + Les credencials desades no s'han pogut verificar. +Torneu a autenticar-vos. + + + + + There was an error posting your status -- sorry! + S'ha produït un error publicant l'estat! + + + + + Tweeted! + Twitejat! + + + + Your tweet has been posted! + El tweet s'ha publicat! + + + + There was an error posting your direct message -- sorry! + S'ha produït un error publicant el missatge directe! + + + + Your message has been posted! + El missatge s'ha publicat! + + + + Tomahawk::Accounts::XmppAccountFactory + + + Log on to your Jabber/XMPP account to connect to your friends + Inicieu sessió al vostre compte Jabber/XMPP per conectar-vos amb els amics + + + + Tomahawk::Accounts::ZeroconfFactory + + + Automatically connect to Tomahawks on the local network + Connecta't automàticament a altres Tomahawks de la xarxa local + + + + Tomahawk::ContextMenu + + + &Play + &Reprodueix + + + + + + Add to &Queue + &Afegeix a la Cua + + + + + &Love + &M'encanta + + + + &Copy Track Link + &Copia l'Enllaç de la Cançó + + + + Un-&Love + &Treu de les preferides + + + + &Delete Items + &Elimina els Ítems + + + + &Continue Playback after this Track + &Continua la reproducció després d'aquesta Cançó + + + + &Stop Playback after this Track + &Atura la Reproducció després d'aquesta Cançó + + + + &Show Track Page + + + + + &Delete Item + &Elimina l'Ítem + + + + &Show Album Page + + + + + &Show Artist Page + + + + + Tomahawk::CustomPlaylistView + + + Top Loved Tracks + Top de Cançons Preferides + + + + Your loved tracks + Les meves cançons preferides + + + + %1's loved tracks + Les cançons preferides de %1 + + + + The most loved tracks from all your friends + Les cançons preferides de tots els amics + + + + All of your loved tracks + Totes les meves cançons preferides + + + + All of %1's loved tracks + Totes les cançons preferides de %1 + + + + Tomahawk::DropJobNotifier + + + playlist + + + + + artist + + + + + track + + + + + album + + + + + Fetching %1 from database + Cercant %1 a la base de dades + + + + Parsing %1 %2 + Analitzant %1 %2 + + + + Tomahawk::DynamicControlList + + + Click to collapse + Cliqueu per reduir + + + + Tomahawk::DynamicModel + + + + Could not find a playable track. + +Please change the filters or try again. + No s'ha trobat cap cançó reproduïble + +Canvieu els filtres o intenteu-ho de nou. + + + + Failed to generate preview with the desired filters + S'ha produït un error en generar la previsualització amb els filtres + + + + Tomahawk::DynamicSetupWidget + + + Type: + Tipus: + + + + Generate + Genera + + + + Tomahawk::DynamicView + + + Add some filters above to seed this station! + Afegeiu slguns filtres per omplir aquesta emissora! + + + + Press Generate to get started! + Premeu Genera per començar! + + + + Add some filters above, and press Generate to get started! + Afegeiu alguns filtres i premeu Genera per començar! + + + + Tomahawk::DynamicWidget + + + Station ran out of tracks! + +Try tweaking the filters for a new set of songs to play. + L'emissora s'ha quedat sense cançons! + +Intenteu ajustar els filtres per reproduir noves cançons. + + + + Tomahawk::EchonestControl + + + + + + + + is + és + + + + from user + de l'usuari + + + + + No users with Echo Nest Catalogs enabled. Try enabling option in Collection settings + No hi ha usuaris amb els catàlegs de Echo Nest activats. Activeu l'opció en els paràmetres de la Col·lecció + + + + similar to + semblant a + + + + + + + + + + Less + Menys + + + + + + + + + + More + Més + + + + 0 BPM + 0 BPM + + + + 500 BPM + 500 BPM + + + + 0 secs + 0 segs + + + + 3600 secs + 3600 segs + + + + -100 dB + -100 dB + + + + 100 dB + 100 dB + + + + Major + Major + + + + Minor + Menor + + + + C + Do + + + + C Sharp + DO Sostingut + + + + D + Re + + + + E Flat + Mi Bemoll + + + + E + Mi + + + + F + Fa + + + + F Sharp + Fa Sostingut + + + + G + Sol + + + + A Flat + La Bemoll + + + + A + La + + + + B Flat + Si Bemoll + + + + B + Si + + + + Ascending + Ascendent + + + + Descending + Descendent + + + + Tempo + Temps + + + + Duration + Durada + + + + Loudness + Volum + + + + Artist Familiarity + Semblança amb l'artista + + + + Artist Hotttnesss + Rellevància d'Artista + + + + Song Hotttnesss + Rellevància de Cançó + + + + Latitude + Latitut + + + + Longitude + Longitud + + + + Mode + Mode + + + + Key + Clau + + + + Energy + Energia + + + + Danceability + Ballabilitat + + + + only by ~%1 + només per ~%1 + + + + similar to ~%1 + semblant a ~%1 + + + + with genre ~%1 + amb el gènere ~%1 + + + + + from no one + de ningú + + + + My Collection + La Meva Col·lecció + + + + from %1 radio + de la ràdio %1 + + + + with %1 %2 + amb %1 %2 + + + + about %1 BPM + sobre %1 BPM + + + + about %n minute(s) long + sobre %n minut de duradasobre %n minuts de durada + + + + about %1 dB + sobre %1 dB + + + + at around %1%2 %3 + sobre %1%2 %3 + + + + in %1 + en %1 + + + + in a %1 key + en clau de %1 + + + + sorted in %1 %2 order + ordenades en %1 %2 + + + + with a %1 mood + amb l'humor %1 + + + + in a %1 style + amb l'estil %1 + + + + Tomahawk::EchonestSteerer + + + Steer this station: + Dirigeix aquesta emissora: + + + + Much less + Molt més + + + + Less + Menys + + + + A bit less + Una mica menys + + + + Keep at current + Mantingues a l'actual + + + + A bit more + Una mica més + + + + More + Més + + + + Much more + Molt més + + + + Tempo + Temps + + + + Loudness + Volum + + + + Danceability + Ballabilitat + + + + Energy + Energia + + + + Song Hotttnesss + Rellevància de Cançó + + + + Artist Hotttnesss + Rellevància d'Artista + + + + Artist Familiarity + Semblança amb l'artista + + + + By Description + Per Descripció + + + + Enter a description + Introduïu una descripció + + + + Apply steering command + Aplica la comanda de control + + + + Reset all steering commands + Restaura totes les comandes de control + + + + Tomahawk::GroovesharkParser + + + Error fetching Grooveshark information from the network! + Error en cercar la informació de Grooveshark a través de la xarxa! + + + + Tomahawk::InfoSystem::ChartsPlugin + + + Top Overall + Top General + + + + Artists + Artistes + + + + Albums + Àlbums + + + + Tracks + Cançons + + + + Tomahawk::InfoSystem::FdoNotifyPlugin + + + Tomahawk is playing "%1" by %2%3. + Tomahawk està reproduint "%1" de %2%3. + + + + on "%1" + a "%1" + + + + Tomahawk::InfoSystem::LastFmInfoPlugin + + + Top Tracks + Top Cançons Musicals + + + + Loved Tracks + Cançons Preferides + + + + Hyped Tracks + Cançons en alça + + + + Top Artists + Top Artistes + + + + Hyped Artists + Artistes en alça + + + + Tomahawk::InfoSystem::NewReleasesPlugin + + + Albums + Àlbums + + + + Tomahawk::InfoSystem::TwitterInfoPlugin + + + Listening to "%1" by %2 and loving it! %3 + Estic escoltant "%1" de "%2" i m'encanta! %3 + + + + Tomahawk::ItunesParser + + + Error fetching iTunes information from the network! + Error en cercar la informació d'iTunes a través de la xarxa! + + + + Tomahawk::JSPFLoader + + + New Playlist + Nova llista de reproducció + + + + Failed to save tracks + Error en desar les cançons + + + + Some tracks in the playlist do not contain an artist and a title. They will be ignored. + Algunes cançons de la llista no contenen ni artista ni titol i s'han ignorat. + + + + XSPF Error + Error XSPF + + + + This is not a valid XSPF playlist. + No és una llista XSPF vàlida. + + + + Tomahawk::LatchManager + + + &Catch Up + &Atrapa + + + + + &Listen Along + &Escolta a la vegada + + + + Tomahawk::Query + + + and + i + + + + You + Jo + + + + you + jo + + + + and + i + + + + %n other(s) + %n més%n més + + + + %1 people + %1 persones + + + + loved this track + els ha encantat aquesta cançó + + + + Tomahawk::RdioParser + + + Error fetching Rdio information from the network! + Error en cercar la informació de Rdio a través de la xarxa! + + + + Tomahawk::ShortenedLinkParser + + + Network error parsing shortened link! + Error de la xarxa en analitzar l'enllaç escurçat! + + + + Tomahawk::Source + + + + Scanning (%L1 tracks) + Escanejant (%L1 cançons) + + + + Scanning + Escanejant + + + + Checking + Comprovant + + + + Fetching + Cercant + + + + Parsing + Analitzant + + + + Saving (%1%) + Desant (%1%) + + + + Online + + + + + Offline + + + + + Tomahawk::SpotifyParser + + + Error fetching Spotify information from the network! + Error en cercar la informació de Spotify a través de la xarxa! + + + + Tomahawk::XspfUpdater + + + Automatically update from XSPF + Actualitza automàticament des de XSPF + + + + TomahawkApp + + + My Collection + La meva Col·lecció + + + + TomahawkOAuthTwitter + + + Twitter PIN + PIN de Twitter + + + + After authenticating on Twitter's web site, +enter the displayed PIN number here: + Després d'autenticar-vos a la pàgina de Twitter, +introduïu el PIN aquí: + + + + TomahawkTrayIcon + + + &Stop Playback after current Track + &Atura la Reproducció després d'aquesta Cançó + + + + + Hide Tomahawk Window + Amaga la finestra de Tomahawk + + + + Show Tomahawk Window + Mostra la finestra de Tomahawk + + + + Currently not playing. + No s'està reproduint res. + + + + Play + Reprodueix + + + + Pause + Pausa + + + + &Love + + + + + Un-&Love + + + + + &Continue Playback after current Track + &Continua la reproducció després d'aquesta Cançó + + + + TomahawkWindow + + + Tomahawk + Tomahawk + + + + &Settings + &Paràmetres + + + + &Controls + &Controls + + + + &Network + &Xarxa + + + + &Window + &Finestra + + + + &Help + &Ajuda + + + + &Quit + &Surt + + + + Ctrl+Q + Ctrl+Q + + + + Go &Online + &Connecta't + + + + Add &Friend... + Afegeix un &Amic... + + + + U&pdate Collection + Act&ualitza la Col·lecció + + + + Update Collection + Actualitza la Col·lecció + + + + &Configure Tomahawk... + &Configura Tomahawk... + + + + Load &XSPF... + Carrega una llista &XSPF... + + + + Create &New Playlist... + Crea una &nova llista de reproducció... + + + + About &Tomahawk... + Quant a &Tomahawk... + + + + Create New &Automatic Playlist + Crea una Llista de Reproducció &Automàtica + + + + Create New &Station + Crea una Nova &Emissora + + + + Show Offline Sources + Mostra les fonts fora de línia + + + + Hide Offline Sources + Amaga les fonts fora de línia + + + + Minimize + Minimitza + + + + Ctrl+M + Ctrl+M + + + + Zoom + Zoom + + + + Meta+Ctrl+Z + Meta+Ctrl+Z + + + + Diagnostics... + Diagnòstics... + + + + Fully &Rescan Collection + &Reescaneja la Col·lecció completament + + + + Fully Rescan Collection + Reescaneja la Col·lecció completament + + + + + Play + Reprodueix + + + + Space + Espai + + + + Previous + Anterior + + + + Next + Següent + + + + Back + + + + + Go back one page + + + + + Forward + + + + + Go forward one page + + + + + Global Search... + Cerca Global... + + + + + Check For Updates... + Comprova les actualitzacions... + + + + + + Connect To Peer + Connexió Remota + + + + Enter peer address: + Introduïu l'adreça remota: + + + + Enter peer port: + Introduïu el port remot: + + + + Enter peer key: + Introduïu la clau remota: + + + + XSPF Error + Error XSPF + + + + This is not a valid XSPF playlist. + No és una llista XSPF vàlida. + + + + Failed to save tracks + Error en desar les cançons + + + + Some tracks in the playlist do not contain an artist and a title. They will be ignored. + Algunes cançons de la llista no contenen ni artista ni titol i s'han ignorat. + + + + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Make sure you have a suitable Phonon backend and required plugins installed. + Hi ha un problema per accedir al dispositiu de so o a la cançó. La cançó actual s'ha saltat. Assegureu-vos que teniu un back.end de Phonon adequant i els plugins necessaris instal·lats. + + + + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. + Hi ha un problema per accedir al dispositiu de so o a la cançó, la cançó actual s'ha saltat. + + + + Station + + + + + Create New Station + Crea una Nova Emissora + + + + Name: + Nom: + + + + Playlist + + + + + Automatic Playlist + + + + + Pause + Pausa + + + + Go &offline + &Desconnecta't + + + + Go &online + &Connecta't + + + + Authentication Error + Error d'autentificació + + + + Error connecting to SIP: Authentication failed! + + + + + %1 by %2 + track, artist name + %1 de %2 + + + + %1 - %2 + current track, some window title + %1 - %2 + + + + <h2><b>Tomahawk %1<br/>(%2)</h2> + <h2><b>Tomahawk %1<br/>(%2)</h2> + + + + <h2><b>Tomahawk %1</h2> + <h2><b>Tomahawk %1</h2> + + + + Copyright 2010 - 2012 + + + + + Thanks to: + + + + + About Tomahawk + Quant a Tomahawk + + + + TopBar + + + Form + Formulari + + + + 0 Sources + 0 Fonts + + + + 0 Tracks + 0 Cançons + + + + 0 Artists + 0 Artistes + + + + 0 Shown + 0 Mostrades + + + + Tracks + Cançons + + + + Artists + Artistes + + + + Filter + Filtra + + + + Artist View + Vista d'Artista + + + + Flat View + Vista Plana + + + + Sources + Fonts + + + + Shown + Mostrades + + + + TopTracksContext + + + Top Hits + Grans Èxits + + + + TrackInfoWidget + + + Form + Formulari + + + + Cover + Cover + + + + Track + Cançó + + + + by + de + + + + Artist + Artista + + + + from + de + + + + Album + Àlbum + + + + Statistics + Estadístiques + + + + Lyrics + + + + + Similar Tracks + Cançons Semblants + + + + Sorry, but we could not find similar tracks for this song! + + + + + You've listened to this track %n time(s). + Heu escoltat aquesta cançó %n cop.Heu escoltat aquesta cançó %n cops. + + + + You've never listened to this track before. + No heu escoltat mai aquesta cançó abans. + + + + You first listened to it on %1. + Vau escoltar aquesta cançó per primer cop el %1. + + + + You've listened to %1 %n time(s). + Heu escoltat %1 %n cop.Heu escoltat %1 %n cops. + + + + You've never listened to %1 before. + No heu escoltat mai %1 abans. + + + + TrackView + + + Sorry, your filter '%1' did not match any results. + El filtre '%1' no ha obtingut cap resultat. + + + + TransferStatusItem + + + from + de + + + + to + a + + + + TreeItemDelegate + + + Unknown + Desconegut + + + + TreeModel + + + All Artists + Tots els Artistes + + + + + My Collection + La meva Col·lecció + + + + + Collection of %1 + Col·lecció de %1 + + + + TreeView + + + Sorry, your filter '%1' did not match any results. + + + + + TwitterConfigWidget + + + Configure this Twitter account + Configura el Compte de Twitter + + + + The Twitter plugin allows you to discover and play music from your Twitter friends running Tomahawk and post messages to your account. + El plugin de Twitter permet descobrir i reproduir música dels seguidors de Twitter que utilitzen Tomahawk i publicar missatges al vostre compte. + + + + Status: No saved credentials + Estat: no hi ha credencials desades + + + + Authenticate with Twitter + Autentifica amb Twitter + + + + Twitter Connections + Connexions de Twitter + + + + +If you only want to post tweets, you're done. + +If you want to connect Tomahawk to your friends using Twitter, select the type of tweet and press the button below to send a sync message. You must both be following each other as Direct Messages are used. Then be (very) patient -- it can take several minutes! + +You can re-send a sync message at any time simply by sending another tweet using the button. + +Si només voleu publicar tweets, ja esteu llestos. + +SI voleu connectar Tomahawk als vostres seguidors de Twitter, escolliu el tipus de Tweet i premeu el botó de sota per enviar un missatge de sincronisme. Tots dos us heu d'estar seguint atès que s'empren Missatges Directes. Aleshores, tingueu (molta) paciència -- pot trigar diversos minuts! + +Podeu reenviar un missatge de sincronisme en qualsevol moment simplement enviant un altre tweet fent servir el botó. + + + + Select the kind of tweet you would like, then press the button to post it: + Escolliu el tipus de tweet que us agradaria, després premeu el botó per publicar-lo: + + + + Global Tweet + Tweet Global + + + + @Mention + @Menció + + + + Direct Message + Missatge Directe + + + + e.g. @tomahawk + e.g. @tomahawk + + + + Send Message + Envia el Missatge + + + + ViewManager + + + After you have scanned your music collection you will find your tracks right here. + + + + + This collection is empty. + + + + + SuperCollection + SuperCol·lecció + + + + Combined libraries of all your online friends + Biblioteques combinades de tots els amis en línia + + + + All available albums + Tots els àlbums disponibles + + + + Recently Played Tracks + Cançons Escoltades Recentment + + + + Recently played tracks from all your friends + Cançons escoltades recentment pels amics + + + + WelcomeWidget + + + Recent Additions + Darreres Novetats + + + + Newest Stations & Playlists + Darreres Emissores i Llistes + + + + Recently Played Tracks + Cançons Escoltades Recentment + + + + No recently created playlists in your network. + No hi ha cançons escoltades recentment a la xarxa. + + + + Welcome to Tomahawk + Us donem la benvinguda a Tomahawk + + + + WhatsHotWidget + + + Charts + Llistes + + + + WikipediaContext + + + Wikipedia + Viquipèdia + + + + XMPPBot + + + +Terms for %1: + + +Termes per %1: + + + + No terms found, sorry. + No s'han trobat termes. + + + + +Hotttness for %1: %2 + + +Rellevància per %1: %2 + + + + +Familiarity for %1: %2 + + Semblança per %1: %2 + + + + +Lyrics for "%1" by %2: + +%3 + + +Lletres de la cancó "%1" de %2: + +%3 + + + + + XSPFLoader + + + Failed to parse contents of XSPF playlist + Error en analitzar els continguts de la llista XSPF + + + + Some playlist entries were found without artist and track name, they will be omitted + Algunes entrades de la llista no disposaven d'informació d'artista ni de nom de cançó, s'han omès + + + + Failed to fetch the desired playlist from the network, or the desired file does not exist + Error en cercar la llista a la xarxa. Pot ser no existeix el fitxer + + + + New Playlist + Nova llista de reproducció + + + + XmlConsole + + + Xml stream console + Consola de flux XML + + + + + Filter + Filtra + + + + Save log + Desa el log + + + + Disabled + Desactivat + + + + By JID + Per JID + + + + By namespace uri + Per namespace uri + + + + By all attributes + Per atributs + + + + Visible stanzas + Instàncies visibles + + + + Information query + Petició d'informació + + + + Message + Missatge + + + + Presence + Presència + + + + Custom + Personalitzat + + + + Close + Tanca + + + + Save XMPP log to file + Desa el log XMPP en un fitxer + + + + OpenDocument Format (*.odf);;HTML file (*.html);;Plain text (*.txt) + Format OpenDocument (*.odf);;fitxer HTML (*.html);;Text Pla (*.txt) + + + + XmppConfigWidget + + + Xmpp Configuration + Configuració XMPP + + + + Configure this Xmpp account + Configura aquest compte XMPP + + + + Enter your Xmpp login to connect with your friends using Tomahawk! + Introduiu les dades d'inici de sessió de XMPP per connectar-vos amb els amics que fan servir Tomahawk! + + + + Login Information + Informació d'inici de sessió + + + + Xmpp ID: + ID XMPP: + + + + e.g. user@example.com + e.g. usuari@exemple.com + + + + Password: + Contrasenya: + + + + An account with this name already exists! + Ja existeix un compte amb aquest nom! + + + + Advanced Xmpp Settings + Paràmetres avançats de XMPP + + + + Server: + Servidor: + + + + Port: + Port: + + + + Lots of servers don't support this (e.g. GTalk, jabber.org) + Molts servidors no suporten aquesta opció (e.g. GTalk, jabber.org) + + + + Publish currently playing track + Publica la cançó que s'estigui reproduint al moment + + + + Enforce secure connection + Força una connexió segura + + + + XmppSipPlugin + + + User Interaction + Interacció d'usuari + + + + Host is unknown + El nom de l'ordinador és desconegut + + + + Item not found + No s'ha trobat l'element + + + + Authorization Error + Error d'autorització + + + + Remote Stream Error + Error de flux remot + + + + Remote Connection failed + Ha fallat la connexió remota + + + + Internal Server Error + Error del servidor intern + + + + System shutdown + Sistema apagat + + + + Conflict + Conflicte + + + + Unknown + Desconegut + + + + No Compression Support + Compressió no suportada + + + + No Encryption Support + Encriptació no suportada + + + + No Authorization Support + Autorització no suportada + + + + No Supported Feature + Característica no suportada + + + + Add Friend + Afegeix un Amic + + + + Enter Xmpp ID: + Introduiu la ID XMPP: + + + + Add Friend... + Afegeix un Amic... + + + + XML Console... + Consola XML... + + + + I'm sorry -- I'm just an automatic presence used by Tomahawk Player (http://gettomahawk.com). If you are getting this message, the person you are trying to reach is probably not signed on, so please try again later! + Sóc una presència automàtica emprada pel Reproductor Tomahawk. (http://gettomahawk.com. Si rebeu aquest missatge, la persona amb qui intenteu contactar probablement no està en línia, intenteu-ho més tard! + + + + Authorize User + Autorització d'Usuari + + + + Do you want to grant <b>%1</b> access to your Collection? + Voleu permetre que <b>%1</b> accedeixi a la vostra Col·lecció? + + + + ZeroconfConfig + + + Form + Formulari + + + + Local Network configuration + Configuració de la Xarxa Local + + + + This plugin will automatically find other users running Tomahawk on your local network + Aquest plugin troba automàticament altres usuaris que fan servir Tomahawk a la vostra Xarxa Local + + + + Connect automatically when Tomahawk starts + Connecta-t'hi automàticament quan Tomahawk arrenqui + + + \ No newline at end of file diff --git a/lang/tomahawk_de.ts b/lang/tomahawk_de.ts index 3a4f7820d..165b40118 100644 --- a/lang/tomahawk_de.ts +++ b/lang/tomahawk_de.ts @@ -2,17 +2,17 @@ AccountFactoryWrapper - + Dialog Dialog - + Description goes here Beschreibung hier - + Add Account Konto hinzufügen @@ -20,17 +20,17 @@ AccountFactoryWrapperDelegate - + Online Verbunden - + Connecting... Verbinde... - + Offline Nicht Verbunden @@ -38,84 +38,84 @@ AclJobDelegate - + Error displaying ACL info - - + + Allow %1 to connect and stream from you? - + Willst du %1 erlauben sich mit dir zu verbinden und deine Sammlung anzuhören? ActionCollection - + &Listen Along &Mithören - + Stop &Listening Along Mithören b&eenden - + &Follow in real-time In Echtzeit &folgen - - + + &Listen Privately &Privat Modus aktivieren - - + + &Listen Publicly &Privat Modus verlassen - + &Load Playlist &Lade Playliste - + &Rename Playlist Playliste &umbenennen - + &Copy Playlist Link &Kopiere Link zu dieser Playliste - + &Play &Abspielen - + &Stop &Stop - + &Previous Track &Vorheriges Lied - + &Next Track &Nächstes Lied - + &Quit &Verlassen @@ -133,30 +133,17 @@ connect and stream from you? Andere Alben des Künstlers - - - Click to show Official Tracks - Hier klicken um die offizielle Trackliste zu sehen + + Sorry, we could not find any other albums for this artist! + - - - Click to show SuperCollection Tracks - Lieder in der Supersammlung anzeigen + + Sorry, we could not find any tracks for this album! + - - - Click to show SuperCollection Albums - Alben in der Supersammlung anzeigen - - - - Click to show Official Albums - Hier klicken um die offiziellen Alben zu sehen - - - + Other Albums by %1 Andere Alben von %1 @@ -164,35 +151,17 @@ connect and stream from you? AlbumModel - - Album - Album - - - - + + All albums from %1 Alle Alben von %1 - + All albums Alle Alben - - AlbumView - - - After you have scanned your music collection you will find your latest album additions right here. - Nachdem Tomahawk deine Sammlung durchsucht hast siehst du hier deine neuesten Alben. - - - - This collection doesn't have any recent albums. - Diese Sammlung ist leer. - - ArtistInfoWidget @@ -211,38 +180,24 @@ connect and stream from you? Ähnliche Künstler - + Albums Alben - - - Click to show SuperCollection Albums - Alben in der Supersammlung anzeigen + + Sorry, we could not find any albums for this artist! + - - Click to show Official Albums - Hier klicken um die offiziellen Alben zu sehen - - - - ArtistView - - - After you have scanned your music collection you will find your tracks right here. - Nachdem Tomahawk deine Sammlung durchsucht hat siehst du deine Lieder hier. + + Sorry, we could not find any related artists! + - - This collection is currently empty. - Diese Sammlung is momentan leer. - - - - Sorry, your filter '%1' did not match any results. - Entschuldige, dein Filter '%1' erzeugte keine Ergebnisse. + + Sorry, we could not find any top hits for this artist! + @@ -326,23 +281,26 @@ connect and stream from you? CategoryAddItem - - - New Playlist - Neue Playliste + + Create new Playlist + - - - - + + Create new Station + + + + + + New Station Neue Station - - - + + + %1 Station %1 Station @@ -368,27 +326,6 @@ connect and stream from you? Leeren - - CollectionFlatModel - - - My Collection - Meine Sammlung - - - - Collection of %1 - Sammlung von %1 - - - - CollectionView - - - This collection is empty. - Diese Sammlung ist leer. - - ContextWidget @@ -398,12 +335,12 @@ connect and stream from you? - + Show Footnotes Footnotes einblenden - + Hide Footnotes Footnotes ausblenden @@ -415,39 +352,49 @@ connect and stream from you? Tomahawk Crash Reporter Tomahawk Fehlermelder - + - <p><b>Sorry!</b>&nbsp;Tomahawk crashed. Information about the crash is now being sent to Tomahawk HQ so that we can fix the bug.</p> - <p><b>Das tut uns leid!</b> Tomahawk ist abgestürzt! Informationen darüber werden nun in die Tomahawk-Einsatzzentrale weitergeleitet, so dass der Fehler behoben werden kann.</p> + <html><head/><body><p><span style=" font-weight:600;">Sorry!</span> Tomahawk crashed. Please tell us about it! Tomahawk has created an error report for you that can help improve the stability in the future. You can now send this report directly to the Tomahawk developers.</p></body></html> + - + + Send this report + Report abschicken + + + + Don't send + Nicht abschicken + + + Abort Abbrechen - + You can disable sending crash reports in the configuration dialog. Du kannst das Übermitteln der Fehlerberichte in den Einstellungen abschalten. - + Uploaded %L1 of %L2 KB. %L1 von %L2 hochgeladen. - - + + Close Schließen - + Sent! <b>Many thanks</b>. Gesendet! <b>Vielen Dank!</b> - + Failed to send crash info. Übertragung des Fehlerberichts fehlgeschlagen. @@ -463,17 +410,17 @@ connect and stream from you? DelegateConfigWrapper - + About Über - + Delete Account Konto entfernen - + About this Account Über dieses Konto @@ -486,24 +433,11 @@ connect and stream from you? Tomahawk Diagnose Tool - - Update - Aktualisieren - - - + Copy to Clipboard In die Zwischenablage kopieren - - DropJob - - - No tracks found for given %1 - Keine Stücke gefunden für %1 - - GlobalSearchWidget @@ -528,29 +462,11 @@ connect and stream from you? Infoleiste - + Filter... Filter... - - JobStatusView - - - Searching For - Suche nach - - - - Pending - Verbleibend - - - - Idle - Warte - - LastFmConfig @@ -578,6 +494,11 @@ connect and stream from you? Test Login Login testen + + + Import Playback History + + LastfmContext @@ -626,12 +547,12 @@ connect and stream from you? LoadXSPFDialog - + Load XSPF File Lade XSPF Datei - + XSPF Files (*.xspf) XSPF Dateien (*.xspf) @@ -680,20 +601,89 @@ connect and stream from you? NewReleasesWidget - + New Releases Neuerscheinungen + + PlayableModel + + + Artist + Künstler + + + + Title + + + + + Composer + + + + + Album + + + + + Track + + + + + Duration + + + + + Bitrate + + + + + Age + + + + + Year + + + + + Size + + + + + Origin + + + + + Score + + + + + + Name + + + PlaylistItemDelegate - + played %1 by you angehört %1 von dir - + played %1 by %2 angehört %1 von %2 @@ -701,17 +691,17 @@ connect and stream from you? PlaylistLargeItemDelegate - + played %1 by you angehört %1 von dir - + played %1 by %2 hörte %1 von %2 - + added %1 fügte %1 hinzu @@ -719,12 +709,12 @@ connect and stream from you? PlaylistModel - + A playlist by %1, created %2 Eine Playliste von %1, erstellt %2 - + you dir @@ -780,7 +770,12 @@ connect and stream from you? PlaylistView - + + This playlist is currently empty. + + + + This playlist is currently empty. Add some tracks to it and enjoy the music! Diese Playliste ist derzeit leer. Füge einige Stücke hinzu und genieße die Musik! @@ -848,82 +843,82 @@ connect and stream from you? QObject - + %n year(s) ago vor %n Jahrvor %n Jahren - + %n year(s) %n Jahr%n Jahre - + %n month(s) ago vor %n Monatvor %n Monaten - + %n month(s) %n Monat%n Monate - + %n week(s) ago vor %n Wochevor %n Wochen - + %n week(s) %n Woche%n Wochen - + %n day(s) ago vor %n Tagvor %n Tagen - + %n day(s) %n Tag%n Tage - + %n hour(s) ago vor %n Stundevor %n Stunden - + %n hour(s) %n Stunde%n Stunden - + %1 minutes ago - vor %1 Minute + vor %1 Minuten - + %1 minutes %1 Minuten - + just now gerade eben - + Friend Finders Freundefinder - + Music Finders Musikfinder - + Status Updaters Status-Updater @@ -945,20 +940,30 @@ connect and stream from you? - - Show Queue - Warteschlange einblenden + + Open Queue + - - Hide Queue - Warteschlange ausblenden + + The queue is currently empty. Drop something to enqueue it! + + + + + Open Queue - %n item(s) + + + + + Close Queue + RelatedArtistsContext - + Related Artists Ähnliche Künstler @@ -1000,37 +1005,37 @@ connect and stream from you? SettingsDialog - + Collection Sammlung - + Advanced Erweitert - + All Alle - + Some changed settings will not take effect until Tomahawk is restarted - + Einige geänderte Einstellungen haben keinen Effekt bis zum nächsten Neustart - + Services Dienste - + Install resolver from file Installiere Resolver Datei - + Information Information @@ -1081,17 +1086,17 @@ connect and stream from you? TextLabel - + Listening to "%1" by %2 and loving it! %3 Höre "%1" von %2 und liebe es! %3 - + Listening to "%1" by %2 on "%3" and loving it! %4 Höre "%1" von %2 auf "%3" und liebe es! %4 - + %1 characters left %1 Zeichen übrig @@ -1124,29 +1129,19 @@ connect and stream from you? Top 10 - - Offline - Nicht Verbunden - - - + All available tracks Alle verfügbaren Stücke - - Online - Verbunden - - - - + + Show Einblenden - - + + Hide Verstecken @@ -1169,17 +1164,17 @@ connect and stream from you? Kürzlich gehörte Lieder - + New Additions Kürzlich hinzugekommen - + My recent activity Meine letzten Aktivitäten - + Recent activity from %1 Die letzten Aktivitäten von %1 @@ -1187,81 +1182,138 @@ connect and stream from you? SourceItem - + Collection Sammlung - - + + Latest Additions Kürzlich hinzugekommen - + Recently Played Kürzlich gehörte Lieder - + Loved Tracks Lieblingslieder - + SuperCollection Supersammlung - - Latest additions to your collection + + Sorry, we could not find any loved tracks! - + + Latest additions to your collection + Neueste Lieder in deiner Sammlung + + + Latest additions to %1's collection + Neueste Lieder in %1's Sammlung + + + + Sorry, we could not find any recent additions! Recently Played Tracks - + Zuletzt gehörte Lieder Your recently played tracks - + Deine zuletzt gehörten Lieder %1's recently played tracks + %1's zuletzt gehörte Lieder + + + + Sorry, we could not find any recent plays! SourceTreeView - + &Copy Link &Kopiere Link - + &Delete %1 &Lösche %1 + + + Add to my Playlists + Zu meinen Playlisten hinzufügen + + + + Add to my Automatic Playlists + Zu meinen Automatischen Playlisten hinzufügen + + + + Add to my Stations + Zu meinen Stationen hinzufügen + &Export Playlist Playliste &exportieren - + + playlist + + + + + automatic playlist + + + + + station + + + + + Delete %1? + playlist/station/... + + + + + Would you like to delete the %1 <b>"%2"</b>? + e.g. Would you like to delete the playlist named Foobar? + + + + Save XSPF XSPF speichern - + Playlists (*.xspf) Playlisten (*.xspf) @@ -1453,7 +1505,7 @@ connect and stream from you? Advanced Settings - + Erweiterte Einstellungen @@ -1468,27 +1520,27 @@ connect and stream from you? Use UPnP to establish port forward (recommended) - + UPnP für Port-Weiterleitung verwenden (empfohlen) Use static external IP address/host name and port - + Statische externe IP Adresse / Hostnamen und Port verwenden Set this to your external IP address or host name. Make sure to forward the port to this host! - + Stelle hier deine externe IP Adresse oder Hostnamen ein. Du musst den Port selbst an diesen Rechner weiterleiten! SOCKS Proxy - + SOCKS Proxy Use SOCKS Proxy - + SOCKS Proxy verwenden @@ -1523,7 +1575,7 @@ connect and stream from you? Other Settings - + Andere Einstellungen @@ -1539,36 +1591,49 @@ connect and stream from you? Tomahawk::Accounts::AccountDelegate - + Add Account Konto hinzufügen - + Remove Account Konto entfernen - + %1 downloads %1 Downloads - + Online Verbunden - + Connecting... Verbinde... - + Offline Nicht Verbunden + + Tomahawk::Accounts::AccountModel + + + Manual Install Required + Manuelle Installation benötigt + + + + Unfortunately, automatic installation of this resolver is not available or disabled for your platform.<br /><br />Please use "Install from file" above, by fetching it from your distribution or compiling it yourself. Further instructions can be found here:<br /><br />http://www.tomahawk-player.org/resolvers/%1 + + + Tomahawk::Accounts::GoogleWrapper @@ -1624,23 +1689,49 @@ connect and stream from you? Tomahawk::Accounts::LastFmConfig - - Test Login + + Testing... - - + + Test Login + Login testen + + + + Importing %1 + e.g. Importing 2012/01/01 + + + + + Importing History... + + + + + History Incomplete. Resume + + + + + Playback History Imported + + + + + Failed Fehlgeschlagen - + Success Erfolgreich - + Could not contact server Konnte den Server nicht erreichen! @@ -1648,12 +1739,17 @@ connect and stream from you? Tomahawk::Accounts::SpotifyAccount - + Sync with Spotify Mit Spotify synchronisieren - + + Re-enable syncing with Spotify + + + + Stop syncing with Spotify Synchronisation beenden @@ -1661,22 +1757,22 @@ connect and stream from you? Tomahawk::Accounts::SpotifyAccountConfig - + Logging in... Anmelden... - + Logged in! Angemeldet! - + Failed: %1 Fehler: %1 - + Log In Anmelden @@ -1684,7 +1780,7 @@ connect and stream from you? Tomahawk::Accounts::SpotifyAccountFactory - + Play music from and sync your playlists with Spotify Premium Musik abspielen und Playlisten synchronisieren mit Spotify Premium @@ -1700,126 +1796,126 @@ connect and stream from you? Tomahawk::Accounts::TwitterConfigWidget - - - + + + Tweet! Tweet! - - + + Status: No saved credentials Status: Keine Anmeldedaten - - - + + + Authenticate Authentifizieren - - + + Status: Credentials saved for %1 Status: Anmeldedaten gespeichert für %1 - - + + De-authenticate - - - - - - - + + + + + + + Tweetin' Error Twitter Fehler - + The credentials could not be verified. You may wish to try re-authenticating. Die Kontodaten konnten nicht verifiziert werden. Du solltest versuchen dich nochmals zu authentifizieren. - + Status: Error validating credentials Status: Keine gespeicherten Zugangsdaten - + Global Tweet Globaler Tweet - + Direct Message Private Nachricht - + Send Message! Abschicken - + @Mention @Erwähnung - + Send Mention! Sende Erwähnung! - + You must enter a user name for this type of tweet. Für diese Art von Tweet musst du einen Benutzernamen angeben. - + Your saved credentials could not be loaded. You may wish to try re-authenticating. Deine gespeicherten Zugangsdaten konnten nicht geladen werden. Du solltest dich nochmal authentifizieren. - + Your saved credentials could not be verified. You may wish to try re-authenticating. Deine gespeicherten Zugangsdaten konnten nicht bestätigt werden. Du solltest dich nochmal authentifizieren. - - + + There was an error posting your status -- sorry! Es tut uns leid, bei der Veröffentlichung deines Status' ist ein Fehler aufgetreten! - - + + Tweeted! Getweetet! - + Your tweet has been posted! Dein Tweet wurde veröffentlicht! - + There was an error posting your direct message -- sorry! Es tut uns leid, beim Verschicken der Nachricht ist ein Fehler aufgetreten! - + Your message has been posted! Deine Nachricht wurde verschickt! @@ -1835,7 +1931,7 @@ You may wish to try re-authenticating. Tomahawk::Accounts::ZeroconfFactory - + Automatically connect to Tomahawks on the local network Automatisch mit anderen Tomahawks im Netzwerk verbinden @@ -1843,20 +1939,20 @@ You may wish to try re-authenticating. Tomahawk::ContextMenu - + &Play &Abspielen - - - + + + Add to &Queue In &Warteschlange einreihen - - + + &Love &Lieblingslied @@ -1866,40 +1962,45 @@ You may wish to try re-authenticating. &Kopiere Link zu diesem Stück - - Show &Album page - &Album-Seite anzeigen - - - - Show &Artist page - &Künstler-Seite anzeigen - - - + Un-&Love Kein &Lieblingslied - + &Delete Items Elemente &entfernen - + &Continue Playback after this Track Nach diesem Lied &weiterspielen - + &Stop Playback after this Track Wiedergabe nach diesem Lied &stoppen - + + &Show Track Page + + + + &Delete Item Element &entfernen + + + &Show Album Page + + + + + &Show Artist Page + + Tomahawk::CustomPlaylistView @@ -1937,12 +2038,32 @@ You may wish to try re-authenticating. Tomahawk::DropJobNotifier - + + playlist + + + + + artist + + + + + track + + + + + album + + + + Fetching %1 from database Lade %1 aus der Datenbank - + Parsing %1 %2 Parse %1 %2 @@ -1950,7 +2071,7 @@ You may wish to try re-authenticating. Tomahawk::DynamicControlList - + Click to collapse Klicken um einzuklappen @@ -1989,17 +2110,17 @@ Bitte ändere den Filter oder versuche es erneut. Tomahawk::DynamicView - + Add some filters above to seed this station! Füge einige Filter hinzu, um diese Station zu starten! - + Press Generate to get started! Drücke Erzeugen, und los geht's! - + Add some filters above, and press Generate to get started! Füge oben einige Filter hinzu und drücke Erzeugen um loszulegen! @@ -2007,7 +2128,7 @@ Bitte ändere den Filter oder versuche es erneut. Tomahawk::DynamicWidget - + Station ran out of tracks! Try tweaking the filters for a new set of songs to play. @@ -2280,7 +2401,7 @@ Versuch die Filter anzupassen für neue Lieder. about %n minute(s) long - ungefähr %n Minute langungefähr %n Minuten lang + ungefähr eine Minute langungefähr %n Minuten lang @@ -2321,97 +2442,97 @@ Versuch die Filter anzupassen für neue Lieder. Tomahawk::EchonestSteerer - + Steer this station: Steuere diese Station: - + Much less Viel weniger - + Less Weniger - + A bit less Etwas weniger - + Keep at current So belassen - + A bit more Etwas mehr - + More Mehr - + Much more Viel mehr - + Tempo Tempo - + Loudness Lautstärke - + Danceability Tanzbarkeit - + Energy Energie - + Song Hotttnesss - + Artist Hotttnesss - + Artist Familiarity Ähnlichkeit Künstler - + By Description Von der Beschreibung - + Enter a description Gib eine Beschreibung ein - + Apply steering command Steuerkommando anwenden - + Reset all steering commands Setze alle Steuerkommandos zurück @@ -2427,22 +2548,22 @@ Versuch die Filter anzupassen für neue Lieder. Tomahawk::InfoSystem::ChartsPlugin - + Top Overall Top Allgemein - + Artists Künstler - + Albums Alben - + Tracks Stücke @@ -2450,12 +2571,12 @@ Versuch die Filter anzupassen für neue Lieder. Tomahawk::InfoSystem::FdoNotifyPlugin - + Tomahawk is playing "%1" by %2%3. Tomahawk spielt "%1" von %2%3. - + on "%1" auf "%1" @@ -2463,27 +2584,27 @@ Versuch die Filter anzupassen für neue Lieder. Tomahawk::InfoSystem::LastFmInfoPlugin - + Top Tracks - + Loved Tracks - + Hyped Tracks - + Top Artists - + Hyped Artists @@ -2491,7 +2612,7 @@ Versuch die Filter anzupassen für neue Lieder. Tomahawk::InfoSystem::NewReleasesPlugin - + Albums Alben @@ -2499,7 +2620,7 @@ Versuch die Filter anzupassen für neue Lieder. Tomahawk::InfoSystem::TwitterInfoPlugin - + Listening to "%1" by %2 and loving it! %3 Höre "%1" von %2 und liebe es! %3 @@ -2557,37 +2678,37 @@ Versuch die Filter anzupassen für neue Lieder. Tomahawk::Query - + and und - + You Du - + you du - + and und - + %n other(s) %n anderer%n andere - + %1 people %1 Leute - + loved this track liebte dieses Lied @@ -2603,7 +2724,7 @@ Versuch die Filter anzupassen für neue Lieder. Tomahawk::ShortenedLinkParser - + Network error parsing shortened link! Konnte den Kurzlink nicht auflösen! @@ -2611,36 +2732,46 @@ Versuch die Filter anzupassen für neue Lieder. Tomahawk::Source - - + + Scanning (%L1 tracks) Scanne (%L1 Stücke) - + Scanning Scanne - + Checking Überprüfe - + Fetching Sammle - + Parsing Parse - + Saving (%1%) Speichere (%1%) + + + Online + + + + + Offline + + Tomahawk::SpotifyParser @@ -2653,7 +2784,7 @@ Versuch die Filter anzupassen für neue Lieder. Tomahawk::XspfUpdater - + Automatically update from XSPF @@ -2661,7 +2792,7 @@ Versuch die Filter anzupassen für neue Lieder. TomahawkApp - + My Collection Meine Sammlung @@ -2684,42 +2815,51 @@ Tomahawk auf Twitter's Website authentifiziert hast: TomahawkTrayIcon - - + &Stop Playback after current Track - + Wiedergabe nach diesem Lied &stoppen - - + + Hide Tomahawk Window Tomahawk verbergen - + Show Tomahawk Window Tomahawk anzeigen - + Currently not playing. Derzeit wird nichts gespielt. - + Play Abspielen - + Pause Pause - - &Continue Playback after current Track + + &Love + + + Un-&Love + + + + + &Continue Playback after current Track + Wiedergabe nach diesem Lied &fortsetzen + TomahawkWindow @@ -2860,7 +3000,7 @@ Tomahawk auf Twitter's Website authentifiziert hast: - + Play Abspielen @@ -2880,137 +3020,172 @@ Tomahawk auf Twitter's Website authentifiziert hast: Weiter - + + Back + Zurück + + + + Go back one page + Gehe eine Seite zurück + + + + Forward + Vorwärts + + + + Go forward one page + Gehe eine Seite vorwärts + + + Global Search... Globale Suche... - - + + Check For Updates... Nach Updates suchen... - - - + + + Connect To Peer Mit anderem Tomahawk verbinden - + Enter peer address: Gib die Adresse der Gegenstelle ein: - + Enter peer port: Gib den Port der Gegenstelle ein: - + Enter peer key: Gib den Schlüssel der Gegenstelle ein: - + XSPF Error XSPF-Fehler - + This is not a valid XSPF playlist. Dies ist keine gültige XSPF-Playliste. - + Failed to save tracks Konnte Stücke nicht abspeichern - + Some tracks in the playlist do not contain an artist and a title. They will be ignored. Einige Stücke in der Playliste enthalten weder Künstler noch Titel. Diese werden ignoriert. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Make sure you have a suitable Phonon backend and required plugins installed. Es tut uns leid, Tomahawk kann auf dein Audio-Gerät oder das gewünschte Stück nicht zugreifen und überspringt es deshalb. Vergewisser dich, dass ein geignetes Phonon-Backend mitsamt benötigten Plugins installiert ist. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Es tut uns leid, Tomahawk kann auf dein Audio-Gerät oder das gewünschte Stück nicht zugreifen und überspringt es deshalb. - + + Station + + + + Create New Station Neue Station erstellen - + Name: Name: - - New Station - Neue Station + + Playlist + - - New Playlist - Neue Playliste + + Automatic Playlist + - + Pause Pause - + Go &offline Verbindung &trennen - + Go &online &Verbindung herstellen - + Authentication Error Authentifizierungsfehler - + + Error connecting to SIP: Authentication failed! + + + + %1 by %2 track, artist name %1 von %2 - + %1 - %2 current track, some window title %1 - %2 - + <h2><b>Tomahawk %1<br/>(%2)</h2> - + <h2><b>Tomahawk %1<br/>(%2)</h2> - + <h2><b>Tomahawk %1</h2> <h2><b>Tomahawk %1</h2> - - Copyright 2010 - 2012<br/>Christian Muehlhaeuser &lt;muesli@tomahawk-player.org&gt;<br/><br/>Thanks to: Leo Franchi, Jeff Mitchell, Dominik Schmidt, Jason Herskowitz, Alejandro Wainzinger, Hugo Lindstr&ouml;m, Michael Zanetti, Harald Sitter and Steve Robertson - Copyright 2010 - 2012<br/>Christian Muehlhaeuser &lt;muesli@tomahawk-player.org&gt;<br/><br/>Danke an: Leo Franchi, Jeff Mitchell, Dominik Schmidt, Jason Herskowitz, Alejandro Wainzinger, Hugo Lindstr&ouml;m, Michael Zanetti, Harald Sitter und Steve Robertson + + Copyright 2010 - 2012 + - + + Thanks to: + + + + About Tomahawk Über Tomahawk @@ -3081,7 +3256,7 @@ Tomahawk auf Twitter's Website authentifiziert hast: TopTracksContext - + Top Hits Top Hits @@ -3091,141 +3266,88 @@ Tomahawk auf Twitter's Website authentifiziert hast: Form - + Formular Cover - + Cover Track - + Lied by - + von Artist - + Künstler from - + auf Album - + Album Statistics - + Statistiken - + + Lyrics + Liedtext + + + Similar Tracks + Ähnliche Lieder + + + + Sorry, but we could not find similar tracks for this song! - + You've listened to this track %n time(s). - + Du hast dieses Lied einmal gehört.Du hast dieses Lied %n mal gehört. - + You've never listened to this track before. - + Du hast dieses Lied noch nie angehört. - + You first listened to it on %1. - + Du hast dieses Lied zum ersten mal am %1 gehört. - + You've listened to %1 %n time(s). - + Du hast %1 einmal angehört.Du hast %1 %n mal angehört. - + You've never listened to %1 before. - - - - - TrackModel - - - Artist - Künstler - - - - Title - Titel - - - - Album - Album - - - - Track - Titel - - - - Duration - Spieldauer - - - - Bitrate - Bitrate - - - - Age - Alter - - - - Year - Jahr - - - - Size - Größe - - - - Origin - Quelle - - - - Score - Wertung - - - - Composer - Komponist + Du hast %1 vorher noch nie gehört. TrackView - + Sorry, your filter '%1' did not match any results. Entschuldige, dein Filter '%1' erzeugte keine Ergebnisse. @@ -3246,7 +3368,7 @@ Tomahawk auf Twitter's Website authentifiziert hast: TreeItemDelegate - + Unknown Unbekannt @@ -3254,63 +3376,31 @@ Tomahawk auf Twitter's Website authentifiziert hast: TreeModel - - Name - Name - - - - Duration - Dauer - - - - Bitrate - Bitrate - - - - Age - Alter - - - - Year - Jahr - - - - Size - Größe - - - - Origin - Quelle - - - - Composer - Komponist - - - + All Artists Alle Künstler - - + + My Collection Meine Sammlung - - + + Collection of %1 Sammlung von %1 + + TreeView + + + Sorry, your filter '%1' did not match any results. + + + TwitterConfigWidget @@ -3382,29 +3472,39 @@ You can re-send a sync message at any time simply by sending another tweet using ViewManager - + + After you have scanned your music collection you will find your tracks right here. + + + + + This collection is empty. + + + + SuperCollection SuperCollection - + Combined libraries of all your online friends Kombinierte Sammlung all deiner Freunde - + All available albums Alle verfügbaren Alben - + Recently Played Tracks - + Zuletzt gehörte Lieder - + Recently played tracks from all your friends - + Zuletzt gehörte Lieder all deiner Freunde @@ -3425,12 +3525,12 @@ You can re-send a sync message at any time simply by sending another tweet using Kürzlich gehörte Lieder - + No recently created playlists in your network. Es gibt keine kürzlich erstellten Playlisten in deinem Netzwerk. - + Welcome to Tomahawk Willkommen bei Tomahawk @@ -3438,7 +3538,7 @@ You can re-send a sync message at any time simply by sending another tweet using WhatsHotWidget - + Charts Charts @@ -3488,7 +3588,10 @@ Lyrics for "%1" by %2: %3 - + Liedtext für "%1" von %2: + +%3 + @@ -3669,107 +3772,107 @@ Lyrics for "%1" by %2: XmppSipPlugin - + User Interaction Nutzer Interaktion - + Host is unknown Host ist unbekannt - + Item not found Eintrag nicht gefunden - + Authorization Error Authentifizierungs Fehler - + Remote Stream Error - + Remote Connection failed - + Internal Server Error Interner Server Fehler - + System shutdown - + Conflict Konflikt - + Unknown Unbekannt - + No Compression Support Keine Kompressions Option - + No Encryption Support Keine Verschluesselungs Option - + No Authorization Support Keine Authorisierungs Option - + No Supported Feature Keine unterstuetzte Faehigkeit - + Add Friend Freund hinzufügen... - + Enter Xmpp ID: XMPP-Benutzer: - + Add Friend... Freund hinzufügen... - + XML Console... XML-Konsole... - + I'm sorry -- I'm just an automatic presence used by Tomahawk Player (http://gettomahawk.com). If you are getting this message, the person you are trying to reach is probably not signed on, so please try again later! - + Authorize User Authorisiere Nutzer - + Do you want to grant <b>%1</b> access to your Collection? Willst du <b>%1</b> Zugriff auf deine Sammlung gewähren? diff --git a/lang/tomahawk_en.ts b/lang/tomahawk_en.ts index 02517d38f..54c697a59 100644 --- a/lang/tomahawk_en.ts +++ b/lang/tomahawk_en.ts @@ -2,17 +2,17 @@ AccountFactoryWrapper - + Dialog Dialog - + Description goes here Description goes here - + Add Account Add Account @@ -20,17 +20,17 @@ AccountFactoryWrapperDelegate - + Online Online - + Connecting... Connecting... - + Offline Offline @@ -38,13 +38,13 @@ AclJobDelegate - + Error displaying ACL info Error displaying ACL info - - + + Allow %1 to connect and stream from you? Allow %1 to @@ -54,69 +54,69 @@ connect and stream from you? ActionCollection - + &Listen Along &Listen Along - + Stop &Listening Along Stop &Listening Along - + &Follow in real-time &Follow in real-time - - + + &Listen Privately &Listen Privately - - + + &Listen Publicly &Listen Publicly - + &Load Playlist &Load Playlist - + &Rename Playlist &Rename Playlist - + &Copy Playlist Link &Copy Playlist Link - + &Play &Play - + &Stop &Stop - + &Previous Track &Previous Track - + &Next Track &Next Track - + &Quit &Quit @@ -134,30 +134,17 @@ connect and stream from you? Other Albums by Artist - - - Click to show Official Tracks - Click to show Official Tracks + + Sorry, we could not find any other albums for this artist! + Sorry, we could not find any other albums for this artist! - - - Click to show SuperCollection Tracks - Click to show SuperCollection Tracks + + Sorry, we could not find any tracks for this album! + Sorry, we could not find any tracks for this album! - - - Click to show SuperCollection Albums - Click to show SuperCollection Albums - - - - Click to show Official Albums - Click to show Official Albums - - - + Other Albums by %1 Other Albums by %1 @@ -165,35 +152,17 @@ connect and stream from you? AlbumModel - - Album - Album - - - - + + All albums from %1 All albums from %1 - + All albums All albums - - AlbumView - - - After you have scanned your music collection you will find your latest album additions right here. - After you have scanned your music collection you will find your latest album additions right here. - - - - This collection doesn't have any recent albums. - This collection doesn't have any recent albums. - - ArtistInfoWidget @@ -212,38 +181,24 @@ connect and stream from you? Related Artists - + Albums Albums - - - Click to show SuperCollection Albums - Click to show SuperCollection Albums + + Sorry, we could not find any albums for this artist! + Sorry, we could not find any albums for this artist! - - Click to show Official Albums - Click to show Official Albums - - - - ArtistView - - - After you have scanned your music collection you will find your tracks right here. - After you have scanned your music collection you will find your tracks right here. + + Sorry, we could not find any related artists! + Sorry, we could not find any related artists! - - This collection is currently empty. - This collection is currently empty. - - - - Sorry, your filter '%1' did not match any results. - Sorry, your filter '%1' did not match any results. + + Sorry, we could not find any top hits for this artist! + Sorry, we could not find any top hits for this artist! @@ -327,23 +282,26 @@ connect and stream from you? CategoryAddItem - - - New Playlist - New Playlist + + Create new Playlist + Create new Playlist - - - - + + Create new Station + Create new Station + + + + + New Station New Station - - - + + + %1 Station %1 Station @@ -369,27 +327,6 @@ connect and stream from you? Clear - - CollectionFlatModel - - - My Collection - My Collection - - - - Collection of %1 - Collection of %1 - - - - CollectionView - - - This collection is empty. - This collection is empty. - - ContextWidget @@ -399,12 +336,12 @@ connect and stream from you? - + Show Footnotes Show Footnotes - + Hide Footnotes Hide Footnotes @@ -416,39 +353,49 @@ connect and stream from you? Tomahawk Crash Reporter Tomahawk Crash Reporter - + - <p><b>Sorry!</b>&nbsp;Tomahawk crashed. Information about the crash is now being sent to Tomahawk HQ so that we can fix the bug.</p> - <p><b>Sorry!</b>&nbsp;Tomahawk crashed. Information about the crash is now being sent to Tomahawk HQ so that we can fix the bug.</p> + <html><head/><body><p><span style=" font-weight:600;">Sorry!</span> Tomahawk crashed. Please tell us about it! Tomahawk has created an error report for you that can help improve the stability in the future. You can now send this report directly to the Tomahawk developers.</p></body></html> + <html><head/><body><p><span style=" font-weight:600;">Sorry!</span> Tomahawk crashed. Please tell us about it! Tomahawk has created an error report for you that can help improve the stability in the future. You can now send this report directly to the Tomahawk developers.</p></body></html> - + + Send this report + Send this report + + + + Don't send + Don't send + + + Abort Abort - + You can disable sending crash reports in the configuration dialog. You can disable sending crash reports in the configuration dialog. - + Uploaded %L1 of %L2 KB. Uploaded %L1 of %L2 KB. - - + + Close Close - + Sent! <b>Many thanks</b>. Sent! <b>Many thanks</b>. - + Failed to send crash info. Failed to send crash info. @@ -464,17 +411,17 @@ connect and stream from you? DelegateConfigWrapper - + About About - + Delete Account Delete Account - + About this Account About this Account @@ -487,24 +434,11 @@ connect and stream from you? Tomahawk Diagnostics - - Update - Update - - - + Copy to Clipboard Copy to Clipboard - - DropJob - - - No tracks found for given %1 - No tracks found for given %1 - - GlobalSearchWidget @@ -529,29 +463,11 @@ connect and stream from you? InfoBar - + Filter... Filter... - - JobStatusView - - - Searching For - Searching For - - - - Pending - Pending - - - - Idle - Idle - - LastFmConfig @@ -579,6 +495,11 @@ connect and stream from you? Test Login Test Login + + + Import Playback History + Import Playback History + LastfmContext @@ -627,12 +548,12 @@ connect and stream from you? LoadXSPFDialog - + Load XSPF File Load XSPF File - + XSPF Files (*.xspf) XSPF Files (*.xspf) @@ -681,20 +602,89 @@ connect and stream from you? NewReleasesWidget - + New Releases New Releases + + PlayableModel + + + Artist + Artist + + + + Title + Title + + + + Composer + Composer + + + + Album + Album + + + + Track + Track + + + + Duration + Duration + + + + Bitrate + Bitrate + + + + Age + Age + + + + Year + Year + + + + Size + Size + + + + Origin + Origin + + + + Score + Score + + + + + Name + Name + + PlaylistItemDelegate - + played %1 by you played %1 by you - + played %1 by %2 played %1 by %2 @@ -702,17 +692,17 @@ connect and stream from you? PlaylistLargeItemDelegate - + played %1 by you played %1 by you - + played %1 by %2 played %1 by %2 - + added %1 added %1 @@ -720,12 +710,12 @@ connect and stream from you? PlaylistModel - + A playlist by %1, created %2 A playlist by %1, created %2 - + you you @@ -781,7 +771,12 @@ connect and stream from you? PlaylistView - + + This playlist is currently empty. + This playlist is currently empty. + + + This playlist is currently empty. Add some tracks to it and enjoy the music! This playlist is currently empty. Add some tracks to it and enjoy the music! @@ -849,82 +844,82 @@ connect and stream from you? QObject - + %n year(s) ago %n year ago%n years ago - + %n year(s) %n year%n years - + %n month(s) ago %n month ago%n months ago - + %n month(s) %n month%n months - + %n week(s) ago %n week ago%n weeks ago - + %n week(s) %n week%n weeks - + %n day(s) ago %n day ago%n days ago - + %n day(s) %n day%n days - + %n hour(s) ago %n hour ago%n hours ago - + %n hour(s) %n hour%n hours - + %1 minutes ago %1 minutes ago - + %1 minutes %1 minutes - + just now just now - + Friend Finders Friend Finders - + Music Finders Music Finders - + Status Updaters Status Updaters @@ -946,20 +941,30 @@ connect and stream from you? - - Show Queue - Show Queue + + Open Queue + Open Queue - - Hide Queue - Hide Queue + + The queue is currently empty. Drop something to enqueue it! + The queue is currently empty. Drop something to enqueue it! + + + + Open Queue - %n item(s) + Open Queue - %n item(s)Open Queue - %n item(s) + + + + Close Queue + Close Queue RelatedArtistsContext - + Related Artists Related Artists @@ -1001,37 +1006,37 @@ connect and stream from you? SettingsDialog - + Collection Collection - + Advanced Advanced - + All All - + Some changed settings will not take effect until Tomahawk is restarted Some changed settings will not take effect until Tomahawk is restarted - + Services Services - + Install resolver from file Install resolver from file - + Information Information @@ -1082,17 +1087,17 @@ connect and stream from you? TextLabel - + Listening to "%1" by %2 and loving it! %3 Listening to "%1" by %2 and loving it! %3 - + Listening to "%1" by %2 on "%3" and loving it! %4 Listening to "%1" by %2 on "%3" and loving it! %4 - + %1 characters left %1 characters left @@ -1125,29 +1130,19 @@ connect and stream from you? Top 10 - - Offline - Offline - - - + All available tracks All available tracks - - Online - Online - - - - + + Show Show - - + + Hide Hide @@ -1170,17 +1165,17 @@ connect and stream from you? Recently Played Tracks - + New Additions New Additions - + My recent activity My recent activity - + Recent activity from %1 Recent activity from %1 @@ -1188,41 +1183,51 @@ connect and stream from you? SourceItem - + Collection Collection - - + + Latest Additions Latest Additions - + Recently Played Recently Played - + Loved Tracks Loved Tracks - + SuperCollection SuperCollection - + + Sorry, we could not find any loved tracks! + Sorry, we could not find any loved tracks! + + + Latest additions to your collection Latest additions to your collection - + Latest additions to %1's collection Latest additions to %1's collection + + + Sorry, we could not find any recent additions! + Sorry, we could not find any recent additions! + Recently Played Tracks @@ -1238,31 +1243,78 @@ connect and stream from you? %1's recently played tracks %1's recently played tracks + + + Sorry, we could not find any recent plays! + Sorry, we could not find any recent plays! + SourceTreeView - + &Copy Link &Copy Link - + &Delete %1 &Delete %1 + + + Add to my Playlists + Add to my Playlists + + + + Add to my Automatic Playlists + Add to my Automatic Playlists + + + + Add to my Stations + Add to my Stations + &Export Playlist &Export Playlist - + + playlist + playlist + + + + automatic playlist + automatic playlist + + + + station + station + + + + Delete %1? + playlist/station/... + Delete %1? + + + + Would you like to delete the %1 <b>"%2"</b>? + e.g. Would you like to delete the playlist named Foobar? + Would you like to delete the %1 <b>"%2"</b>? + + + Save XSPF Save XSPF - + Playlists (*.xspf) Playlists (*.xspf) @@ -1543,36 +1595,49 @@ connect and stream from you? Tomahawk::Accounts::AccountDelegate - + Add Account Add Account - + Remove Account Remove Account - + %1 downloads %1 downloads - + Online Online - + Connecting... Connecting... - + Offline Offline + + Tomahawk::Accounts::AccountModel + + + Manual Install Required + Manual Install Required + + + + Unfortunately, automatic installation of this resolver is not available or disabled for your platform.<br /><br />Please use "Install from file" above, by fetching it from your distribution or compiling it yourself. Further instructions can be found here:<br /><br />http://www.tomahawk-player.org/resolvers/%1 + Unfortunately, automatic installation of this resolver is not available or disabled for your platform.<br /><br />Please use "Install from file" above, by fetching it from your distribution or compiling it yourself. Further instructions can be found here:<br /><br />http://www.tomahawk-player.org/resolvers/%1 + + Tomahawk::Accounts::GoogleWrapper @@ -1628,23 +1693,49 @@ connect and stream from you? Tomahawk::Accounts::LastFmConfig - + + Testing... + Testing... + + + Test Login Test Login - - + + Importing %1 + e.g. Importing 2012/01/01 + Importing %1 + + + + Importing History... + Importing History... + + + + History Incomplete. Resume + History Incomplete. Resume + + + + Playback History Imported + Playback History Imported + + + + Failed Failed - + Success Success - + Could not contact server Could not contact server @@ -1652,12 +1743,17 @@ connect and stream from you? Tomahawk::Accounts::SpotifyAccount - + Sync with Spotify Sync with Spotify - + + Re-enable syncing with Spotify + Re-enable syncing with Spotify + + + Stop syncing with Spotify Stop syncing with Spotify @@ -1665,22 +1761,22 @@ connect and stream from you? Tomahawk::Accounts::SpotifyAccountConfig - + Logging in... Logging in... - + Logged in! Logged in! - + Failed: %1 Failed: %1 - + Log In Log In @@ -1688,7 +1784,7 @@ connect and stream from you? Tomahawk::Accounts::SpotifyAccountFactory - + Play music from and sync your playlists with Spotify Premium Play music from and sync your playlists with Spotify Premium @@ -1704,128 +1800,128 @@ connect and stream from you? Tomahawk::Accounts::TwitterConfigWidget - - - + + + Tweet! Tweet! - - + + Status: No saved credentials Status: No saved credentials - - - + + + Authenticate Authenticate - - + + Status: Credentials saved for %1 Status: Credentials saved for %1 - - + + De-authenticate De-authenticate - - - - - - - + + + + + + + Tweetin' Error Tweetin' Error - + The credentials could not be verified. You may wish to try re-authenticating. The credentials could not be verified. You may wish to try re-authenticating. - + Status: Error validating credentials Status: Error validating credentials - + Global Tweet Global Tweet - + Direct Message Direct Message - + Send Message! Send Message! - + @Mention @Mention - + Send Mention! Send Mention! - + You must enter a user name for this type of tweet. You must enter a user name for this type of tweet. - + Your saved credentials could not be loaded. You may wish to try re-authenticating. Your saved credentials could not be loaded. You may wish to try re-authenticating. - + Your saved credentials could not be verified. You may wish to try re-authenticating. Your saved credentials could not be verified. You may wish to try re-authenticating. - - + + There was an error posting your status -- sorry! There was an error posting your status -- sorry! - - + + Tweeted! Tweeted! - + Your tweet has been posted! Your tweet has been posted! - + There was an error posting your direct message -- sorry! There was an error posting your direct message -- sorry! - + Your message has been posted! Your message has been posted! @@ -1841,7 +1937,7 @@ You may wish to try re-authenticating. Tomahawk::Accounts::ZeroconfFactory - + Automatically connect to Tomahawks on the local network Automatically connect to Tomahawks on the local network @@ -1849,20 +1945,20 @@ You may wish to try re-authenticating. Tomahawk::ContextMenu - + &Play &Play - - - + + + Add to &Queue Add to &Queue - - + + &Love &Love @@ -1872,40 +1968,45 @@ You may wish to try re-authenticating. &Copy Track Link - - Show &Album page - Show &Album page - - - - Show &Artist page - Show &Artist page - - - + Un-&Love Un-&Love - + &Delete Items &Delete Items - + &Continue Playback after this Track &Continue Playback after this Track - + &Stop Playback after this Track &Stop Playback after this Track - + + &Show Track Page + &Show Track Page + + + &Delete Item &Delete Item + + + &Show Album Page + &Show Album Page + + + + &Show Artist Page + &Show Artist Page + Tomahawk::CustomPlaylistView @@ -1943,12 +2044,32 @@ You may wish to try re-authenticating. Tomahawk::DropJobNotifier - + + playlist + playlist + + + + artist + artist + + + + track + track + + + + album + album + + + Fetching %1 from database Fetching %1 from database - + Parsing %1 %2 Parsing %1 %2 @@ -1956,7 +2077,7 @@ You may wish to try re-authenticating. Tomahawk::DynamicControlList - + Click to collapse Click to collapse @@ -1995,17 +2116,17 @@ Please change the filters or try again. Tomahawk::DynamicView - + Add some filters above to seed this station! Add some filters above to seed this station! - + Press Generate to get started! Press Generate to get started! - + Add some filters above, and press Generate to get started! Add some filters above, and press Generate to get started! @@ -2013,7 +2134,7 @@ Please change the filters or try again. Tomahawk::DynamicWidget - + Station ran out of tracks! Try tweaking the filters for a new set of songs to play. @@ -2327,97 +2448,97 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::EchonestSteerer - + Steer this station: Steer this station: - + Much less Much less - + Less Less - + A bit less A bit less - + Keep at current Keep at current - + A bit more A bit more - + More More - + Much more Much more - + Tempo Tempo - + Loudness Loudness - + Danceability Danceability - + Energy Energy - + Song Hotttnesss Song Hotttnesss - + Artist Hotttnesss Artist Hotttnesss - + Artist Familiarity Artist Familiarity - + By Description By Description - + Enter a description Enter a description - + Apply steering command Apply steering command - + Reset all steering commands Reset all steering commands @@ -2433,22 +2554,22 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::InfoSystem::ChartsPlugin - + Top Overall Top Overall - + Artists Artists - + Albums Albums - + Tracks Tracks @@ -2456,12 +2577,12 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::InfoSystem::FdoNotifyPlugin - + Tomahawk is playing "%1" by %2%3. Tomahawk is playing "%1" by %2%3. - + on "%1" on "%1" @@ -2469,27 +2590,27 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::InfoSystem::LastFmInfoPlugin - + Top Tracks Top Tracks - + Loved Tracks Loved Tracks - + Hyped Tracks Hyped Tracks - + Top Artists Top Artists - + Hyped Artists Hyped Artists @@ -2497,7 +2618,7 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::InfoSystem::NewReleasesPlugin - + Albums Albums @@ -2505,7 +2626,7 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::InfoSystem::TwitterInfoPlugin - + Listening to "%1" by %2 and loving it! %3 Listening to "%1" by %2 and loving it! %3 @@ -2563,37 +2684,37 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Query - + and and - + You You - + you you - + and and - + %n other(s) %n other%n others - + %1 people %1 people - + loved this track loved this track @@ -2609,7 +2730,7 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::ShortenedLinkParser - + Network error parsing shortened link! Network error parsing shortened link! @@ -2617,36 +2738,46 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) Scanning (%L1 tracks) - + Scanning Scanning - + Checking Checking - + Fetching Fetching - + Parsing Parsing - + Saving (%1%) Saving (%1%) + + + Online + Online + + + + Offline + Offline + Tomahawk::SpotifyParser @@ -2659,7 +2790,7 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::XspfUpdater - + Automatically update from XSPF Automatically update from XSPF @@ -2667,7 +2798,7 @@ Try tweaking the filters for a new set of songs to play. TomahawkApp - + My Collection My Collection @@ -2690,39 +2821,48 @@ enter the displayed PIN number here: TomahawkTrayIcon - - + &Stop Playback after current Track &Stop Playback after current Track - - + + Hide Tomahawk Window Hide Tomahawk Window - + Show Tomahawk Window Show Tomahawk Window - + Currently not playing. Currently not playing. - + Play Play - + Pause Pause - + + &Love + &Love + + + + Un-&Love + Un-&Love + + + &Continue Playback after current Track &Continue Playback after current Track @@ -2866,7 +3006,7 @@ enter the displayed PIN number here: - + Play Play @@ -2886,137 +3026,172 @@ enter the displayed PIN number here: Next - + + Back + Back + + + + Go back one page + Go back one page + + + + Forward + Forward + + + + Go forward one page + Go forward one page + + + Global Search... Global Search... - - + + Check For Updates... Check For Updates... - - - + + + Connect To Peer Connect To Peer - + Enter peer address: Enter peer address: - + Enter peer port: Enter peer port: - + Enter peer key: Enter peer key: - + XSPF Error XSPF Error - + This is not a valid XSPF playlist. This is not a valid XSPF playlist. - + Failed to save tracks Failed to save tracks - + Some tracks in the playlist do not contain an artist and a title. They will be ignored. Some tracks in the playlist do not contain an artist and a title. They will be ignored. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Make sure you have a suitable Phonon backend and required plugins installed. Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Make sure you have a suitable Phonon backend and required plugins installed. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. - + + Station + Station + + + Create New Station Create New Station - + Name: Name: - - New Station - New Station + + Playlist + Playlist - - New Playlist - New Playlist + + Automatic Playlist + Automatic Playlist - + Pause Pause - + Go &offline Go &offline - + Go &online Go &online - + Authentication Error Authentication Error - + + Error connecting to SIP: Authentication failed! + Error connecting to SIP: Authentication failed! + + + %1 by %2 track, artist name %1 by %2 - + %1 - %2 current track, some window title %1 - %2 - + <h2><b>Tomahawk %1<br/>(%2)</h2> <h2><b>Tomahawk %1<br/>(%2)</h2> - + <h2><b>Tomahawk %1</h2> <h2><b>Tomahawk %1</h2> - - Copyright 2010 - 2012<br/>Christian Muehlhaeuser &lt;muesli@tomahawk-player.org&gt;<br/><br/>Thanks to: Leo Franchi, Jeff Mitchell, Dominik Schmidt, Jason Herskowitz, Alejandro Wainzinger, Hugo Lindstr&ouml;m, Michael Zanetti, Harald Sitter and Steve Robertson - Copyright 2010 - 2012<br/>Christian Muehlhaeuser &lt;muesli@tomahawk-player.org&gt;<br/><br/>Thanks to: Leo Franchi, Jeff Mitchell, Dominik Schmidt, Jason Herskowitz, Alejandro Wainzinger, Hugo Lindstr&ouml;m, Michael Zanetti, Harald Sitter and Steve Robertson + + Copyright 2010 - 2012 + Copyright 2010 - 2012 - + + Thanks to: + Thanks to: + + + About Tomahawk About Tomahawk @@ -3087,7 +3262,7 @@ enter the displayed PIN number here: TopTracksContext - + Top Hits Top Hits @@ -3135,103 +3310,50 @@ enter the displayed PIN number here: Statistics - + + Lyrics + Lyrics + + + Similar Tracks Similar Tracks + + + Sorry, but we could not find similar tracks for this song! + Sorry, but we could not find similar tracks for this song! + - + You've listened to this track %n time(s). You've listened to this track %n time(s).You've listened to this track %n time(s). - + You've never listened to this track before. You've never listened to this track before. - + You first listened to it on %1. You first listened to it on %1. - + You've listened to %1 %n time(s). You've listened to %1 %n time(s).You've listened to %1 %n time(s). - + You've never listened to %1 before. You've never listened to %1 before. - - TrackModel - - - Artist - Artist - - - - Title - Title - - - - Album - Album - - - - Track - Track - - - - Duration - Duration - - - - Bitrate - Bitrate - - - - Age - Age - - - - Year - Year - - - - Size - Size - - - - Origin - Origin - - - - Score - Score - - - - Composer - Composer - - TrackView - + Sorry, your filter '%1' did not match any results. Sorry, your filter '%1' did not match any results. @@ -3252,7 +3374,7 @@ enter the displayed PIN number here: TreeItemDelegate - + Unknown Unknown @@ -3260,63 +3382,31 @@ enter the displayed PIN number here: TreeModel - - Name - Name - - - - Duration - Duration - - - - Bitrate - Bitrate - - - - Age - Age - - - - Year - Year - - - - Size - Size - - - - Origin - Origin - - - - Composer - Composer - - - + All Artists All Artists - - + + My Collection My Collection - - + + Collection of %1 Collection of %1 + + TreeView + + + Sorry, your filter '%1' did not match any results. + Sorry, your filter '%1' did not match any results. + + TwitterConfigWidget @@ -3393,27 +3483,37 @@ You can re-send a sync message at any time simply by sending another tweet using ViewManager - + + After you have scanned your music collection you will find your tracks right here. + After you have scanned your music collection you will find your tracks right here. + + + + This collection is empty. + This collection is empty. + + + SuperCollection SuperCollection - + Combined libraries of all your online friends Combined libraries of all your online friends - + All available albums All available albums - + Recently Played Tracks Recently Played Tracks - + Recently played tracks from all your friends Recently played tracks from all your friends @@ -3436,12 +3536,12 @@ You can re-send a sync message at any time simply by sending another tweet using Recently Played Tracks - + No recently created playlists in your network. No recently created playlists in your network. - + Welcome to Tomahawk Welcome to Tomahawk @@ -3449,7 +3549,7 @@ You can re-send a sync message at any time simply by sending another tweet using WhatsHotWidget - + Charts Charts @@ -3688,107 +3788,107 @@ Lyrics for "%1" by %2: XmppSipPlugin - + User Interaction User Interaction - + Host is unknown Host is unknown - + Item not found Item not found - + Authorization Error Authorization Error - + Remote Stream Error Remote Stream Error - + Remote Connection failed Remote Connection failed - + Internal Server Error Internal Server Error - + System shutdown System shutdown - + Conflict Conflict - + Unknown Unknown - + No Compression Support No Compression Support - + No Encryption Support No Encryption Support - + No Authorization Support No Authorization Support - + No Supported Feature No Supported Feature - + Add Friend Add Friend - + Enter Xmpp ID: Enter Xmpp ID: - + Add Friend... Add Friend... - + XML Console... XML Console... - + I'm sorry -- I'm just an automatic presence used by Tomahawk Player (http://gettomahawk.com). If you are getting this message, the person you are trying to reach is probably not signed on, so please try again later! I'm sorry -- I'm just an automatic presence used by Tomahawk Player (http://gettomahawk.com). If you are getting this message, the person you are trying to reach is probably not signed on, so please try again later! - + Authorize User Authorize User - + Do you want to grant <b>%1</b> access to your Collection? Do you want to grant <b>%1</b> access to your Collection? diff --git a/lang/tomahawk_es.ts b/lang/tomahawk_es.ts index 4254f828b..8d6ccad7e 100644 --- a/lang/tomahawk_es.ts +++ b/lang/tomahawk_es.ts @@ -2,17 +2,17 @@ AccountFactoryWrapper - + Dialog Diálogo - + Description goes here Descripción - + Add Account Añadir Cuenta @@ -20,17 +20,17 @@ AccountFactoryWrapperDelegate - + Online Conectado - + Connecting... Conectando... - + Offline Desconectado @@ -38,84 +38,84 @@ AclJobDelegate - + Error displaying ACL info Error mostrando informacion de ACL - - + + Allow %1 to connect and stream from you? - + ¿Permitir a %1 conectarse y transmitirle música? ActionCollection - + &Listen Along &Escuchar juntos - + Stop &Listening Along Detener la &reproducción conjunta - + &Follow in real-time &Seguir en tiempo real - - + + &Listen Privately &Escuchar en privado - - + + &Listen Publicly &Escuchar públicamente - + &Load Playlist &Cargar lista de reproducción - + &Rename Playlist &Renombrar lista de reproducción - + &Copy Playlist Link &Copiar enlace de lista de reproducción - + &Play &Reproducir - + &Stop &Detener - + &Previous Track &Pista anterior - + &Next Track &Pista siguiente - + &Quit &Salir @@ -125,7 +125,7 @@ connect and stream from you? Form - Form + Formulario @@ -133,30 +133,17 @@ connect and stream from you? Otros álbumes por el artista - - - Click to show Official Tracks - Mostrar Pistas Oficiales + + Sorry, we could not find any other albums for this artist! + - - - Click to show SuperCollection Tracks - Mostrar pistas en SuperColección + + Sorry, we could not find any tracks for this album! + - - - Click to show SuperCollection Albums - Mostrar álbumes en SuperColección - - - - Click to show Official Albums - Mostrar álbumes oficiales - - - + Other Albums by %1 Otros álbumes por %1 @@ -164,41 +151,23 @@ connect and stream from you? AlbumModel - - Album - Álbum - - - - + + All albums from %1 Todos los álbumes de %1 - + All albums Todos los álbumes - - AlbumView - - - After you have scanned your music collection you will find your latest album additions right here. - Tras haber escaneado su colección aquí encontrará los últimos álbumes añadidos. - - - - This collection doesn't have any recent albums. - Esta colección no tiene álbumes recientes. - - ArtistInfoWidget Form - Form + Formulario @@ -211,38 +180,24 @@ connect and stream from you? Artistas relacionados - + Albums Álbumes - - - Click to show SuperCollection Albums - Mostrar álbumes en SuperColección + + Sorry, we could not find any albums for this artist! + - - Click to show Official Albums - Mostrar álbumes oficiales - - - - ArtistView - - - After you have scanned your music collection you will find your tracks right here. - Tras haber escaneado su colección aquí encontrará las últimas pistas añadidas. + + Sorry, we could not find any related artists! + - - This collection is currently empty. - Esta colección está vacía. - - - - Sorry, your filter '%1' did not match any results. - Tu filtro '%1' no reportó ningún resultado. + + Sorry, we could not find any top hits for this artist! + @@ -326,23 +281,26 @@ connect and stream from you? CategoryAddItem - - - New Playlist - Nueva lista de reproducción + + Create new Playlist + - - - - + + Create new Station + + + + + + New Station Nueva estación - - - + + + %1 Station %1 estación @@ -368,27 +326,6 @@ connect and stream from you? Limpiar - - CollectionFlatModel - - - My Collection - Mi colección - - - - Collection of %1 - Colección de %1 - - - - CollectionView - - - This collection is empty. - Esta colección está vacía. - - ContextWidget @@ -398,12 +335,12 @@ connect and stream from you? - + Show Footnotes Mostrar información de contexto - + Hide Footnotes Ocultar información de contexto @@ -415,39 +352,49 @@ connect and stream from you? Tomahawk Crash Reporter Reportador de fallos de Tomahawk - + - <p><b>Sorry!</b>&nbsp;Tomahawk crashed. Information about the crash is now being sent to Tomahawk HQ so that we can fix the bug.</p> - <p><b>¡Ups!</b>&nbsp;Tomahawk ha fallado. La información acerca del fallo se enviará a Tomahawk HQ para que podamos solucionar el error.</p> + <html><head/><body><p><span style=" font-weight:600;">Sorry!</span> Tomahawk crashed. Please tell us about it! Tomahawk has created an error report for you that can help improve the stability in the future. You can now send this report directly to the Tomahawk developers.</p></body></html> + - + + Send this report + + + + + Don't send + + + + Abort Abortar - + You can disable sending crash reports in the configuration dialog. Puede desactivar el envío de informes de error en el diálogo de configuración. - + Uploaded %L1 of %L2 KB. Subidos %L1 of %L2 KBs. - - + + Close Cerrar - + Sent! <b>Many thanks</b>. ¡Enviado!<b>Muchas gracias</b>. - + Failed to send crash info. Error al enviar la información del fallo. @@ -463,17 +410,17 @@ connect and stream from you? DelegateConfigWrapper - + About Acerca de - + Delete Account Eliminar Cuenta - + About this Account Acerca de esta Cuenta @@ -486,30 +433,17 @@ connect and stream from you? Diagnósticos de Tomahawk - - Update - Actualizar - - - + Copy to Clipboard Copiar al portapapeles - - DropJob - - - No tracks found for given %1 - Ninguna pista fue encontrada para %1 - - GlobalSearchWidget Form - Form + Formulario @@ -517,7 +451,7 @@ connect and stream from you? Indexing database - + Indexando la base de datos @@ -528,35 +462,17 @@ connect and stream from you? Barra de información - + Filter... Filtro... - - JobStatusView - - - Searching For - Buscando por - - - - Pending - Pendiente - - - - Idle - Ocupado - - LastFmConfig Form - + Formulario @@ -578,6 +494,11 @@ connect and stream from you? Test Login Probar conexión + + + Import Playback History + + LastfmContext @@ -626,12 +547,12 @@ connect and stream from you? LoadXSPFDialog - + Load XSPF File Cargar archivo XSPF - + XSPF Files (*.xspf) Archivos XSPF (*.xspf) @@ -680,20 +601,89 @@ connect and stream from you? NewReleasesWidget - + New Releases Últimas novedades + + PlayableModel + + + Artist + + + + + Title + + + + + Composer + + + + + Album + + + + + Track + + + + + Duration + + + + + Bitrate + + + + + Age + + + + + Year + + + + + Size + + + + + Origin + + + + + Score + + + + + + Name + + + PlaylistItemDelegate - + played %1 by you %1 reproducido por usted - + played %1 by %2 %1 reproducido por %2 @@ -701,17 +691,17 @@ connect and stream from you? PlaylistLargeItemDelegate - + played %1 by you %1 reproducido por usted - + played %1 by %2 %1 reproducido por %2 - + added %1 añadio %1 @@ -719,12 +709,12 @@ connect and stream from you? PlaylistModel - + A playlist by %1, created %2 Una lista de reproducción de %1, creada %2 - + you usted @@ -780,7 +770,12 @@ connect and stream from you? PlaylistView - + + This playlist is currently empty. + + + + This playlist is currently empty. Add some tracks to it and enjoy the music! Esta lista de reproducción está vacía. ¡Añada algunas pistas y disfrute de la música! @@ -831,7 +826,7 @@ connect and stream from you? No Proxy Hosts: (Overrides system proxy) - + Sin proxy (Sobreescribe el proxy del sistema) @@ -847,82 +842,82 @@ connect and stream from you? QObject - + %n year(s) ago hace %n añohace%n años - + %n year(s) %n año%n años - + %n month(s) ago hace %n meshace %n meses - + %n month(s) %n mes%n meses - + %n week(s) ago hace %n semanahace %n semanas - + %n week(s) %n semana%n semanas - + %n day(s) ago hace %n díahace %n días - + %n day(s) %n día%n días - + %n hour(s) ago hace %n horahace %n horas - + %n hour(s) %n hora%n horas - + %1 minutes ago hace %1 minutos - + %1 minutes %1 minutos - + just now justo ahora - + Friend Finders Buscador de amigos - + Music Finders Buscador de musica - + Status Updaters Actualizadores de estado @@ -944,20 +939,30 @@ connect and stream from you? - - Show Queue - Mostrar cola + + Open Queue + - - Hide Queue - Ocultar cola + + The queue is currently empty. Drop something to enqueue it! + + + + + Open Queue - %n item(s) + + + + + Close Queue + RelatedArtistsContext - + Related Artists Artistas relacionados @@ -999,37 +1004,37 @@ connect and stream from you? SettingsDialog - + Collection Colección - + Advanced Avanzado - + All Todo - + Some changed settings will not take effect until Tomahawk is restarted Algunos cambios no tomarán efecto hasta reiniciar Tomahawk - + Services Servicios - + Install resolver from file - + Instalar un Resolver desde un fichero - + Information Información @@ -1057,7 +1062,7 @@ connect and stream from you? Form - + Formulario @@ -1077,22 +1082,22 @@ connect and stream from you? TextLabel - + TextLabel - + Listening to "%1" by %2 and loving it! %3 - + Escuchando "%1" de %2 y me encanta! %3 - + Listening to "%1" by %2 on "%3" and loving it! %4 - + Escuchando "%1" de %2 en "%3" y me encanta! %4 - + %1 characters left - + %1 caracteres restantes @@ -1100,7 +1105,7 @@ connect and stream from you? Track - Pista + Tema @@ -1123,29 +1128,19 @@ connect and stream from you? Top 10 - - Offline - Desconectado - - - + All available tracks Todas las pistas disponibles - - Online - Conectado - - - - + + Show Mostrar - - + + Hide Ocultar @@ -1168,17 +1163,17 @@ connect and stream from you? Pistas reproducidas recientemente - + New Additions Nuevas adiciones - + My recent activity Mi actividad reciente - + Recent activity from %1 Actividad reciente de %1 @@ -1186,81 +1181,138 @@ connect and stream from you? SourceItem - + Collection Colección - - + + Latest Additions Añadidos recientemente - + Recently Played Reproducido recientemente - + Loved Tracks Pistas favoritas - + SuperCollection SuperColección - - Latest additions to your collection + + Sorry, we could not find any loved tracks! - + + Latest additions to your collection + Últimas novedades en mi colección + + + Latest additions to %1's collection + Últimas novedadoes en la colección de %1 + + + + Sorry, we could not find any recent additions! Recently Played Tracks - + Temas Escuchados Recientemente Your recently played tracks - + Mis canciones escuchadas recientemente %1's recently played tracks + Las canciones escuchadas recientemente por %1 + + + + Sorry, we could not find any recent plays! SourceTreeView - + &Copy Link &Copiar enlace - + &Delete %1 &Eliminar %1 + + + Add to my Playlists + + + + + Add to my Automatic Playlists + + + + + Add to my Stations + + &Export Playlist &Exportar lista de reproducción - + + playlist + + + + + automatic playlist + + + + + station + + + + + Delete %1? + playlist/station/... + + + + + Would you like to delete the %1 <b>"%2"</b>? + e.g. Would you like to delete the playlist named Foobar? + + + + Save XSPF Guardar XSPF - + Playlists (*.xspf) Listas de reproducción (*.xspf) @@ -1270,7 +1322,7 @@ connect and stream from you? Group - + Grupo @@ -1295,7 +1347,7 @@ connect and stream from you? Browse - + Buscar @@ -1320,7 +1372,7 @@ connect and stream from you? Dashboard - + Panel de inicio @@ -1330,7 +1382,7 @@ connect and stream from you? Charts - + Listas @@ -1348,52 +1400,52 @@ connect and stream from you? Form - + Formulario Configure your Spotify account - + Configura tu cuenta de Spotify Username or Facebook Email - + Usurio o dirección de correo de Facebook Log In - + Inciar Sesión Right click on any Tomahawk playlist to sync it to Spotify. - + Click derecho en cualquier lista de Tomahawk para sincronizar con Spotify. High Quality Streams - + Streams de Alta Calidad Spotify playlists to keep in sync: - + Listas de Spotify a mantener sincronizadas: Delete Tomahawk playlist when removing synchronization - + Borra las listas de Tomahawk cuando se quiten de la sincronización Username: - + Usuario: Password: - + Contraseña: @@ -1401,12 +1453,12 @@ connect and stream from you? Delete in Spotify? - + ¿Borrar en Spotify? Would you like to delete the corresponding Spotify playlist as well? - + Quieres eliminar la lista de Spotify correspondiente, también? @@ -1455,57 +1507,57 @@ y estaciones basadas en sus gustos personales. Advanced Settings - + Ajustes avanzados Remote Peer Connection Method - + Método de conexión remota None (outgoing connections only) - + Ninguna (sólo conexiones salientes) Use UPnP to establish port forward (recommended) - + Usar UPnP para establecer la redirección de puertos (recomendado) Use static external IP address/host name and port - + Usar IP estática externa/nombre de host y puero Set this to your external IP address or host name. Make sure to forward the port to this host! - + Ajustar a la dirección IP externa o nombre de la máquina. Hay que redirigir los puertos a esta máquina! SOCKS Proxy - + Proxy SOCKS Use SOCKS Proxy - + Utilizar el Proxy SOCKS Internet Services - + Servicios de Internet Install from file... - + Instalar desde un fichero... Filter by capability: - + Filtrar por capacidad: @@ -1525,7 +1577,7 @@ y estaciones basadas en sus gustos personales. Other Settings - + Otros Ajustes @@ -1535,58 +1587,71 @@ y estaciones basadas en sus gustos personales. Allow web browsers to interact with Tomahawk (recommended) - + Permitir a los navegadores web interactuar con Tomahawk (recomendado) Tomahawk::Accounts::AccountDelegate - + Add Account Añadir Cuenta - + Remove Account Eliminar Cuenta - + %1 downloads - + %1 descargas - + Online Conectado - + Connecting... Conectando... - + Offline Desconectado + + Tomahawk::Accounts::AccountModel + + + Manual Install Required + + + + + Unfortunately, automatic installation of this resolver is not available or disabled for your platform.<br /><br />Please use "Install from file" above, by fetching it from your distribution or compiling it yourself. Further instructions can be found here:<br /><br />http://www.tomahawk-player.org/resolvers/%1 + + + Tomahawk::Accounts::GoogleWrapper Configure this Google Account - + Configura esta Cuenta Google Google Address - + Dirección de correo Google Enter your Google login to connect with your friends using Tomahawk! - + Introducir los datos de la cuenta Google para contectar con los amigos que usen Tomahawk! @@ -1599,7 +1664,7 @@ y estaciones basadas en sus gustos personales. Connect to Google Talk to find your friends - + Conectarse a Google Talk y encontrar mis amigos @@ -1607,12 +1672,12 @@ y estaciones basadas en sus gustos personales. Add Friend - + Añadir Amigo Enter Google Address: - + Introducir la dirección de correo de Google: @@ -1620,75 +1685,106 @@ y estaciones basadas en sus gustos personales. Scrobble your tracks to last.fm, and find freely downloadable tracks to play - + Hacer Scrobble de mis temas a last.fm y encontrar canciones gratuitas para reproducirlas Tomahawk::Accounts::LastFmConfig - + + Testing... + + + + Test Login + Comprobar Inicio de Sesión + + + + Importing %1 + e.g. Importing 2012/01/01 - - + + Importing History... + + + + + History Incomplete. Resume + + + + + Playback History Imported + + + + + Failed - + Fallo - + Success - + Éxito - + Could not contact server - + No se pudo contactar el servidor Tomahawk::Accounts::SpotifyAccount - + Sync with Spotify + Sincronizar con Spotify + + + + Re-enable syncing with Spotify - + Stop syncing with Spotify - + Dejar de sincronizar con Spotify Tomahawk::Accounts::SpotifyAccountConfig - + Logging in... - + Inciando sesión... - + Logged in! - + Sesión iniciada! - + Failed: %1 - + Fallo: %1 - + Log In - + Iniciar Sesión Tomahawk::Accounts::SpotifyAccountFactory - + Play music from and sync your playlists with Spotify Premium - + Reproduce música de Spotofy Premium y sincroniza las listas de reprodución @@ -1696,133 +1792,136 @@ y estaciones basadas en sus gustos personales. Connect to your Twitter followers. - + Conectar con los seguidores de Twitter. Tomahawk::Accounts::TwitterConfigWidget - - - + + + Tweet! - - - - - - Status: No saved credentials - + Twittear! - - Authenticate - + Status: No saved credentials + Estado: Credenciales no guardadas - - - Status: Credentials saved for %1 - + + + + Authenticate + Autenticar + Status: Credentials saved for %1 + Estado: Credenciales de %1 guardadas + + + + De-authenticate - + Desautenticar - - - - - - - + + + + + + + Tweetin' Error - + Error al Twittear - + The credentials could not be verified. You may wish to try re-authenticating. - + Las credencials no se han podido verificar. +Hay que volver a intentar la autenticación. - + Status: Error validating credentials - + Estado: Error al validar las credenciales - + Global Tweet - - - - - Direct Message - + Tweet Global - Send Message! - + Direct Message + Mensaje Directo - @Mention - + Send Message! + Enviar Mensaje! + @Mention + @Mención + + + Send Mention! - + Enviar Mención! - + You must enter a user name for this type of tweet. - + Hay que introducir un nombre de usuario para este tipo de tweet. - + Your saved credentials could not be loaded. You may wish to try re-authenticating. - + Las credenciales guardadas no se han podido cargar. +Hay que volverse a autenticar. - + Your saved credentials could not be verified. You may wish to try re-authenticating. - + Las credenciales guardadas no se han podido verificar. +Hay que volverse a autenticar. - - + + There was an error posting your status -- sorry! - + Error publicando el estado! - - + + Tweeted! - + Twitteado! - + Your tweet has been posted! - + El tweet se ha publicado! - + There was an error posting your direct message -- sorry! - + Error publicando el mensahe directo! - + Your message has been posted! - + El mensaje se ha publicado! @@ -1830,77 +1929,82 @@ You may wish to try re-authenticating. Log on to your Jabber/XMPP account to connect to your friends - + Inciar sesón en Jabber/XMPP para conectar con mis amigos Tomahawk::Accounts::ZeroconfFactory - + Automatically connect to Tomahawks on the local network - + Conectarse automáticament a los Tomahawks de la red local Tomahawk::ContextMenu - + &Play &Reproducir - - - + + + Add to &Queue Añadir a la &cola - - + + &Love - + &Favorito &Copy Track Link - + &Copiar enlace del Tema - - Show &Album page - - - - - Show &Artist page - - - - + Un-&Love - + Quitar de &Favoritos - + &Delete Items &Eliminar de la cola - + &Continue Playback after this Track - + &Continuar la Reproducción después de este Tema - + &Stop Playback after this Track + &Para la Reproducción después de este Tema + + + + &Show Track Page - + &Delete Item &Eliminar de la cola + + + &Show Album Page + + + + + &Show Artist Page + + Tomahawk::CustomPlaylistView @@ -1938,12 +2042,32 @@ You may wish to try re-authenticating. Tomahawk::DropJobNotifier - + + playlist + + + + + artist + + + + + track + + + + + album + + + + Fetching %1 from database Obteniendo %1 de la base de datos - + Parsing %1 %2 Analizando %1 %2 @@ -1951,7 +2075,7 @@ You may wish to try re-authenticating. Tomahawk::DynamicControlList - + Click to collapse Click para contraer @@ -1990,17 +2114,17 @@ Por favor, cambie los filtros o inténtelo de nuevo. Tomahawk::DynamicView - + Add some filters above to seed this station! ¡Añada algunos filtros para la estación! - + Press Generate to get started! ¡Seleccione Generar para comenzar! - + Add some filters above, and press Generate to get started! ¡Añada algunos filtros, y seleccione Generar para comenzar! @@ -2008,11 +2132,13 @@ Por favor, cambie los filtros o inténtelo de nuevo. Tomahawk::DynamicWidget - + Station ran out of tracks! Try tweaking the filters for a new set of songs to play. - + La Emisora se ha quedado sin temas! + +Intente ajustar los filtros para reproducir nuevas canciones. @@ -2320,97 +2446,97 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::EchonestSteerer - + Steer this station: Dirigir esta estación: - + Much less Mucho menos - + Less Menos - + A bit less Un poco menos - + Keep at current Mantener en el actual - + A bit more Un poco más - + More Más - + Much more Mucho más - + Tempo Tempo - + Loudness Volumen - + Danceability Bailabilidad - + Energy Energía - + Song Hotttnesss Popularidad de la pista - + Artist Hotttnesss Popularidad del artista - + Artist Familiarity Familiaridad del artista - + By Description Por descripción - + Enter a description Introducir una descripción - + Apply steering command Aplicar comando - + Reset all steering commands Resetear todos los comandos @@ -2420,28 +2546,28 @@ Try tweaking the filters for a new set of songs to play. Error fetching Grooveshark information from the network! - + Error al buscar la información de Grooveshar en la red! Tomahawk::InfoSystem::ChartsPlugin - + Top Overall Top total - + Artists Artistas - + Albums Álbumes - + Tracks Pistas @@ -2449,58 +2575,58 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::InfoSystem::FdoNotifyPlugin - + Tomahawk is playing "%1" by %2%3. - + Tomahawk está reproduciendo "%1" de %2%3. - + on "%1" - + en "%1" Tomahawk::InfoSystem::LastFmInfoPlugin - + Top Tracks - + Temas más escuchados - + Loved Tracks - + Temas Favoritos - + Hyped Tracks - + Temas a la alza - + Top Artists - + Artistas más escuchados - + Hyped Artists - + Artistas a la alza Tomahawk::InfoSystem::NewReleasesPlugin - + Albums - + Álbumes Tomahawk::InfoSystem::TwitterInfoPlugin - + Listening to "%1" by %2 and loving it! %3 - + Escuchando "%1" de %2 y me encanta! %3 @@ -2508,7 +2634,7 @@ Try tweaking the filters for a new set of songs to play. Error fetching iTunes information from the network! - + Error al buscar la información de iTunes en la red! @@ -2556,39 +2682,39 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Query - + and - + y - + You - + usted - + you - + usted - + and - + y - + %n other(s) - + %n más%n más - + %1 people - + %1 personas - + loved this track - + han añadido este tema a Favoritos @@ -2596,71 +2722,81 @@ Try tweaking the filters for a new set of songs to play. Error fetching Rdio information from the network! - + Error al buscar la información de Rdio en la red! Tomahawk::ShortenedLinkParser - + Network error parsing shortened link! - + Error en la red al analizar el enlace acortado! Tomahawk::Source - - + + Scanning (%L1 tracks) Escaneando (%L1 pistas) - + Scanning - + Escaneando - + Checking Comprobando - + Fetching Obteniendo - + Parsing Analizando - + Saving (%1%) Guardando (%1%) + + + Online + + + + + Offline + + Tomahawk::SpotifyParser Error fetching Spotify information from the network! - + Error al buscar la información de Spotify en la red! Tomahawk::XspfUpdater - + Automatically update from XSPF - + Actualizar automáticamente desde XSPF TomahawkApp - + My Collection Mi colección @@ -2683,42 +2819,51 @@ introduzca su número PIN aquí: TomahawkTrayIcon - - + &Stop Playback after current Track - + &Para la Reproducción después de este Tema - - + + Hide Tomahawk Window Ocultar ventana de Tomahawk - + Show Tomahawk Window Mostrar ventana de Tomahawk - + Currently not playing. Ninguna pista en reproducción. - + Play Reproducir - + Pause Pausar - - &Continue Playback after current Track + + &Love + + + Un-&Love + + + + + &Continue Playback after current Track + &Continuar la Reproducción después de este Tema + TomahawkWindow @@ -2735,7 +2880,7 @@ introduzca su número PIN aquí: &Controls - + &Controles @@ -2859,7 +3004,7 @@ introduzca su número PIN aquí: - + Play Reproducir @@ -2879,139 +3024,174 @@ introduzca su número PIN aquí: Siguiente - + + Back + + + + + Go back one page + + + + + Forward + + + + + Go forward one page + + + + Global Search... Búsqueda global... - - + + Check For Updates... Buscar actualizaciones... - - - + + + Connect To Peer Conectar a un par - + Enter peer address: Introducir dirección del par: - + Enter peer port: Introducir puerto del par: - + Enter peer key: Introducir contraseña del par: - + XSPF Error Error XSPF - + This is not a valid XSPF playlist. Esta no es una lista de reproducción XSPF válida. - + Failed to save tracks Fallo al guardar pistas - + Some tracks in the playlist do not contain an artist and a title. They will be ignored. Algunas pistas en la lista de reproducción no contienen artista ni título. Serán ignoradas. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Make sure you have a suitable Phonon backend and required plugins installed. - + Se ha producido un error al acceder al dispostivo de audio o al tema deseado y se va saltar. Asegúrese de que ha instalado un backend de Phonon adecuado y los plugins necesarios. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. + Se ha producido un error al acceder al dispostivo de audio o al tema deseado y se va saltar. + + + + Station - + Create New Station Crear nueva estación - + Name: Nombre: - - New Station - Nueva estación + + Playlist + - - New Playlist - Nueva lista de reproducción + + Automatic Playlist + - + Pause Pausar - + Go &offline &Desconectarse - + Go &online &Conectarse - + Authentication Error Error de autenticación - + + Error connecting to SIP: Authentication failed! + + + + %1 by %2 track, artist name %1 por %2 - + %1 - %2 current track, some window title %1 - %2 - + <h2><b>Tomahawk %1<br/>(%2)</h2> - + <h2><b>Tomahawk %1<br/>(%2)</h2> - + <h2><b>Tomahawk %1</h2> + <h2><b>Tomahawk %1</h2> + + + + Copyright 2010 - 2012 - - Copyright 2010 - 2012<br/>Christian Muehlhaeuser &lt;muesli@tomahawk-player.org&gt;<br/><br/>Thanks to: Leo Franchi, Jeff Mitchell, Dominik Schmidt, Jason Herskowitz, Alejandro Wainzinger, Hugo Lindstr&ouml;m, Michael Zanetti, Harald Sitter and Steve Robertson + + Thanks to: - + About Tomahawk - + Acerca de Tomahawk @@ -3019,7 +3199,7 @@ introduzca su número PIN aquí: Form - Form + Formulario @@ -3080,7 +3260,7 @@ introduzca su número PIN aquí: TopTracksContext - + Top Hits Grandes éxitos @@ -3090,141 +3270,88 @@ introduzca su número PIN aquí: Form - + Formulario Cover - + Portada Track - + Tema by - + de Artist - + Artista from - + en Album - + Álbum Statistics + Estadísticas + + + + Lyrics - + Similar Tracks + Temas Similares + + + + Sorry, but we could not find similar tracks for this song! - + You've listened to this track %n time(s). - + Ha escuchado esta canción %n vez.Ha escuchado esta canción %n veces. - + You've never listened to this track before. - + Nunca ha escuchado este tema antes. - + You first listened to it on %1. - + Escuchó este tema pro primera vez en %1. - + You've listened to %1 %n time(s). - + Ha escuchado %1 %n vez.Ha escuchado %1 %n veces. - + You've never listened to %1 before. - - - - - TrackModel - - - Artist - Artista - - - - Title - Título - - - - Album - Álbum - - - - Track - Pista - - - - Duration - Duración - - - - Bitrate - Bitrate - - - - Age - Edad - - - - Year - Año - - - - Size - Tamaño - - - - Origin - Origen - - - - Score - Puntuación - - - - Composer - + Nunca ha escuchado %1 antes. TrackView - + Sorry, your filter '%1' did not match any results. Lo siento, tu filtro '%1' no ha encontrado resultados. @@ -3245,7 +3372,7 @@ introduzca su número PIN aquí: TreeItemDelegate - + Unknown Desconocido @@ -3253,63 +3380,31 @@ introduzca su número PIN aquí: TreeModel - - Name - Nombre - - - - Duration - Duración - - - - Bitrate - Bitrate - - - - Age - Edad - - - - Year - Año - - - - Size - Tamaño - - - - Origin - Origen - - - - Composer - Compositor - - - + All Artists Todos los artistas - - + + My Collection Mi colección - - + + Collection of %1 Colección de %1 + + TreeView + + + Sorry, your filter '%1' did not match any results. + + + TwitterConfigWidget @@ -3386,29 +3481,39 @@ Puede reenviar el mensaje de sincronización en cualquier momento simplemente en ViewManager - + + After you have scanned your music collection you will find your tracks right here. + + + + + This collection is empty. + + + + SuperCollection SuperColección - + Combined libraries of all your online friends - + Bibliotecas combinadas de todos tus amigos conectados - + All available albums Todos los álbumes disponibles - + Recently Played Tracks - + Temas Escuchados Recientemente - + Recently played tracks from all your friends - + Temas escuchados recientemente por mis amigos @@ -3429,12 +3534,12 @@ Puede reenviar el mensaje de sincronización en cualquier momento simplemente en Pistas reproducidas recientemente - + No recently created playlists in your network. No hay listas de reproducción recientemente creadas en su red. - + Welcome to Tomahawk Bienvenido a Tomahawk @@ -3442,7 +3547,7 @@ Puede reenviar el mensaje de sincronización en cualquier momento simplemente en WhatsHotWidget - + Charts Listas de éxitos @@ -3507,17 +3612,17 @@ Letras de "%1" por %2: Failed to parse contents of XSPF playlist - + Error al analizar los contenidos de la lista XSPF Some playlist entries were found without artist and track name, they will be omitted - + Algunas entradas de la lista no tenía información de artista o de títul y serán omitidas Failed to fetch the desired playlist from the network, or the desired file does not exist - + Error al buscar la lista deseada en la red, o el fichero no existe @@ -3609,180 +3714,180 @@ Letras de "%1" por %2: Xmpp Configuration - + Configuración XMPP Configure this Xmpp account - + Configurar esta cuenta XMPP Enter your Xmpp login to connect with your friends using Tomahawk! - + Introduzca sus datos de inicio de sesión de XMPP para conectarse son sus amigos que usen Tomahawk! Login Information - + Información de Inicio de Sesión Xmpp ID: - + ID XMPP: e.g. user@example.com - + e.g. usuario@ejemplo.com Password: - + Contraseña: An account with this name already exists! - + Ya existe una cuenta con el mismo nombre! Advanced Xmpp Settings - + Ajustes avanzados de XMPP Server: - + Servidor: Port: - + Puerto: Lots of servers don't support this (e.g. GTalk, jabber.org) - + Muchos servidores no soporta esta opción (e.g. GTalk, jabber.org) Publish currently playing track - + Publicar el tema reproducido al momento Enforce secure connection - + Forzar conexión segura XmppSipPlugin - + User Interaction - + Interacción de usuario - + Host is unknown - + Máquina desconocida - + Item not found - + Elemento no encontrado - + Authorization Error - + Error de Autorización - + Remote Stream Error - + Error de Stream Remoto - + Remote Connection failed - + Fallo en la Conexión Remota - + Internal Server Error - + Error de Servidor Interno - + System shutdown - + Sistema apagado - + Conflict - - - - - Unknown - + Conflicto + Unknown + Desconocido + + + No Compression Support - + Compresión no sportada - + No Encryption Support - + Encriptación no soportada - + No Authorization Support - + Autorización no soportada - + No Supported Feature - + Característica no soportada - + Add Friend - + Añadir Amigo - + Enter Xmpp ID: - + Introducir ID XMPP: - + Add Friend... - + Añadir Amigo... - + XML Console... - + Consola XML... - + I'm sorry -- I'm just an automatic presence used by Tomahawk Player (http://gettomahawk.com). If you are getting this message, the person you are trying to reach is probably not signed on, so please try again later! - + Lo siento -- soy una presencia automática usada por el Reproductor Tomahawk (http://gettomahawk.com). Si recibe este mensaje, la persona con quién intenta contactar no esté contectada probablemente. Inténtelo más tarde! - + Authorize User - + Autorizar Usuario - + Do you want to grant <b>%1</b> access to your Collection? - + ¿Quiere permitir que <b>%1</b> acceda a su Colección? @@ -3790,7 +3895,7 @@ Letras de "%1" por %2: Form - Form + Formulario diff --git a/lang/tomahawk_fr.ts b/lang/tomahawk_fr.ts index 8138f2510..d51788a0b 100644 --- a/lang/tomahawk_fr.ts +++ b/lang/tomahawk_fr.ts @@ -2,17 +2,17 @@ AccountFactoryWrapper - + Dialog Dialog - + Description goes here ici la description - + Add Account Ajouter un compte @@ -20,17 +20,17 @@ AccountFactoryWrapperDelegate - + Online En Ligne - + Connecting... Connexion en cours... - + Offline Hors ligne @@ -38,84 +38,85 @@ AclJobDelegate - + Error displaying ACL info - + Erreur d'affichage des infos ACL - - + + Allow %1 to connect and stream from you? - + Autoriser %1 à +se connecter et streamer depuis chez vous ? ActionCollection - + &Listen Along &Ecouter avec - + Stop &Listening Along Arrêter d'&écouter avec - + &Follow in real-time &Suivre en temps réel - - + + &Listen Privately &Ecouter en privé - - + + &Listen Publicly &Ecouter publiquement - + &Load Playlist &Charger une liste de lecture - + &Rename Playlist &Renommer la liste de lecture - + &Copy Playlist Link &Copier le lien de la piste de lecture - + &Play &Lire - + &Stop &Stop - + &Previous Track Piste &Précédente - + &Next Track Piste &Suivante - + &Quit &Quitter @@ -133,66 +134,35 @@ connect and stream from you? Tous les albums par cet artiste - - - Click to show Official Tracks - Cliquer pour afficher les pistes officielles + + Sorry, we could not find any other albums for this artist! + Désolé, aucun autre album n'a pu être trouvé pour cet artiste ! - - - Click to show SuperCollection Tracks - Cliquer pour afficher les pistes de la SuperCollectio, + + Sorry, we could not find any tracks for this album! + Désolé, on a pas pu trouver aucune pistes pour cet album! - - - Click to show SuperCollection Albums - Cliquer pour afficher les albums de la SuperCollection - - - - Click to show Official Albums - Cliquer pour afficher les albums officiels - - - + Other Albums by %1 - Autres albums par %1 + Autres albums de %1 AlbumModel - - Album - Album - - - - + + All albums from %1 Tous les albums de %1 - + All albums Tous les albums - - AlbumView - - - After you have scanned your music collection you will find your latest album additions right here. - Après avoir scanné votre collection musicale, vous trouverez les derniers albums ajoutés ici. - - - - This collection doesn't have any recent albums. - Cette collection n'a pas d'albums récents - - ArtistInfoWidget @@ -211,38 +181,24 @@ connect and stream from you? Artistes similaires - + Albums Albums - - - Click to show SuperCollection Albums - Cliquer pour afficher les albums de la SuperCollection + + Sorry, we could not find any albums for this artist! + Désolé, on a pas pu trouver aucun album pour cet artiste! - - Click to show Official Albums - Cliquer pour afficher les albums officiels - - - - ArtistView - - - After you have scanned your music collection you will find your tracks right here. - Après avoir scanné votre collection musicale, vous trouverez vos pistes ici. + + Sorry, we could not find any related artists! + Désolé, on a rien trouvé par rapport a cet artite! - - This collection is currently empty. - La collection est vide actuellement. - - - - Sorry, your filter '%1' did not match any results. - Désolé, votre filtre '%1' ne correspond à aucun résultat + + Sorry, we could not find any top hits for this artist! + Désolé, on a pas pu trouver aucun top hit pour cet artiste! @@ -285,7 +241,7 @@ connect and stream from you? social - + social @@ -326,23 +282,26 @@ connect and stream from you? CategoryAddItem - - - New Playlist - Nouvelle liste de lecture + + Create new Playlist + Créer une nouvelle liste de lecture - - - - + + Create new Station + Créer une nouvelle station + + + + + New Station Nouvelle Station - - - + + + %1 Station Station %1 @@ -368,27 +327,6 @@ connect and stream from you? Vider - - CollectionFlatModel - - - My Collection - Ma Collection - - - - Collection of %1 - Collection de %1 - - - - CollectionView - - - This collection is empty. - La collection est vide. - - ContextWidget @@ -398,12 +336,12 @@ connect and stream from you? - + Show Footnotes Afficher les notes - + Hide Footnotes Masquer les notes @@ -415,41 +353,51 @@ connect and stream from you? Tomahawk Crash Reporter Tomahawk Crash Reporter - + - <p><b>Sorry!</b>&nbsp;Tomahawk crashed. Information about the crash is now being sent to Tomahawk HQ so that we can fix the bug.</p> - <p><b>Désolé !</b>&nbsp;Tomahawk a planté. Les informations du plantage sont maintenant envoyés au siège de Tomahawk pour que nous puissions corriger le bug.</p> + <html><head/><body><p><span style=" font-weight:600;">Sorry!</span> Tomahawk crashed. Please tell us about it! Tomahawk has created an error report for you that can help improve the stability in the future. You can now send this report directly to the Tomahawk developers.</p></body></html> + <html><head/><body><p><span style=" font-weight:600;">Désolé !</span>&nbsp;Tomahawk a planté. Dites-nous ce qui ne va pas ! Tomahawk a créé un rapport d'erreur qui sera utile pour améliorer la stabilité des prochaines versions. Vous pouvez dès maintenant envoyer ce rapport aux développeurs de Tomahawk.</p></body></html> - + + Send this report + Envoyer le rapport + + + + Don't send + Ne pas envoyer + + + Abort Abandonner - + You can disable sending crash reports in the configuration dialog. Vous pouvez désactiver l'envoi des rapports de plantage dans la boite de dialogue de configuration. - + Uploaded %L1 of %L2 KB. Chargement %L1 de %L2 ko. - - + + Close Fermer - + Sent! <b>Many thanks</b>. Envoyé ! <b>Merci beaucoup</b>. - + Failed to send crash info. - Échec de l'envoi des informations de plantage + Échec de l'envoi des informations de plantage. @@ -463,19 +411,19 @@ connect and stream from you? DelegateConfigWrapper - + About - + A propos - + Delete Account Supprimer le compte - + About this Account - + A propos de ce compte @@ -486,24 +434,11 @@ connect and stream from you? Diagnostics de Tomahawk - - Update - Mettre à jour - - - + Copy to Clipboard Copier dans le presse papier - - DropJob - - - No tracks found for given %1 - Aucune piste trouvée pour %1 - - GlobalSearchWidget @@ -528,29 +463,11 @@ connect and stream from you? Barre d'information - + Filter... Filtre... - - JobStatusView - - - Searching For - Recherche de - - - - Pending - En suspens - - - - Idle - Pas d'activité - - LastFmConfig @@ -561,7 +478,7 @@ connect and stream from you? Scrobble tracks to Last.fm - Scrobbler les pistes sur Last.fm + Scrobbler les titres sur Last.fm @@ -578,6 +495,11 @@ connect and stream from you? Test Login Login test + + + Import Playback History + Importer l'historique + LastfmContext @@ -592,7 +514,7 @@ connect and stream from you? %1 is listening along to you! - %1 écoute avec vous + %1 écoute avec vous! @@ -626,12 +548,12 @@ connect and stream from you? LoadXSPFDialog - + Load XSPF File Charger un fichier XSPF - + XSPF Files (*.xspf) Fichiers XSPF (*.xspf) @@ -641,12 +563,12 @@ connect and stream from you? Bookmarks - + Signets Saved tracks - Pistes sauvegardés + Titres sauvegardés @@ -654,7 +576,7 @@ connect and stream from you? Enter a title for the new playlist: - Entrer un titre pour la nouvelle liste de lecture + Entrer un titre pour la nouvelle liste de lecture: @@ -680,20 +602,89 @@ connect and stream from you? NewReleasesWidget - + New Releases Nouveautés + + PlayableModel + + + Artist + Artiste + + + + Title + Titre + + + + Composer + Compositeur + + + + Album + Album + + + + Track + Piste + + + + Duration + Durée + + + + Bitrate + Bitrate + + + + Age + Age + + + + Year + Année + + + + Size + Taille + + + + Origin + Origine + + + + Score + Score + + + + + Name + Nom + + PlaylistItemDelegate - + played %1 by you joué %1 par vous - + played %1 by %2 joué %1 par %2 @@ -701,42 +692,42 @@ connect and stream from you? PlaylistLargeItemDelegate - + played %1 by you joué %1 par vous - + played %1 by %2 joué %1 par %2 - + added %1 - + ajouté par %1 PlaylistModel - + A playlist by %1, created %2 Une liste de lecture par %1, créée %2 - + you vous All tracks by %1 on album %2 - Toutes les pistes par %1 sur l'album %2 + Tous les titres de %1 sur l'album %2 All tracks by %1 - Toutes les pistes par %1 + Tous les titres de %1 @@ -749,12 +740,12 @@ connect and stream from you? Just a regular old playlist... Give it a name, drag in some tracks, and go! - Juste une liste de lecture normale... Donnez lui un nom, faites glisser quelques pistes et c'est parti ! + Une liste de lecture toute simple... Donnez lui un nom, faites glisser quelques pistes et c'est parti ! Don't know exactly what you want? Give Tomahawk a few pointers and let it build a playlist for you! - Vous ne savez pas ce que vous voulez exactement ? Donnez quelques idées à Tomahawk et laissez le créer une liste de lecture pour vous. + Vous ne savez pas ce que vous voulez exactement ? Donnez quelques idées à Tomahawk et laissez le créer une liste de lecture pour vous ! @@ -769,18 +760,23 @@ connect and stream from you? Create Manual Playlist - Créer une liste de lecture manuellement + Liste de lecture manuelle Create Automatic Playlist - Créer une liste de lecture automatiquement + Liste de lecture automatique PlaylistView - + + This playlist is currently empty. + La collection est vide actuellement. + + + This playlist is currently empty. Add some tracks to it and enjoy the music! La liste de lecture est vide. Ajoutez des pistes et profitez de la musique ! @@ -848,84 +844,84 @@ connect and stream from you? QObject - + %n year(s) ago il y a %n anil y a %n ans - + %n year(s) %n an%n ans - + %n month(s) ago il y a %n moisil y a %n mois - + %n month(s) %n mois%n mois - + %n week(s) ago il y a %n semaineil y a %n semaines - + %n week(s) %n semaine%n semaines - + %n day(s) ago il y a %n jouril y a %n jours - + %n day(s) %n jour%n jours - + %n hour(s) ago il y a %n heureil y a %n heures - + %n hour(s) %n heure%n heures - + %1 minutes ago il y a %1 minutes - + %1 minutes %1 minutes - + just now à l'instant - - - Friend Finders - - - Music Finders - + Friend Finders + Rechercher vos amis - + + Music Finders + Trouver de la musique + + + Status Updaters - + Mettre à jour votre statut @@ -933,7 +929,7 @@ connect and stream from you? ZIP/UNZIP API error %1 - + Erreur %1 ZIP/UNZIP API @@ -945,22 +941,32 @@ connect and stream from you? - - Show Queue - Afficher la file d'attente + + Open Queue + Ouvrir la file d'attente - - Hide Queue - Masquer la file d'attente + + The queue is currently empty. Drop something to enqueue it! + Le queue est vide actuellement. Laisse tomber quelque chose pour l'ajouter! + + + + Open Queue - %n item(s) + Ouvrir la file d'attente - %n élémentOuvrir la file d'attente - %n éléments + + + + Close Queue + Fermer la file d'attente RelatedArtistsContext - + Related Artists - + Artistes similaires @@ -968,7 +974,7 @@ connect and stream from you? Not found: %1 - + Non trouvé : %1 @@ -1000,37 +1006,37 @@ connect and stream from you? SettingsDialog - + Collection Collection - + Advanced Avancés - + All Tous - + Some changed settings will not take effect until Tomahawk is restarted - + Certaines modifications ne prendront effet qu'au prochain démarrage de Tomahawk - + Services Services - + Install resolver from file - + Installer un script de résolution depuis un fichier - + Information Information @@ -1050,7 +1056,7 @@ connect and stream from you? Most Played Tracks You Don't Have - Pistes les plus joués que vous n'avez pas + Titres les plus joués que vous n'avez pas @@ -1073,7 +1079,7 @@ connect and stream from you? Cover - + Pochette @@ -1081,17 +1087,17 @@ connect and stream from you? TextLabel - + Listening to "%1" by %2 and loving it! %3 - + J'écoute "%1" par %2 et j'adore ! %3 - + Listening to "%1" by %2 on "%3" and loving it! %4 J'écoute "%1" par %2 sur "%3" et j'adore ! %4 - + %1 characters left %1 caractères restants @@ -1124,29 +1130,19 @@ connect and stream from you? Top 10 - - Offline - Hors ligne - - - + All available tracks - Toutes les pistes disponibles + Tous les titres disponibles - - Online - En Ligne - - - - + + Show Afficher - - + + Hide Masquer @@ -1166,20 +1162,20 @@ connect and stream from you? Recently Played Tracks - Dernières pistes jouées + Dernières titres joués - + New Additions Nouveaux ajouts - + My recent activity Mon activité récente - + Recent activity from %1 Activité récente de %1 @@ -1187,81 +1183,138 @@ connect and stream from you? SourceItem - + Collection Collection - - + + Latest Additions Derniers ajouts - + Recently Played Joués récemment - + Loved Tracks - + Titres favoris - + SuperCollection SuperCollection - - Latest additions to your collection - + + Sorry, we could not find any loved tracks! + Désolé, on a pas pu trouver aucune piste favoris! - + + Latest additions to your collection + Derniers ajouts à votre collection + + + Latest additions to %1's collection - + Derniers ajouts à la collection de %1 + + + + Sorry, we could not find any recent additions! + Désolé, on a pas pu trouver des dernier ajouts! Recently Played Tracks - + Derniers titres joués Your recently played tracks - + Les derniers titres que vous avez joués %1's recently played tracks - + Derniers titres joués par %1 + + + + Sorry, we could not find any recent plays! + Désolé, on a pas pu trouver aucune piste récement joués! SourceTreeView - + &Copy Link &Copier le lien - + &Delete %1 &Supprimer %1 + + + Add to my Playlists + Ajoute a mes listes de lecture + + + + Add to my Automatic Playlists + Ajoute a mes listes de lecture automatique + + + + Add to my Stations + Ajouter à mes stations + &Export Playlist &Exporter la liste de lecture - + + playlist + liste de lecture + + + + automatic playlist + liste de lecture automatique + + + + station + station + + + + Delete %1? + playlist/station/... + Supprimer %1? + + + + Would you like to delete the %1 <b>"%2"</b>? + e.g. Would you like to delete the playlist named Foobar? + Voulez-vous supprimer la %1 <b>"%2"</b>? + + + Save XSPF Enregistrer XSPF - + Playlists (*.xspf) Listes de lecture (*.xspf) @@ -1316,7 +1369,7 @@ connect and stream from you? Top Loved Tracks - + Top des titres favoris @@ -1354,37 +1407,37 @@ connect and stream from you? Configure your Spotify account - + Configurer votre compte Spotify Username or Facebook Email - + Nom d'utilisateur ou email Facebook Log In - + Connexion Right click on any Tomahawk playlist to sync it to Spotify. - + Clic droit pour synchroniser une liste de lecture Tomahawk vers Spotify. High Quality Streams - + Streaming haute qualité Spotify playlists to keep in sync: - + Liste de lecture Spotify à synchroniser : Delete Tomahawk playlist when removing synchronization - + Supprimer la liste de lecture de Tomahawk à la suppression de la synchronisation @@ -1402,12 +1455,12 @@ connect and stream from you? Delete in Spotify? - + Supprimer dans Spotify ? Would you like to delete the corresponding Spotify playlist as well? - + Voulez-vous aussi supprimer la liste de lecture correspondante dans Spotify ? @@ -1420,7 +1473,7 @@ connect and stream from you? Local Music Information - + Fichiers de musique locaux @@ -1434,9 +1487,9 @@ connect and stream from you? will allow you (and all your friends) to create automatic playlists and stations based on your personal taste profile. The Echo Nest peut garder les métadonnées de votre catalogue -et l'utiliser pour créer des radios personnalisées. En activant cette option +et les utiliser pour créer des radios personnalisées. En activant cette option vous (et vos amis) pourrez créer des listes de lecture automatiquement -et des stations basés sur vos goûts. +et des stations basées sur vos goûts. @@ -1456,42 +1509,42 @@ et des stations basés sur vos goûts. Advanced Settings - + Paramètres avancés Remote Peer Connection Method - + Méthode de connexion distante à un pair None (outgoing connections only) - + Aucune (Connexions sortantes uniquement) Use UPnP to establish port forward (recommended) - + Utiliser UPnP pour la redirection des ports (recommandé) Use static external IP address/host name and port - + Utiliser une adresse IP externe statique/nom d'hôte et un port Set this to your external IP address or host name. Make sure to forward the port to this host! - + Indiquez votre adresse IP externe ou nom d'hôte. Vérifiez que le port est bien redirigé vers cet hôte ! SOCKS Proxy - + Proxy SOCKS Use SOCKS Proxy - + Utiliser un proxy SOCKS @@ -1506,7 +1559,7 @@ et des stations basés sur vos goûts. Filter by capability: - + Filtrer par fonctionnalité : @@ -1526,52 +1579,65 @@ et des stations basés sur vos goûts. Other Settings - + Autres paramètres Send reports after Tomahawk crashed - Envoyer un rapport apès un crash de Tomahawk + Envoyer un rapport après un crash de Tomahawk Allow web browsers to interact with Tomahawk (recommended) - Permettrre aux navigateurs web pour intéragir avec Tomahawk (recommandé) + Permettre aux navigateurs web d’interagir avec Tomahawk (recommandé) Tomahawk::Accounts::AccountDelegate - + Add Account Ajouter un compte - + Remove Account Supprimer le compte - + %1 downloads %1 téléchargements - + Online En Ligne - + Connecting... Connexion en cours... - + Offline Hors ligne + + Tomahawk::Accounts::AccountModel + + + Manual Install Required + Installation manuelle requise + + + + Unfortunately, automatic installation of this resolver is not available or disabled for your platform.<br /><br />Please use "Install from file" above, by fetching it from your distribution or compiling it yourself. Further instructions can be found here:<br /><br />http://www.tomahawk-player.org/resolvers/%1 + Malheureusement, l'installation automatique de ce script de résolution n'est pas disponible ou a été désactivé sur votre plateforme.<br /><br />Utiliser "Installer depuis un fichier" ci-dessus et téléchargez le fichier pour votre distribution, ou compilez-le. D'autres instructions sont disponibles ici :<br /><br />http://www.tomahawk-player.org/resolvers/%1 + + Tomahawk::Accounts::GoogleWrapper @@ -1627,23 +1693,49 @@ et des stations basés sur vos goûts. Tomahawk::Accounts::LastFmConfig - - Test Login - + + Testing... + Test... - - + + Test Login + Test de connexion + + + + Importing %1 + e.g. Importing 2012/01/01 + Importation %1 + + + + Importing History... + Import de l'historique... + + + + History Incomplete. Resume + Historique incomplet. Reprendre + + + + Playback History Imported + Historique de lecture importé + + + + Failed Échec - + Success Succès - + Could not contact server Impossible de contacter le serveur @@ -1651,43 +1743,48 @@ et des stations basés sur vos goûts. Tomahawk::Accounts::SpotifyAccount - + Sync with Spotify - + Synchroniser avec Spotify - + + Re-enable syncing with Spotify + Réactiver la synchronisation avec Spotify + + + Stop syncing with Spotify - + Stopper la synchronisation avec Spotify Tomahawk::Accounts::SpotifyAccountConfig - + Logging in... - + Connexion... - + Logged in! - + Connecté ! - + Failed: %1 - + Echec : %1 - + Log In - + Connexion Tomahawk::Accounts::SpotifyAccountFactory - + Play music from and sync your playlists with Spotify Premium Jouer la musique et synchroniser vos listes avec Spotify Premium @@ -1697,134 +1794,134 @@ et des stations basés sur vos goûts. Connect to your Twitter followers. - Se connecter à votre followers sur Twitter + Se connecter à vos followers sur Twitter. Tomahawk::Accounts::TwitterConfigWidget - - - + + + Tweet! Tweet! - - + + Status: No saved credentials Statut : Pas de compte enregistré - - - + + + Authenticate S'authentifier - - + + Status: Credentials saved for %1 Statut : Compte enregistré pour %1 - - + + De-authenticate Se déconnecter - - - - - - - + + + + + + + Tweetin' Error Erreur de Tweet - + The credentials could not be verified. You may wish to try re-authenticating. Votre compte n'a pas pu être vérifié. Essayer de vous authentifier de nouveau. - + Status: Error validating credentials Statut : Erreur de validation de votre compte - + Global Tweet Tweet Global - + Direct Message Message direct - + Send Message! Envoyer le message ! - + @Mention @Mention - + Send Mention! Envoyer la mention ! - + You must enter a user name for this type of tweet. Vous devez saisir un nom d'utilisateur pour ce type de tweet. - + Your saved credentials could not be loaded. You may wish to try re-authenticating. Les informations de votre compte ne peuvent être chargées. Essayez de vous authentifier de nouveau. - + Your saved credentials could not be verified. You may wish to try re-authenticating. Les informations de votre compte ne peuvent être vérifiées. Essayez de vous authentifier de nouveau. - - + + There was an error posting your status -- sorry! Une erreur s'est produite en postant votre statut -- Désolé ! - - + + Tweeted! Tweeté ! - + Your tweet has been posted! Votre tweet a été posté ! - + There was an error posting your direct message -- sorry! Une erreur s'est produit pendant l'envoi de votre message direct ! - + Your message has been posted! Votre message a été posté ! @@ -1834,13 +1931,13 @@ Essayez de vous authentifier de nouveau. Log on to your Jabber/XMPP account to connect to your friends - Connectez vous à votre compte Jabber/XMPP pour vous connecter avec vos amis + Connectez vous à votre compte Jabber/XMPP pour retrouver vos amis Tomahawk::Accounts::ZeroconfFactory - + Automatically connect to Tomahawks on the local network Se connecter automatiquement aux Tomahawks sur le réseau local @@ -1848,22 +1945,22 @@ Essayez de vous authentifier de nouveau. Tomahawk::ContextMenu - + &Play &Lecture - - - + + + Add to &Queue &Ajouter à la file d'attente - - + + &Love - + &Favori @@ -1871,39 +1968,44 @@ Essayez de vous authentifier de nouveau. &Copier le lien de la piste - - Show &Album page - Afficher la page de l'&album - - - - Show &Artist page - Afficher la page de l'&artiste - - - + Un-&Love - + &Supprimer des Favoris - + &Delete Items - + &Supprimer les éléments - + &Continue Playback after this Track &Continuer la lecture après cette piste - + &Stop Playback after this Track &Stopper la lecture après cette piste - + + &Show Track Page + &Afficher la page de la piste + + + &Delete Item - + &Supprimer l'élément + + + + &Show Album Page + &Afficher la page de l'album + + + + &Show Artist Page + &Afficher la page artiste @@ -1911,43 +2013,63 @@ Essayez de vous authentifier de nouveau. Top Loved Tracks - + Top des titres favoris Your loved tracks - + Vos titres favoris %1's loved tracks - + Titres favoris de %1 The most loved tracks from all your friends - + Les titres favoris de vos amis All of your loved tracks - + Tous vos titres favoris All of %1's loved tracks - + Tous les titres favoris de %1 Tomahawk::DropJobNotifier - + + playlist + liste de lecture + + + + artist + artiste + + + + track + titre + + + + album + album + + + Fetching %1 from database Chargement de %1 depuis la base de données - + Parsing %1 %2 Décodage %1 %2 @@ -1955,7 +2077,7 @@ Essayez de vous authentifier de nouveau. Tomahawk::DynamicControlList - + Click to collapse Cliquez pour masquer @@ -1994,17 +2116,17 @@ Veuillez changer les filtres et essayez de nouveau. Tomahawk::DynamicView - + Add some filters above to seed this station! Ajoutez des filtres ci dessus pour commencer une station ! - + Press Generate to get started! Appuyez sur Générer pour commencer ! - + Add some filters above, and press Generate to get started! Ajoutez des filtres ci dessus et appuyez sur "Générer" pour commencer ! @@ -2012,11 +2134,11 @@ Veuillez changer les filtres et essayez de nouveau. Tomahawk::DynamicWidget - + Station ran out of tracks! Try tweaking the filters for a new set of songs to play. - Il n'y a plus de pistes dans la station ! + Il n'y a plus de chansons dans la station ! Essayez de changer les filtres pour avoir de nouveaux morceaux à jouer. @@ -2042,7 +2164,7 @@ Essayez de changer les filtres pour avoir de nouveaux morceaux à jouer. No users with Echo Nest Catalogs enabled. Try enabling option in Collection settings - Aucun utilisateur avec un Catalogue The Echo Nest actif. Essayez d'activer l'option dans les préférences de Collection. + Aucun utilisateur avec un Catalogue The Echo Nest actif. Essayez d'activer l'option dans les préférences de Collection @@ -2244,23 +2366,23 @@ Essayez de changer les filtres pour avoir de nouveaux morceaux à jouer. only by ~%1 - + uniquement par ~%1 similar to ~%1 - + similaire à ~%1 with genre ~%1 - + avec le genre ~%1 from no one - + de personne @@ -2270,12 +2392,12 @@ Essayez de changer les filtres pour avoir de nouveaux morceaux à jouer. from %1 radio - + de la radio %1 with %1 %2 - + avec %1 %2 @@ -2295,128 +2417,128 @@ Essayez de changer les filtres pour avoir de nouveaux morceaux à jouer. at around %1%2 %3 - + à environ %1%2 %3 in %1 - + dans %1 in a %1 key - + en tonalité %1 sorted in %1 %2 order - + trié par ordre %1 %2 with a %1 mood - + avec une ambiance %1 in a %1 style - + ayant un style %1 Tomahawk::EchonestSteerer - + Steer this station: - Direction de cette station : + Modifier cette station : - + Much less Beaucoup moins - + Less Moins - + A bit less Un peu moins - + Keep at current Garder le même - + A bit more Un peu plus - + More Plus - + Much more Beaucoup plus - + Tempo Tempo - + Loudness Intensité - + Danceability Dansabilité - + Energy Energie - + Song Hotttnesss Hotttnesss du morceau - + Artist Hotttnesss Hotttnesss de l'artiste - + Artist Familiarity Familiarité de l'artiste - + By Description Par description - + Enter a description Entrer une description - + Apply steering command Appliquer la commande de direction - + Reset all steering commands Annuler les commandes de direction @@ -2432,22 +2554,22 @@ Essayez de changer les filtres pour avoir de nouveaux morceaux à jouer. Tomahawk::InfoSystem::ChartsPlugin - + Top Overall Top global - + Artists Artistes - + Albums Albums - + Tracks Pistes @@ -2455,48 +2577,48 @@ Essayez de changer les filtres pour avoir de nouveaux morceaux à jouer. Tomahawk::InfoSystem::FdoNotifyPlugin - + Tomahawk is playing "%1" by %2%3. Tomahawk joue "%1" par %2%3. - + on "%1" - + sur "%1" Tomahawk::InfoSystem::LastFmInfoPlugin - + Top Tracks - + Top des titres - + Loved Tracks - + Titres favoris - + Hyped Tracks - + Titres Hype - + Top Artists - + Top Artistes - + Hyped Artists - + Artistes Hype Tomahawk::InfoSystem::NewReleasesPlugin - + Albums Albums @@ -2504,7 +2626,7 @@ Essayez de changer les filtres pour avoir de nouveaux morceaux à jouer. Tomahawk::InfoSystem::TwitterInfoPlugin - + Listening to "%1" by %2 and loving it! %3 J'écoute "%1" par %2 et j'adore ! %3 @@ -2514,7 +2636,7 @@ Essayez de changer les filtres pour avoir de nouveaux morceaux à jouer. Error fetching iTunes information from the network! - Échec du chargement des informations iTunes depuis le réseau! + Échec du chargement des informations iTunes depuis le réseau ! @@ -2562,39 +2684,39 @@ Essayez de changer les filtres pour avoir de nouveaux morceaux à jouer. Tomahawk::Query - + and et - + You Vous - + you vous - + and et - + %n other(s) %n autre%n autres - + %1 people %1 personnes - + loved this track - + a enregistré cette piste dans ses favoris @@ -2608,7 +2730,7 @@ Essayez de changer les filtres pour avoir de nouveaux morceaux à jouer. Tomahawk::ShortenedLinkParser - + Network error parsing shortened link! Erreur réseau lors du décodage de l'URL courte! @@ -2616,36 +2738,46 @@ Essayez de changer les filtres pour avoir de nouveaux morceaux à jouer. Tomahawk::Source - - + + Scanning (%L1 tracks) - Scan en cours (%L1 pistes) + Scan en cours (%L1 titres) - + Scanning Scan en cours - + Checking Vérification - + Fetching Chargement - + Parsing Décodage - + Saving (%1%) Enregistrement (%1%) + + + Online + En Ligne + + + + Offline + Hors ligne + Tomahawk::SpotifyParser @@ -2658,15 +2790,15 @@ Essayez de changer les filtres pour avoir de nouveaux morceaux à jouer. Tomahawk::XspfUpdater - + Automatically update from XSPF - + Mise à jour automatique XSPF TomahawkApp - + My Collection Ma Collection @@ -2676,54 +2808,63 @@ Essayez de changer les filtres pour avoir de nouveaux morceaux à jouer. Twitter PIN - + Identification Twitter After authenticating on Twitter's web site, enter the displayed PIN number here: - Après vous être authentifier sur le site de Twitter, + Après vous être authentifié sur le site de Twitter, saisissez le numéro PIN ici : TomahawkTrayIcon - - + &Stop Playback after current Track - + &Stopper la lecture après cette piste - - + + Hide Tomahawk Window Masquer la fenêtre Tomahawk - + Show Tomahawk Window Afficher la fenêtre Tomahawk - + Currently not playing. - Pas de lecture en cours + Pas de lecture en cours. - + Play Lecture - + Pause Pause - + + &Love + &Favori + + + + Un-&Love + &Supprimer des Favoris + + + &Continue Playback after current Track - + &Continuer la lecture après cette piste @@ -2796,7 +2937,7 @@ saisissez le numéro PIN ici : Load &XSPF... - Charger &XSPF + Charger &XSPF... @@ -2806,12 +2947,12 @@ saisissez le numéro PIN ici : About &Tomahawk... - A propos de &Tomahawk + A propos de &Tomahawk... Create New &Automatic Playlist - Créer une nouvelle liste de lecture automatique + Créer une nouvelle liste de lecture &automatique @@ -2865,7 +3006,7 @@ saisissez le numéro PIN ici : - + Play Lecture @@ -2885,139 +3026,174 @@ saisissez le numéro PIN ici : Suivant - + + Back + Retour + + + + Go back one page + Reculer d'une page + + + + Forward + Avancer + + + + Go forward one page + Avancer d'une page + + + Global Search... Recherche Globale... - - + + Check For Updates... Rechercher une mise à jour... - - - + + + Connect To Peer - + Connexion à un pair - + Enter peer address: - + Adresse du pair : - + Enter peer port: - + Port du pair : - + Enter peer key: - + Clé du pair : - + XSPF Error Erreur XSPF - + This is not a valid XSPF playlist. Ceci n'est pas une liste de lecture XSPF valide. - + Failed to save tracks Échec de la sauvegarde des pistes - + Some tracks in the playlist do not contain an artist and a title. They will be ignored. Certaines pistes dans la liste de lecture ne contiennent pas d'artiste ou de titre. Elles seront ignorées. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Make sure you have a suitable Phonon backend and required plugins installed. - Désolé, il y a un problème d'accès à votre matériel audio ou la piste en cours va être sauter. Vérifiez que vous avez un backend Phonon et les plugins requis installés. + Désolé, il y a un problème d'accès à votre matériel audio ou la piste en cours va être sautée. Vérifiez que vous avez un backend Phonon et les plugins requis installés. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Désolé, il y a un problème d'accès à votre matériel audio ou la piste en cours va être sauter. - + + Station + Station + + + Create New Station Créer une nouvelle station - + Name: Nom : - - New Station - Nouvelle station + + Playlist + Liste de lecture - - New Playlist - Nouvelle liste de lecture + + Automatic Playlist + Liste de lecture automatique - + Pause Pause - + Go &offline Se &déconnecter - + Go &online Se c&onnecter - + Authentication Error Erreur d'authentification - + + Error connecting to SIP: Authentication failed! + Erreur de connexion SIP : échec de l'authentification ! + + + %1 by %2 track, artist name %1 par %2 - + %1 - %2 current track, some window title %1 - %2 - + <h2><b>Tomahawk %1<br/>(%2)</h2> - + <h2><b>Tomahawk %1<br/>(%2)</h2> - + <h2><b>Tomahawk %1</h2> - + <h2><b>Tomahawk %1</h2> - - Copyright 2010 - 2012<br/>Christian Muehlhaeuser &lt;muesli@tomahawk-player.org&gt;<br/><br/>Thanks to: Leo Franchi, Jeff Mitchell, Dominik Schmidt, Jason Herskowitz, Alejandro Wainzinger, Hugo Lindstr&ouml;m, Michael Zanetti, Harald Sitter and Steve Robertson - + + Copyright 2010 - 2012 + Droit d'auteur 2010 - 2012 - + + Thanks to: + Merci a: + + + About Tomahawk - + A propos de Tomahawk @@ -3065,12 +3241,12 @@ saisissez le numéro PIN ici : Artist View - + Vue artiste Flat View - + Vue fixe @@ -3086,9 +3262,9 @@ saisissez le numéro PIN ici : TopTracksContext - + Top Hits - + Top Hits @@ -3096,143 +3272,90 @@ saisissez le numéro PIN ici : Form - + Form Cover - + Pochette Track - + Piste by - + par Artist - + Artiste from - + de Album - + Album Statistics - + Statistiques - + + Lyrics + Paroles + + + Similar Tracks - + Piste similaire + + + + Sorry, but we could not find similar tracks for this song! + Désolé, on a pas pu trouver aucune pistes pour cette chanson! - + You've listened to this track %n time(s). - + Vous avez écouté cette piste %n fois.Vous avez écouté cette piste %n fois. - + You've never listened to this track before. - + Vous n'avez encore jamais écouté cette piste. - + You first listened to it on %1. - + Vous l'avez écouté pour la première fois le %1. - + You've listened to %1 %n time(s). - + Vous avez écouté %1 %n fois.Vous avez écouté %1 %n fois. - + You've never listened to %1 before. - - - - - TrackModel - - - Artist - Artiste - - - - Title - Titre - - - - Album - Album - - - - Track - Piste - - - - Duration - Durée - - - - Bitrate - - - - - Age - Age - - - - Year - Année - - - - Size - Taille - - - - Origin - Origine - - - - Score - Score - - - - Composer - Compositeur + Vous n'avez encore jamais écouté %1. TrackView - + Sorry, your filter '%1' did not match any results. - Désolé, votre filtre '%1' ne correspond à aucun résultat + Désolé, votre filtre '%1' ne correspond à aucun résultat. @@ -3240,18 +3363,18 @@ saisissez le numéro PIN ici : from - + de to - + à TreeItemDelegate - + Unknown Inconnu @@ -3259,63 +3382,31 @@ saisissez le numéro PIN ici : TreeModel - - Name - Nom - - - - Duration - Durée - - - - Bitrate - - - - - Age - Age - - - - Year - Année - - - - Size - Taille - - - - Origin - Origine - - - - Composer - Compositeur - - - + All Artists Tous les artistes - - + + My Collection Ma Collection - - + + Collection of %1 Collection de %1 + + TreeView + + + Sorry, your filter '%1' did not match any results. + Désolé, votre filtre '%1' ne correspond à aucun résultat. + + TwitterConfigWidget @@ -3326,7 +3417,7 @@ saisissez le numéro PIN ici : The Twitter plugin allows you to discover and play music from your Twitter friends running Tomahawk and post messages to your account. - Le plugin Twitter vous permet de découvrir et jouer de la musique de vos amis Twitter qui utilisent Tomahawk et de poster des messages sur votre compte. + Le plugin Twitter vous permet de découvrir et jouer la musique de vos amis Twitter utilisant Tomahawk et de publier des messages sur votre compte. @@ -3392,29 +3483,39 @@ Vous pouvez envoyer un message de synchronisation quand vous le souhaitez en env ViewManager - + + After you have scanned your music collection you will find your tracks right here. + Après avoir scanné votre collection musicale, vous trouverez tous vos titres ici. + + + + This collection is empty. + La collection est vide. + + + SuperCollection SuperCollection - + Combined libraries of all your online friends Collections regroupant toutes celles de vos amis en ligne - + All available albums Tous les albums disponibles - + Recently Played Tracks - + Derniers titres joués - + Recently played tracks from all your friends - + Derniers titres joués par vos amis @@ -3427,7 +3528,7 @@ Vous pouvez envoyer un message de synchronisation quand vous le souhaitez en env Newest Stations & Playlists - Dernières stations & listes de lecture + Dernières stations et listes de lecture @@ -3435,12 +3536,12 @@ Vous pouvez envoyer un message de synchronisation quand vous le souhaitez en env Joués récemment - + No recently created playlists in your network. Pas de liste de lecture créée récemment sur votre réseau. - + Welcome to Tomahawk Bienvenue dans Tomahawk @@ -3448,7 +3549,7 @@ Vous pouvez envoyer un message de synchronisation quand vous le souhaitez en env WhatsHotWidget - + Charts Charts @@ -3468,26 +3569,32 @@ Vous pouvez envoyer un message de synchronisation quand vous le souhaitez en env Terms for %1: - + +Résultats pour %1 : + No terms found, sorry. - + Aucun terme trouvé, désolé. Hotttness for %1: %2 - + +Hotttness pour %1 : %2 + Familiarity for %1: %2 - + +Familiarité pour %1 : %2 + @@ -3496,7 +3603,11 @@ Lyrics for "%1" by %2: %3 - + +Paroles de "%1" par %2 : + +%3 + @@ -3509,7 +3620,7 @@ Lyrics for "%1" by %2: Some playlist entries were found without artist and track name, they will be omitted - Certaines entrées de la liste de lecture n'ont pas d'artiste ou de titre, elles seront omises. + Certaines entrées de la liste de lecture n'ont pas d'artiste ou de titre, elles seront omises @@ -3527,7 +3638,7 @@ Lyrics for "%1" by %2: Xml stream console - + Console stream xml @@ -3538,17 +3649,17 @@ Lyrics for "%1" by %2: Save log - + Sauvegarder le journal Disabled - + Désactivé By JID - + Par JID @@ -3558,17 +3669,17 @@ Lyrics for "%1" by %2: By all attributes - + Par tous les attributs Visible stanzas - + Strophes visibles Information query - + Requête d'information @@ -3621,7 +3732,7 @@ Lyrics for "%1" by %2: Login Information - Information de login + Informations de connexion @@ -3677,107 +3788,107 @@ Lyrics for "%1" by %2: XmppSipPlugin - + User Interaction Interaction utilisateur - + Host is unknown L'hôte est inconnu - + Item not found Objet non trouvé - + Authorization Error Erreur d'autorisation - + Remote Stream Error - + Erreur de lecture à distance - + Remote Connection failed - + Erreur de connexion à distance - + Internal Server Error - + Erreur interne du serveur - + System shutdown - + Arrêt du système - + Conflict Conflit - - - Unknown - Ajouter un &ami... - + Unknown + Inconnu + + + No Compression Support - + Pas de support de la compression - + No Encryption Support - + Pas de support du chiffrement - + No Authorization Support - + Pas de support de l'authorization - + No Supported Feature - + Fonctionnalité non supportée - + Add Friend Ajouter un ami - + Enter Xmpp ID: - Entrer l'ID XMPP + Entrer l'ID XMPP: - + Add Friend... Ajouter un ami... - + XML Console... Console XML... - + I'm sorry -- I'm just an automatic presence used by Tomahawk Player (http://gettomahawk.com). If you are getting this message, the person you are trying to reach is probably not signed on, so please try again later! Désolé -- Je suis une présence automatique utilisé par le lecteur Tomahawk (http://gettomahawk.com). Si vous lisez ce message, la personne que vous essayez de joindre n'est probablement pas connecter, donc essayez plus tard ! Merci ! - + Authorize User - + Autoriser l'utilisateur - + Do you want to grant <b>%1</b> access to your Collection? Voulez vous donner accès à votre collection à %1 ? diff --git a/lang/tomahawk_i18n.qrc b/lang/tomahawk_i18n.qrc index 575f8b9e1..f01815d68 100644 --- a/lang/tomahawk_i18n.qrc +++ b/lang/tomahawk_i18n.qrc @@ -10,5 +10,6 @@ tomahawk_es.qm tomahawk_sv.qm tomahawk_ja.qm +tomahawk_ar.qm diff --git a/lang/tomahawk_ja.ts b/lang/tomahawk_ja.ts index 7604d2a43..85cdfaf0e 100644 --- a/lang/tomahawk_ja.ts +++ b/lang/tomahawk_ja.ts @@ -2,17 +2,17 @@ AccountFactoryWrapper - + Dialog ダイアログ - + Description goes here - + Add Account アカウントを追加 @@ -20,17 +20,17 @@ AccountFactoryWrapperDelegate - + Online オンライン - + Connecting... 接続中... - + Offline オフライン @@ -38,13 +38,13 @@ AclJobDelegate - + Error displaying ACL info - - + + Allow %1 to connect and stream from you? @@ -53,69 +53,69 @@ connect and stream from you? ActionCollection - + &Listen Along 共有聴取 - + Stop &Listening Along 共有聴取を中止 - + &Follow in real-time 実時間にフォロー - - + + &Listen Privately - + 非公開で聴く - - + + &Listen Publicly - + 公開で聴く - + &Load Playlist プレイリストを読み込み - + &Rename Playlist プレイリスト名を変更 - + &Copy Playlist Link プレイリストリンクをコピー - + &Play 再生 - + &Stop 停止 - - - &Previous Track - - - - - &Next Track - - + &Previous Track + 前のトラック + + + + &Next Track + 次のトラック + + + &Quit 終了 @@ -125,80 +125,49 @@ connect and stream from you? Form - + フォーム Other Albums by Artist + 他のアルバム + + + + Sorry, we could not find any other albums for this artist! - - - Click to show Official Tracks + + Sorry, we could not find any tracks for this album! - - - Click to show SuperCollection Tracks - - - - - - Click to show SuperCollection Albums - - - - - Click to show Official Albums - - - - + Other Albums by %1 - + %1の他のアルバム AlbumModel - - Album - アルバム - - - - + + All albums from %1 - + %1のすべてのアルバム - + All albums 全アルバム - - AlbumView - - - After you have scanned your music collection you will find your latest album additions right here. - - - - - This collection doesn't have any recent albums. - - - ArtistInfoWidget Form - + フォーム @@ -211,37 +180,23 @@ connect and stream from you? 似たアーティスト - + Albums アルバム - - - Click to show SuperCollection Albums - クリクしてスーパーコレクションアルバムを表示 - - - - Click to show Official Albums - クリクして公式アルバムを表示 - - - - ArtistView - - - After you have scanned your music collection you will find your tracks right here. + + Sorry, we could not find any albums for this artist! - - This collection is currently empty. - このコレクションは現在空です。 + + Sorry, we could not find any related artists! + - - Sorry, your filter '%1' did not match any results. + + Sorry, we could not find any top hits for this artist! @@ -315,36 +270,39 @@ connect and stream from you? Low - + High - + CategoryAddItem - - - New Playlist - 新規プレイリスト + + Create new Playlist + - - - - + + Create new Station + + + + + + New Station 新規ステーション - - - + + + %1 Station - + %1 ステーション @@ -368,27 +326,6 @@ connect and stream from you? クリアー - - CollectionFlatModel - - - My Collection - - - - - Collection of %1 - %1のコレクション - - - - CollectionView - - - This collection is empty. - このコレクションは空です。 - - ContextWidget @@ -398,12 +335,12 @@ connect and stream from you? - + Show Footnotes 脚注を表示 - + Hide Footnotes 脚注を隠す @@ -413,43 +350,53 @@ connect and stream from you? Tomahawk Crash Reporter - + Tomahawkの問題のレポート - + - <p><b>Sorry!</b>&nbsp;Tomahawk crashed. Information about the crash is now being sent to Tomahawk HQ so that we can fix the bug.</p> + <html><head/><body><p><span style=" font-weight:600;">Sorry!</span> Tomahawk crashed. Please tell us about it! Tomahawk has created an error report for you that can help improve the stability in the future. You can now send this report directly to the Tomahawk developers.</p></body></html> - + + Send this report + + + + + Don't send + + + + Abort アボート - + You can disable sending crash reports in the configuration dialog. - + Uploaded %L1 of %L2 KB. - + %L1/%L2 KB アップロード完了 - - + + Close 閉じる - + Sent! <b>Many thanks</b>. - + 送信完了! <b>ありがとうございます!</b> - + Failed to send crash info. - + クラッシュ情報の送信に失敗しました。 @@ -463,17 +410,17 @@ connect and stream from you? DelegateConfigWrapper - + About - + Delete Account - + アカウントを削除 - + About this Account @@ -483,33 +430,20 @@ connect and stream from you? Tomahawk Diagnostics - + Tomahawkの診断 - - Update - 更新 - - - + Copy to Clipboard クリップボードにコピーする - - DropJob - - - No tracks found for given %1 - - - GlobalSearchWidget Form - + フォーム @@ -517,7 +451,7 @@ connect and stream from you? Indexing database - + データベースのインデックスを作成中 @@ -528,40 +462,22 @@ connect and stream from you? - + Filter... フィルター... - - JobStatusView - - - Searching For - 検索中 - - - - Pending - 保留中 - - - - Idle - 待機中 - - LastFmConfig Form - + フォーム Scrobble tracks to Last.fm - + Last.fmにトラックをscrobbleする @@ -576,6 +492,11 @@ connect and stream from you? Test Login + ログインを確認する + + + + Import Playback History @@ -592,7 +513,7 @@ connect and stream from you? %1 is listening along to you! - + %1と一緒に聴いています! @@ -626,14 +547,14 @@ connect and stream from you? LoadXSPFDialog - + Load XSPF File - + XSPFファイルを読み込む - + XSPF Files (*.xspf) - + XSPFファイル (*.xspf) @@ -646,7 +567,7 @@ connect and stream from you? Saved tracks - + トラックを保存する @@ -654,17 +575,17 @@ connect and stream from you? Enter a title for the new playlist: - + 新規プレイリストのタイトルを入力: Tomahawk offers a variety of ways to help you create playlists and find music you enjoy! - + Tomahawkは新規プレイリストを作成したり、好きな曲を見つける様々な方法を提供しています! Just enter a genre or tag name and Tomahawk will suggest a few songs to get you started with your new playlist: - + ジャンルやタグ名を入力すると、Tomahawkが新しいプレイリストに曲を推薦します: @@ -680,63 +601,132 @@ connect and stream from you? NewReleasesWidget - + New Releases + + PlayableModel + + + Artist + アーティスト + + + + Title + + + + + Composer + + + + + Album + アルバム + + + + Track + + + + + Duration + + + + + Bitrate + + + + + Age + + + + + Year + + + + + Size + + + + + Origin + + + + + Score + + + + + + Name + + + PlaylistItemDelegate - + played %1 by you - + %1を再生しました。 - + played %1 by %2 - + %2が%1を再生しました。 PlaylistLargeItemDelegate - + played %1 by you - + %1を再生しました。 - + played %1 by %2 - + %2が%1を再生しました。 - + added %1 - + %1を追加しました。 PlaylistModel - + A playlist by %1, created %2 - + %2に作成した%1のプレイリスト - + you - + あなた All tracks by %1 on album %2 - + %1のアルバム%2のトラック All tracks by %1 - + %1のトラック @@ -744,7 +734,7 @@ connect and stream from you? New Playlist - + 新規プレイリスト @@ -764,26 +754,31 @@ connect and stream from you? New Playlist... - + 新規プレイリスト... Create Manual Playlist - + 自分でプレイリストを作成する Create Automatic Playlist - + 自動でプレイリストを作成する PlaylistView - - This playlist is currently empty. Add some tracks to it and enjoy the music! + + This playlist is currently empty. + + + This playlist is currently empty. Add some tracks to it and enjoy the music! + プレイリストには何も入っていません。トラックを追加して、音楽を楽しみましょう! + ProxyDialog @@ -795,7 +790,7 @@ connect and stream from you? Hostname of proxy server - + プロキシサーバのホスト @@ -810,121 +805,127 @@ connect and stream from you? Proxy login - + プロキシのログインネーム User - + ユーザー Password - + パスワード Proxy password - + プロキシのパスワード No Proxy Hosts: (Overrides system proxy) - + 優先プロキシ: +(システムプロキシより優先されます) localhost *.example.com (space separated) - + localhost *.example.com (スペースで区切られます) Use proxy for DNS lookups? - + 正引きDNSにプロキシを使用しますか? QObject - + %n year(s) ago one: %n年前 other: %n年前 - + %n year(s) - + one: %n年 +other: %n年 - + %n month(s) ago - + one: %nヶ月前 +other: %nヶ月前 - + %n month(s) - + one: %nヶ月 +other: %nヶ月 - + %n week(s) ago - + one: %n週間前 +other: %n週間前 - + %n week(s) - + one: %n週間 +other: %n週間 - + %n day(s) ago - + %n day(s) - + %n hour(s) ago - + %n hour(s) - + %1 minutes ago %1分前 - + %1 minutes %1分 - + just now 只今 - + Friend Finders - + Music Finders - + Status Updaters @@ -946,20 +947,30 @@ other: %n年前 - - Show Queue + + Open Queue - - Hide Queue + + The queue is currently empty. Drop something to enqueue it! + + + + + Open Queue - %n item(s) + + + + + Close Queue RelatedArtistsContext - + Related Artists @@ -1001,37 +1012,37 @@ other: %n年前 SettingsDialog - + Collection - + Advanced - + All - + Some changed settings will not take effect until Tomahawk is restarted - + Services - + Install resolver from file - + Information 情報 @@ -1082,17 +1093,17 @@ other: %n年前 - + Listening to "%1" by %2 and loving it! %3 - + Listening to "%1" by %2 on "%3" and loving it! %4 - + %1 characters left @@ -1102,17 +1113,17 @@ other: %n年前 Track - + トラック Album - + アルバム Artist - + アーティスト @@ -1125,29 +1136,19 @@ other: %n年前 - - Offline - - - - + All available tracks - - Online - - - - - + + Show - - + + Hide @@ -1170,17 +1171,17 @@ other: %n年前 - + New Additions - + My recent activity - + Recent activity from %1 @@ -1188,41 +1189,51 @@ other: %n年前 SourceItem - + Collection - + コレクション - - + + Latest Additions - + Recently Played - + Loved Tracks - + SuperCollection スーパーコレクション - + + Sorry, we could not find any loved tracks! + + + + Latest additions to your collection - + Latest additions to %1's collection + + + Sorry, we could not find any recent additions! + + Recently Played Tracks @@ -1238,31 +1249,78 @@ other: %n年前 %1's recently played tracks + + + Sorry, we could not find any recent plays! + + SourceTreeView - + &Copy Link リンクをコピー - + &Delete %1 + + + Add to my Playlists + + + + + Add to my Automatic Playlists + + + + + Add to my Stations + + &Export Playlist - + + playlist + + + + + automatic playlist + + + + + station + + + + + Delete %1? + playlist/station/... + + + + + Would you like to delete the %1 <b>"%2"</b>? + e.g. Would you like to delete the playlist named Foobar? + + + + Save XSPF XSPFを保存する - + Playlists (*.xspf) @@ -1350,7 +1408,7 @@ other: %n年前 Form - + フォーム @@ -1365,7 +1423,7 @@ other: %n年前 Log In - + ログイン @@ -1479,7 +1537,7 @@ other: %n年前 Set this to your external IP address or host name. Make sure to forward the port to this host! - + フォーム @@ -1540,33 +1598,46 @@ other: %n年前 Tomahawk::Accounts::AccountDelegate - + Add Account - + アカウントを追加 - + Remove Account - + アカウントを削除 - + %1 downloads - + Online - + オンライン - + Connecting... - + Offline + ・オフライン + + + + Tomahawk::Accounts::AccountModel + + + Manual Install Required + + + + + Unfortunately, automatic installation of this resolver is not available or disabled for your platform.<br /><br />Please use "Install from file" above, by fetching it from your distribution or compiling it yourself. Further instructions can be found here:<br /><br />http://www.tomahawk-player.org/resolvers/%1 @@ -1625,23 +1696,49 @@ other: %n年前 Tomahawk::Accounts::LastFmConfig - + + Testing... + + + + Test Login - - + + Importing %1 + e.g. Importing 2012/01/01 + + + + + Importing History... + + + + + History Incomplete. Resume + + + + + Playback History Imported + + + + + Failed - + Success - + Could not contact server @@ -1649,12 +1746,17 @@ other: %n年前 Tomahawk::Accounts::SpotifyAccount - + Sync with Spotify - + + Re-enable syncing with Spotify + + + + Stop syncing with Spotify @@ -1662,30 +1764,30 @@ other: %n年前 Tomahawk::Accounts::SpotifyAccountConfig - + Logging in... - + ログイン中... - + Logged in! - + ログインしました! - + Failed: %1 - + Log In - + ログイン Tomahawk::Accounts::SpotifyAccountFactory - + Play music from and sync your playlists with Spotify Premium @@ -1701,125 +1803,125 @@ other: %n年前 Tomahawk::Accounts::TwitterConfigWidget - - - + + + Tweet! - - + + Status: No saved credentials - - - + + + Authenticate - - - - Status: Credentials saved for %1 - - + Status: Credentials saved for %1 + + + + + De-authenticate - - - - - - - + + + + + + + Tweetin' Error - + The credentials could not be verified. You may wish to try re-authenticating. - + Status: Error validating credentials - + Global Tweet - + Direct Message ダイレクトメッセージ - + Send Message! メッセージを送信! - + @Mention - + Send Mention! - + You must enter a user name for this type of tweet. - + Your saved credentials could not be loaded. You may wish to try re-authenticating. - + Your saved credentials could not be verified. You may wish to try re-authenticating. - - + + There was an error posting your status -- sorry! - - + + Tweeted! - + Your tweet has been posted! - + There was an error posting your direct message -- sorry! - + Your message has been posted! あなたのメッセージが投稿されました! @@ -1835,7 +1937,7 @@ You may wish to try re-authenticating. Tomahawk::Accounts::ZeroconfFactory - + Automatically connect to Tomahawks on the local network @@ -1843,20 +1945,20 @@ You may wish to try re-authenticating. Tomahawk::ContextMenu - + &Play 再生 - - - + + + Add to &Queue - - + + &Love &Love @@ -1866,40 +1968,45 @@ You may wish to try re-authenticating. - - Show &Album page - アルバムページを表示 - - - - Show &Artist page - アーティストページを表示 - - - + Un-&Love Loveじゃないトラック - + &Delete Items 項目を削除 - + &Continue Playback after this Track - + &Stop Playback after this Track - + + &Show Track Page + + + + &Delete Item 項目を削除 + + + &Show Album Page + + + + + &Show Artist Page + + Tomahawk::CustomPlaylistView @@ -1937,12 +2044,32 @@ You may wish to try re-authenticating. Tomahawk::DropJobNotifier - + + playlist + プレイリスト + + + + artist + アーティスト + + + + track + トラック + + + + album + アルバム + + + Fetching %1 from database - + Parsing %1 %2 @@ -1950,7 +2077,7 @@ You may wish to try re-authenticating. Tomahawk::DynamicControlList - + Click to collapse @@ -1976,7 +2103,7 @@ Please change the filters or try again. Type: - + 入力: @@ -1987,17 +2114,17 @@ Please change the filters or try again. Tomahawk::DynamicView - + Add some filters above to seed this station! - + Press Generate to get started! - + Add some filters above, and press Generate to get started! @@ -2005,7 +2132,7 @@ Please change the filters or try again. Tomahawk::DynamicWidget - + Station ran out of tracks! Try tweaking the filters for a new set of songs to play. @@ -2317,97 +2444,97 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::EchonestSteerer - + Steer this station: - + Much less - + Less - + A bit less - + Keep at current - + A bit more - + More - + Much more - + Tempo - + Loudness - + Danceability - + Energy - + Song Hotttnesss - + Artist Hotttnesss - + Artist Familiarity - + By Description - + Enter a description - + Apply steering command - + Reset all steering commands @@ -2423,35 +2550,35 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::InfoSystem::ChartsPlugin - + Top Overall - + Artists - + アーティスト - + Albums - + アルバム - + Tracks - + トラック Tomahawk::InfoSystem::FdoNotifyPlugin - + Tomahawk is playing "%1" by %2%3. - + on "%1" @@ -2459,27 +2586,27 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::InfoSystem::LastFmInfoPlugin - + Top Tracks - + Loved Tracks - + Hyped Tracks - + Top Artists - + Hyped Artists @@ -2487,15 +2614,15 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::InfoSystem::NewReleasesPlugin - + Albums - + アルバム Tomahawk::InfoSystem::TwitterInfoPlugin - + Listening to "%1" by %2 and loving it! %3 @@ -2553,37 +2680,37 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Query - + and - + You - + you - + and - + %n other(s) - + %1 people - + loved this track @@ -2599,7 +2726,7 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::ShortenedLinkParser - + Network error parsing shortened link! @@ -2607,36 +2734,46 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) - + Scanning 走査中 - + Checking - + Fetching 取得中 - + Parsing 解析中 - + Saving (%1%) + + + Online + + + + + Offline + + Tomahawk::SpotifyParser @@ -2649,7 +2786,7 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::XspfUpdater - + Automatically update from XSPF @@ -2657,7 +2794,7 @@ Try tweaking the filters for a new set of songs to play. TomahawkApp - + My Collection @@ -2679,39 +2816,48 @@ enter the displayed PIN number here: TomahawkTrayIcon - - + &Stop Playback after current Track - - + + Hide Tomahawk Window Tomahawkのウインドウを隠す - + Show Tomahawk Window Tomahawkのウインドウを表示 - + Currently not playing. - + Play 再生 - + Pause 一時停止 - + + &Love + + + + + Un-&Love + + + + &Continue Playback after current Track @@ -2756,7 +2902,7 @@ enter the displayed PIN number here: Ctrl+Q - + Ctrl+Q @@ -2826,7 +2972,7 @@ enter the displayed PIN number here: Ctrl+M - + Ctrl+M @@ -2855,9 +3001,9 @@ enter the displayed PIN number here: - + Play - + 再生 @@ -2875,137 +3021,172 @@ enter the displayed PIN number here: - + + Back + プレイリスト + + + + Go back one page + + + + + Forward + + + + + Go forward one page + + + + Global Search... - - + + Check For Updates... - - - + + + Connect To Peer - + Enter peer address: - + Enter peer port: - + Enter peer key: - + XSPF Error - + This is not a valid XSPF playlist. - + Failed to save tracks - + Some tracks in the playlist do not contain an artist and a title. They will be ignored. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Make sure you have a suitable Phonon backend and required plugins installed. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. - + + Station + + + + Create New Station 新規ステーションを作成 - + Name: + 名前: + + + + Playlist + プレイリスト + + + + Automatic Playlist - - New Station - - - - - New Playlist - - - - + Pause - + Go &offline - + Go &online - + Authentication Error - + + Error connecting to SIP: Authentication failed! + + + + %1 by %2 track, artist name - + %1 - %2 current track, some window title - + <h2><b>Tomahawk %1<br/>(%2)</h2> - + <h2><b>Tomahawk %1</h2> - - Copyright 2010 - 2012<br/>Christian Muehlhaeuser &lt;muesli@tomahawk-player.org&gt;<br/><br/>Thanks to: Leo Franchi, Jeff Mitchell, Dominik Schmidt, Jason Herskowitz, Alejandro Wainzinger, Hugo Lindstr&ouml;m, Michael Zanetti, Harald Sitter and Steve Robertson + + Copyright 2010 - 2012 - + + Thanks to: + + + + About Tomahawk @@ -3015,7 +3196,7 @@ enter the displayed PIN number here: Form - + フォーム @@ -3076,7 +3257,7 @@ enter the displayed PIN number here: TopTracksContext - + Top Hits @@ -3116,7 +3297,7 @@ enter the displayed PIN number here: Album - + アルバム @@ -3124,103 +3305,50 @@ enter the displayed PIN number here: - + + Lyrics + 歌詞 + + + Similar Tracks + + + Sorry, but we could not find similar tracks for this song! + + - + You've listened to this track %n time(s). - + You've never listened to this track before. - + You first listened to it on %1. - + You've listened to %1 %n time(s). - + You've never listened to %1 before. - - TrackModel - - - Artist - - - - - Title - - - - - Album - - - - - Track - - - - - Duration - - - - - Bitrate - - - - - Age - - - - - Year - - - - - Size - - - - - Origin - - - - - Score - - - - - Composer - - - TrackView - + Sorry, your filter '%1' did not match any results. @@ -3241,7 +3369,7 @@ enter the displayed PIN number here: TreeItemDelegate - + Unknown @@ -3249,63 +3377,31 @@ enter the displayed PIN number here: TreeModel - - Name - - - - - Duration - - - - - Bitrate - - - - - Age - - - - - Year - - - - - Size - - - - - Origin - - - - - Composer - - - - + All Artists - + すべてのアーティスト - - + + My Collection - - + + Collection of %1 + + TreeView + + + Sorry, your filter '%1' did not match any results. + + + TwitterConfigWidget @@ -3377,27 +3473,37 @@ You can re-send a sync message at any time simply by sending another tweet using ViewManager - + + After you have scanned your music collection you will find your tracks right here. + + + + + This collection is empty. + + + + SuperCollection - + Combined libraries of all your online friends - + All available albums - + Recently Played Tracks - + Recently played tracks from all your friends @@ -3420,12 +3526,12 @@ You can re-send a sync message at any time simply by sending another tweet using - + No recently created playlists in your network. - + Welcome to Tomahawk @@ -3433,7 +3539,7 @@ You can re-send a sync message at any time simply by sending another tweet using WhatsHotWidget - + Charts @@ -3621,7 +3727,7 @@ Lyrics for "%1" by %2: Password: - + パスワード: @@ -3636,12 +3742,12 @@ Lyrics for "%1" by %2: Server: - + サーバー: Port: - + ポート: @@ -3662,107 +3768,107 @@ Lyrics for "%1" by %2: XmppSipPlugin - + User Interaction - + Host is unknown - + Item not found - + Authorization Error - + Remote Stream Error - + Remote Connection failed - + Internal Server Error - + System shutdown - + Conflict - + Unknown - + No Compression Support - + No Encryption Support - + No Authorization Support - + No Supported Feature - + Add Friend - + Enter Xmpp ID: - + Add Friend... - + XML Console... - + I'm sorry -- I'm just an automatic presence used by Tomahawk Player (http://gettomahawk.com). If you are getting this message, the person you are trying to reach is probably not signed on, so please try again later! - + Authorize User - + Do you want to grant <b>%1</b> access to your Collection? @@ -3772,7 +3878,7 @@ Lyrics for "%1" by %2: Form - + フォーム diff --git a/lang/tomahawk_pl.ts b/lang/tomahawk_pl.ts index 13f8bbb30..9a49b147b 100644 --- a/lang/tomahawk_pl.ts +++ b/lang/tomahawk_pl.ts @@ -2,17 +2,17 @@ AccountFactoryWrapper - + Dialog - + Description goes here Tutaj pojawi się opis - + Add Account Dodaj Konto @@ -20,17 +20,17 @@ AccountFactoryWrapperDelegate - + Online Online - + Connecting... Łączenie... - + Offline Offline @@ -38,13 +38,13 @@ AclJobDelegate - + Error displaying ACL info - - + + Allow %1 to connect and stream from you? @@ -53,69 +53,69 @@ connect and stream from you? ActionCollection - + &Listen Along &Słuchaj razem - + Stop &Listening Along Przestań &Słuchać razem - + &Follow in real-time &Podążaj na bieżąco - - + + &Listen Privately &Słuchaj Prywatnie - - + + &Listen Publicly &Słuchaj Publicznie - + &Load Playlist &Załaduj listę - + &Rename Playlist &Zmień nazwę listy - + &Copy Playlist Link &Skopiuj link listy - + &Play &Odtwarzaj - + &Stop &Zatrzymaj - + &Previous Track &Poprzednia piosenka - + &Next Track &Następna piosenka - + &Quit &Wyjdź @@ -133,30 +133,17 @@ connect and stream from you? Inne albumy artysty - - - Click to show Official Tracks - Kliknij aby pokazać Oficjalne Utwory + + Sorry, we could not find any other albums for this artist! + - - - Click to show SuperCollection Tracks - Kliknij, aby pokazać utwory Superkolekcji + + Sorry, we could not find any tracks for this album! + - - - Click to show SuperCollection Albums - Kliknij, aby pokazać albumy Superkolekcji - - - - Click to show Official Albums - Kliknij aby pokazać Oficjalnie Wydane Albumy - - - + Other Albums by %1 Inne albumy %1 @@ -164,35 +151,17 @@ connect and stream from you? AlbumModel - - Album - Album - - - - + + All albums from %1 Wszystkie albumy %1 - + All albums Wszystkie albumy - - AlbumView - - - After you have scanned your music collection you will find your latest album additions right here. - Po zeskanowaniu swojej kolekcji muzycznej w tym miejscu znajdziesz ostatnio dodane albumy. - - - - This collection doesn't have any recent albums. - Ta kolekcja nie zawiera żadnych nowo dodanych albumów. - - ArtistInfoWidget @@ -211,38 +180,24 @@ connect and stream from you? Powiązani artyści - + Albums Albumy - - - Click to show SuperCollection Albums - Kliknij, aby pokazać albumy Superkolekcji + + Sorry, we could not find any albums for this artist! + - - Click to show Official Albums - Kliknij aby pokazać Oficjalnie Wydane Albumy - - - - ArtistView - - - After you have scanned your music collection you will find your tracks right here. - Po zeskanowaniu swojej kolekcji muzycznej utwory znajdziesz w tym miejscu. + + Sorry, we could not find any related artists! + - - This collection is currently empty. - Ta kolekcja jest aktualnie pusta. - - - - Sorry, your filter '%1' did not match any results. - Przepraszamy, twój filtr '%1' nie pasuje do żadnych wyników. + + Sorry, we could not find any top hits for this artist! + @@ -326,23 +281,26 @@ connect and stream from you? CategoryAddItem - - - New Playlist - Nowa lista + + Create new Playlist + - - - - + + Create new Station + + + + + + New Station Nowa stacja - - - + + + %1 Station Stacja %1 @@ -368,27 +326,6 @@ connect and stream from you? Wyczyść - - CollectionFlatModel - - - My Collection - Moja Kolekcja - - - - Collection of %1 - Kolekcja %1 - - - - CollectionView - - - This collection is empty. - Ta kolekcja jest pusta. - - ContextWidget @@ -398,12 +335,12 @@ connect and stream from you? - + Show Footnotes Pokaż przypisy - + Hide Footnotes Schowaj przypisy @@ -415,39 +352,49 @@ connect and stream from you? Tomahawk Crash Reporter Agent zgłaszania awarii Tomahawka - + - <p><b>Sorry!</b>&nbsp;Tomahawk crashed. Information about the crash is now being sent to Tomahawk HQ so that we can fix the bug.</p> - <p><b>Przepraszamy!</b>&nbsp;Tomahawk uległ awarii. Abyśmy mogli poprawić błędy, informacja o incydencie zostanie wysłana do kwatery głównej programu.</p> + <html><head/><body><p><span style=" font-weight:600;">Sorry!</span> Tomahawk crashed. Please tell us about it! Tomahawk has created an error report for you that can help improve the stability in the future. You can now send this report directly to the Tomahawk developers.</p></body></html> + - + + Send this report + + + + + Don't send + + + + Abort Przerwij - + You can disable sending crash reports in the configuration dialog. Możesz wyłączyć zgłaszanie awarii w ustawieniach. - + Uploaded %L1 of %L2 KB. Wysłano %L1 z %L2 KB. - - + + Close Zamknij - + Sent! <b>Many thanks</b>. Wysłano! <b>Wielkie dzięki</b>. - + Failed to send crash info. Nie udało sie wysłać informacji o awarii. @@ -463,17 +410,17 @@ connect and stream from you? DelegateConfigWrapper - + About - + Delete Account Usuń Konto - + About this Account @@ -486,24 +433,11 @@ connect and stream from you? Diagnostyka Tomahawk - - Update - Aktualizuj - - - + Copy to Clipboard Skopiuj do schowka - - DropJob - - - No tracks found for given %1 - - - GlobalSearchWidget @@ -528,29 +462,11 @@ connect and stream from you? Informacje - + Filter... Filtruj... - - JobStatusView - - - Searching For - Szukanie - - - - Pending - w trakcie - - - - Idle - Bezczynny - - LastFmConfig @@ -578,6 +494,11 @@ connect and stream from you? Test Login Test logowania + + + Import Playback History + + LastfmContext @@ -626,12 +547,12 @@ connect and stream from you? LoadXSPFDialog - + Load XSPF File Załaduj plik XSPF - + XSPF Files (*.xspf) Pliki XSPF (*.xspf) @@ -680,20 +601,89 @@ connect and stream from you? NewReleasesWidget - + New Releases Nowe Wydania + + PlayableModel + + + Artist + + + + + Title + + + + + Composer + + + + + Album + + + + + Track + + + + + Duration + + + + + Bitrate + + + + + Age + + + + + Year + + + + + Size + + + + + Origin + + + + + Score + + + + + + Name + + + PlaylistItemDelegate - + played %1 by you odtworzone %1 przez ciebie - + played %1 by %2 odtworzone %1 przez %2 @@ -701,17 +691,17 @@ connect and stream from you? PlaylistLargeItemDelegate - + played %1 by you odtworzone %1 przez ciebie - + played %1 by %2 - + added %1 dodano %1 @@ -719,12 +709,12 @@ connect and stream from you? PlaylistModel - + A playlist by %1, created %2 %1 lista, utworzona %2 - + you Twoja @@ -780,7 +770,12 @@ connect and stream from you? PlaylistView - + + This playlist is currently empty. + + + + This playlist is currently empty. Add some tracks to it and enjoy the music! Ta lista jest aktualnie pusta. Dodaj do niej trochę piosenek i ciesz się muzyką! @@ -847,82 +842,82 @@ connect and stream from you? QObject - + %n year(s) ago %n rok temu%n lata temu%n lat temu - + %n year(s) %n rok%n lata%n lat - + %n month(s) ago %n miesiąc temu%n miesiące temu%n miesięcy temu - + %n month(s) %n miesiąc%n miesiące%n miesięcy - + %n week(s) ago %n tydzień temu%n tygodnie temu%n tygodni temu - + %n week(s) %n tydzień%n tygodnie%n tygodni - + %n day(s) ago %n dzień temu%n dni temu%n dni temu - + %n day(s) %n dzień%n dni%n dni - + %n hour(s) ago %n godzinę temu%n godziny temu%n godzin temu - + %n hour(s) %n godzinę%n godziny%n godzin - + %1 minutes ago %1 minut temu - + %1 minutes %1 minut - + just now przed chwilą - + Friend Finders - + Music Finders - + Status Updaters @@ -944,20 +939,30 @@ connect and stream from you? - - Show Queue - Pokaż Kolejkę + + Open Queue + - - Hide Queue - Ukryj Kolejkę + + The queue is currently empty. Drop something to enqueue it! + + + + + Open Queue - %n item(s) + + + + + Close Queue + RelatedArtistsContext - + Related Artists Podobni Artyści @@ -999,37 +1004,37 @@ connect and stream from you? SettingsDialog - + Collection Kolekcje - + Advanced Zaawansowane - + All Wszystkie - + Some changed settings will not take effect until Tomahawk is restarted - + Services Usługi - + Install resolver from file - + Information Informacja @@ -1080,17 +1085,17 @@ connect and stream from you? - + Listening to "%1" by %2 and loving it! %3 - + Listening to "%1" by %2 on "%3" and loving it! %4 - + %1 characters left pozostało znaków: %1 @@ -1123,29 +1128,19 @@ connect and stream from you? Top 10 - - Offline - Offline - - - + All available tracks Wszystkie dostępne utwory - - Online - Online - - - - + + Show Pokaż - - + + Hide Ukryj @@ -1168,17 +1163,17 @@ connect and stream from you? Ostatnio Odtwarzane Utwory - + New Additions Nowo dodane - + My recent activity Moja ostatnia aktywność - + Recent activity from %1 Ostatnia aktywność %1 @@ -1186,41 +1181,51 @@ connect and stream from you? SourceItem - + Collection Kolekcja - - + + Latest Additions Ostatnio Dodane - + Recently Played Ostatnio Odtworzone - + Loved Tracks Ulubione Utwory - + SuperCollection Superkolekcja - + + Sorry, we could not find any loved tracks! + + + + Latest additions to your collection - + Latest additions to %1's collection + + + Sorry, we could not find any recent additions! + + Recently Played Tracks @@ -1236,31 +1241,78 @@ connect and stream from you? %1's recently played tracks + + + Sorry, we could not find any recent plays! + + SourceTreeView - + &Copy Link &Kopiuj Link - + &Delete %1 &Usuń %1 + + + Add to my Playlists + + + + + Add to my Automatic Playlists + + + + + Add to my Stations + + &Export Playlist &Eksportuj Listę - + + playlist + + + + + automatic playlist + + + + + station + + + + + Delete %1? + playlist/station/... + + + + + Would you like to delete the %1 <b>"%2"</b>? + e.g. Would you like to delete the playlist named Foobar? + + + + Save XSPF Zapisz XSPF - + Playlists (*.xspf) Listy (*.xspf) @@ -1542,36 +1594,49 @@ indywidualnego profilu gustu. Tomahawk::Accounts::AccountDelegate - + Add Account Dodaj Konto - + Remove Account Usuń Konto - + %1 downloads pobrań: %1 - + Online Online - + Connecting... Łączenie... - + Offline Offline + + Tomahawk::Accounts::AccountModel + + + Manual Install Required + + + + + Unfortunately, automatic installation of this resolver is not available or disabled for your platform.<br /><br />Please use "Install from file" above, by fetching it from your distribution or compiling it yourself. Further instructions can be found here:<br /><br />http://www.tomahawk-player.org/resolvers/%1 + + + Tomahawk::Accounts::GoogleWrapper @@ -1627,23 +1692,49 @@ indywidualnego profilu gustu. Tomahawk::Accounts::LastFmConfig - + + Testing... + + + + Test Login - - + + Importing %1 + e.g. Importing 2012/01/01 + + + + + Importing History... + + + + + History Incomplete. Resume + + + + + Playback History Imported + + + + + Failed - + Success - + Could not contact server @@ -1651,12 +1742,17 @@ indywidualnego profilu gustu. Tomahawk::Accounts::SpotifyAccount - + Sync with Spotify - + + Re-enable syncing with Spotify + + + + Stop syncing with Spotify @@ -1664,22 +1760,22 @@ indywidualnego profilu gustu. Tomahawk::Accounts::SpotifyAccountConfig - + Logging in... - + Logged in! - + Failed: %1 - + Log In @@ -1687,7 +1783,7 @@ indywidualnego profilu gustu. Tomahawk::Accounts::SpotifyAccountFactory - + Play music from and sync your playlists with Spotify Premium @@ -1703,125 +1799,125 @@ indywidualnego profilu gustu. Tomahawk::Accounts::TwitterConfigWidget - - - + + + Tweet! Tweet! - - + + Status: No saved credentials - - - + + + Authenticate - - - - Status: Credentials saved for %1 - - + Status: Credentials saved for %1 + + + + + De-authenticate - - - - - - - + + + + + + + Tweetin' Error - + The credentials could not be verified. You may wish to try re-authenticating. - + Status: Error validating credentials - + Global Tweet - + Direct Message - + Send Message! - + @Mention - + Send Mention! - + You must enter a user name for this type of tweet. - + Your saved credentials could not be loaded. You may wish to try re-authenticating. - + Your saved credentials could not be verified. You may wish to try re-authenticating. - - + + There was an error posting your status -- sorry! - - + + Tweeted! - + Your tweet has been posted! - + There was an error posting your direct message -- sorry! - + Your message has been posted! Twoja wiadomość została wysłana! @@ -1837,7 +1933,7 @@ You may wish to try re-authenticating. Tomahawk::Accounts::ZeroconfFactory - + Automatically connect to Tomahawks on the local network @@ -1845,20 +1941,20 @@ You may wish to try re-authenticating. Tomahawk::ContextMenu - + &Play &Odtwarzaj - - - + + + Add to &Queue Dodaj do &kolejki - - + + &Love &Uwielbiam @@ -1868,40 +1964,45 @@ You may wish to try re-authenticating. - - Show &Album page - - - - - Show &Artist page - - - - + Un-&Love - + &Delete Items &Usuń pozycje - + &Continue Playback after this Track &Kontynuuj odtwarzanie po tym utworze - + &Stop Playback after this Track &Zatrzymaj odtwarzanie po tym utworze - + + &Show Track Page + + + + &Delete Item &Usuń pozycję + + + &Show Album Page + + + + + &Show Artist Page + + Tomahawk::CustomPlaylistView @@ -1939,12 +2040,32 @@ You may wish to try re-authenticating. Tomahawk::DropJobNotifier - + + playlist + + + + + artist + + + + + track + + + + + album + + + + Fetching %1 from database Wydobywanie %1 z bazy danych - + Parsing %1 %2 Analizowanie %1 %2 @@ -1952,7 +2073,7 @@ You may wish to try re-authenticating. Tomahawk::DynamicControlList - + Click to collapse Kliknij, aby schować @@ -1991,17 +2112,17 @@ Proszę zmienić filtry lub spróbować ponownie. Tomahawk::DynamicView - + Add some filters above to seed this station! Dodaj powyżej trochę filtrów, a Tomahawk utworzy stację z podobną muzyką! - + Press Generate to get started! Naciśnij Generuj aby zacząć! - + Add some filters above, and press Generate to get started! Dodaj powyżej trochę filtrów i naciśnij Generuj aby zacząć! @@ -2009,7 +2130,7 @@ Proszę zmienić filtry lub spróbować ponownie. Tomahawk::DynamicWidget - + Station ran out of tracks! Try tweaking the filters for a new set of songs to play. @@ -2321,97 +2442,97 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::EchonestSteerer - + Steer this station: Steruj tą stacją: - + Much less Dużo mniej - + Less Mniej - + A bit less Trochę mniej - + Keep at current Pozostaw jak jest - + A bit more Trochę więcej - + More Więcej - + Much more Dużo więcej - + Tempo Tempo - + Loudness Głośność - + Danceability Taneczność - + Energy Energiczność - + Song Hotttnesss - + Artist Hotttnesss - + Artist Familiarity Znajomość Artysty - + By Description Po Opisie - + Enter a description Wprowadź opis - + Apply steering command Zastosuj komendę sterowania - + Reset all steering commands Zresetuj wszystkie komendy sterowania @@ -2427,22 +2548,22 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::InfoSystem::ChartsPlugin - + Top Overall Top Wszechczasów - + Artists Artyści - + Albums Albumy - + Tracks Utwory @@ -2450,12 +2571,12 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::InfoSystem::FdoNotifyPlugin - + Tomahawk is playing "%1" by %2%3. - + on "%1" z "%1" @@ -2463,27 +2584,27 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::InfoSystem::LastFmInfoPlugin - + Top Tracks - + Loved Tracks - + Hyped Tracks - + Top Artists - + Hyped Artists @@ -2491,7 +2612,7 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::InfoSystem::NewReleasesPlugin - + Albums Albumy @@ -2499,7 +2620,7 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::InfoSystem::TwitterInfoPlugin - + Listening to "%1" by %2 and loving it! %3 @@ -2557,37 +2678,37 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Query - + and i - + You Ty - + you ty - + and i - + %n other(s) %n inny%n inne%n innych - + %1 people - + loved this track @@ -2603,7 +2724,7 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::ShortenedLinkParser - + Network error parsing shortened link! @@ -2611,36 +2732,46 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) Skanowanie (%L1 utworów) - + Scanning Skanowanie - + Checking Sprawdzanie - + Fetching Pobieranie - + Parsing Analizowanie - + Saving (%1%) Zapisywanie (%1%) + + + Online + + + + + Offline + + Tomahawk::SpotifyParser @@ -2653,7 +2784,7 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::XspfUpdater - + Automatically update from XSPF @@ -2661,7 +2792,7 @@ Try tweaking the filters for a new set of songs to play. TomahawkApp - + My Collection Moja Kolekcja @@ -2684,39 +2815,48 @@ wprowadź pokazany numer PIN tutaj: TomahawkTrayIcon - - + &Stop Playback after current Track - - + + Hide Tomahawk Window Ukryj Okno Tomahawka - + Show Tomahawk Window Pokaż Okno Tomahawka - + Currently not playing. Aktualnie nie odtwarza. - + Play Odtwarzaj - + Pause Pauza - + + &Love + + + + + Un-&Love + + + + &Continue Playback after current Track @@ -2860,7 +3000,7 @@ wprowadź pokazany numer PIN tutaj: - + Play Odtwarzaj @@ -2880,137 +3020,172 @@ wprowadź pokazany numer PIN tutaj: Następny - + + Back + + + + + Go back one page + + + + + Forward + + + + + Go forward one page + + + + Global Search... Globalne Wyszukiwanie... - - + + Check For Updates... Sprawdź uaktualnienia... - - - + + + Connect To Peer - + Enter peer address: - + Enter peer port: - + Enter peer key: - + XSPF Error Błąd XSPF - + This is not a valid XSPF playlist. To nie jest poprawna lista XSPF. - + Failed to save tracks Nie udało się zapisać utworów - + Some tracks in the playlist do not contain an artist and a title. They will be ignored. Niektóre utwory na liście nie zawierają artysty i tytułu. Zostaną one zignorowane. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Make sure you have a suitable Phonon backend and required plugins installed. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. - + + Station + + + + Create New Station Utwórz Nową Stację - + Name: Nazwa: - - New Station - Nowa stacja + + Playlist + - - New Playlist - Nowa Lista + + Automatic Playlist + - + Pause Pauza - + Go &offline Przejdź do trybu &offline - + Go &online Przejdź do trybu &online - + Authentication Error Błąd uwierzytelniania - + + Error connecting to SIP: Authentication failed! + + + + %1 by %2 track, artist name %1 wykonawcy %2 - + %1 - %2 current track, some window title %1 - %2 - + <h2><b>Tomahawk %1<br/>(%2)</h2> - + <h2><b>Tomahawk %1</h2> - - Copyright 2010 - 2012<br/>Christian Muehlhaeuser &lt;muesli@tomahawk-player.org&gt;<br/><br/>Thanks to: Leo Franchi, Jeff Mitchell, Dominik Schmidt, Jason Herskowitz, Alejandro Wainzinger, Hugo Lindstr&ouml;m, Michael Zanetti, Harald Sitter and Steve Robertson + + Copyright 2010 - 2012 - + + Thanks to: + + + + About Tomahawk @@ -3081,7 +3256,7 @@ wprowadź pokazany numer PIN tutaj: TopTracksContext - + Top Hits Hity na Topie @@ -3129,103 +3304,50 @@ wprowadź pokazany numer PIN tutaj: - + + Lyrics + + + + Similar Tracks + + + Sorry, but we could not find similar tracks for this song! + + - + You've listened to this track %n time(s). - + You've never listened to this track before. - + You first listened to it on %1. - + You've listened to %1 %n time(s). - + You've never listened to %1 before. - - TrackModel - - - Artist - Artysta - - - - Title - Tytuł - - - - Album - Album - - - - Track - Utwór - - - - Duration - Czas trwania - - - - Bitrate - Bitrate - - - - Age - Wiek - - - - Year - Rok - - - - Size - Rozmiar - - - - Origin - Pochodzenie - - - - Score - Punktacja - - - - Composer - Kompozytor - - TrackView - + Sorry, your filter '%1' did not match any results. Przepraszamy, twój filtr '%1' nie dopasował żadnych wyników. @@ -3246,7 +3368,7 @@ wprowadź pokazany numer PIN tutaj: TreeItemDelegate - + Unknown Nieznany @@ -3254,63 +3376,31 @@ wprowadź pokazany numer PIN tutaj: TreeModel - - Name - Nazwa - - - - Duration - Czas trwania - - - - Bitrate - Bitrate - - - - Age - Wiek - - - - Year - Rok - - - - Size - Rozmiar - - - - Origin - Pochodzenie - - - - Composer - Kompozytor - - - + All Artists Wszyscy Artyści - - + + My Collection Moja Kolekcja - - + + Collection of %1 Kolekcja %1 + + TreeView + + + Sorry, your filter '%1' did not match any results. + + + TwitterConfigWidget @@ -3387,27 +3477,37 @@ Zawsze możesz ponownie wysłać wiadomość synchronizacyjną - po prostu wyśl ViewManager - + + After you have scanned your music collection you will find your tracks right here. + + + + + This collection is empty. + + + + SuperCollection SuperKolekcja - + Combined libraries of all your online friends Połączone biblioteki wszystkich twoich znajomych online - + All available albums Wszystkie dostępne albumy - + Recently Played Tracks - + Recently played tracks from all your friends @@ -3430,12 +3530,12 @@ Zawsze możesz ponownie wysłać wiadomość synchronizacyjną - po prostu wyśl Ostatnio Odtworzone Utwory - + No recently created playlists in your network. Brak ostatnio utworzonych list w twojej sieci. - + Welcome to Tomahawk Witaj w Tomahawk @@ -3443,7 +3543,7 @@ Zawsze możesz ponownie wysłać wiadomość synchronizacyjną - po prostu wyśl WhatsHotWidget - + Charts Listy przebojów @@ -3678,107 +3778,107 @@ Tekst dla "%1" wykonawcy %2: XmppSipPlugin - + User Interaction - + Host is unknown Nieznany Host - + Item not found - + Authorization Error - + Remote Stream Error - + Remote Connection failed Połączenie sieciowe się nie powiodło - + Internal Server Error Wewnętrzny błąd serwera - + System shutdown Wyłączenie systemu - + Conflict Konflikt - + Unknown Nieznany - + No Compression Support Brak obsługi kompresji - + No Encryption Support Brak obsługi szyfrowania - + No Authorization Support Brak obsługi autoryzacji - + No Supported Feature Brak obsługi danej funkcji - + Add Friend Dodaj Znajomego - + Enter Xmpp ID: - + Add Friend... Dodaj Znajomego... - + XML Console... Konsola XML... - + I'm sorry -- I'm just an automatic presence used by Tomahawk Player (http://gettomahawk.com). If you are getting this message, the person you are trying to reach is probably not signed on, so please try again later! - + Authorize User Autoryzuj Użytkownika - + Do you want to grant <b>%1</b> access to your Collection? Czy chcesz udzielić dostępu do swojej kolekcji <b>%1</b>? diff --git a/lang/tomahawk_pt_BR.ts b/lang/tomahawk_pt_BR.ts index 2049d15c2..4b741a490 100644 --- a/lang/tomahawk_pt_BR.ts +++ b/lang/tomahawk_pt_BR.ts @@ -2,17 +2,17 @@ AccountFactoryWrapper - + Dialog - + Description goes here - + Add Account Adicionar Conta @@ -20,17 +20,17 @@ AccountFactoryWrapperDelegate - + Online - + Connecting... Conectando... - + Offline @@ -38,13 +38,13 @@ AclJobDelegate - + Error displaying ACL info - - + + Allow %1 to connect and stream from you? @@ -53,69 +53,69 @@ connect and stream from you? ActionCollection - + &Listen Along &Reproduzir acompanhado - + Stop &Listening Along &Parar acompanhado - + &Follow in real-time &Seguir em tempo real - - + + &Listen Privately &Escutar de forma privada - - + + &Listen Publicly &Escutar forma pública - + &Load Playlist &Carregar lista de reprodução - + &Rename Playlist &Renomear lista - + &Copy Playlist Link &Copiar link da lista de reprodução - + &Play &Reproduzir - + &Stop &Parar - + &Previous Track &Faixa anterior - + &Next Track &Próxima faixa - + &Quit &Sair @@ -133,30 +133,17 @@ connect and stream from you? Outros álbuns do artista - - - Click to show Official Tracks - Clique para mostrar as Faixas Oficiais + + Sorry, we could not find any other albums for this artist! + - - - Click to show SuperCollection Tracks - Clique para mostrar as faixas da SuperColeção + + Sorry, we could not find any tracks for this album! + - - - Click to show SuperCollection Albums - Clique para mostrar os albums da SuperColeção - - - - Click to show Official Albums - Clique para mostrar os Ábuns Oficiais - - - + Other Albums by %1 Outros álbuns por %1 @@ -164,35 +151,17 @@ connect and stream from you? AlbumModel - - Album - Álbum - - - - + + All albums from %1 Todos os álbuns de %1 - + All albums Todos os álbuns - - AlbumView - - - After you have scanned your music collection you will find your latest album additions right here. - Depois que você escanear a sua biblioteca musical você encontrará aqui o último álbum adicionado. - - - - This collection doesn't have any recent albums. - Essa biblioteca não tem nenhum álbum recente. - - ArtistInfoWidget @@ -211,38 +180,24 @@ connect and stream from you? Artistas relacionados - + Albums Álbuns - - - Click to show SuperCollection Albums - Clique para mostrar os albums da SuperColeção + + Sorry, we could not find any albums for this artist! + - - Click to show Official Albums - Clique para mostrar os Ábuns Oficiais - - - - ArtistView - - - After you have scanned your music collection you will find your tracks right here. - Depois que você escanear a sua biblioteca musical você encontrará aqui as suas faixas. + + Sorry, we could not find any related artists! + - - This collection is currently empty. - Essa biblioteca está vazia. - - - - Sorry, your filter '%1' did not match any results. - Desculpe, o seu filtro '%1' não encontreou nenhum resultado. + + Sorry, we could not find any top hits for this artist! + @@ -326,23 +281,26 @@ connect and stream from you? CategoryAddItem - - - New Playlist - Nova lista de reprodução + + Create new Playlist + - - - - + + Create new Station + + + + + + New Station Nova estação - - - + + + %1 Station %1 Estação @@ -368,27 +326,6 @@ connect and stream from you? Limpar - - CollectionFlatModel - - - My Collection - Minha biblioteca - - - - Collection of %1 - Biblioteca de %1 - - - - CollectionView - - - This collection is empty. - Essa biblioteca está vazia. - - ContextWidget @@ -398,12 +335,12 @@ connect and stream from you? - + Show Footnotes Mostrar rodapé - + Hide Footnotes Ocultar rodapé @@ -415,39 +352,49 @@ connect and stream from you? Tomahawk Crash Reporter Informante de falha do Tomahawk - + - <p><b>Sorry!</b>&nbsp;Tomahawk crashed. Information about the crash is now being sent to Tomahawk HQ so that we can fix the bug.</p> - <p><b>Desculpe!</b>&nbsp;O Tomahawk travou. As informações sobre o travamento estão sendo enviadas para o quartel general do Tomahawk para que possamos solucionar esse problema.</p> + <html><head/><body><p><span style=" font-weight:600;">Sorry!</span> Tomahawk crashed. Please tell us about it! Tomahawk has created an error report for you that can help improve the stability in the future. You can now send this report directly to the Tomahawk developers.</p></body></html> + - + + Send this report + + + + + Don't send + + + + Abort Cancelar - + You can disable sending crash reports in the configuration dialog. - + Uploaded %L1 of %L2 KB. Enviado %L1 de %L2 KB. - - + + Close Fechar - + Sent! <b>Many thanks</b>. Enviado! <b>Muito obrigado</b>. - + Failed to send crash info. Falha ao enviar as informações de travamento. @@ -463,17 +410,17 @@ connect and stream from you? DelegateConfigWrapper - + About - + Delete Account Excluir Conta - + About this Account @@ -486,24 +433,11 @@ connect and stream from you? Diagnóstico do Tomahawk - - Update - Atualização - - - + Copy to Clipboard Copiar - - DropJob - - - No tracks found for given %1 - Nenhuma faixa encontrada para %1 - - GlobalSearchWidget @@ -528,29 +462,11 @@ connect and stream from you? Barra de informações - + Filter... Filtro... - - JobStatusView - - - Searching For - Buscando por - - - - Pending - Pendente - - - - Idle - Ocioso - - LastFmConfig @@ -578,6 +494,11 @@ connect and stream from you? Test Login + + + Import Playback History + + LastfmContext @@ -626,12 +547,12 @@ connect and stream from you? LoadXSPFDialog - + Load XSPF File Carregar arquivo XSPF - + XSPF Files (*.xspf) Arquivos XSPF (*.xspf) @@ -680,20 +601,89 @@ connect and stream from you? NewReleasesWidget - + New Releases + + PlayableModel + + + Artist + + + + + Title + + + + + Composer + + + + + Album + + + + + Track + + + + + Duration + + + + + Bitrate + + + + + Age + + + + + Year + + + + + Size + + + + + Origin + + + + + Score + + + + + + Name + + + PlaylistItemDelegate - + played %1 by you reproduzido %1 por você - + played %1 by %2 reproduzido %1 por %2 @@ -701,17 +691,17 @@ connect and stream from you? PlaylistLargeItemDelegate - + played %1 by you tocada %1 por você - + played %1 by %2 tocada %1 por %2 - + added %1 adicionou %1 @@ -719,12 +709,12 @@ connect and stream from you? PlaylistModel - + A playlist by %1, created %2 Uma lista de reprodução de %1, criada %2 - + you você @@ -780,7 +770,12 @@ connect and stream from you? PlaylistView - + + This playlist is currently empty. + + + + This playlist is currently empty. Add some tracks to it and enjoy the music! Esta lista de reprodução está vazia. Adicione algumas faixas para disfrutar de uma música! @@ -847,82 +842,82 @@ connect and stream from you? QObject - + %n year(s) ago %n year ago%n years ago - + %n year(s) %n year%n years - + %n month(s) ago %n month ago%n months ago - + %n month(s) %n month%n months - + %n week(s) ago %n week ago%n weeks ago - + %n week(s) %n week%n weeks - + %n day(s) ago %n day ago%n days ago - + %n day(s) %n day%n days - + %n hour(s) ago %n hour ago%n hours ago - + %n hour(s) %n hour%n hours - + %1 minutes ago %1 minutos atrás - + %1 minutes %1 minutos - + just now agora - + Friend Finders - + Music Finders - + Status Updaters @@ -944,20 +939,30 @@ connect and stream from you? - - Show Queue - Mostrar fila + + Open Queue + - - Hide Queue - Ocultar fila + + The queue is currently empty. Drop something to enqueue it! + + + + + Open Queue - %n item(s) + + + + + Close Queue + RelatedArtistsContext - + Related Artists Artistas relacionados @@ -999,37 +1004,37 @@ connect and stream from you? SettingsDialog - + Collection Biblioteca - + Advanced Avançado - + All - + Some changed settings will not take effect until Tomahawk is restarted - + Services - + Install resolver from file - + Information Informação @@ -1080,17 +1085,17 @@ connect and stream from you? - + Listening to "%1" by %2 and loving it! %3 - + Listening to "%1" by %2 on "%3" and loving it! %4 - + %1 characters left @@ -1123,29 +1128,19 @@ connect and stream from you? 10 Mais - - Offline - Desconectado - - - + All available tracks Todas as faixas disponíveis - - Online - Conectado - - - - + + Show Mostrar - - + + Hide Ocultar @@ -1168,17 +1163,17 @@ connect and stream from you? Faixas reproduzidas recentemente - + New Additions Novas adições - + My recent activity Minha atividade recentes - + Recent activity from %1 Atividade recente de %1 @@ -1186,41 +1181,51 @@ connect and stream from you? SourceItem - + Collection Biblioteca - - + + Latest Additions - + Recently Played - + Loved Tracks Faixas favoritas - + SuperCollection - + + Sorry, we could not find any loved tracks! + + + + Latest additions to your collection - + Latest additions to %1's collection + + + Sorry, we could not find any recent additions! + + Recently Played Tracks @@ -1236,31 +1241,78 @@ connect and stream from you? %1's recently played tracks + + + Sorry, we could not find any recent plays! + + SourceTreeView - + &Copy Link &Copiar link - + &Delete %1 &Eliminar %1 + + + Add to my Playlists + + + + + Add to my Automatic Playlists + + + + + Add to my Stations + + &Export Playlist &Exportar lista de reprodução - + + playlist + + + + + automatic playlist + + + + + station + + + + + Delete %1? + playlist/station/... + + + + + Would you like to delete the %1 <b>"%2"</b>? + e.g. Would you like to delete the playlist named Foobar? + + + + Save XSPF Salvar XSPF - + Playlists (*.xspf) Listas de reprodução (*.xspf) @@ -1541,36 +1593,49 @@ connect and stream from you? Tomahawk::Accounts::AccountDelegate - + Add Account Adicionar Conta - + Remove Account Remover Conta - + %1 downloads - + Online - + Connecting... Conectando... - + Offline + + Tomahawk::Accounts::AccountModel + + + Manual Install Required + + + + + Unfortunately, automatic installation of this resolver is not available or disabled for your platform.<br /><br />Please use "Install from file" above, by fetching it from your distribution or compiling it yourself. Further instructions can be found here:<br /><br />http://www.tomahawk-player.org/resolvers/%1 + + + Tomahawk::Accounts::GoogleWrapper @@ -1626,23 +1691,49 @@ connect and stream from you? Tomahawk::Accounts::LastFmConfig - + + Testing... + + + + Test Login - - + + Importing %1 + e.g. Importing 2012/01/01 + + + + + Importing History... + + + + + History Incomplete. Resume + + + + + Playback History Imported + + + + + Failed Falhou - + Success Sucesso - + Could not contact server @@ -1650,12 +1741,17 @@ connect and stream from you? Tomahawk::Accounts::SpotifyAccount - + Sync with Spotify - + + Re-enable syncing with Spotify + + + + Stop syncing with Spotify @@ -1663,22 +1759,22 @@ connect and stream from you? Tomahawk::Accounts::SpotifyAccountConfig - + Logging in... - + Logged in! - + Failed: %1 - + Log In @@ -1686,7 +1782,7 @@ connect and stream from you? Tomahawk::Accounts::SpotifyAccountFactory - + Play music from and sync your playlists with Spotify Premium @@ -1702,125 +1798,125 @@ connect and stream from you? Tomahawk::Accounts::TwitterConfigWidget - - - + + + Tweet! Tweet! - - + + Status: No saved credentials - - - + + + Authenticate - - - - Status: Credentials saved for %1 - - + Status: Credentials saved for %1 + + + + + De-authenticate - - - - - - - + + + + + + + Tweetin' Error - + The credentials could not be verified. You may wish to try re-authenticating. - + Status: Error validating credentials - + Global Tweet - + Direct Message - + Send Message! - + @Mention - + Send Mention! - + You must enter a user name for this type of tweet. - + Your saved credentials could not be loaded. You may wish to try re-authenticating. - + Your saved credentials could not be verified. You may wish to try re-authenticating. - - + + There was an error posting your status -- sorry! - - + + Tweeted! - + Your tweet has been posted! Seu tweet foi postado! - + There was an error posting your direct message -- sorry! - + Your message has been posted! Sua mensagem foi postada! @@ -1836,7 +1932,7 @@ You may wish to try re-authenticating. Tomahawk::Accounts::ZeroconfFactory - + Automatically connect to Tomahawks on the local network @@ -1844,20 +1940,20 @@ You may wish to try re-authenticating. Tomahawk::ContextMenu - + &Play &Reproduzir - - - + + + Add to &Queue Adicionar à &lista - - + + &Love @@ -1867,40 +1963,45 @@ You may wish to try re-authenticating. - - Show &Album page - - - - - Show &Artist page - - - - + Un-&Love - + &Delete Items &Eliminar itens - + &Continue Playback after this Track - + &Stop Playback after this Track - + + &Show Track Page + + + + &Delete Item &Eliminar item + + + &Show Album Page + + + + + &Show Artist Page + + Tomahawk::CustomPlaylistView @@ -1938,12 +2039,32 @@ You may wish to try re-authenticating. Tomahawk::DropJobNotifier - + + playlist + + + + + artist + + + + + track + + + + + album + + + + Fetching %1 from database Buscando %1 na base de dados - + Parsing %1 %2 Analisando %1 %2 @@ -1951,7 +2072,7 @@ You may wish to try re-authenticating. Tomahawk::DynamicControlList - + Click to collapse Clique para colapsar @@ -1990,17 +2111,17 @@ Por favor, mude os filtros ou tente novamente. Tomahawk::DynamicView - + Add some filters above to seed this station! Adicione alguns filtros para alimentar essa estação! - + Press Generate to get started! Precione Criar para iniciar! - + Add some filters above, and press Generate to get started! Adicione alguns filtros acima e precione Criar para iniciar! @@ -2008,7 +2129,7 @@ Por favor, mude os filtros ou tente novamente. Tomahawk::DynamicWidget - + Station ran out of tracks! Try tweaking the filters for a new set of songs to play. @@ -2320,97 +2441,97 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::EchonestSteerer - + Steer this station: Conduzir essa estação: - + Much less Muito menos - + Less Menos - + A bit less Um pouco menos - + Keep at current Manter atual - + A bit more Um pouco mais - + More Mais - + Much more Muito mais - + Tempo Tempo - + Loudness Mais alto - + Danceability Habilidade de dança - + Energy Energia - + Song Hotttnesss Canções preferidas - + Artist Hotttnesss Artistas preferidos - + Artist Familiarity Familiaridade do artista - + By Description Por descrição - + Enter a description Coloque uma descrição - + Apply steering command Aplicar comando de condução - + Reset all steering commands Redefinir todos os comandos de direção @@ -2426,22 +2547,22 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::InfoSystem::ChartsPlugin - + Top Overall Classificação geral - + Artists Artistas - + Albums Álbuns - + Tracks Faixas @@ -2449,12 +2570,12 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::InfoSystem::FdoNotifyPlugin - + Tomahawk is playing "%1" by %2%3. Tomahawk está tocando "%1" de %2%3. - + on "%1" @@ -2462,27 +2583,27 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::InfoSystem::LastFmInfoPlugin - + Top Tracks - + Loved Tracks - + Hyped Tracks - + Top Artists - + Hyped Artists @@ -2490,7 +2611,7 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::InfoSystem::NewReleasesPlugin - + Albums @@ -2498,7 +2619,7 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::InfoSystem::TwitterInfoPlugin - + Listening to "%1" by %2 and loving it! %3 @@ -2556,37 +2677,37 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Query - + and e - + You - + you - + and e - + %n other(s) - + %1 people %1 pessoas - + loved this track @@ -2602,7 +2723,7 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::ShortenedLinkParser - + Network error parsing shortened link! @@ -2610,36 +2731,46 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) Escaneando (%L1 faixas) - + Scanning Escaneando - + Checking Verificando - + Fetching Buscando - + Parsing Analisando - + Saving (%1%) Guardando (%1%) + + + Online + + + + + Offline + + Tomahawk::SpotifyParser @@ -2652,7 +2783,7 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::XspfUpdater - + Automatically update from XSPF @@ -2660,7 +2791,7 @@ Try tweaking the filters for a new set of songs to play. TomahawkApp - + My Collection Minha biblioteca @@ -2683,39 +2814,48 @@ colocar o número PIN mostrado aqui: TomahawkTrayIcon - - + &Stop Playback after current Track - - + + Hide Tomahawk Window Esconder janela do Tomahawk - + Show Tomahawk Window Mostrar janela do Tomahawk - + Currently not playing. Não reproduzindo nada. - + Play Reporduzir - + Pause Pausar - + + &Love + + + + + Un-&Love + + + + &Continue Playback after current Track @@ -2859,7 +2999,7 @@ colocar o número PIN mostrado aqui: - + Play Reporduzir @@ -2879,137 +3019,172 @@ colocar o número PIN mostrado aqui: Próximo - + + Back + + + + + Go back one page + + + + + Forward + + + + + Go forward one page + + + + Global Search... Busca global... - - + + Check For Updates... Buscar atualizações... - - - + + + Connect To Peer Conectar-se ao par - + Enter peer address: Coloque o endereço do par: - + Enter peer port: Coloque a porta do par: - + Enter peer key: Coloque a chave do par: - + XSPF Error Erro de XSPF - + This is not a valid XSPF playlist. Esta não é uma lista de reprodução XSPF válida. - + Failed to save tracks Falha ao salvar faixas - + Some tracks in the playlist do not contain an artist and a title. They will be ignored. Algumas faixas da lista de reprodução não contem artista e título. Estas serão ignoradas. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Make sure you have a suitable Phonon backend and required plugins installed. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. - + + Station + + + + Create New Station Criar uma nova estação - + Name: Nome: - - New Station - Nova estação + + Playlist + - - New Playlist - Nova lista de reprodução + + Automatic Playlist + - + Pause Pausar - + Go &offline Desc&onectar - + Go &online C&onectar - + Authentication Error Erro de autenticação - + + Error connecting to SIP: Authentication failed! + + + + %1 by %2 track, artist name %1 de %2 - + %1 - %2 current track, some window title %1 - %2 - + <h2><b>Tomahawk %1<br/>(%2)</h2> <h2><b>Tomahawk %1<br/>(%2)</h2> - + <h2><b>Tomahawk %1</h2> <h2><b>Tomahawk %1</h2> - - Copyright 2010 - 2012<br/>Christian Muehlhaeuser &lt;muesli@tomahawk-player.org&gt;<br/><br/>Thanks to: Leo Franchi, Jeff Mitchell, Dominik Schmidt, Jason Herskowitz, Alejandro Wainzinger, Hugo Lindstr&ouml;m, Michael Zanetti, Harald Sitter and Steve Robertson + + Copyright 2010 - 2012 - + + Thanks to: + + + + About Tomahawk Sobre o Tomahawk @@ -3080,7 +3255,7 @@ colocar o número PIN mostrado aqui: TopTracksContext - + Top Hits Maiores sucessos @@ -3128,103 +3303,50 @@ colocar o número PIN mostrado aqui: - + + Lyrics + + + + Similar Tracks + + + Sorry, but we could not find similar tracks for this song! + + - + You've listened to this track %n time(s). - + You've never listened to this track before. - + You first listened to it on %1. - + You've listened to %1 %n time(s). - + You've never listened to %1 before. - - TrackModel - - - Artist - Artista - - - - Title - Título - - - - Album - Álbum - - - - Track - Faixa - - - - Duration - Duração - - - - Bitrate - Taxa de bits - - - - Age - Idate - - - - Year - Ano - - - - Size - Tamanho - - - - Origin - Origem - - - - Score - Pontuação - - - - Composer - - - TrackView - + Sorry, your filter '%1' did not match any results. Desculpe, o seu filtro '%1' não encontreou nenhum resultado. @@ -3245,7 +3367,7 @@ colocar o número PIN mostrado aqui: TreeItemDelegate - + Unknown Desconhecido @@ -3253,63 +3375,31 @@ colocar o número PIN mostrado aqui: TreeModel - - Name - Nome - - - - Duration - Duração - - - - Bitrate - Taxa de bits - - - - Age - Idate - - - - Year - Ano - - - - Size - Tamanho - - - - Origin - Origem - - - - Composer - - - - + All Artists Todos artistas - - + + My Collection Minha biblioteca - - + + Collection of %1 Biblioteca de %1 + + TreeView + + + Sorry, your filter '%1' did not match any results. + + + TwitterConfigWidget @@ -3386,27 +3476,37 @@ Você pode enviar uma outra mensagem de sincronia a qualquer momento simplesment ViewManager - + + After you have scanned your music collection you will find your tracks right here. + + + + + This collection is empty. + + + + SuperCollection - + Combined libraries of all your online friends - + All available albums Todos os álbuns disponíveis - + Recently Played Tracks - + Recently played tracks from all your friends @@ -3429,12 +3529,12 @@ Você pode enviar uma outra mensagem de sincronia a qualquer momento simplesment Faixas reproduzidas recentemente - + No recently created playlists in your network. Nenhuma lista de reprodução criada recentemente na sua rede. - + Welcome to Tomahawk Bem-vindo ao Tomahawk @@ -3442,7 +3542,7 @@ Você pode enviar uma outra mensagem de sincronia a qualquer momento simplesment WhatsHotWidget - + Charts Gráficos @@ -3678,107 +3778,107 @@ Letras de "%1" por %2: XmppSipPlugin - + User Interaction - + Host is unknown - + Item not found Item não encontrado - + Authorization Error - + Remote Stream Error - + Remote Connection failed Conexão Remota falhou - + Internal Server Error - + System shutdown - + Conflict - + Unknown Desconhecido - + No Compression Support - + No Encryption Support - + No Authorization Support - + No Supported Feature - + Add Friend Adicionar Amigo - + Enter Xmpp ID: - + Add Friend... Adicionar Amigo... - + XML Console... - + I'm sorry -- I'm just an automatic presence used by Tomahawk Player (http://gettomahawk.com). If you are getting this message, the person you are trying to reach is probably not signed on, so please try again later! - + Authorize User Autorizar Usuário - + Do you want to grant <b>%1</b> access to your Collection? diff --git a/lang/tomahawk_ru.ts b/lang/tomahawk_ru.ts index 9b3b9ebdc..c139bfcbe 100644 --- a/lang/tomahawk_ru.ts +++ b/lang/tomahawk_ru.ts @@ -2,17 +2,17 @@ AccountFactoryWrapper - + Dialog Диалог - + Description goes here Описание тут - + Add Account Добавить аккаунт @@ -20,17 +20,17 @@ AccountFactoryWrapperDelegate - + Online В сети - + Connecting... Соединяюсь... - + Offline Не в сети @@ -38,13 +38,13 @@ AclJobDelegate - + Error displaying ACL info Ошибка при отображении информации ACL - - + + Allow %1 to connect and stream from you? Разрешить %1 ⏎ @@ -54,69 +54,69 @@ connect and stream from you? ActionCollection - + &Listen Along &Слушать Его - + Stop &Listening Along &Прекратить его слушать - + &Follow in real-time &Следить в реальном времени - - + + &Listen Privately &Слушать Cамому - - + + &Listen Publicly &Слушать со всеми - + &Load Playlist &Загрузить Плейлист - + &Rename Playlist &Переименовать Плейлист - + &Copy Playlist Link &Скопировать Cсылку Плейлиста - + &Play &Играть - + &Stop &Стоп - + &Previous Track &Предыдущий - + &Next Track &Следующий - + &Quit &Выйти @@ -134,30 +134,17 @@ connect and stream from you? Другие альбомы артиста - - - Click to show Official Tracks - Показать Официальные Песни + + Sorry, we could not find any other albums for this artist! + - - - Click to show SuperCollection Tracks - Показать общую коллекцию песен + + Sorry, we could not find any tracks for this album! + - - - Click to show SuperCollection Albums - Показать общую коллекцию альбомов - - - - Click to show Official Albums - Показать официальные альбомы - - - + Other Albums by %1 Другие альбомы %1 @@ -165,35 +152,17 @@ connect and stream from you? AlbumModel - - Album - Альбом - - - - + + All albums from %1 Все альбомы %1 - + All albums Все альбомы - - AlbumView - - - After you have scanned your music collection you will find your latest album additions right here. - После сканирования вашей музыкальной коллекции вы найдете свой альбомы тут. - - - - This collection doesn't have any recent albums. - Эта коллекция не имеет последних альбомов. - - ArtistInfoWidget @@ -212,38 +181,24 @@ connect and stream from you? Похожие исполнители - + Albums Альбомы - - - Click to show SuperCollection Albums - Показать общую коллекцию альбомов + + Sorry, we could not find any albums for this artist! + - - Click to show Official Albums - Показать официальные альбомы - - - - ArtistView - - - After you have scanned your music collection you will find your tracks right here. - После сканирования вашей музыкальной коллекции вы найдете свои песни тут. + + Sorry, we could not find any related artists! + - - This collection is currently empty. - Коллекция пуста. - - - - Sorry, your filter '%1' did not match any results. - К сожалению, '%1' фильтр не найдено ни одного результата. + + Sorry, we could not find any top hits for this artist! + @@ -327,23 +282,26 @@ connect and stream from you? CategoryAddItem - - - New Playlist - Новый плейлист + + Create new Playlist + - - - - + + Create new Station + + + + + + New Station Новая станция - - - + + + %1 Station %1 Станция @@ -369,27 +327,6 @@ connect and stream from you? Очистить - - CollectionFlatModel - - - My Collection - Моя Коллекция - - - - Collection of %1 - Коллекция %1 - - - - CollectionView - - - This collection is empty. - Коллекция пуста. - - ContextWidget @@ -399,12 +336,12 @@ connect and stream from you? - + Show Footnotes Показать Информацию об Исполнителе - + Hide Footnotes Спрятать Информацию об Исполнителе @@ -416,39 +353,49 @@ connect and stream from you? Tomahawk Crash Reporter Отчет о ошибках - + - <p><b>Sorry!</b>&nbsp;Tomahawk crashed. Information about the crash is now being sent to Tomahawk HQ so that we can fix the bug.</p> - <p><b>Упс!</b>&nbsp;Произошла ошибка. Информация о ошибке сейчас отправляется разработчикам, чтобы они могли её исправить. Извиняемся :)</p> + <html><head/><body><p><span style=" font-weight:600;">Sorry!</span> Tomahawk crashed. Please tell us about it! Tomahawk has created an error report for you that can help improve the stability in the future. You can now send this report directly to the Tomahawk developers.</p></body></html> + - + + Send this report + + + + + Don't send + + + + Abort Отменить - + You can disable sending crash reports in the configuration dialog. Вы можете отключить отправку отчетов об ошибках в диалоге настройки. - + Uploaded %L1 of %L2 KB. Загружено %L1 из %L2 KB. - - + + Close Закрыть - + Sent! <b>Many thanks</b>. Отправлен! <b>Большое спасибо!!!</b>. - + Failed to send crash info. Невозможно отправить отчет о ошибке. @@ -464,17 +411,17 @@ connect and stream from you? DelegateConfigWrapper - + About О - + Delete Account Удалить аккаунт - + About this Account Об этом аккаунте @@ -487,24 +434,11 @@ connect and stream from you? Диагностика - - Update - Обновить - - - + Copy to Clipboard Сохранить в буфер обмена - - DropJob - - - No tracks found for given %1 - Не найдено песен по %1 - - GlobalSearchWidget @@ -529,29 +463,11 @@ connect and stream from you? Информация - + Filter... Фильтр... - - JobStatusView - - - Searching For - Искать - - - - Pending - В ожидании - - - - Idle - Бездействие - - LastFmConfig @@ -579,6 +495,11 @@ connect and stream from you? Test Login Проверить + + + Import Playback History + + LastfmContext @@ -627,12 +548,12 @@ connect and stream from you? LoadXSPFDialog - + Load XSPF File Загрузить файл XSPF - + XSPF Files (*.xspf) Файлы XSPF (*.xspf) @@ -681,20 +602,89 @@ connect and stream from you? NewReleasesWidget - + New Releases Новые релизы + + PlayableModel + + + Artist + + + + + Title + + + + + Composer + + + + + Album + + + + + Track + + + + + Duration + + + + + Bitrate + + + + + Age + + + + + Year + + + + + Size + + + + + Origin + + + + + Score + + + + + + Name + + + PlaylistItemDelegate - + played %1 by you Проиграно %1 мной - + played %1 by %2 Песня %1 проиграна %2 @@ -702,17 +692,17 @@ connect and stream from you? PlaylistLargeItemDelegate - + played %1 by you Воспроизводилось %1 вами - + played %1 by %2 Воспроизводилось %1 %2 - + added %1 Добавлено %1 @@ -720,12 +710,12 @@ connect and stream from you? PlaylistModel - + A playlist by %1, created %2 Плейлист %1 создан %2 - + you Вы @@ -781,7 +771,12 @@ connect and stream from you? PlaylistView - + + This playlist is currently empty. + + + + This playlist is currently empty. Add some tracks to it and enjoy the music! Этот плейлист пустой. Добавьте какие-нибудь треки и наслаждайтесь музыкой! @@ -849,82 +844,82 @@ connect and stream from you? QObject - + %n year(s) ago %n год назад%n года назад%n лет назад - + %n year(s) %n год%n года%n лет - + %n month(s) ago %n месяц назад%n месяца назад%n месяцей назад - + %n month(s) %n месяц%n месяца%n месяцей - + %n week(s) ago %n неделю назад%n недели назад%n недель назад - + %n week(s) %n неделю%n недели%n недель - + %n day(s) ago %n день назад%n дня назад%n дней назад - + %n day(s) %n день%n дня%n дней - + %n hour(s) ago %n час назад%n часа назад%n часов назад - + %n hour(s) %n час%n часа%n часов - + %1 minutes ago %1 минут(ы) назад - + %1 minutes %1 минут(ы) - + just now только что - + Friend Finders - + Music Finders Music Finders - + Status Updaters Статус обновления @@ -946,20 +941,30 @@ connect and stream from you? - - Show Queue - Показать Очередь + + Open Queue + - - Hide Queue - Скрыть Очередь + + The queue is currently empty. Drop something to enqueue it! + + + + + Open Queue - %n item(s) + + + + + Close Queue + RelatedArtistsContext - + Related Artists Похожие исполнители @@ -1001,37 +1006,37 @@ connect and stream from you? SettingsDialog - + Collection Коллекция - + Advanced Дополнительны - + All Все - + Some changed settings will not take effect until Tomahawk is restarted - + Services Сервисы - + Install resolver from file Установить resolver из файла - + Information Инофрмация @@ -1082,17 +1087,17 @@ connect and stream from you? TextLabel - + Listening to "%1" by %2 and loving it! %3 - + Listening to "%1" by %2 on "%3" and loving it! %4 - + %1 characters left %1 символов осталось @@ -1125,29 +1130,19 @@ connect and stream from you? Топ 10 - - Offline - Не в сети - - - + All available tracks Доступные песни - - Online - В сети - - - - + + Show Показать - - + + Hide Спрятать @@ -1170,17 +1165,17 @@ connect and stream from you? Последние проигрыные песни - + New Additions Последние добавленые - + My recent activity Моя последняя активность - + Recent activity from %1 Последняя активно %1 @@ -1188,41 +1183,51 @@ connect and stream from you? SourceItem - + Collection Коллекция - - + + Latest Additions Последние добавленные - + Recently Played Последние воспроизводимые - + Loved Tracks Любимые песни - + SuperCollection Общая коллекция - + + Sorry, we could not find any loved tracks! + + + + Latest additions to your collection - + Latest additions to %1's collection + + + Sorry, we could not find any recent additions! + + Recently Played Tracks @@ -1238,31 +1243,78 @@ connect and stream from you? %1's recently played tracks + + + Sorry, we could not find any recent plays! + + SourceTreeView - + &Copy Link &Скопировать Cсылку - + &Delete %1 &Удалить %1 + + + Add to my Playlists + + + + + Add to my Automatic Playlists + + + + + Add to my Stations + + &Export Playlist &Экспорт Плейлиста - + + playlist + + + + + automatic playlist + + + + + station + + + + + Delete %1? + playlist/station/... + + + + + Would you like to delete the %1 <b>"%2"</b>? + e.g. Would you like to delete the playlist named Foobar? + + + + Save XSPF Сохранить XSPF - + Playlists (*.xspf) Плейлисты (*.xspf) @@ -1540,36 +1592,49 @@ connect and stream from you? Tomahawk::Accounts::AccountDelegate - + Add Account Добавить аккаунт - + Remove Account Удалить аккаунт - + %1 downloads %1 загружено - + Online В сети - + Connecting... Соединяюсь... - + Offline Не в сети + + Tomahawk::Accounts::AccountModel + + + Manual Install Required + + + + + Unfortunately, automatic installation of this resolver is not available or disabled for your platform.<br /><br />Please use "Install from file" above, by fetching it from your distribution or compiling it yourself. Further instructions can be found here:<br /><br />http://www.tomahawk-player.org/resolvers/%1 + + + Tomahawk::Accounts::GoogleWrapper @@ -1625,23 +1690,49 @@ connect and stream from you? Tomahawk::Accounts::LastFmConfig - + + Testing... + + + + Test Login - - + + Importing %1 + e.g. Importing 2012/01/01 + + + + + Importing History... + + + + + History Incomplete. Resume + + + + + Playback History Imported + + + + + Failed Неудача - + Success Успех - + Could not contact server Не удается связаться с сервером @@ -1649,12 +1740,17 @@ connect and stream from you? Tomahawk::Accounts::SpotifyAccount - + Sync with Spotify Синхронизировать с Spotify - + + Re-enable syncing with Spotify + + + + Stop syncing with Spotify Прекратить синхронизацию с Spotify @@ -1662,22 +1758,22 @@ connect and stream from you? Tomahawk::Accounts::SpotifyAccountConfig - + Logging in... Вхожу... - + Logged in! Вход выполнен! - + Failed: %1 Ошибка: %1 - + Log In Войти @@ -1685,7 +1781,7 @@ connect and stream from you? Tomahawk::Accounts::SpotifyAccountFactory - + Play music from and sync your playlists with Spotify Premium @@ -1701,128 +1797,128 @@ connect and stream from you? Tomahawk::Accounts::TwitterConfigWidget - - - + + + Tweet! Tweet! - - + + Status: No saved credentials Статус: Нет сохраненных учетных данных - - - + + + Authenticate Аутентификация - - + + Status: Credentials saved for %1 Статус: Полномочия сохранены для %1 - - + + De-authenticate Де-аутентификация - - - - - - - + + + + + + + Tweetin' Error Ошибка твита - + The credentials could not be verified. You may wish to try re-authenticating. Полномочия не могут быть проверены. ⏎ Вы можете попробовать повторно аутенфицироваться. - + Status: Error validating credentials Статус: Ошибка при проверке полномочий - + Global Tweet Global Tweet - + Direct Message Прямое сообщение - + Send Message! Отправить сообщение! - + @Mention @ Упоминание - + Send Mention! Отправить упоминание! - + You must enter a user name for this type of tweet. Вы должны ввести имя пользователя для этого типа твита. - + Your saved credentials could not be loaded. You may wish to try re-authenticating. Сохраненные учетные данные не могут быть загружены. ⏎ Вы можете попробовать повторно аутенфицироваться. - + Your saved credentials could not be verified. You may wish to try re-authenticating. Сохраненные учетные данные не могут быть проверены. ⏎ Вы можете попробовать повторно аутенфицироваться. - - + + There was an error posting your status -- sorry! Ошибка при отправке вашего статуса, извините! - - + + Tweeted! Твитното! - + Your tweet has been posted! Ваш твит был отправлен! - + There was an error posting your direct message -- sorry! Извините! Возникла ошибка при отправке твита. - + Your message has been posted! Ваше сообщение было отправлено! @@ -1838,7 +1934,7 @@ You may wish to try re-authenticating. Tomahawk::Accounts::ZeroconfFactory - + Automatically connect to Tomahawks on the local network Автоматическое подключение к Tomahawks в локальной сети @@ -1846,20 +1942,20 @@ You may wish to try re-authenticating. Tomahawk::ContextMenu - + &Play &Играть - - - + + + Add to &Queue Добавить В &Очередь - - + + &Love &Любимая @@ -1869,40 +1965,45 @@ You may wish to try re-authenticating. &Скопировать Ссылку Песни - - Show &Album page - &Показать Станицу Альбомов - - - - Show &Artist page - &Показать Станицу Исполнителя(ей) - - - + Un-&Love &Не Любимая - + &Delete Items &Удалить Песни - + &Continue Playback after this Track &Продолжить воспроизведение трека после этой песни - + &Stop Playback after this Track &Остановить воспроизведения и после этого трека - + + &Show Track Page + + + + &Delete Item &Удалить Песню + + + &Show Album Page + + + + + &Show Artist Page + + Tomahawk::CustomPlaylistView @@ -1940,12 +2041,32 @@ You may wish to try re-authenticating. Tomahawk::DropJobNotifier - + + playlist + + + + + artist + + + + + track + + + + + album + + + + Fetching %1 from database Выборка %1 из базы данных - + Parsing %1 %2 Анализирую %1 %2 @@ -1953,7 +2074,7 @@ You may wish to try re-authenticating. Tomahawk::DynamicControlList - + Click to collapse Нажмите, чтобы свернуть @@ -1992,17 +2113,17 @@ Please change the filters or try again. Tomahawk::DynamicView - + Add some filters above to seed this station! Добавить некоторые фильтры, чтобы увидеть эту станцию​​! - + Press Generate to get started! Нажмите кнопку Создать, чтобы начать! - + Add some filters above, and press Generate to get started! Добавьте несколько фильтров выше, а затем нажмите Создать, чтобы начать! @@ -2010,7 +2131,7 @@ Please change the filters or try again. Tomahawk::DynamicWidget - + Station ran out of tracks! Try tweaking the filters for a new set of songs to play. @@ -2322,97 +2443,97 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::EchonestSteerer - + Steer this station: Управляй этой станции: - + Much less Гораздо меньше - + Less Менее - + A bit less Чуть меньше - + Keep at current Имейтся в текущих - + A bit more Немного более - + More Более - + Much more Гораздо больше - + Tempo Ритм - + Loudness - + Danceability - + Energy Мощность - + Song Hotttnesss - + Artist Hotttnesss - + Artist Familiarity - + By Description По описанию - + Enter a description Введите описание - + Apply steering command - + Reset all steering commands @@ -2428,22 +2549,22 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::InfoSystem::ChartsPlugin - + Top Overall - + Artists Исполнители - + Albums Альбомы - + Tracks Песни @@ -2451,12 +2572,12 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::InfoSystem::FdoNotifyPlugin - + Tomahawk is playing "%1" by %2%3. Tomahawk играет "%1" %2%3. - + on "%1" @@ -2464,27 +2585,27 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::InfoSystem::LastFmInfoPlugin - + Top Tracks - + Loved Tracks - + Hyped Tracks - + Top Artists - + Hyped Artists @@ -2492,7 +2613,7 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::InfoSystem::NewReleasesPlugin - + Albums Альбом @@ -2500,7 +2621,7 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::InfoSystem::TwitterInfoPlugin - + Listening to "%1" by %2 and loving it! %3 @@ -2558,37 +2679,37 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Query - + and и - + You Ты - + you ты - + and и - + %n other(s) - + %1 people %1 человек - + loved this track любимый @@ -2604,7 +2725,7 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::ShortenedLinkParser - + Network error parsing shortened link! @@ -2612,36 +2733,46 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) Сканирование (%L1 песни) - + Scanning Сканирую - + Checking Проверяю - + Fetching Выбираю - + Parsing Анализирую - + Saving (%1%) Сохраняю (%1%) + + + Online + + + + + Offline + + Tomahawk::SpotifyParser @@ -2654,7 +2785,7 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::XspfUpdater - + Automatically update from XSPF Автоматическое обновление из XSPF @@ -2662,7 +2793,7 @@ Try tweaking the filters for a new set of songs to play. TomahawkApp - + My Collection Моя коллекция @@ -2684,39 +2815,48 @@ enter the displayed PIN number here: TomahawkTrayIcon - - + &Stop Playback after current Track &Остановить после текущего трека - - + + Hide Tomahawk Window Спрятать окно Tomahawk - + Show Tomahawk Window Показать окно Tomahawk - + Currently not playing. Не воспроизводится. - + Play Играть - + Pause Пауза - + + &Love + + + + + Un-&Love + + + + &Continue Playback after current Track &Продолжить воспроизведение после текущего трека @@ -2860,7 +3000,7 @@ enter the displayed PIN number here: - + Play Играть @@ -2880,137 +3020,172 @@ enter the displayed PIN number here: Следующая - + + Back + + + + + Go back one page + + + + + Forward + + + + + Go forward one page + + + + Global Search... Глобальный поиск - - + + Check For Updates... Проверить обновления... - - - + + + Connect To Peer Связаться с Peer - + Enter peer address: Введите адрес узла: - + Enter peer port: Введите адрес порта: - + Enter peer key: Введите адрес ключа: - + XSPF Error Ошибка XSPF - + This is not a valid XSPF playlist. Это не является допустимым XSPF плейлистом. - + Failed to save tracks Не удалось сохранить песни - + Some tracks in the playlist do not contain an artist and a title. They will be ignored. Некоторые треки в плейлисте не содержат исполнителя и название. Они будут проигнорированы. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Make sure you have a suitable Phonon backend and required plugins installed. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. - + + Station + + + + Create New Station Создать новую станцию - + Name: Имя: - - New Station - Новая станция + + Playlist + - - New Playlist - Новый плейлист + + Automatic Playlist + - + Pause Пауза - + Go &offline Отключиться - + Go &online Подлючиться - + Authentication Error Ошибка авторизации - + + Error connecting to SIP: Authentication failed! + + + + %1 by %2 track, artist name - + %1 - %2 current track, some window title - + <h2><b>Tomahawk %1<br/>(%2)</h2> <h2><b>Tomahawk %1<br/>(%2)</h2> - + <h2><b>Tomahawk %1</h2> <h2><b>Tomahawk %1</h2> - - Copyright 2010 - 2012<br/>Christian Muehlhaeuser &lt;muesli@tomahawk-player.org&gt;<br/><br/>Thanks to: Leo Franchi, Jeff Mitchell, Dominik Schmidt, Jason Herskowitz, Alejandro Wainzinger, Hugo Lindstr&ouml;m, Michael Zanetti, Harald Sitter and Steve Robertson - Copyright 2010 - 2012<br/>Christian Muehlhaeuser &lt;muesli@tomahawk-player.org&gt;<br/><br/>Thanks to: Leo Franchi, Jeff Mitchell, Dominik Schmidt, Jason Herskowitz, Alejandro Wainzinger, Hugo Lindstr&ouml;m, Michael Zanetti, Harald Sitter and Steve Robertson + + Copyright 2010 - 2012 + - + + Thanks to: + + + + About Tomahawk О Tomahawk @@ -3081,7 +3256,7 @@ enter the displayed PIN number here: TopTracksContext - + Top Hits Хиты @@ -3129,103 +3304,50 @@ enter the displayed PIN number here: - + + Lyrics + + + + Similar Tracks + + + Sorry, but we could not find similar tracks for this song! + + - + You've listened to this track %n time(s). - + You've never listened to this track before. - + You first listened to it on %1. - + You've listened to %1 %n time(s). - + You've never listened to %1 before. - - TrackModel - - - Artist - Исполнитель - - - - Title - Название - - - - Album - Альбом - - - - Track - Песня - - - - Duration - Длительность - - - - Bitrate - Битрей - - - - Age - Возраст - - - - Year - Год - - - - Size - Размер - - - - Origin - Расположение - - - - Score - Проиграно - - - - Composer - Композитор - - TrackView - + Sorry, your filter '%1' did not match any results. Ваш поиск '%1' недал результатов. @@ -3246,7 +3368,7 @@ enter the displayed PIN number here: TreeItemDelegate - + Unknown Неизвестный @@ -3254,63 +3376,31 @@ enter the displayed PIN number here: TreeModel - - Name - Имя - - - - Duration - Длительность - - - - Bitrate - Битрей - - - - Age - Возраст - - - - Year - Год - - - - Size - Размер - - - - Origin - Расположение - - - - Composer - Композитор - - - + All Artists Все исполнители - - + + My Collection Моя коллекция - - + + Collection of %1 Коллекция из %1 + + TreeView + + + Sorry, your filter '%1' did not match any results. + + + TwitterConfigWidget @@ -3386,27 +3476,37 @@ You can re-send a sync message at any time simply by sending another tweet using ViewManager - + + After you have scanned your music collection you will find your tracks right here. + + + + + This collection is empty. + + + + SuperCollection Общая коллекция - + Combined libraries of all your online friends Комбинированные библиотек всех ваших друзей онлайн - + All available albums Доступные альбомы - + Recently Played Tracks - + Recently played tracks from all your friends @@ -3429,12 +3529,12 @@ You can re-send a sync message at any time simply by sending another tweet using Последние Воспроизводимые Песни - + No recently created playlists in your network. Нет списков, созданных в последнее время в вашей сети. - + Welcome to Tomahawk Добро пожаловать в Tomahawk @@ -3442,7 +3542,7 @@ You can re-send a sync message at any time simply by sending another tweet using WhatsHotWidget - + Charts Чарты @@ -3677,107 +3777,107 @@ Lyrics for "%1" by %2: XmppSipPlugin - + User Interaction Взаимодействие с пользователем - + Host is unknown Неизвестный хост - + Item not found Песня не найдена - + Authorization Error Ошибка авторизации - + Remote Stream Error Удаленный поток ошибок - + Remote Connection failed Ошибка подключения - + Internal Server Error Внутренняя ошибка сервера - + System shutdown Выключение системы - + Conflict Конфликт - + Unknown Неизвестный - + No Compression Support Нет поддержки сжатия - + No Encryption Support Нет поддержки шифрования - + No Authorization Support Нет поддержки авторизации - + No Supported Feature Не поддерживаемые функции - + Add Friend Добавить друга - + Enter Xmpp ID: Введите XMPP ID: - + Add Friend... Добавить друга... - + XML Console... XML Console... - + I'm sorry -- I'm just an automatic presence used by Tomahawk Player (http://gettomahawk.com). If you are getting this message, the person you are trying to reach is probably not signed on, so please try again later! - + Authorize User Авторизация пользователя - + Do you want to grant <b>%1</b> access to your Collection? diff --git a/lang/tomahawk_sv.ts b/lang/tomahawk_sv.ts index 7313bf206..0d5e4907a 100644 --- a/lang/tomahawk_sv.ts +++ b/lang/tomahawk_sv.ts @@ -2,17 +2,17 @@ AccountFactoryWrapper - + Dialog Dialog - + Description goes here Beskrivning här - + Add Account Lägg till konto @@ -20,17 +20,17 @@ AccountFactoryWrapperDelegate - + Online Online - + Connecting... Ansluter... - + Offline Offline @@ -38,13 +38,13 @@ AclJobDelegate - + Error displaying ACL info - - + + Allow %1 to connect and stream from you? @@ -53,69 +53,69 @@ connect and stream from you? ActionCollection - + &Listen Along &Lyssna med - + Stop &Listening Along Sluta &Lyssna med - + &Follow in real-time &Lyssna med i realtid - - + + &Listen Privately &Lyssna privat - - + + &Listen Publicly &Lyssna publikt - + &Load Playlist &Läs in spellista - + &Rename Playlist &Byt namn på spellista - + &Copy Playlist Link &Kopiera länk till spellista - + &Play Spela &upp - + &Stop &Stoppa - + &Previous Track &Föregående spår - + &Next Track &Nästa spår - + &Quit A&vsluta @@ -133,30 +133,17 @@ connect and stream from you? Andra album av artisten - - - Click to show Official Tracks - Klicka för att visa officiella spår + + Sorry, we could not find any other albums for this artist! + - - - Click to show SuperCollection Tracks - Klicka för att visa SuperCollection spår + + Sorry, we could not find any tracks for this album! + - - - Click to show SuperCollection Albums - Klicka för att visa SuperCollection Album - - - - Click to show Official Albums - Klicka för att visa Officiella Album - - - + Other Albums by %1 Andra album av %1 @@ -164,35 +151,17 @@ connect and stream from you? AlbumModel - - Album - Album - - - - + + All albums from %1 Alla album från %1 - + All albums Alla album - - AlbumView - - - After you have scanned your music collection you will find your latest album additions right here. - Efter att du har scannat din musiksamling kommer du finna dina senaste tillagda album här. - - - - This collection doesn't have any recent albums. - Den här samlingen inga nya album. - - ArtistInfoWidget @@ -211,38 +180,24 @@ connect and stream from you? Relaterade artister - + Albums Album - - - Click to show SuperCollection Albums - Klicka för att visa SuperCollection Album + + Sorry, we could not find any albums for this artist! + - - Click to show Official Albums - Klicka för visa Officiella Album - - - - ArtistView - - - After you have scanned your music collection you will find your tracks right here. - Efter att du har scannat din musiksamling kommer du finna dina senaste tillagda låtar här. + + Sorry, we could not find any related artists! + - - This collection is currently empty. - Denna samling är tom. - - - - Sorry, your filter '%1' did not match any results. - Tyvärr, ditt filter "%1" matchade inte några resultat. + + Sorry, we could not find any top hits for this artist! + @@ -326,23 +281,26 @@ connect and stream from you? CategoryAddItem - - - New Playlist - Ny spellista + + Create new Playlist + - - - - + + Create new Station + + + + + + New Station Ny station - - - + + + %1 Station %1 station @@ -368,27 +326,6 @@ connect and stream from you? Töm - - CollectionFlatModel - - - My Collection - Min samling - - - - Collection of %1 - Samling av %1 - - - - CollectionView - - - This collection is empty. - Denna samling är tom. - - ContextWidget @@ -398,12 +335,12 @@ connect and stream from you? - + Show Footnotes Visa fotnot - + Hide Footnotes Dölj fotnot @@ -415,39 +352,49 @@ connect and stream from you? Tomahawk Crash Reporter Tomahawk kraschrapport - + - <p><b>Sorry!</b>&nbsp;Tomahawk crashed. Information about the crash is now being sent to Tomahawk HQ so that we can fix the bug.</p> - <p><b>Oj!</b>&nbsp; Tomahawk kraschade. Information om denna krasch skickas nu till Tomahawk HQ så att vi kan lösa problemet.</p> + <html><head/><body><p><span style=" font-weight:600;">Sorry!</span> Tomahawk crashed. Please tell us about it! Tomahawk has created an error report for you that can help improve the stability in the future. You can now send this report directly to the Tomahawk developers.</p></body></html> + - + + Send this report + + + + + Don't send + + + + Abort Avbryt - + You can disable sending crash reports in the configuration dialog. Du kan välja att inte skicka kraschrapporter i konfigurationsdialogen. - + Uploaded %L1 of %L2 KB. Laddade upp %L1 av %L2 KB. - - + + Close Stäng - + Sent! <b>Many thanks</b>. Skickat! <b>Tack så mycket</b>. - + Failed to send crash info. Misslyckades skicka kraschrapport. @@ -463,17 +410,17 @@ connect and stream from you? DelegateConfigWrapper - + About - + Delete Account Ta bort konto - + About this Account @@ -486,24 +433,11 @@ connect and stream from you? Diagnostik för Tomahawk - - Update - Uppdatera - - - + Copy to Clipboard Kopiera till urklipp - - DropJob - - - No tracks found for given %1 - Inga låtar hittades för %1 - - GlobalSearchWidget @@ -528,29 +462,11 @@ connect and stream from you? Inforad - + Filter... Filter... - - JobStatusView - - - Searching For - Söker efter - - - - Pending - Väntar - - - - Idle - Overksam - - LastFmConfig @@ -578,6 +494,11 @@ connect and stream from you? Test Login Testinloggning + + + Import Playback History + + LastfmContext @@ -626,12 +547,12 @@ connect and stream from you? LoadXSPFDialog - + Load XSPF File Läs in XSPF-fil - + XSPF Files (*.xspf) XSPF Filer (*.xspf) @@ -680,20 +601,89 @@ connect and stream from you? NewReleasesWidget - + New Releases + + PlayableModel + + + Artist + + + + + Title + + + + + Composer + + + + + Album + + + + + Track + + + + + Duration + + + + + Bitrate + + + + + Age + + + + + Year + + + + + Size + + + + + Origin + + + + + Score + + + + + + Name + + + PlaylistItemDelegate - + played %1 by you %1 spelades av dig - + played %1 by %2 %1 spelades av %2 @@ -701,17 +691,17 @@ connect and stream from you? PlaylistLargeItemDelegate - + played %1 by you %1 spelades av dig - + played %1 by %2 %1 spelades av %2 - + added %1 %1 tillagd @@ -719,12 +709,12 @@ connect and stream from you? PlaylistModel - + A playlist by %1, created %2 En spellista av %1, skapad %2 - + you dig @@ -780,7 +770,12 @@ connect and stream from you? PlaylistView - + + This playlist is currently empty. + + + + This playlist is currently empty. Add some tracks to it and enjoy the music! Denna spellista är för närvarande tom. Lägg till några spår och njut av musiken! @@ -848,82 +843,82 @@ connect and stream from you? QObject - + %n year(s) ago %n år sedan%n år sedan - + %n year(s) %n år sedan%n år sedan - + %n month(s) ago %n månad sedan%n månader sedan - + %n month(s) %n månad%n månader - + %n week(s) ago %n vecka sedan%n veckor sedan - + %n week(s) %n vecka%n veckor - + %n day(s) ago %n dag sedan%n dagar sedan - + %n day(s) %n dag%n dagar - + %n hour(s) ago %n timme sedan%n timmar sedan - + %n hour(s) %n timme%n timmar - + %1 minutes ago %1 minuter sedan - + %1 minutes %1 minuter - + just now precis nyss - + Friend Finders Hitta vänner - + Music Finders Hitta musik - + Status Updaters Status uppdaterare @@ -945,20 +940,30 @@ connect and stream from you? - - Show Queue - Visa kö + + Open Queue + - - Hide Queue - Dölj kö + + The queue is currently empty. Drop something to enqueue it! + + + + + Open Queue - %n item(s) + + + + + Close Queue + RelatedArtistsContext - + Related Artists Relaterade artister @@ -1000,37 +1005,37 @@ connect and stream from you? SettingsDialog - + Collection Samling - + Advanced Avancerat - + All Alla - + Some changed settings will not take effect until Tomahawk is restarted - + Services Tjänster - + Install resolver from file Installera resolver från fil - + Information Information @@ -1081,17 +1086,17 @@ connect and stream from you? - + Listening to "%1" by %2 and loving it! %3 - + Listening to "%1" by %2 on "%3" and loving it! %4 - + %1 characters left @@ -1124,29 +1129,19 @@ connect and stream from you? Topp 10 - - Offline - Frånkopplad - - - + All available tracks Alla tillgängliga spår - - Online - Ansluten - - - - + + Show Visa - - + + Hide Göm @@ -1169,17 +1164,17 @@ connect and stream from you? Senaste spelade spår - + New Additions Nya tillägg - + My recent activity Min senaste aktivitet - + Recent activity from %1 Senaste aktivitet från %1 @@ -1187,41 +1182,51 @@ connect and stream from you? SourceItem - + Collection Samling - - + + Latest Additions Senast tillagda - + Recently Played Senast spelade spår - + Loved Tracks Älskade låtar - + SuperCollection SuperCollection - + + Sorry, we could not find any loved tracks! + + + + Latest additions to your collection - + Latest additions to %1's collection + + + Sorry, we could not find any recent additions! + + Recently Played Tracks @@ -1237,31 +1242,78 @@ connect and stream from you? %1's recently played tracks + + + Sorry, we could not find any recent plays! + + SourceTreeView - + &Copy Link &Kopiera länk - + &Delete %1 &Ta bort %1 + + + Add to my Playlists + + + + + Add to my Automatic Playlists + + + + + Add to my Stations + + &Export Playlist &Exportera spellista - + + playlist + + + + + automatic playlist + + + + + station + + + + + Delete %1? + playlist/station/... + + + + + Would you like to delete the %1 <b>"%2"</b>? + e.g. Would you like to delete the playlist named Foobar? + + + + Save XSPF Spara XSPF - + Playlists (*.xspf) Spellistor (*.xspf) @@ -1539,36 +1591,49 @@ connect and stream from you? Tomahawk::Accounts::AccountDelegate - + Add Account - + Remove Account - + %1 downloads - + Online - + Connecting... - + Offline + + Tomahawk::Accounts::AccountModel + + + Manual Install Required + + + + + Unfortunately, automatic installation of this resolver is not available or disabled for your platform.<br /><br />Please use "Install from file" above, by fetching it from your distribution or compiling it yourself. Further instructions can be found here:<br /><br />http://www.tomahawk-player.org/resolvers/%1 + + + Tomahawk::Accounts::GoogleWrapper @@ -1624,23 +1689,49 @@ connect and stream from you? Tomahawk::Accounts::LastFmConfig - + + Testing... + + + + Test Login - - + + Importing %1 + e.g. Importing 2012/01/01 + + + + + Importing History... + + + + + History Incomplete. Resume + + + + + Playback History Imported + + + + + Failed - + Success - + Could not contact server @@ -1648,12 +1739,17 @@ connect and stream from you? Tomahawk::Accounts::SpotifyAccount - + Sync with Spotify - + + Re-enable syncing with Spotify + + + + Stop syncing with Spotify @@ -1661,22 +1757,22 @@ connect and stream from you? Tomahawk::Accounts::SpotifyAccountConfig - + Logging in... - + Logged in! - + Failed: %1 - + Log In @@ -1684,7 +1780,7 @@ connect and stream from you? Tomahawk::Accounts::SpotifyAccountFactory - + Play music from and sync your playlists with Spotify Premium @@ -1700,125 +1796,125 @@ connect and stream from you? Tomahawk::Accounts::TwitterConfigWidget - - - + + + Tweet! - - + + Status: No saved credentials - - - + + + Authenticate - - - - Status: Credentials saved for %1 - - + Status: Credentials saved for %1 + + + + + De-authenticate - - - - - - - + + + + + + + Tweetin' Error - + The credentials could not be verified. You may wish to try re-authenticating. - + Status: Error validating credentials - + Global Tweet - + Direct Message - + Send Message! - + @Mention - + Send Mention! - + You must enter a user name for this type of tweet. - + Your saved credentials could not be loaded. You may wish to try re-authenticating. - + Your saved credentials could not be verified. You may wish to try re-authenticating. - - + + There was an error posting your status -- sorry! - - + + Tweeted! - + Your tweet has been posted! - + There was an error posting your direct message -- sorry! - + Your message has been posted! @@ -1834,7 +1930,7 @@ You may wish to try re-authenticating. Tomahawk::Accounts::ZeroconfFactory - + Automatically connect to Tomahawks on the local network @@ -1842,20 +1938,20 @@ You may wish to try re-authenticating. Tomahawk::ContextMenu - + &Play Spela &upp - - - + + + Add to &Queue Lägg till i &kö - - + + &Love @@ -1865,40 +1961,45 @@ You may wish to try re-authenticating. - - Show &Album page - - - - - Show &Artist page - - - - + Un-&Love - + &Delete Items &Ta bort objekt - + &Continue Playback after this Track - + &Stop Playback after this Track - + + &Show Track Page + + + + &Delete Item &Ta bort objekt + + + &Show Album Page + + + + + &Show Artist Page + + Tomahawk::CustomPlaylistView @@ -1936,12 +2037,32 @@ You may wish to try re-authenticating. Tomahawk::DropJobNotifier - + + playlist + + + + + artist + + + + + track + + + + + album + + + + Fetching %1 from database Hämtar %1 från databasen - + Parsing %1 %2 Tolkar %1 %2 @@ -1949,7 +2070,7 @@ You may wish to try re-authenticating. Tomahawk::DynamicControlList - + Click to collapse Klicka för att fälla in @@ -1986,17 +2107,17 @@ Please change the filters or try again. Tomahawk::DynamicView - + Add some filters above to seed this station! - + Press Generate to get started! - + Add some filters above, and press Generate to get started! @@ -2004,7 +2125,7 @@ Please change the filters or try again. Tomahawk::DynamicWidget - + Station ran out of tracks! Try tweaking the filters for a new set of songs to play. @@ -2316,97 +2437,97 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::EchonestSteerer - + Steer this station: - + Much less Mycket mindre - + Less Mindre - + A bit less Lite mindre - + Keep at current - + A bit more Lite mer - + More Mer - + Much more Mycket mer - + Tempo Tempo - + Loudness Loudness - + Danceability - + Energy Energi - + Song Hotttnesss - + Artist Hotttnesss - + Artist Familiarity - + By Description Efter beskrivning - + Enter a description Ange en beskrivning - + Apply steering command - + Reset all steering commands @@ -2422,22 +2543,22 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::InfoSystem::ChartsPlugin - + Top Overall - + Artists Artister - + Albums Album - + Tracks Spår @@ -2445,12 +2566,12 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::InfoSystem::FdoNotifyPlugin - + Tomahawk is playing "%1" by %2%3. - + on "%1" @@ -2458,27 +2579,27 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::InfoSystem::LastFmInfoPlugin - + Top Tracks - + Loved Tracks - + Hyped Tracks - + Top Artists - + Hyped Artists @@ -2486,7 +2607,7 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::InfoSystem::NewReleasesPlugin - + Albums @@ -2494,7 +2615,7 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::InfoSystem::TwitterInfoPlugin - + Listening to "%1" by %2 and loving it! %3 @@ -2552,37 +2673,37 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Query - + and - + You - + you - + and - + %n other(s) - + %1 people - + loved this track @@ -2598,7 +2719,7 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::ShortenedLinkParser - + Network error parsing shortened link! @@ -2606,36 +2727,46 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) Söker igenom (%L1 spår) - + Scanning - + Checking Kontrollerar - + Fetching Hämtar - + Parsing Tolkar - + Saving (%1%) Sparar (%1%) + + + Online + + + + + Offline + + Tomahawk::SpotifyParser @@ -2648,7 +2779,7 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::XspfUpdater - + Automatically update from XSPF @@ -2656,7 +2787,7 @@ Try tweaking the filters for a new set of songs to play. TomahawkApp - + My Collection @@ -2678,39 +2809,48 @@ enter the displayed PIN number here: TomahawkTrayIcon - - + &Stop Playback after current Track - - + + Hide Tomahawk Window Dölj Tomahawk-fönstret - + Show Tomahawk Window Visa Tomahawk-fönstret - + Currently not playing. Spelar ingenting för närvarande. - + Play Spela upp - + Pause Paus - + + &Love + + + + + Un-&Love + + + + &Continue Playback after current Track @@ -2854,7 +2994,7 @@ enter the displayed PIN number here: - + Play Spela upp @@ -2874,137 +3014,172 @@ enter the displayed PIN number here: Nästa - + + Back + + + + + Go back one page + + + + + Forward + + + + + Go forward one page + + + + Global Search... Global sökning... - - + + Check For Updates... Leta efter uppdateringar... - - - + + + Connect To Peer Anslut till klient - + Enter peer address: Ange klientadress: - + Enter peer port: Ange klientport: - + Enter peer key: Ange klientnyckel: - + XSPF Error XSPF-fel - + This is not a valid XSPF playlist. Detta är inte en giltig XSPF-spellista. - + Failed to save tracks Misslyckades med att spara spår - + Some tracks in the playlist do not contain an artist and a title. They will be ignored. Några spår i spellistan innehåller inte någon artist och titel. De kommer att ignoreras. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Make sure you have a suitable Phonon backend and required plugins installed. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. - + + Station + + + + Create New Station Skapa ny station - + Name: Namn: - - New Station - Ny station + + Playlist + - - New Playlist - Ny spellista + + Automatic Playlist + - + Pause Paus - + Go &offline Koppla &från - + Go &online A&nslut - + Authentication Error Autentiseringsfel - + + Error connecting to SIP: Authentication failed! + + + + %1 by %2 track, artist name %1 av %2 - + %1 - %2 current track, some window title %1 - %2 - + <h2><b>Tomahawk %1<br/>(%2)</h2> - + <h2><b>Tomahawk %1</h2> - - Copyright 2010 - 2012<br/>Christian Muehlhaeuser &lt;muesli@tomahawk-player.org&gt;<br/><br/>Thanks to: Leo Franchi, Jeff Mitchell, Dominik Schmidt, Jason Herskowitz, Alejandro Wainzinger, Hugo Lindstr&ouml;m, Michael Zanetti, Harald Sitter and Steve Robertson + + Copyright 2010 - 2012 - + + Thanks to: + + + + About Tomahawk @@ -3075,7 +3250,7 @@ enter the displayed PIN number here: TopTracksContext - + Top Hits @@ -3123,103 +3298,50 @@ enter the displayed PIN number here: - + + Lyrics + + + + Similar Tracks + + + Sorry, but we could not find similar tracks for this song! + + - + You've listened to this track %n time(s). - + You've never listened to this track before. - + You first listened to it on %1. - + You've listened to %1 %n time(s). - + You've never listened to %1 before. - - TrackModel - - - Artist - Artist - - - - Title - Titel - - - - Album - Album - - - - Track - Spår - - - - Duration - Speltid - - - - Bitrate - Bitfrekvens - - - - Age - Ålder - - - - Year - År - - - - Size - Storlek - - - - Origin - Källa - - - - Score - Betyg - - - - Composer - - - TrackView - + Sorry, your filter '%1' did not match any results. Entschuldige, dein Filter '%1' erzeugte keine Ergebnisse. @@ -3240,7 +3362,7 @@ enter the displayed PIN number here: TreeItemDelegate - + Unknown Okänt @@ -3248,63 +3370,31 @@ enter the displayed PIN number here: TreeModel - - Name - Namn - - - - Duration - Speltid - - - - Bitrate - Bitfrekvens - - - - Age - Ålder - - - - Year - År - - - - Size - Storlek - - - - Origin - Källa - - - - Composer - - - - + All Artists Alla artister - - + + My Collection Min samling - - + + Collection of %1 Samling av %1 + + TreeView + + + Sorry, your filter '%1' did not match any results. + + + TwitterConfigWidget @@ -3376,27 +3466,37 @@ You can re-send a sync message at any time simply by sending another tweet using ViewManager - + + After you have scanned your music collection you will find your tracks right here. + + + + + This collection is empty. + + + + SuperCollection - + Combined libraries of all your online friends - + All available albums Alla tillgängliga album - + Recently Played Tracks - + Recently played tracks from all your friends @@ -3419,12 +3519,12 @@ You can re-send a sync message at any time simply by sending another tweet using Senaste spelade spår - + No recently created playlists in your network. Inga skapade spellistor i ditt nätverk på sistone. - + Welcome to Tomahawk Välkommen till Tomahawk @@ -3432,7 +3532,7 @@ You can re-send a sync message at any time simply by sending another tweet using WhatsHotWidget - + Charts Topplistor @@ -3661,107 +3761,107 @@ Lyrics for "%1" by %2: XmppSipPlugin - + User Interaction - + Host is unknown - + Item not found - + Authorization Error - + Remote Stream Error - + Remote Connection failed - + Internal Server Error - + System shutdown - + Conflict - + Unknown - + No Compression Support - + No Encryption Support - + No Authorization Support - + No Supported Feature - + Add Friend - + Enter Xmpp ID: - + Add Friend... - + XML Console... - + I'm sorry -- I'm just an automatic presence used by Tomahawk Player (http://gettomahawk.com). If you are getting this message, the person you are trying to reach is probably not signed on, so please try again later! - + Authorize User - + Do you want to grant <b>%1</b> access to your Collection? diff --git a/lang/tomahawk_tr.ts b/lang/tomahawk_tr.ts index 9faf9d27f..7c10de425 100644 --- a/lang/tomahawk_tr.ts +++ b/lang/tomahawk_tr.ts @@ -2,17 +2,17 @@ AccountFactoryWrapper - + Dialog İletişim Kutusu - + Description goes here Buraya açıklama gelecek - + Add Account Hesap Ekle @@ -20,17 +20,17 @@ AccountFactoryWrapperDelegate - + Online Çevrimiçi - + Connecting... Bağlanıyor... - + Offline Çevrimdışı @@ -38,13 +38,13 @@ AclJobDelegate - + Error displaying ACL info - - + + Allow %1 to connect and stream from you? @@ -53,69 +53,69 @@ connect and stream from you? ActionCollection - + &Listen Along &Birlikte Dinle - + Stop &Listening Along &Birlikte Dinlemeyi Durdur - + &Follow in real-time &Gerçek zamanlı takip et - - + + &Listen Privately &Gizli Dinle - - + + &Listen Publicly &Yayınlayarak Dinle - + &Load Playlist &Şarkı Listesini Yükle - + &Rename Playlist &Şarkı Listesini Yeniden Adlandır - + &Copy Playlist Link &Şarkı Listesinin Bağlantısını Kopyala - + &Play &Yürüt - + &Stop &Durdur - + &Previous Track &Önceki Parça - + &Next Track &Sonraki Parça - + &Quit &Çıkış @@ -133,30 +133,17 @@ connect and stream from you? Sanatçının Diğer Albümleri - - - Click to show Official Tracks - Resmi Parçaları Göstermek için Tıklayın + + Sorry, we could not find any other albums for this artist! + - - - Click to show SuperCollection Tracks - SüperKoleksiyon Parçalarını Göstermek için Tıklayın + + Sorry, we could not find any tracks for this album! + - - - Click to show SuperCollection Albums - SüperKoleksiyon Albümlerini Göstermek için Tıklayın - - - - Click to show Official Albums - Resmi Albümleri Göstermek için Tıklayın - - - + Other Albums by %1 Diğer %1 Albümleri @@ -164,35 +151,17 @@ connect and stream from you? AlbumModel - - Album - Albüm - - - - + + All albums from %1 Bütün %1 albümleri - + All albums Bütün albümler - - AlbumView - - - After you have scanned your music collection you will find your latest album additions right here. - Müzik koleksiyonunuzu taradıktan sonra en son eklenen albümleri burada bulacaksınız. - - - - This collection doesn't have any recent albums. - Bu koleksiyonda en son eklenen bir albüm yok. - - ArtistInfoWidget @@ -211,38 +180,24 @@ connect and stream from you? Benzer Sanatçılar - + Albums Albümler - - - Click to show SuperCollection Albums - SüperKoleksiyon Albümlerini göstermek için tıklayın + + Sorry, we could not find any albums for this artist! + - - Click to show Official Albums - Resmi Albümleri görmek için tıklayın - - - - ArtistView - - - After you have scanned your music collection you will find your tracks right here. - Müzik koleksiyonunuzu taradıktan parçalarınız tam burada bulacaksınız. + + Sorry, we could not find any related artists! + - - This collection is currently empty. - Bu koleksiyon şu anda boş. - - - - Sorry, your filter '%1' did not match any results. - Üzgünüm, '%1' filtresi hiçbir sonuç getirmedi. + + Sorry, we could not find any top hits for this artist! + @@ -326,23 +281,26 @@ connect and stream from you? CategoryAddItem - - - New Playlist - Yeni Şarkı Listesi + + Create new Playlist + - - - - + + Create new Station + + + + + + New Station Yeni İstasyon - - - + + + %1 Station %1 İstasyon @@ -368,27 +326,6 @@ connect and stream from you? Temizle - - CollectionFlatModel - - - My Collection - Koleksiyonum - - - - Collection of %1 - %1 Koleksiyonu - - - - CollectionView - - - This collection is empty. - Bu koleksiyon boş. - - ContextWidget @@ -398,12 +335,12 @@ connect and stream from you? - + Show Footnotes Dipnotları Göster - + Hide Footnotes Dipnotları Gizle @@ -415,39 +352,49 @@ connect and stream from you? Tomahawk Crash Reporter Tomahawk Çökme Raporcusu - + - <p><b>Sorry!</b>&nbsp;Tomahawk crashed. Information about the crash is now being sent to Tomahawk HQ so that we can fix the bug.</p> - <p><b>Üzgünüm!</b>&nbsp; Tomahawk çöktü. Hatayı çözmebilmemiz için çökme ile ilgili bilgi şu anda Tomahawk Merkezine gönderiliyor.<p> + <html><head/><body><p><span style=" font-weight:600;">Sorry!</span> Tomahawk crashed. Please tell us about it! Tomahawk has created an error report for you that can help improve the stability in the future. You can now send this report directly to the Tomahawk developers.</p></body></html> + - + + Send this report + + + + + Don't send + + + + Abort Vazgeç - + You can disable sending crash reports in the configuration dialog. Çökme raporu göndermeyi yapılandırma iletişim kutusundan devre dışı bırakabilirsiz. - + Uploaded %L1 of %L2 KB. %L2 içinden %L1 KB karşıya yüklendin. - - + + Close Kapat. - + Sent! <b>Many thanks</b>. Gönderildi! <b>Çok teşekkürler.</b>. - + Failed to send crash info. Çökme bilgisi gönderimi başarısız. @@ -463,17 +410,17 @@ connect and stream from you? DelegateConfigWrapper - + About - + Delete Account Hesabı Sil - + About this Account @@ -486,24 +433,11 @@ connect and stream from you? Tomahawk Tanılama - - Update - Güncelle - - - + Copy to Clipboard Pano'ya Kopyala - - DropJob - - - No tracks found for given %1 - Verilen %1 için hiç parça bulunamadı. - - GlobalSearchWidget @@ -528,29 +462,11 @@ connect and stream from you? BilgiÇubuğu - + Filter... Filtre... - - JobStatusView - - - Searching For - Şunu Ara: - - - - Pending - Beklemede - - - - Idle - Boşta - - LastFmConfig @@ -578,6 +494,11 @@ connect and stream from you? Test Login Bilgileri Test Et + + + Import Playback History + + LastfmContext @@ -626,12 +547,12 @@ connect and stream from you? LoadXSPFDialog - + Load XSPF File XSPF Dosyası Yükle - + XSPF Files (*.xspf) XSPF Dosyaları (*.xspf) @@ -680,20 +601,89 @@ connect and stream from you? NewReleasesWidget - + New Releases + + PlayableModel + + + Artist + + + + + Title + + + + + Composer + + + + + Album + + + + + Track + + + + + Duration + + + + + Bitrate + + + + + Age + + + + + Year + + + + + Size + + + + + Origin + + + + + Score + + + + + + Name + + + PlaylistItemDelegate - + played %1 by you - + played %1 by %2 @@ -701,17 +691,17 @@ connect and stream from you? PlaylistLargeItemDelegate - + played %1 by you - + played %1 by %2 - + added %1 @@ -719,12 +709,12 @@ connect and stream from you? PlaylistModel - + A playlist by %1, created %2 - + you @@ -780,7 +770,12 @@ connect and stream from you? PlaylistView - + + This playlist is currently empty. + + + + This playlist is currently empty. Add some tracks to it and enjoy the music! @@ -847,82 +842,82 @@ connect and stream from you? QObject - + %n year(s) ago - + %n year(s) - + %n month(s) ago - + %n month(s) - + %n week(s) ago - + %n week(s) - + %n day(s) ago - + %n day(s) - + %n hour(s) ago - + %n hour(s) - + %1 minutes ago - + %1 minutes - + just now - + Friend Finders - + Music Finders - + Status Updaters @@ -944,20 +939,30 @@ connect and stream from you? - - Show Queue + + Open Queue - - Hide Queue + + The queue is currently empty. Drop something to enqueue it! + + + + + Open Queue - %n item(s) + + + + + Close Queue RelatedArtistsContext - + Related Artists @@ -999,37 +1004,37 @@ connect and stream from you? SettingsDialog - + Collection - + Advanced - + All - + Some changed settings will not take effect until Tomahawk is restarted - + Services - + Install resolver from file - + Information @@ -1080,17 +1085,17 @@ connect and stream from you? - + Listening to "%1" by %2 and loving it! %3 - + Listening to "%1" by %2 on "%3" and loving it! %4 - + %1 characters left @@ -1123,29 +1128,19 @@ connect and stream from you? - - Offline - - - - + All available tracks - - Online - - - - - + + Show - - + + Hide @@ -1168,17 +1163,17 @@ connect and stream from you? - + New Additions - + My recent activity - + Recent activity from %1 @@ -1186,41 +1181,51 @@ connect and stream from you? SourceItem - + Collection - - + + Latest Additions - + Recently Played - + Loved Tracks - + SuperCollection - + + Sorry, we could not find any loved tracks! + + + + Latest additions to your collection - + Latest additions to %1's collection + + + Sorry, we could not find any recent additions! + + Recently Played Tracks @@ -1236,31 +1241,78 @@ connect and stream from you? %1's recently played tracks + + + Sorry, we could not find any recent plays! + + SourceTreeView - + &Copy Link - + &Delete %1 + + + Add to my Playlists + + + + + Add to my Automatic Playlists + + + + + Add to my Stations + + &Export Playlist - + + playlist + + + + + automatic playlist + + + + + station + + + + + Delete %1? + playlist/station/... + + + + + Would you like to delete the %1 <b>"%2"</b>? + e.g. Would you like to delete the playlist named Foobar? + + + + Save XSPF - + Playlists (*.xspf) @@ -1538,36 +1590,49 @@ connect and stream from you? Tomahawk::Accounts::AccountDelegate - + Add Account - + Remove Account - + %1 downloads - + Online - + Connecting... - + Offline + + Tomahawk::Accounts::AccountModel + + + Manual Install Required + + + + + Unfortunately, automatic installation of this resolver is not available or disabled for your platform.<br /><br />Please use "Install from file" above, by fetching it from your distribution or compiling it yourself. Further instructions can be found here:<br /><br />http://www.tomahawk-player.org/resolvers/%1 + + + Tomahawk::Accounts::GoogleWrapper @@ -1623,23 +1688,49 @@ connect and stream from you? Tomahawk::Accounts::LastFmConfig - + + Testing... + + + + Test Login - - + + Importing %1 + e.g. Importing 2012/01/01 + + + + + Importing History... + + + + + History Incomplete. Resume + + + + + Playback History Imported + + + + + Failed - + Success - + Could not contact server @@ -1647,12 +1738,17 @@ connect and stream from you? Tomahawk::Accounts::SpotifyAccount - + Sync with Spotify - + + Re-enable syncing with Spotify + + + + Stop syncing with Spotify @@ -1660,22 +1756,22 @@ connect and stream from you? Tomahawk::Accounts::SpotifyAccountConfig - + Logging in... - + Logged in! - + Failed: %1 - + Log In @@ -1683,7 +1779,7 @@ connect and stream from you? Tomahawk::Accounts::SpotifyAccountFactory - + Play music from and sync your playlists with Spotify Premium @@ -1699,125 +1795,125 @@ connect and stream from you? Tomahawk::Accounts::TwitterConfigWidget - - - + + + Tweet! - - + + Status: No saved credentials - - - + + + Authenticate - - - - Status: Credentials saved for %1 - - + Status: Credentials saved for %1 + + + + + De-authenticate - - - - - - - + + + + + + + Tweetin' Error - + The credentials could not be verified. You may wish to try re-authenticating. - + Status: Error validating credentials - + Global Tweet - + Direct Message - + Send Message! - + @Mention - + Send Mention! - + You must enter a user name for this type of tweet. - + Your saved credentials could not be loaded. You may wish to try re-authenticating. - + Your saved credentials could not be verified. You may wish to try re-authenticating. - - + + There was an error posting your status -- sorry! - - + + Tweeted! - + Your tweet has been posted! - + There was an error posting your direct message -- sorry! - + Your message has been posted! @@ -1833,7 +1929,7 @@ You may wish to try re-authenticating. Tomahawk::Accounts::ZeroconfFactory - + Automatically connect to Tomahawks on the local network @@ -1841,20 +1937,20 @@ You may wish to try re-authenticating. Tomahawk::ContextMenu - + &Play - - - + + + Add to &Queue - - + + &Love @@ -1864,40 +1960,45 @@ You may wish to try re-authenticating. - - Show &Album page - - - - - Show &Artist page - - - - + Un-&Love - + &Delete Items - + &Continue Playback after this Track - + &Stop Playback after this Track - + + &Show Track Page + + + + &Delete Item + + + &Show Album Page + + + + + &Show Artist Page + + Tomahawk::CustomPlaylistView @@ -1935,12 +2036,32 @@ You may wish to try re-authenticating. Tomahawk::DropJobNotifier - + + playlist + + + + + artist + + + + + track + + + + + album + + + + Fetching %1 from database - + Parsing %1 %2 @@ -1948,7 +2069,7 @@ You may wish to try re-authenticating. Tomahawk::DynamicControlList - + Click to collapse @@ -1985,17 +2106,17 @@ Please change the filters or try again. Tomahawk::DynamicView - + Add some filters above to seed this station! - + Press Generate to get started! - + Add some filters above, and press Generate to get started! @@ -2003,7 +2124,7 @@ Please change the filters or try again. Tomahawk::DynamicWidget - + Station ran out of tracks! Try tweaking the filters for a new set of songs to play. @@ -2315,97 +2436,97 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::EchonestSteerer - + Steer this station: - + Much less - + Less - + A bit less - + Keep at current - + A bit more - + More - + Much more - + Tempo - + Loudness - + Danceability - + Energy - + Song Hotttnesss - + Artist Hotttnesss - + Artist Familiarity - + By Description - + Enter a description - + Apply steering command - + Reset all steering commands @@ -2421,22 +2542,22 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::InfoSystem::ChartsPlugin - + Top Overall - + Artists - + Albums - + Tracks @@ -2444,12 +2565,12 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::InfoSystem::FdoNotifyPlugin - + Tomahawk is playing "%1" by %2%3. - + on "%1" @@ -2457,27 +2578,27 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::InfoSystem::LastFmInfoPlugin - + Top Tracks - + Loved Tracks - + Hyped Tracks - + Top Artists - + Hyped Artists @@ -2485,7 +2606,7 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::InfoSystem::NewReleasesPlugin - + Albums @@ -2493,7 +2614,7 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::InfoSystem::TwitterInfoPlugin - + Listening to "%1" by %2 and loving it! %3 @@ -2551,37 +2672,37 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Query - + and - + You - + you - + and - + %n other(s) - + %1 people - + loved this track @@ -2597,7 +2718,7 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::ShortenedLinkParser - + Network error parsing shortened link! @@ -2605,36 +2726,46 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) - + Scanning - + Checking - + Fetching - + Parsing - + Saving (%1%) + + + Online + + + + + Offline + + Tomahawk::SpotifyParser @@ -2647,7 +2778,7 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::XspfUpdater - + Automatically update from XSPF @@ -2655,7 +2786,7 @@ Try tweaking the filters for a new set of songs to play. TomahawkApp - + My Collection @@ -2677,39 +2808,48 @@ enter the displayed PIN number here: TomahawkTrayIcon - - + &Stop Playback after current Track - - + + Hide Tomahawk Window - + Show Tomahawk Window - + Currently not playing. - + Play - + Pause - + + &Love + + + + + Un-&Love + + + + &Continue Playback after current Track @@ -2853,7 +2993,7 @@ enter the displayed PIN number here: - + Play @@ -2873,137 +3013,172 @@ enter the displayed PIN number here: - + + Back + + + + + Go back one page + + + + + Forward + + + + + Go forward one page + + + + Global Search... - - + + Check For Updates... - - - + + + Connect To Peer - + Enter peer address: - + Enter peer port: - + Enter peer key: - + XSPF Error - + This is not a valid XSPF playlist. - + Failed to save tracks - + Some tracks in the playlist do not contain an artist and a title. They will be ignored. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Make sure you have a suitable Phonon backend and required plugins installed. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. - + + Station + + + + Create New Station - + Name: - - New Station + + Playlist - - New Playlist + + Automatic Playlist - + Pause - + Go &offline - + Go &online - + Authentication Error - + + Error connecting to SIP: Authentication failed! + + + + %1 by %2 track, artist name - + %1 - %2 current track, some window title - + <h2><b>Tomahawk %1<br/>(%2)</h2> - + <h2><b>Tomahawk %1</h2> - - Copyright 2010 - 2012<br/>Christian Muehlhaeuser &lt;muesli@tomahawk-player.org&gt;<br/><br/>Thanks to: Leo Franchi, Jeff Mitchell, Dominik Schmidt, Jason Herskowitz, Alejandro Wainzinger, Hugo Lindstr&ouml;m, Michael Zanetti, Harald Sitter and Steve Robertson + + Copyright 2010 - 2012 - + + Thanks to: + + + + About Tomahawk @@ -3074,7 +3249,7 @@ enter the displayed PIN number here: TopTracksContext - + Top Hits @@ -3122,103 +3297,50 @@ enter the displayed PIN number here: - + + Lyrics + + + + Similar Tracks + + + Sorry, but we could not find similar tracks for this song! + + - + You've listened to this track %n time(s). - + You've never listened to this track before. - + You first listened to it on %1. - + You've listened to %1 %n time(s). - + You've never listened to %1 before. - - TrackModel - - - Artist - - - - - Title - - - - - Album - - - - - Track - - - - - Duration - - - - - Bitrate - - - - - Age - - - - - Year - - - - - Size - - - - - Origin - - - - - Score - - - - - Composer - - - TrackView - + Sorry, your filter '%1' did not match any results. @@ -3239,7 +3361,7 @@ enter the displayed PIN number here: TreeItemDelegate - + Unknown @@ -3247,63 +3369,31 @@ enter the displayed PIN number here: TreeModel - - Name - - - - - Duration - - - - - Bitrate - - - - - Age - - - - - Year - - - - - Size - - - - - Origin - - - - - Composer - - - - + All Artists - - + + My Collection - - + + Collection of %1 + + TreeView + + + Sorry, your filter '%1' did not match any results. + + + TwitterConfigWidget @@ -3375,27 +3465,37 @@ You can re-send a sync message at any time simply by sending another tweet using ViewManager - + + After you have scanned your music collection you will find your tracks right here. + + + + + This collection is empty. + + + + SuperCollection - + Combined libraries of all your online friends - + All available albums - + Recently Played Tracks - + Recently played tracks from all your friends @@ -3418,12 +3518,12 @@ You can re-send a sync message at any time simply by sending another tweet using - + No recently created playlists in your network. - + Welcome to Tomahawk @@ -3431,7 +3531,7 @@ You can re-send a sync message at any time simply by sending another tweet using WhatsHotWidget - + Charts @@ -3660,107 +3760,107 @@ Lyrics for "%1" by %2: XmppSipPlugin - + User Interaction - + Host is unknown - + Item not found - + Authorization Error - + Remote Stream Error - + Remote Connection failed - + Internal Server Error - + System shutdown - + Conflict - + Unknown - + No Compression Support - + No Encryption Support - + No Authorization Support - + No Supported Feature - + Add Friend - + Enter Xmpp ID: - + Add Friend... - + XML Console... - + I'm sorry -- I'm just an automatic presence used by Tomahawk Player (http://gettomahawk.com). If you are getting this message, the person you are trying to reach is probably not signed on, so please try again later! - + Authorize User - + Do you want to grant <b>%1</b> access to your Collection? diff --git a/lang/tomahawk_zh_CN.ts b/lang/tomahawk_zh_CN.ts index eca67b6ef..ff0048d67 100644 --- a/lang/tomahawk_zh_CN.ts +++ b/lang/tomahawk_zh_CN.ts @@ -2,17 +2,17 @@ AccountFactoryWrapper - + Dialog 对话框 - + Description goes here 注释于此处 - + Add Account 添加帐号 @@ -20,17 +20,17 @@ AccountFactoryWrapperDelegate - + Online 在线 - + Connecting... 连接中... - + Offline 离线 @@ -38,13 +38,13 @@ AclJobDelegate - + Error displaying ACL info - - + + Allow %1 to connect and stream from you? @@ -53,69 +53,69 @@ connect and stream from you? ActionCollection - + &Listen Along 独自收听 - + Stop &Listening Along 停止独自收听 - + &Follow in real-time - - + + &Listen Privately 私下收听 - - + + &Listen Publicly 公开收听 - + &Load Playlist 载入播放列表 - + &Rename Playlist 重命名播放列表 - + &Copy Playlist Link 复制播放列表链接 - + &Play 播放 - + &Stop 停止 - + &Previous Track 上一首 - + &Next Track 下一首 - + &Quit 退出 @@ -133,30 +133,17 @@ connect and stream from you? 艺人的其他专辑 - - - Click to show Official Tracks - 点击以显示官方发行曲目 + + Sorry, we could not find any other albums for this artist! + - - - Click to show SuperCollection Tracks - 点击显示超级收藏中的曲目 + + Sorry, we could not find any tracks for this album! + - - - Click to show SuperCollection Albums - 点击显示超级收藏中的专辑 - - - - Click to show Official Albums - 点击以显示官方发行专辑 - - - + Other Albums by %1 %1 的其他专辑 @@ -164,35 +151,17 @@ connect and stream from you? AlbumModel - - Album - 专辑 - - - - + + All albums from %1 来自 %1 的所有专辑 - + All albums 所有专辑 - - AlbumView - - - After you have scanned your music collection you will find your latest album additions right here. - - - - - This collection doesn't have any recent albums. - - - ArtistInfoWidget @@ -211,38 +180,24 @@ connect and stream from you? 相关艺人 - + Albums 专辑 - - - Click to show SuperCollection Albums - 点击显示超级收藏中的专辑 - - - - Click to show Official Albums - 点击显示官方发行专辑 - - - - ArtistView - - - After you have scanned your music collection you will find your tracks right here. + + Sorry, we could not find any albums for this artist! - - This collection is currently empty. - 此收藏目前为空 + + Sorry, we could not find any related artists! + - - Sorry, your filter '%1' did not match any results. - 抱歉,你的过滤条件 '%1' 未匹配任何结果 + + Sorry, we could not find any top hits for this artist! + @@ -326,23 +281,26 @@ connect and stream from you? CategoryAddItem - - - New Playlist - 新播放列表 + + Create new Playlist + - - - - + + Create new Station + + + + + + New Station 新电台 - - - + + + %1 Station %1 电台 @@ -368,27 +326,6 @@ connect and stream from you? - - CollectionFlatModel - - - My Collection - - - - - Collection of %1 - - - - - CollectionView - - - This collection is empty. - - - ContextWidget @@ -398,12 +335,12 @@ connect and stream from you? - + Show Footnotes 显示脚注 - + Hide Footnotes 隐藏脚注 @@ -415,39 +352,49 @@ connect and stream from you? Tomahawk Crash Reporter Tomahawk 崩溃报告 - + - <p><b>Sorry!</b>&nbsp;Tomahawk crashed. Information about the crash is now being sent to Tomahawk HQ so that we can fix the bug.</p> - <p><b>抱歉!</b>&nbsp;Tomahawk 出错了。相关信息正被上传到 Tomahawk HQ 以便我们修复此错误。</p> + <html><head/><body><p><span style=" font-weight:600;">Sorry!</span> Tomahawk crashed. Please tell us about it! Tomahawk has created an error report for you that can help improve the stability in the future. You can now send this report directly to the Tomahawk developers.</p></body></html> + - + + Send this report + + + + + Don't send + + + + Abort 中止 - + You can disable sending crash reports in the configuration dialog. 你可以在设置中禁用发送崩溃报告 - + Uploaded %L1 of %L2 KB. 已发送 %L2 KB 中的 %L1 - - + + Close 关闭 - + Sent! <b>Many thanks</b>. 已发送!<b>非常感谢</b>。 - + Failed to send crash info. 发送崩溃信息失败。 @@ -463,17 +410,17 @@ connect and stream from you? DelegateConfigWrapper - + About - + Delete Account 删除账户 - + About this Account @@ -486,24 +433,11 @@ connect and stream from you? Tomahawk 诊断信息 - - Update - 更新 - - - + Copy to Clipboard 复制到剪贴板 - - DropJob - - - No tracks found for given %1 - - - GlobalSearchWidget @@ -528,29 +462,11 @@ connect and stream from you? - + Filter... - - JobStatusView - - - Searching For - - - - - Pending - - - - - Idle - - - LastFmConfig @@ -578,6 +494,11 @@ connect and stream from you? Test Login 测试登录 + + + Import Playback History + + LastfmContext @@ -626,12 +547,12 @@ connect and stream from you? LoadXSPFDialog - + Load XSPF File - + XSPF Files (*.xspf) @@ -680,20 +601,89 @@ connect and stream from you? NewReleasesWidget - + New Releases + + PlayableModel + + + Artist + + + + + Title + + + + + Composer + + + + + Album + + + + + Track + + + + + Duration + + + + + Bitrate + + + + + Age + + + + + Year + + + + + Size + + + + + Origin + + + + + Score + + + + + + Name + + + PlaylistItemDelegate - + played %1 by you - + played %1 by %2 @@ -701,17 +691,17 @@ connect and stream from you? PlaylistLargeItemDelegate - + played %1 by you - + played %1 by %2 - + added %1 @@ -719,12 +709,12 @@ connect and stream from you? PlaylistModel - + A playlist by %1, created %2 - + you @@ -780,7 +770,12 @@ connect and stream from you? PlaylistView - + + This playlist is currently empty. + + + + This playlist is currently empty. Add some tracks to it and enjoy the music! @@ -847,82 +842,82 @@ connect and stream from you? QObject - + %n year(s) ago - + %n year(s) - + %n month(s) ago - + %n month(s) - + %n week(s) ago - + %n week(s) - + %n day(s) ago - + %n day(s) - + %n hour(s) ago - + %n hour(s) - + %1 minutes ago - + %1 minutes - + just now - + Friend Finders - + Music Finders - + Status Updaters @@ -944,20 +939,30 @@ connect and stream from you? - - Show Queue + + Open Queue - - Hide Queue + + The queue is currently empty. Drop something to enqueue it! + + + + + Open Queue - %n item(s) + + + + + Close Queue RelatedArtistsContext - + Related Artists @@ -999,37 +1004,37 @@ connect and stream from you? SettingsDialog - + Collection - + Advanced - + All - + Some changed settings will not take effect until Tomahawk is restarted - + Services - + Install resolver from file - + Information @@ -1080,17 +1085,17 @@ connect and stream from you? - + Listening to "%1" by %2 and loving it! %3 - + Listening to "%1" by %2 on "%3" and loving it! %4 - + %1 characters left @@ -1123,29 +1128,19 @@ connect and stream from you? - - Offline - - - - + All available tracks - - Online - - - - - + + Show - - + + Hide @@ -1168,17 +1163,17 @@ connect and stream from you? - + New Additions - + My recent activity - + Recent activity from %1 @@ -1186,41 +1181,51 @@ connect and stream from you? SourceItem - + Collection - - + + Latest Additions - + Recently Played - + Loved Tracks - + SuperCollection - + + Sorry, we could not find any loved tracks! + + + + Latest additions to your collection - + Latest additions to %1's collection + + + Sorry, we could not find any recent additions! + + Recently Played Tracks @@ -1236,31 +1241,78 @@ connect and stream from you? %1's recently played tracks + + + Sorry, we could not find any recent plays! + + SourceTreeView - + &Copy Link - + &Delete %1 + + + Add to my Playlists + + + + + Add to my Automatic Playlists + + + + + Add to my Stations + + &Export Playlist - + + playlist + + + + + automatic playlist + + + + + station + + + + + Delete %1? + playlist/station/... + + + + + Would you like to delete the %1 <b>"%2"</b>? + e.g. Would you like to delete the playlist named Foobar? + + + + Save XSPF - + Playlists (*.xspf) @@ -1538,36 +1590,49 @@ connect and stream from you? Tomahawk::Accounts::AccountDelegate - + Add Account - + Remove Account - + %1 downloads - + Online - + Connecting... - + Offline + + Tomahawk::Accounts::AccountModel + + + Manual Install Required + + + + + Unfortunately, automatic installation of this resolver is not available or disabled for your platform.<br /><br />Please use "Install from file" above, by fetching it from your distribution or compiling it yourself. Further instructions can be found here:<br /><br />http://www.tomahawk-player.org/resolvers/%1 + + + Tomahawk::Accounts::GoogleWrapper @@ -1623,23 +1688,49 @@ connect and stream from you? Tomahawk::Accounts::LastFmConfig - + + Testing... + + + + Test Login - - + + Importing %1 + e.g. Importing 2012/01/01 + + + + + Importing History... + + + + + History Incomplete. Resume + + + + + Playback History Imported + + + + + Failed - + Success - + Could not contact server @@ -1647,12 +1738,17 @@ connect and stream from you? Tomahawk::Accounts::SpotifyAccount - + Sync with Spotify - + + Re-enable syncing with Spotify + + + + Stop syncing with Spotify @@ -1660,22 +1756,22 @@ connect and stream from you? Tomahawk::Accounts::SpotifyAccountConfig - + Logging in... - + Logged in! - + Failed: %1 - + Log In @@ -1683,7 +1779,7 @@ connect and stream from you? Tomahawk::Accounts::SpotifyAccountFactory - + Play music from and sync your playlists with Spotify Premium @@ -1699,125 +1795,125 @@ connect and stream from you? Tomahawk::Accounts::TwitterConfigWidget - - - + + + Tweet! - - + + Status: No saved credentials - - - + + + Authenticate - - - - Status: Credentials saved for %1 - - + Status: Credentials saved for %1 + + + + + De-authenticate - - - - - - - + + + + + + + Tweetin' Error - + The credentials could not be verified. You may wish to try re-authenticating. - + Status: Error validating credentials - + Global Tweet - + Direct Message - + Send Message! - + @Mention - + Send Mention! - + You must enter a user name for this type of tweet. - + Your saved credentials could not be loaded. You may wish to try re-authenticating. - + Your saved credentials could not be verified. You may wish to try re-authenticating. - - + + There was an error posting your status -- sorry! - - + + Tweeted! - + Your tweet has been posted! - + There was an error posting your direct message -- sorry! - + Your message has been posted! @@ -1833,7 +1929,7 @@ You may wish to try re-authenticating. Tomahawk::Accounts::ZeroconfFactory - + Automatically connect to Tomahawks on the local network @@ -1841,20 +1937,20 @@ You may wish to try re-authenticating. Tomahawk::ContextMenu - + &Play - - - + + + Add to &Queue - - + + &Love @@ -1864,40 +1960,45 @@ You may wish to try re-authenticating. - - Show &Album page - - - - - Show &Artist page - - - - + Un-&Love - + &Delete Items - + &Continue Playback after this Track - + &Stop Playback after this Track - + + &Show Track Page + + + + &Delete Item + + + &Show Album Page + + + + + &Show Artist Page + + Tomahawk::CustomPlaylistView @@ -1935,12 +2036,32 @@ You may wish to try re-authenticating. Tomahawk::DropJobNotifier - + + playlist + + + + + artist + + + + + track + + + + + album + + + + Fetching %1 from database - + Parsing %1 %2 @@ -1948,7 +2069,7 @@ You may wish to try re-authenticating. Tomahawk::DynamicControlList - + Click to collapse @@ -1985,17 +2106,17 @@ Please change the filters or try again. Tomahawk::DynamicView - + Add some filters above to seed this station! - + Press Generate to get started! - + Add some filters above, and press Generate to get started! @@ -2003,7 +2124,7 @@ Please change the filters or try again. Tomahawk::DynamicWidget - + Station ran out of tracks! Try tweaking the filters for a new set of songs to play. @@ -2315,97 +2436,97 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::EchonestSteerer - + Steer this station: - + Much less - + Less - + A bit less - + Keep at current - + A bit more - + More - + Much more - + Tempo - + Loudness - + Danceability - + Energy - + Song Hotttnesss - + Artist Hotttnesss - + Artist Familiarity - + By Description - + Enter a description - + Apply steering command - + Reset all steering commands @@ -2421,22 +2542,22 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::InfoSystem::ChartsPlugin - + Top Overall - + Artists - + Albums - + Tracks @@ -2444,12 +2565,12 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::InfoSystem::FdoNotifyPlugin - + Tomahawk is playing "%1" by %2%3. - + on "%1" @@ -2457,27 +2578,27 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::InfoSystem::LastFmInfoPlugin - + Top Tracks - + Loved Tracks - + Hyped Tracks - + Top Artists - + Hyped Artists @@ -2485,7 +2606,7 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::InfoSystem::NewReleasesPlugin - + Albums @@ -2493,7 +2614,7 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::InfoSystem::TwitterInfoPlugin - + Listening to "%1" by %2 and loving it! %3 @@ -2551,37 +2672,37 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Query - + and - + You - + you - + and - + %n other(s) - + %1 people - + loved this track @@ -2597,7 +2718,7 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::ShortenedLinkParser - + Network error parsing shortened link! @@ -2605,36 +2726,46 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) - + Scanning - + Checking - + Fetching - + Parsing - + Saving (%1%) + + + Online + + + + + Offline + + Tomahawk::SpotifyParser @@ -2647,7 +2778,7 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::XspfUpdater - + Automatically update from XSPF @@ -2655,7 +2786,7 @@ Try tweaking the filters for a new set of songs to play. TomahawkApp - + My Collection @@ -2677,39 +2808,48 @@ enter the displayed PIN number here: TomahawkTrayIcon - - + &Stop Playback after current Track - - + + Hide Tomahawk Window - + Show Tomahawk Window - + Currently not playing. - + Play - + Pause - + + &Love + + + + + Un-&Love + + + + &Continue Playback after current Track @@ -2853,7 +2993,7 @@ enter the displayed PIN number here: - + Play @@ -2873,137 +3013,172 @@ enter the displayed PIN number here: - + + Back + + + + + Go back one page + + + + + Forward + + + + + Go forward one page + + + + Global Search... - - + + Check For Updates... - - - + + + Connect To Peer - + Enter peer address: - + Enter peer port: - + Enter peer key: - + XSPF Error - + This is not a valid XSPF playlist. - + Failed to save tracks - + Some tracks in the playlist do not contain an artist and a title. They will be ignored. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Make sure you have a suitable Phonon backend and required plugins installed. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. - + + Station + + + + Create New Station - + Name: - - New Station + + Playlist - - New Playlist + + Automatic Playlist - + Pause - + Go &offline - + Go &online - + Authentication Error - + + Error connecting to SIP: Authentication failed! + + + + %1 by %2 track, artist name - + %1 - %2 current track, some window title - + <h2><b>Tomahawk %1<br/>(%2)</h2> - + <h2><b>Tomahawk %1</h2> - - Copyright 2010 - 2012<br/>Christian Muehlhaeuser &lt;muesli@tomahawk-player.org&gt;<br/><br/>Thanks to: Leo Franchi, Jeff Mitchell, Dominik Schmidt, Jason Herskowitz, Alejandro Wainzinger, Hugo Lindstr&ouml;m, Michael Zanetti, Harald Sitter and Steve Robertson + + Copyright 2010 - 2012 - + + Thanks to: + + + + About Tomahawk @@ -3074,7 +3249,7 @@ enter the displayed PIN number here: TopTracksContext - + Top Hits @@ -3122,103 +3297,50 @@ enter the displayed PIN number here: - + + Lyrics + + + + Similar Tracks + + + Sorry, but we could not find similar tracks for this song! + + - + You've listened to this track %n time(s). - + You've never listened to this track before. - + You first listened to it on %1. - + You've listened to %1 %n time(s). - + You've never listened to %1 before. - - TrackModel - - - Artist - - - - - Title - - - - - Album - - - - - Track - - - - - Duration - - - - - Bitrate - - - - - Age - - - - - Year - - - - - Size - - - - - Origin - - - - - Score - - - - - Composer - - - TrackView - + Sorry, your filter '%1' did not match any results. @@ -3239,7 +3361,7 @@ enter the displayed PIN number here: TreeItemDelegate - + Unknown @@ -3247,63 +3369,31 @@ enter the displayed PIN number here: TreeModel - - Name - - - - - Duration - - - - - Bitrate - - - - - Age - - - - - Year - - - - - Size - - - - - Origin - - - - - Composer - - - - + All Artists - - + + My Collection - - + + Collection of %1 + + TreeView + + + Sorry, your filter '%1' did not match any results. + + + TwitterConfigWidget @@ -3375,27 +3465,37 @@ You can re-send a sync message at any time simply by sending another tweet using ViewManager - + + After you have scanned your music collection you will find your tracks right here. + + + + + This collection is empty. + + + + SuperCollection - + Combined libraries of all your online friends - + All available albums - + Recently Played Tracks - + Recently played tracks from all your friends @@ -3418,12 +3518,12 @@ You can re-send a sync message at any time simply by sending another tweet using - + No recently created playlists in your network. - + Welcome to Tomahawk @@ -3431,7 +3531,7 @@ You can re-send a sync message at any time simply by sending another tweet using WhatsHotWidget - + Charts @@ -3660,107 +3760,107 @@ Lyrics for "%1" by %2: XmppSipPlugin - + User Interaction - + Host is unknown - + Item not found - + Authorization Error - + Remote Stream Error - + Remote Connection failed - + Internal Server Error - + System shutdown - + Conflict - + Unknown - + No Compression Support - + No Encryption Support - + No Authorization Support - + No Supported Feature - + Add Friend - + Enter Xmpp ID: - + Add Friend... - + XML Console... - + I'm sorry -- I'm just an automatic presence used by Tomahawk Player (http://gettomahawk.com). If you are getting this message, the person you are trying to reach is probably not signed on, so please try again later! - + Authorize User - + Do you want to grant <b>%1</b> access to your Collection? diff --git a/lang/tomahawk_zh_TW.ts b/lang/tomahawk_zh_TW.ts index ff44fcbdf..7cf9618c0 100644 --- a/lang/tomahawk_zh_TW.ts +++ b/lang/tomahawk_zh_TW.ts @@ -2,17 +2,17 @@ AccountFactoryWrapper - + Dialog 對話框 - + Description goes here 說明放在這裡 - + Add Account 新增帳戶 @@ -20,17 +20,17 @@ AccountFactoryWrapperDelegate - + Online 線上 - + Connecting... 連接中... - + Offline 離線 @@ -38,13 +38,13 @@ AclJobDelegate - + Error displaying ACL info - - + + Allow %1 to connect and stream from you? @@ -53,69 +53,69 @@ connect and stream from you? ActionCollection - + &Listen Along - + Stop &Listening Along - + &Follow in real-time 即時跟隨 - - + + &Listen Privately 私下聆聽 - - + + &Listen Publicly 公開聆聽 - + &Load Playlist 載入播放清單 - + &Rename Playlist 重新命名播放清單 - + &Copy Playlist Link 複製播放清單連結 - + &Play 播放 - + &Stop 停止 - + &Previous Track 上一首曲目 - + &Next Track 下一個曲目 - + &Quit 結束 @@ -133,30 +133,17 @@ connect and stream from you? 列出演出者的其他專輯 - - - Click to show Official Tracks - 點擊顯示正式的曲目 + + Sorry, we could not find any other albums for this artist! + - - - Click to show SuperCollection Tracks - 點擊顯示超級收藏曲目 + + Sorry, we could not find any tracks for this album! + - - - Click to show SuperCollection Albums - 點擊顯示超級收藏專輯 - - - - Click to show Official Albums - 點擊顯示正式的專輯 - - - + Other Albums by %1 列出所有其他專輯,依 %1 @@ -164,35 +151,17 @@ connect and stream from you? AlbumModel - - Album - 專輯 - - - - + + All albums from %1 從 %1 的所有專輯 - + All albums 所有專輯 - - AlbumView - - - After you have scanned your music collection you will find your latest album additions right here. - 當您掃描您的音樂收藏,您會發現您的最新專輯添加在這裡。 - - - - This collection doesn't have any recent albums. - 這個收藏沒有任何最新專輯。 - - ArtistInfoWidget @@ -211,37 +180,23 @@ connect and stream from you? 相關演出者 - + Albums 專輯 - - - Click to show SuperCollection Albums - 點擊顯示超級收藏專輯 + + Sorry, we could not find any albums for this artist! + - - Click to show Official Albums - 點擊顯示正式的專輯 - - - - ArtistView - - - After you have scanned your music collection you will find your tracks right here. - 當您掃描您的音樂收藏,您將在這裡找到您的曲目。 + + Sorry, we could not find any related artists! + - - This collection is currently empty. - 目前這個收藏是空的。 - - - - Sorry, your filter '%1' did not match any results. + + Sorry, we could not find any top hits for this artist! @@ -326,23 +281,26 @@ connect and stream from you? CategoryAddItem - - - New Playlist - 新增播放清單 + + Create new Playlist + - - - - + + Create new Station + + + + + + New Station - - - + + + %1 Station @@ -368,27 +326,6 @@ connect and stream from you? 清除 - - CollectionFlatModel - - - My Collection - 我的收藏 - - - - Collection of %1 - - - - - CollectionView - - - This collection is empty. - 這個收藏是空的。 - - ContextWidget @@ -398,12 +335,12 @@ connect and stream from you? - + Show Footnotes 顯示註腳 - + Hide Footnotes 隱藏註腳 @@ -415,39 +352,49 @@ connect and stream from you? Tomahawk Crash Reporter Tomahawk 崩潰報告 - + - <p><b>Sorry!</b>&nbsp;Tomahawk crashed. Information about the crash is now being sent to Tomahawk HQ so that we can fix the bug.</p> + <html><head/><body><p><span style=" font-weight:600;">Sorry!</span> Tomahawk crashed. Please tell us about it! Tomahawk has created an error report for you that can help improve the stability in the future. You can now send this report directly to the Tomahawk developers.</p></body></html> - + + Send this report + + + + + Don't send + + + + Abort 中止 - + You can disable sending crash reports in the configuration dialog. 在配置對話框中,您可以禁用發送崩潰報告。 - + Uploaded %L1 of %L2 KB. - - + + Close 關閉 - + Sent! <b>Many thanks</b>. - + Failed to send crash info. 無法發送故障信息。 @@ -463,17 +410,17 @@ connect and stream from you? DelegateConfigWrapper - + About 關於 - + Delete Account 刪除帳戶 - + About this Account 關於此帳戶 @@ -486,24 +433,11 @@ connect and stream from you? Tomahawk 診斷 - - Update - 更新 - - - + Copy to Clipboard 複製到剪貼簿 - - DropJob - - - No tracks found for given %1 - - - GlobalSearchWidget @@ -528,29 +462,11 @@ connect and stream from you? 信息欄 - + Filter... 過濾器... - - JobStatusView - - - Searching For - 搜尋 - - - - Pending - - - - - Idle - 閒置 - - LastFmConfig @@ -578,6 +494,11 @@ connect and stream from you? Test Login 測試登錄 + + + Import Playback History + + LastfmContext @@ -626,12 +547,12 @@ connect and stream from you? LoadXSPFDialog - + Load XSPF File 載入 XSPF 檔 - + XSPF Files (*.xspf) XSPF 檔 (*.xspf) @@ -680,20 +601,89 @@ connect and stream from you? NewReleasesWidget - + New Releases 新版本 + + PlayableModel + + + Artist + + + + + Title + + + + + Composer + + + + + Album + + + + + Track + + + + + Duration + + + + + Bitrate + + + + + Age + + + + + Year + + + + + Size + + + + + Origin + + + + + Score + + + + + + Name + + + PlaylistItemDelegate - + played %1 by you - + played %1 by %2 @@ -701,17 +691,17 @@ connect and stream from you? PlaylistLargeItemDelegate - + played %1 by you - + played %1 by %2 - + added %1 @@ -719,12 +709,12 @@ connect and stream from you? PlaylistModel - + A playlist by %1, created %2 - + you @@ -780,7 +770,12 @@ connect and stream from you? PlaylistView - + + This playlist is currently empty. + + + + This playlist is currently empty. Add some tracks to it and enjoy the music! 目前這個播放清單是空的。請加入一些曲目,然後盡情享受音樂! @@ -847,82 +842,82 @@ connect and stream from you? QObject - + %n year(s) ago - + %n year(s) - + %n month(s) ago - + %n month(s) - + %n week(s) ago - + %n week(s) - + %n day(s) ago - + %n day(s) - + %n hour(s) ago - + %n hour(s) - + %1 minutes ago %1 分鐘前 - + %1 minutes %1 分鐘 - + just now 剛才 - + Friend Finders - + Music Finders - + Status Updaters @@ -944,20 +939,30 @@ connect and stream from you? - - Show Queue - 顯示佇列 + + Open Queue + - - Hide Queue - 隱藏佇列 + + The queue is currently empty. Drop something to enqueue it! + + + + + Open Queue - %n item(s) + + + + + Close Queue + RelatedArtistsContext - + Related Artists 相關演出者 @@ -999,37 +1004,37 @@ connect and stream from you? SettingsDialog - + Collection 收藏 - + Advanced 進階 - + All 所有 - + Some changed settings will not take effect until Tomahawk is restarted - + Services 服務 - + Install resolver from file 從檔案安裝解析器 - + Information 資訊 @@ -1080,17 +1085,17 @@ connect and stream from you? 文字標籤 - + Listening to "%1" by %2 and loving it! %3 - + Listening to "%1" by %2 on "%3" and loving it! %4 - + %1 characters left @@ -1123,29 +1128,19 @@ connect and stream from you? 前10名 - - Offline - 離線 - - - + All available tracks - - Online - 線上 - - - - + + Show 顯示 - - + + Hide 隱藏 @@ -1168,17 +1163,17 @@ connect and stream from you? 最近播放的曲目 - + New Additions 新增 - + My recent activity 我的近期活動 - + Recent activity from %1 @@ -1186,41 +1181,51 @@ connect and stream from you? SourceItem - + Collection 收藏 - - + + Latest Additions 最新加入 - + Recently Played 最近播放的 - + Loved Tracks - + SuperCollection 超級收藏 - + + Sorry, we could not find any loved tracks! + + + + Latest additions to your collection - + Latest additions to %1's collection + + + Sorry, we could not find any recent additions! + + Recently Played Tracks @@ -1236,31 +1241,78 @@ connect and stream from you? %1's recently played tracks + + + Sorry, we could not find any recent plays! + + SourceTreeView - + &Copy Link 複製鏈接 - + &Delete %1 + + + Add to my Playlists + + + + + Add to my Automatic Playlists + + + + + Add to my Stations + + &Export Playlist 匯出播放清單 - + + playlist + + + + + automatic playlist + + + + + station + + + + + Delete %1? + playlist/station/... + + + + + Would you like to delete the %1 <b>"%2"</b>? + e.g. Would you like to delete the playlist named Foobar? + + + + Save XSPF 儲存 XSPF - + Playlists (*.xspf) 播放清單(*.xspf) @@ -1538,36 +1590,49 @@ connect and stream from you? Tomahawk::Accounts::AccountDelegate - + Add Account 新增帳戶 - + Remove Account 刪除帳戶 - + %1 downloads - + Online 線上 - + Connecting... 連接中... - + Offline 離線 + + Tomahawk::Accounts::AccountModel + + + Manual Install Required + + + + + Unfortunately, automatic installation of this resolver is not available or disabled for your platform.<br /><br />Please use "Install from file" above, by fetching it from your distribution or compiling it yourself. Further instructions can be found here:<br /><br />http://www.tomahawk-player.org/resolvers/%1 + + + Tomahawk::Accounts::GoogleWrapper @@ -1623,23 +1688,49 @@ connect and stream from you? Tomahawk::Accounts::LastFmConfig - + + Testing... + + + + Test Login - - + + Importing %1 + e.g. Importing 2012/01/01 + + + + + Importing History... + + + + + History Incomplete. Resume + + + + + Playback History Imported + + + + + Failed 失敗 - + Success 成功 - + Could not contact server 無法聯繫服務器 @@ -1647,12 +1738,17 @@ connect and stream from you? Tomahawk::Accounts::SpotifyAccount - + Sync with Spotify - + + Re-enable syncing with Spotify + + + + Stop syncing with Spotify @@ -1660,22 +1756,22 @@ connect and stream from you? Tomahawk::Accounts::SpotifyAccountConfig - + Logging in... 登錄中... - + Logged in! 登錄了! - + Failed: %1 失敗:%1 - + Log In 登錄 @@ -1683,7 +1779,7 @@ connect and stream from you? Tomahawk::Accounts::SpotifyAccountFactory - + Play music from and sync your playlists with Spotify Premium @@ -1699,125 +1795,125 @@ connect and stream from you? Tomahawk::Accounts::TwitterConfigWidget - - - + + + Tweet! Tweet! - - + + Status: No saved credentials - - - + + + Authenticate - - - - Status: Credentials saved for %1 - - + Status: Credentials saved for %1 + + + + + De-authenticate - - - - - - - + + + + + + + Tweetin' Error - + The credentials could not be verified. You may wish to try re-authenticating. - + Status: Error validating credentials - + Global Tweet - + Direct Message - + Send Message! - + @Mention - + Send Mention! - + You must enter a user name for this type of tweet. - + Your saved credentials could not be loaded. You may wish to try re-authenticating. - + Your saved credentials could not be verified. You may wish to try re-authenticating. - - + + There was an error posting your status -- sorry! - - + + Tweeted! - + Your tweet has been posted! - + There was an error posting your direct message -- sorry! - + Your message has been posted! @@ -1833,7 +1929,7 @@ You may wish to try re-authenticating. Tomahawk::Accounts::ZeroconfFactory - + Automatically connect to Tomahawks on the local network 自動連接到區域網路上的 Tomahawks @@ -1841,20 +1937,20 @@ You may wish to try re-authenticating. Tomahawk::ContextMenu - + &Play 播放 - - - + + + Add to &Queue 添加至佇列 - - + + &Love @@ -1864,40 +1960,45 @@ You may wish to try re-authenticating. - - Show &Album page - - - - - Show &Artist page - - - - + Un-&Love - + &Delete Items - + &Continue Playback after this Track - + &Stop Playback after this Track - + + &Show Track Page + + + + &Delete Item + + + &Show Album Page + + + + + &Show Artist Page + + Tomahawk::CustomPlaylistView @@ -1935,12 +2036,32 @@ You may wish to try re-authenticating. Tomahawk::DropJobNotifier - + + playlist + + + + + artist + + + + + track + + + + + album + + + + Fetching %1 from database - + Parsing %1 %2 @@ -1948,7 +2069,7 @@ You may wish to try re-authenticating. Tomahawk::DynamicControlList - + Click to collapse @@ -1985,17 +2106,17 @@ Please change the filters or try again. Tomahawk::DynamicView - + Add some filters above to seed this station! - + Press Generate to get started! - + Add some filters above, and press Generate to get started! @@ -2003,7 +2124,7 @@ Please change the filters or try again. Tomahawk::DynamicWidget - + Station ran out of tracks! Try tweaking the filters for a new set of songs to play. @@ -2315,97 +2436,97 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::EchonestSteerer - + Steer this station: - + Much less - + Less - + A bit less - + Keep at current - + A bit more - + More - + Much more - + Tempo - + Loudness - + Danceability - + Energy - + Song Hotttnesss - + Artist Hotttnesss - + Artist Familiarity - + By Description - + Enter a description 輸入一個描述 - + Apply steering command - + Reset all steering commands @@ -2421,22 +2542,22 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::InfoSystem::ChartsPlugin - + Top Overall - + Artists 演出者 - + Albums 專輯 - + Tracks 曲目 @@ -2444,12 +2565,12 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::InfoSystem::FdoNotifyPlugin - + Tomahawk is playing "%1" by %2%3. - + on "%1" @@ -2457,27 +2578,27 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::InfoSystem::LastFmInfoPlugin - + Top Tracks - + Loved Tracks - + Hyped Tracks - + Top Artists - + Hyped Artists @@ -2485,7 +2606,7 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::InfoSystem::NewReleasesPlugin - + Albums 專輯 @@ -2493,7 +2614,7 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::InfoSystem::TwitterInfoPlugin - + Listening to "%1" by %2 and loving it! %3 @@ -2551,37 +2672,37 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Query - + and - + You - + you - + and - + %n other(s) - + %1 people - + loved this track @@ -2597,7 +2718,7 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::ShortenedLinkParser - + Network error parsing shortened link! @@ -2605,36 +2726,46 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) - + Scanning - + Checking - + Fetching - + Parsing 解析 - + Saving (%1%) + + + Online + + + + + Offline + + Tomahawk::SpotifyParser @@ -2647,7 +2778,7 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::XspfUpdater - + Automatically update from XSPF @@ -2655,7 +2786,7 @@ Try tweaking the filters for a new set of songs to play. TomahawkApp - + My Collection 我的收藏 @@ -2677,39 +2808,48 @@ enter the displayed PIN number here: TomahawkTrayIcon - - + &Stop Playback after current Track - - + + Hide Tomahawk Window - + Show Tomahawk Window - + Currently not playing. 目前沒有播放。 - + Play 播放 - + Pause 暫停 - + + &Love + + + + + Un-&Love + + + + &Continue Playback after current Track @@ -2853,7 +2993,7 @@ enter the displayed PIN number here: - + Play 播放 @@ -2873,137 +3013,172 @@ enter the displayed PIN number here: 下一首 - + + Back + + + + + Go back one page + + + + + Forward + + + + + Go forward one page + + + + Global Search... 全域搜尋... - - + + Check For Updates... 檢查更新... - - - + + + Connect To Peer 連接點對點 - + Enter peer address: 輸入對等地址: - + Enter peer port: 輸入對等連接埠: - + Enter peer key: 輸入對等密鑰: - + XSPF Error XSPF 錯誤 - + This is not a valid XSPF playlist. - + Failed to save tracks 無法儲存曲目 - + Some tracks in the playlist do not contain an artist and a title. They will be ignored. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Make sure you have a suitable Phonon backend and required plugins installed. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. - + + Station + + + + Create New Station - + Name: 名稱: - - New Station + + Playlist - - New Playlist - 新增播放清單 + + Automatic Playlist + - + Pause 暫停 - + Go &offline 離線 - + Go &online 上網 - + Authentication Error 驗證錯誤 - + + Error connecting to SIP: Authentication failed! + + + + %1 by %2 track, artist name - + %1 - %2 current track, some window title - + <h2><b>Tomahawk %1<br/>(%2)</h2> <h2><b>Tomahawk %1<br/>(%2)</h2> - + <h2><b>Tomahawk %1</h2> <h2><b>Tomahawk %1</h2> - - Copyright 2010 - 2012<br/>Christian Muehlhaeuser &lt;muesli@tomahawk-player.org&gt;<br/><br/>Thanks to: Leo Franchi, Jeff Mitchell, Dominik Schmidt, Jason Herskowitz, Alejandro Wainzinger, Hugo Lindstr&ouml;m, Michael Zanetti, Harald Sitter and Steve Robertson + + Copyright 2010 - 2012 - + + Thanks to: + + + + About Tomahawk @@ -3074,7 +3249,7 @@ enter the displayed PIN number here: TopTracksContext - + Top Hits 流行精選 @@ -3122,103 +3297,50 @@ enter the displayed PIN number here: - + + Lyrics + + + + Similar Tracks + + + Sorry, but we could not find similar tracks for this song! + + - + You've listened to this track %n time(s). - + You've never listened to this track before. - + You first listened to it on %1. - + You've listened to %1 %n time(s). - + You've never listened to %1 before. - - TrackModel - - - Artist - 演出者 - - - - Title - 標題 - - - - Album - 專輯 - - - - Track - 曲目 - - - - Duration - 長度 - - - - Bitrate - 比特率 - - - - Age - 年代 - - - - Year - - - - - Size - - - - - Origin - - - - - Score - - - - - Composer - - - TrackView - + Sorry, your filter '%1' did not match any results. @@ -3239,7 +3361,7 @@ enter the displayed PIN number here: TreeItemDelegate - + Unknown @@ -3247,64 +3369,31 @@ enter the displayed PIN number here: TreeModel - - Name - 名稱 - - - - Duration - 長度 - - - - - Bitrate - - - - - Age - 年代 - - - - Year - - - - - Size - - - - - Origin - - - - - Composer - - - - + All Artists - - + + My Collection 我的收藏 - - + + Collection of %1 + + TreeView + + + Sorry, your filter '%1' did not match any results. + + + TwitterConfigWidget @@ -3376,27 +3465,37 @@ You can re-send a sync message at any time simply by sending another tweet using ViewManager - + + After you have scanned your music collection you will find your tracks right here. + + + + + This collection is empty. + + + + SuperCollection 超級收藏 - + Combined libraries of all your online friends 聯合您所有線上朋友的音樂庫 - + All available albums 所有現有專輯 - + Recently Played Tracks - + Recently played tracks from all your friends @@ -3419,12 +3518,12 @@ You can re-send a sync message at any time simply by sending another tweet using 最近播放的曲目 - + No recently created playlists in your network. 沒有最近建立的播放清單在您的網路。 - + Welcome to Tomahawk 歡迎到 Tomahawk @@ -3432,7 +3531,7 @@ You can re-send a sync message at any time simply by sending another tweet using WhatsHotWidget - + Charts @@ -3661,107 +3760,107 @@ Lyrics for "%1" by %2: XmppSipPlugin - + User Interaction 使用者互動 - + Host is unknown 主機是未知 - + Item not found - + Authorization Error 授權錯誤 - + Remote Stream Error 遠端串流錯誤 - + Remote Connection failed 遠端連線失敗 - + Internal Server Error 內部服務器錯誤 - + System shutdown 系統關閉 - + Conflict 衝突 - + Unknown 未知 - + No Compression Support 沒有壓縮支持 - + No Encryption Support 沒有加密支持 - + No Authorization Support 沒有授權支持 - + No Supported Feature 沒有支持的功能 - + Add Friend 加為好友 - + Enter Xmpp ID: 輸入XMPP識別碼: - + Add Friend... 加為好友... - + XML Console... XML的控制台... - + I'm sorry -- I'm just an automatic presence used by Tomahawk Player (http://gettomahawk.com). If you are getting this message, the person you are trying to reach is probably not signed on, so please try again later! - + Authorize User 授權用戶 - + Do you want to grant <b>%1</b> access to your Collection? diff --git a/resources.qrc b/resources.qrc index 602774165..05f94a4b9 100644 --- a/resources.qrc +++ b/resources.qrc @@ -1,7 +1,5 @@ - data/images/avatar-dude-plus.png - data/images/avatar-dude.png data/images/back-pressed.png data/images/back-rest.png data/images/filter.png @@ -9,6 +7,8 @@ data/images/not-loved.png data/images/no-album-art-placeholder.png data/images/no-artist-image-placeholder.png + data/images/artist-placeholder-grid.png + data/images/album-placeholder-grid.png data/images/track-placeholder.png data/images/now-playing-panel.png data/images/now-playing-speaker.png @@ -66,12 +66,10 @@ data/images/echonest_logo.png data/images/loading-animation.gif data/images/info.png - data/images/home.png data/images/back.png data/images/forward.png data/images/music-icon.png data/images/configure.png - data/images/create-playlist.png data/images/private-listening.png data/images/add.png data/images/recently-played.png @@ -136,12 +134,14 @@ data/images/rdio.png data/images/grooveshark.png data/images/lastfm-icon.png - data/images/spotifycore-logo.png + data/images/spotifycore-logo.png data/images/playlist-header-tiled.png data/images/share.png data/sql/dbmigrate-27_to_28.sql data/images/process-stop.png data/icons/tomahawk-icon-128x128-grayscale.png data/images/collection.png + data/misc/tomahawk_pubkey.pem + data/images/track-icon-sidebar.png diff --git a/src/AudioControls.cpp b/src/AudioControls.cpp index ba2d39414..58e6fba06 100644 --- a/src/AudioControls.cpp +++ b/src/AudioControls.cpp @@ -28,13 +28,14 @@ #include "playlist/PlaylistView.h" #include "database/Database.h" #include "widgets/ImageButton.h" -#include "utils/TomahawkUtils.h" +#include "utils/TomahawkUtilsGui.h" #include "utils/Logger.h" #include "Album.h" #include "DropJob.h" #include "SocialWidget.h" #include "GlobalActionManager.h" #include "ViewManager.h" +#include "Source.h" using namespace Tomahawk; @@ -42,7 +43,7 @@ using namespace Tomahawk; AudioControls::AudioControls( QWidget* parent ) : QWidget( parent ) , ui( new Ui::AudioControls ) - , m_repeatMode( PlaylistInterface::NoRepeat ) + , m_repeatMode( PlaylistModes::NoRepeat ) , m_shuffled( false ) , m_lastSliderCheck( 0 ) , m_parent( parent ) @@ -212,7 +213,7 @@ AudioControls::onPlaybackStarted( const Tomahawk::result_ptr& result ) m_sliderTimeLine.setCurveShape( QTimeLine::LinearCurve ); m_sliderTimeLine.setCurrentTime( 0 ); m_seekMsecs = -1; - + ui->seekSlider->setVisible( true ); int updateRate = (double)1000 / ( (double)ui->seekSlider->contentsRect().width() / (double)( duration / 1000 ) ); @@ -374,7 +375,7 @@ AudioControls::onPlaybackTimer( qint64 msElapsed ) ui->timeLeftLabel->setText( "-" + TomahawkUtils::timeToString( m_currentTrack->duration() - seconds ) ); m_lastTextSecondShown = seconds; } - + //tDebug( LOGEXTRA ) << Q_FUNC_INFO << "msElapsed =" << msElapsed << "and timer current time =" << m_sliderTimeLine.currentTime() << "and m_seekMsecs =" << m_seekMsecs; if ( msElapsed > 0 && msElapsed != m_lastSliderCheck && m_seekMsecs == -1 && msElapsed - 500 < m_lastSliderCheck ) return; @@ -390,7 +391,7 @@ AudioControls::onPlaybackTimer( qint64 msElapsed ) if ( sender() != &m_phononTickCheckTimer ) m_phononTickCheckTimer.start( 1000 ); - + int currentTime = m_sliderTimeLine.currentTime(); if ( m_noTimeChange ) { @@ -430,13 +431,13 @@ AudioControls::onPlaybackTimer( qint64 msElapsed ) void -AudioControls::onRepeatModeChanged( PlaylistInterface::RepeatMode mode ) +AudioControls::onRepeatModeChanged( PlaylistModes::RepeatMode mode ) { m_repeatMode = mode; switch ( m_repeatMode ) { - case PlaylistInterface::NoRepeat: + case PlaylistModes::NoRepeat: { // switch to RepeatOne ui->repeatButton->setPixmap( RESPATH "images/repeat-off-rest.png" ); @@ -444,7 +445,7 @@ AudioControls::onRepeatModeChanged( PlaylistInterface::RepeatMode mode ) } break; - case PlaylistInterface::RepeatOne: + case PlaylistModes::RepeatOne: { // switch to RepeatAll ui->repeatButton->setPixmap( RESPATH "images/repeat-1-on-rest.png" ); @@ -452,7 +453,7 @@ AudioControls::onRepeatModeChanged( PlaylistInterface::RepeatMode mode ) } break; - case PlaylistInterface::RepeatAll: + case PlaylistModes::RepeatAll: { // switch to NoRepeat ui->repeatButton->setPixmap( RESPATH "images/repeat-all-on-rest.png" ); @@ -471,24 +472,24 @@ AudioControls::onRepeatClicked() { switch ( m_repeatMode ) { - case PlaylistInterface::NoRepeat: + case PlaylistModes::NoRepeat: { // switch to RepeatOne - ViewManager::instance()->setRepeatMode( PlaylistInterface::RepeatOne ); + ViewManager::instance()->setRepeatMode( PlaylistModes::RepeatOne ); } break; - case PlaylistInterface::RepeatOne: + case PlaylistModes::RepeatOne: { // switch to RepeatAll - ViewManager::instance()->setRepeatMode( PlaylistInterface::RepeatAll ); + ViewManager::instance()->setRepeatMode( PlaylistModes::RepeatAll ); } break; - case PlaylistInterface::RepeatAll: + case PlaylistModes::RepeatAll: { // switch to NoRepeat - ViewManager::instance()->setRepeatMode( PlaylistInterface::NoRepeat ); + ViewManager::instance()->setRepeatMode( PlaylistModes::NoRepeat ); } break; @@ -544,7 +545,8 @@ AudioControls::onAlbumClicked() void AudioControls::onTrackClicked() { - ViewManager::instance()->showCurrentTrack(); + ViewManager::instance()->show( m_currentTrack->toQuery() ); +// ViewManager::instance()->showCurrentTrack(); } diff --git a/src/AudioControls.h b/src/AudioControls.h index fb2b2c84c..d454bf2fe 100644 --- a/src/AudioControls.h +++ b/src/AudioControls.h @@ -50,9 +50,9 @@ signals: void pausePressed(); public slots: - void onRepeatModeChanged( Tomahawk::PlaylistInterface::RepeatMode mode ); + void onRepeatModeChanged( Tomahawk::PlaylistModes::RepeatMode mode ); void onShuffleModeChanged( bool enabled ); - + protected: void changeEvent( QEvent* e ); void dragEnterEvent ( QDragEnterEvent* ); @@ -61,7 +61,7 @@ protected: private slots: void phononTickCheckTimeout(); - + void onPlaybackStarted( const Tomahawk::result_ptr& result ); void onPlaybackLoading( const Tomahawk::result_ptr& result ); void onPlaybackPaused(); @@ -93,7 +93,7 @@ private: Ui::AudioControls* ui; Tomahawk::result_ptr m_currentTrack; - Tomahawk::PlaylistInterface::RepeatMode m_repeatMode; + Tomahawk::PlaylistModes::RepeatMode m_repeatMode; bool m_shuffled; QTimer m_phononTickCheckTimer; @@ -102,7 +102,7 @@ private: qint64 m_lastSliderCheck; bool m_noTimeChange; qint64 m_lastTextSecondShown; - + QWidget* m_parent; }; diff --git a/src/AudioControls.ui b/src/AudioControls.ui index 3a68a7f04..d8db84968 100644 --- a/src/AudioControls.ui +++ b/src/AudioControls.ui @@ -151,14 +151,14 @@ - 58 - 58 + 60 + 60 - 58 - 58 + 60 + 60 diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 6349911ad..f283094f5 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -68,27 +68,13 @@ SET( tomahawkSourcesGui ${tomahawkSourcesGui} sourcetree/items/GroupItem.cpp sourcetree/items/HistoryItem.cpp - utils/GuiHelpers.cpp - - accounts/lastfm/LastFmAccount.cpp - accounts/lastfm/LastFmConfig.cpp - accounts/lastfm/LastFmInfoPlugin.cpp - - accounts/spotify/SpotifyAccount.cpp - accounts/spotify/SpotifyAccountConfig.cpp - accounts/spotify/SpotifyPlaylistUpdater.cpp - TomahawkTrayIcon.cpp AudioControls.cpp SettingsDialog.cpp DiagnosticsDialog.cpp - AccountDelegate.cpp SettingsListDelegate.cpp - DelegateConfigWrapper.cpp TomahawkWindow.cpp LoadXSPFDialog.cpp - AccountFactoryWrapper.cpp - AccountFactoryWrapperDelegate.cpp SocialWidget.cpp ) @@ -102,12 +88,8 @@ SET( tomahawkUI ${tomahawkUI} StackedSettingsDialog.ui ProxyDialog.ui - accounts/lastfm/LastFmConfig.ui - accounts/spotify/SpotifyAccountConfig.ui - AudioControls.ui LoadXSPFDialog.ui - AccountFactoryWrapper.ui SocialWidget.ui ) @@ -122,7 +104,6 @@ INCLUDE_DIRECTORIES( network sourcetree topbar - utils libtomahawk mac @@ -154,12 +135,8 @@ IF( APPLE ) SET( tomahawkSources ${tomahawkSources} mac/TomahawkApp_Mac.mm mac/MacShortcutHandler.cpp ) ENDIF( APPLE ) -IF(GLOOX_FOUND) - INCLUDE_DIRECTORIES( ${GLOOX_INCLUDE_DIR} ) - SET( tomahawkSources ${tomahawkSources} xmppbot/XmppBot.cpp ) -ENDIF(GLOOX_FOUND) - ADD_SUBDIRECTORY( accounts ) +ADD_SUBDIRECTORY( infoplugins ) IF(QCA2_FOUND) INCLUDE_DIRECTORIES( ${QCA2_INCLUDE_DIR} ) @@ -206,9 +183,6 @@ SET(LINK_LIBRARIES "") IF(LIBLASTFM_FOUND) SET(LINK_LIBRARIES ${LINK_LIBRARIES} tomahawk_lastfm2 ) ENDIF(LIBLASTFM_FOUND) -IF(GLOOX_FOUND) - SET(LINK_LIBRARIES ${LINK_LIBRARIES} ${GLOOX_LIBRARIES} ) -ENDIF(GLOOX_FOUND) IF(QCA2_FOUND) SET(LINK_LIBRARIES ${LINK_LIBRARIES} ${QCA2_LIBRARIES} ) ENDIF(QCA2_FOUND) diff --git a/src/Config.h.in b/src/Config.h.in index 87ec9b48d..1aa764c96 100644 --- a/src/Config.h.in +++ b/src/Config.h.in @@ -19,9 +19,10 @@ #cmakedefine WITH_BREAKPAD #cmakedefine WITH_CRASHREPORTER +#cmakedefine WITH_BINARY_ATTICA + #cmakedefine LIBLASTFM_FOUND -#cmakedefine GLOOX_FOUND #cmakedefine QCA2_FOUND #endif // CONFIG_H_IN diff --git a/src/DiagnosticsDialog.cpp b/src/DiagnosticsDialog.cpp index 35ff7e9dc..e63a6fe16 100644 --- a/src/DiagnosticsDialog.cpp +++ b/src/DiagnosticsDialog.cpp @@ -26,11 +26,13 @@ #include "network/Servent.h" #include "SourceList.h" +#include #include #include #include #include #include +#include #include "utils/Logger.h" #include "sip/SipHandler.h" @@ -42,14 +44,17 @@ DiagnosticsDialog::DiagnosticsDialog( QWidget *parent ) { ui->setupUi( this ); - connect( ui->updateButton, SIGNAL( clicked() ), this, SLOT( updateLogView() ) ); connect( ui->clipboardButton, SIGNAL( clicked() ), this, SLOT( copyToClipboard() ) ); connect( ui->buttonBox, SIGNAL( rejected() ), this, SLOT( reject() ) ); + ui->scrollAreaWidgetContents->setLayout( new QVBoxLayout() ); + updateLogView(); } -void DiagnosticsDialog::updateLogView() + +void +DiagnosticsDialog::updateLogView() { QString log; @@ -59,16 +64,11 @@ void DiagnosticsDialog::updateLogView() ); // network - log.append( - "TOMAHAWK-VERSION: " TOMAHAWK_VERSION "\n\n\n" - ); + log.append( "TOMAHAWK-VERSION: " TOMAHAWK_VERSION "\n\n" ); // network - log.append( - "NETWORK:\n" - " General:\n" - ); - if( Servent::instance()->visibleExternally() ) + log.append( "NETWORK:\n General:\n" ); + if ( Servent::instance()->visibleExternally() ) { log.append( QString( @@ -83,17 +83,13 @@ void DiagnosticsDialog::updateLogView() } else { - log.append( - QString( - " visible: false" - ) - ); + log.append( " visible: false" ); } - log.append("\n\n"); - + ui->scrollAreaWidgetContents->layout()->addWidget( new QLabel( log, this ) ); // Peers / Accounts, TODO - log.append("ACCOUNTS:\n"); + ui->scrollAreaWidgetContents->layout()->addWidget( new QLabel( "ACCOUNTS:\n", this ) ); + const QList< Tomahawk::source_ptr > sources = SourceList::instance()->sources( true ); const QList< Tomahawk::Accounts::Account* > accounts = Tomahawk::Accounts::AccountManager::instance()->accounts( Tomahawk::Accounts::SipType ); foreach ( Tomahawk::Accounts::Account* account, accounts ) @@ -102,6 +98,104 @@ void DiagnosticsDialog::updateLogView() if ( !account || !account->sipPlugin() ) continue; + connect( account, SIGNAL( connectionStateChanged( Tomahawk::Accounts::Account::ConnectionState ) ), + SLOT( onAccountConnectionStateChanged( Tomahawk::Accounts::Account::ConnectionState ) ) ); + connect( account, SIGNAL( error( int, QString ) ), + SLOT( onAccountError( int, QString ) ) ); + + connect( account->sipPlugin(), SIGNAL( peerOnline( QString ) ), SLOT( onPeerOnline( QString ) ) ); + connect( account->sipPlugin(), SIGNAL( peerOffline( QString ) ), SLOT( onPeerOffline( QString ) ) ); + connect( account->sipPlugin(), SIGNAL( sipInfoReceived( QString, SipInfo ) ), SLOT( onSipInfoReceived( QString, SipInfo ) ) ); + connect( account->sipPlugin(), SIGNAL( softwareVersionReceived( QString, QString ) ), SLOT( onSoftwareVersionReceived( QString, QString ) ) ); + + QLabel* accountInfoLabel = new QLabel( this ); + ui->scrollAreaWidgetContents->layout()->addWidget( accountInfoLabel ); + m_accountDescriptionStore.insert( account, accountInfoLabel ); + + updateAccountLabel( account ); + } +} + + +void +DiagnosticsDialog::copyToClipboard() +{ + QString log; + foreach ( QLabel* label, m_accountDescriptionStore.values() ) + { + log += label->text() + "\n\n"; + } + + QApplication::clipboard()->setText( log ); +} + + +void +DiagnosticsDialog::onAccountConnectionStateChanged( Tomahawk::Accounts::Account::ConnectionState /* state */ ) +{ + Tomahawk::Accounts::Account* account = qobject_cast< Tomahawk::Accounts::Account* >( sender() ); + Q_ASSERT( account ); + + updateAccountLabel( account ); +} + + +void +DiagnosticsDialog::onAccountError( int /* errorId */ , QString /* errorString */ ) +{ + Tomahawk::Accounts::Account* account = qobject_cast< Tomahawk::Accounts::Account* >( sender() ); + Q_ASSERT( account ); +} + + +void +DiagnosticsDialog::onPeerOnline( const QString& ) +{ + Tomahawk::Accounts::Account* account = qobject_cast< SipPlugin* >( sender() )->account(); + Q_ASSERT( account ); + + updateAccountLabel( account ); +} + + +void +DiagnosticsDialog::onPeerOffline( const QString& ) +{ + Tomahawk::Accounts::Account* account = qobject_cast< SipPlugin* >( sender() )->account(); + Q_ASSERT( account ); + + updateAccountLabel( account ); +} + + +void +DiagnosticsDialog::onSipInfoReceived( const QString& /* peerId */ , const SipInfo& /* info */ ) +{ + Tomahawk::Accounts::Account* account = qobject_cast< SipPlugin* >( sender() )->account(); + Q_ASSERT( account ); + + updateAccountLabel( account ); +} + + +void +DiagnosticsDialog::onSoftwareVersionReceived( const QString& /* peerId */ , const QString& /* versionString */ ) +{ + Tomahawk::Accounts::Account* account = qobject_cast< SipPlugin* >( sender() )->account(); + Q_ASSERT( account ); + + updateAccountLabel( account ); +} + + +void +DiagnosticsDialog::updateAccountLabel( Tomahawk::Accounts::Account* account ) +{ + QLabel* accountInfoLabel = m_accountDescriptionStore.value( account ); + + if ( accountInfoLabel ) + { + QString accountInfo; QString stateString; switch( account->connectionState() ) { @@ -118,7 +212,7 @@ void DiagnosticsDialog::updateLogView() case Tomahawk::Accounts::Account::Disconnecting: stateString = "Disconnecting"; } - log.append( + accountInfo.append( QString( " %2 (%1): %3 (%4)\n" ) .arg( account->accountServiceName() ) .arg( account->sipPlugin()->friendlyName() ) @@ -126,55 +220,43 @@ void DiagnosticsDialog::updateLogView() .arg( stateString ) ); - foreach( const QString &peerId, account->sipPlugin()->peersOnline() ) + foreach( const QString& peerId, account->sipPlugin()->peersOnline() ) { - /* enable this again, when we check the Source.has this peerId - bool connected = false; - Q_FOREACH( const Tomahawk::source_ptr &source, sources ) - { - if( source->controlConnection() ) - { - connected = true; - break; - } - }*/ - QString versionString = SipHandler::instance()->versionString( peerId ); SipInfo sipInfo = SipHandler::instance()->sipInfo( peerId ); - if( !sipInfo.isValid() ) - log.append( + if ( !sipInfo.isValid() ) + { + accountInfo.append( QString(" %1: %2 %3" /*"(%4)"*/ "\n") .arg( peerId ) .arg( "sipinfo invalid" ) .arg( versionString ) // .arg( connected ? "connected" : "not connected") ); - else if( sipInfo.isVisible() ) - log.append( + } + else if ( sipInfo.isVisible() ) + { + accountInfo.append( QString(" %1: %2:%3 %4" /*" (%5)"*/ "\n") .arg( peerId ) .arg( sipInfo.host().hostName() ) .arg( sipInfo.port() ) .arg( versionString ) // .arg( connected ? "connected" : "not connected") - ); + } else - log.append( + { + accountInfo.append( QString(" %1: visible: false %2" /*" (%3)"*/ "\n") .arg( peerId ) .arg( versionString ) // .arg( connected ? "connected" : "not connected") - ); + } } - log.append("\n"); + accountInfo.append( "\n" ); + + accountInfoLabel->setText( accountInfo ); } - ui->logView->setPlainText(log); -} - -void DiagnosticsDialog::copyToClipboard() -{ - QApplication::clipboard()->setText( ui->logView->toPlainText() ); -} - +} \ No newline at end of file diff --git a/src/DiagnosticsDialog.h b/src/DiagnosticsDialog.h index e3fed2b59..99da5aae9 100644 --- a/src/DiagnosticsDialog.h +++ b/src/DiagnosticsDialog.h @@ -19,7 +19,15 @@ #ifndef DIGANOSTICSDIALOG_H #define DIAGNOSTICSDIALOG_H +#include "accounts/Account.h" + #include +#include + + +class QLabel; + +class SipInfo; namespace Ui { @@ -37,8 +45,19 @@ public: private slots: void updateLogView(); void copyToClipboard(); + + void onAccountConnectionStateChanged( Tomahawk::Accounts::Account::ConnectionState state ); + void onAccountError( int errorId, QString errorString ); + void onPeerOnline( const QString& ); + void onPeerOffline( const QString& ); + void onSipInfoReceived( const QString& peerId, const SipInfo& info ); + void onSoftwareVersionReceived( const QString& peerId, const QString& versionString ); + void updateAccountLabel( Tomahawk::Accounts::Account* ); private: + + QMap< Tomahawk::Accounts::Account*, QLabel* > m_accountDescriptionStore; + Ui::DiagnosticsDialog* ui; }; diff --git a/src/DiagnosticsDialog.ui b/src/DiagnosticsDialog.ui index 6d884eba6..d304b65f6 100644 --- a/src/DiagnosticsDialog.ui +++ b/src/DiagnosticsDialog.ui @@ -26,19 +26,26 @@ - + + + true + + + + + 0 + 0 + 708 + 386 + + + + - - - - Update - - - diff --git a/src/HeadlessCheck.h b/src/HeadlessCheck.h index 046d6d712..9436ae223 100644 --- a/src/HeadlessCheck.h +++ b/src/HeadlessCheck.h @@ -29,7 +29,6 @@ #define TOMAHAWK_APPLICATION QApplication #include -#include "TomahawkWindow.h" #endif diff --git a/src/LoadXSPFDialog.cpp b/src/LoadXSPFDialog.cpp index ec49e4529..eaa2e16b1 100644 --- a/src/LoadXSPFDialog.cpp +++ b/src/LoadXSPFDialog.cpp @@ -18,6 +18,7 @@ #include "LoadXSPFDialog.h" #include "TomahawkSettings.h" +#include "Source.h" #include "ui_LoadXSPFDialog.h" #include diff --git a/src/MusicScanner.cpp b/src/MusicScanner.cpp index f7e30c5e0..f1b489878 100644 --- a/src/MusicScanner.cpp +++ b/src/MusicScanner.cpp @@ -106,15 +106,17 @@ MusicScanner::MusicScanner( const QStringList& dirs, quint32 bs ) , m_batchsize( bs ) , m_dirListerThreadController( 0 ) { - m_ext2mime.insert( "mp3", TomahawkUtils::extensionToMimetype( "mp3" ) ); - m_ext2mime.insert( "ogg", TomahawkUtils::extensionToMimetype( "ogg" ) ); - m_ext2mime.insert( "oga", TomahawkUtils::extensionToMimetype( "oga" ) ); - m_ext2mime.insert( "mpc", TomahawkUtils::extensionToMimetype( "mpc" ) ); - m_ext2mime.insert( "wma", TomahawkUtils::extensionToMimetype( "wma" ) ); - m_ext2mime.insert( "aac", TomahawkUtils::extensionToMimetype( "aac" ) ); - m_ext2mime.insert( "m4a", TomahawkUtils::extensionToMimetype( "m4a" ) ); - m_ext2mime.insert( "mp4", TomahawkUtils::extensionToMimetype( "mp4" ) ); + m_ext2mime.insert( "mp3", TomahawkUtils::extensionToMimetype( "mp3" ) ); + m_ext2mime.insert( "ogg", TomahawkUtils::extensionToMimetype( "ogg" ) ); + m_ext2mime.insert( "oga", TomahawkUtils::extensionToMimetype( "oga" ) ); + m_ext2mime.insert( "mpc", TomahawkUtils::extensionToMimetype( "mpc" ) ); + m_ext2mime.insert( "wma", TomahawkUtils::extensionToMimetype( "wma" ) ); + m_ext2mime.insert( "aac", TomahawkUtils::extensionToMimetype( "aac" ) ); + m_ext2mime.insert( "m4a", TomahawkUtils::extensionToMimetype( "m4a" ) ); + m_ext2mime.insert( "mp4", TomahawkUtils::extensionToMimetype( "mp4" ) ); m_ext2mime.insert( "flac", TomahawkUtils::extensionToMimetype( "flac" ) ); + m_ext2mime.insert( "aiff", TomahawkUtils::extensionToMimetype( "aiff" ) ); + m_ext2mime.insert( "aif", TomahawkUtils::extensionToMimetype( "aif" ) ); } @@ -199,18 +201,16 @@ MusicScanner::listerFinished() foreach( const QString& key, m_filemtimes.keys() ) m_filesToDelete << m_filemtimes[ key ].keys().first(); - tDebug() << "Lister finished: to delete:" << m_filesToDelete; + tDebug( LOGINFO ) << "Scanning complete, saving to database. ( deleted" << m_filesToDelete.count() << "- scanned" << m_scanned << "- skipped" << m_skipped << ")"; + tDebug( LOGEXTRA ) << "Skipped the following files (no tags / no valid audio):"; + foreach ( const QString& s, m_skippedFiles ) + tDebug( LOGEXTRA ) << s; if ( m_filesToDelete.length() || m_scannedfiles.length() ) { commitBatch( m_scannedfiles, m_filesToDelete ); m_scannedfiles.clear(); m_filesToDelete.clear(); - - tDebug( LOGINFO ) << "Scanning complete, saving to database. ( scanned" << m_scanned << "skipped" << m_skipped << ")"; - tDebug( LOGEXTRA ) << "Skipped the following files (no tags / no valid audio):"; - foreach ( const QString& s, m_skippedFiles ) - tDebug( LOGEXTRA ) << s; } else cleanup(); @@ -335,9 +335,8 @@ MusicScanner::readFile( const QFileInfo& fi ) int bitrate = 0; int duration = 0; - + Tag *tag = Tag::fromFile( f ); - if ( f.audioProperties() ) { TagLib::AudioProperties *properties = f.audioProperties(); @@ -345,10 +344,14 @@ MusicScanner::readFile( const QFileInfo& fi ) bitrate = properties->bitrate(); } - QString artist = tag->artist().trimmed(); - QString album = tag->album().trimmed(); - QString track = tag->title().trimmed(); - if ( artist.isEmpty() || track.isEmpty() ) + QString artist, album, track; + if ( tag ) + { + artist = tag->artist().trimmed(); + album = tag->album().trimmed(); + track = tag->title().trimmed(); + } + if ( !tag || artist.isEmpty() || track.isEmpty() ) { // FIXME: do some clever filename guessing m_skippedFiles << fi.canonicalFilePath(); diff --git a/src/PipelineStatusView.cpp b/src/PipelineStatusView.cpp deleted file mode 100644 index e87614d70..000000000 --- a/src/PipelineStatusView.cpp +++ /dev/null @@ -1,117 +0,0 @@ -/* === This file is part of Tomahawk Player - === - * - * Copyright 2010-2011, Christian Muehlhaeuser - * Copyright 2011, Leo Franchi - * - * Tomahawk is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Tomahawk is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Tomahawk. If not, see . - */ - -#include "JobStatusView.h" - -#include -#include - -#include "libtomahawk/Pipeline.h" - -#include "utils/Logger.h" - -using namespace Tomahawk; - - -JobStatusView::JobStatusView( AnimatedSplitter* parent ) - : AnimatedWidget( parent ) - , m_parent( parent ) -{ - setHiddenSize( QSize( 0, 0 ) ); - setLayout( new QVBoxLayout() ); - m_tree = new QTreeWidget( this ); - - layout()->setMargin( 0 ); - layout()->addWidget( m_tree ); - - QStringList headers; - headers << tr( "Searching For" ) << tr( "Pending" ); - m_tree->setHeaderLabels( headers ); - - m_tree->setHorizontalScrollBarPolicy( Qt::ScrollBarAlwaysOff ); - m_tree->setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Ignored ); - m_tree->setColumnCount( 2 ); - m_tree->setColumnWidth( 0, 200 ); - m_tree->setColumnWidth( 1, 50 ); - - m_tree->header()->setStretchLastSection( true ); - m_tree->setRootIsDecorated( false ); - - m_tree->setFrameShape( QFrame::NoFrame ); - m_tree->setAttribute( Qt::WA_MacShowFocusRect, 0 ); - - new QTreeWidgetItem( m_tree ); - - connect( Pipeline::instance(), SIGNAL( resolving( Tomahawk::query_ptr ) ), SLOT( onPipelineUpdate( Tomahawk::query_ptr ) ) ); - connect( Pipeline::instance(), SIGNAL( idle() ), SLOT( onPipelineUpdate() ) ); - -#ifndef Q_WS_WIN - QFont f = font(); - f.setPointSize( f.pointSize() - 1 ); - setFont( f ); -#endif - -#ifdef Q_WS_MAC - f.setPointSize( f.pointSize() - 2 ); - setFont( f ); -#endif - - onPipelineUpdate(); -} - - -void -JobStatusView::onPipelineUpdate( const query_ptr& query ) -{ - QTreeWidgetItem* ti = m_tree->invisibleRootItem()->child( 0 ); - - if ( Pipeline::instance()->activeQueryCount() && !query.isNull() ) - { - ti->setText( 0, QString( "%1 - %2" ).arg( query->artist() ).arg( query->track() ) ); - ti->setText( 1, QString( "%1" ).arg( Pipeline::instance()->activeQueryCount() + Pipeline::instance()->pendingQueryCount() ) ); - - if ( isHidden() ) - emit showWidget(); - } - else - { - ti->setText( 0, tr( "Idle" ) ); - ti->setText( 1, QString( "None" ) ); - - if ( !isHidden() ) - emit hideWidget(); - } -} - - -QSize -JobStatusView::sizeHint() const -{ - unsigned int y = 0; - y += m_tree->header()->height(); - y += m_tree->contentsMargins().top() + m_tree->contentsMargins().bottom(); - - if ( m_tree->invisibleRootItem()->childCount() ) - { - unsigned int rowheight = m_tree->sizeHintForRow( 0 ); - y += rowheight * m_tree->invisibleRootItem()->childCount() + 2; - } - - return QSize( 0, y ); -} diff --git a/src/PipelineStatusView.h b/src/PipelineStatusView.h deleted file mode 100644 index 857357082..000000000 --- a/src/PipelineStatusView.h +++ /dev/null @@ -1,51 +0,0 @@ -/* === This file is part of Tomahawk Player - === - * - * Copyright 2010-2011, Christian Muehlhaeuser - * Copyright 2011, Leo Franchi - * - * Tomahawk is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Tomahawk is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Tomahawk. If not, see . - */ - -#ifndef JOBSTATUSVIEW_H -#define JOBSTATUSVIEW_H - -#include - -#include "Typedefs.h" -#include "widgets/AnimatedSplitter.h" -#include "Query.h" - -class StreamConnection; - -class JobStatusView : public AnimatedWidget -{ -Q_OBJECT - -public: - explicit JobStatusView( AnimatedSplitter* parent ); - virtual ~JobStatusView() - { - } - - QSize sizeHint() const; - -private slots: - void onPipelineUpdate( const Tomahawk::query_ptr& query = Tomahawk::query_ptr() ); - -private: - QTreeView* m_tree; - AnimatedSplitter* m_parent; -}; - -#endif // JOBSTATUSVIEW_H diff --git a/src/Scrobbler.cpp b/src/Scrobbler.cpp index dd2f41942..8c3285161 100644 --- a/src/Scrobbler.cpp +++ b/src/Scrobbler.cpp @@ -29,6 +29,7 @@ #include "audio/AudioEngine.h" #include "TomahawkSettings.h" #include "infosystem/InfoSystem.h" +#include "Source.h" #include "utils/Logger.h" @@ -43,8 +44,8 @@ Scrobbler::Scrobbler( QObject* parent ) SLOT( engineTick( unsigned int ) ), Qt::QueuedConnection ); connect( Tomahawk::InfoSystem::InfoSystem::instance(), - SIGNAL( info( Tomahawk::InfoSystem::InfoRequestData, QVariant ) ), - SLOT( infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData, QVariant ) ) ); + SIGNAL( info( Tomahawk::InfoSystem::InfoRequestData, QVariant ) ), + SLOT( infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData, QVariant ) ) ); connect( AudioEngine::instance(), SIGNAL( started( const Tomahawk::result_ptr& ) ), SLOT( trackStarted( const Tomahawk::result_ptr& ) ), Qt::QueuedConnection ); @@ -71,16 +72,13 @@ void Scrobbler::trackStarted( const Tomahawk::result_ptr& track ) { Q_ASSERT( QThread::currentThread() == thread() ); -// qDebug() << Q_FUNC_INFO; - if( m_reachedScrobblePoint ) + if ( m_reachedScrobblePoint ) { m_reachedScrobblePoint = false; scrobble(); } - QVariantMap playInfo; - Tomahawk::InfoSystem::InfoStringHash trackInfo; trackInfo["title"] = track->track(); trackInfo["artist"] = track->artist()->name(); @@ -88,6 +86,7 @@ Scrobbler::trackStarted( const Tomahawk::result_ptr& track ) trackInfo["duration"] = QString::number( track->duration() ); trackInfo["albumpos"] = QString::number( track->albumpos() ); + QVariantMap playInfo; playInfo["trackinfo"] = QVariant::fromValue< Tomahawk::InfoSystem::InfoStringHash >( trackInfo ); playInfo["private"] = TomahawkSettings::instance()->privateListeningMode(); @@ -125,7 +124,7 @@ Scrobbler::trackStopped() { Q_ASSERT( QThread::currentThread() == thread() ); - if( m_reachedScrobblePoint ) + if ( m_reachedScrobblePoint ) { m_reachedScrobblePoint = false; scrobble(); diff --git a/src/SettingsDialog.cpp b/src/SettingsDialog.cpp index 3999121de..54fab5fa5 100644 --- a/src/SettingsDialog.cpp +++ b/src/SettingsDialog.cpp @@ -32,16 +32,16 @@ #include "AtticaManager.h" #include "TomahawkApp.h" #include "TomahawkSettings.h" -#include "DelegateConfigWrapper.h" +#include "accounts/DelegateConfigWrapper.h" #include "MusicScanner.h" #include "Pipeline.h" #include "Resolver.h" #include "ExternalResolverGui.h" #include "utils/TomahawkUtilsGui.h" -#include "GuiHelpers.h" +#include "utils/GuiHelpers.h" #include "ScanManager.h" #include "SettingsListDelegate.h" -#include "AccountDelegate.h" +#include "accounts/AccountDelegate.h" #include "database/Database.h" #include "network/Servent.h" #include "utils/AnimatedSpinner.h" @@ -51,7 +51,8 @@ #include #include #include "utils/Logger.h" -#include "AccountFactoryWrapper.h" +#include "accounts/AccountFactoryWrapper.h" +#include "accounts/spotify/SpotifyAccount.h" #include "ui_ProxyDialog.h" #include "ui_StackedSettingsDialog.h" @@ -97,7 +98,7 @@ SettingsDialog::SettingsDialog( QWidget *parent ) ui->enableProxyCheckBox->setChecked( useProxy ); ui->proxyButton->setEnabled( useProxy ); - + createIcons(); #ifdef Q_WS_X11 ui->listWidget->setFrameShape( QFrame::StyledPanel ); @@ -116,6 +117,13 @@ SettingsDialog::SettingsDialog( QWidget *parent ) m_proxySettings.setSizeGripEnabled( true ); QSizeGrip* p = m_proxySettings.findChild< QSizeGrip* >(); p->setFixedSize( 0, 0 ); + + ui->groupBoxNetworkAdvanced->layout()->removeItem( ui->verticalSpacer ); + ui->groupBoxNetworkAdvanced->layout()->removeItem( ui->verticalSpacer_2 ); + ui->groupBoxNetworkAdvanced->layout()->removeItem( ui->verticalSpacer_4 ); + delete ui->verticalSpacer; + delete ui->verticalSpacer_2; + delete ui->verticalSpacer_4; #endif // Accounts @@ -135,6 +143,7 @@ SettingsDialog::SettingsDialog( QWidget *parent ) connect( m_accountProxy, SIGNAL( startInstalling( QPersistentModelIndex ) ), accountDelegate, SLOT( startInstalling(QPersistentModelIndex) ) ); connect( m_accountProxy, SIGNAL( doneInstalling( QPersistentModelIndex ) ), accountDelegate, SLOT( doneInstalling(QPersistentModelIndex) ) ); + connect( m_accountProxy, SIGNAL( errorInstalling( QPersistentModelIndex ) ), accountDelegate, SLOT( errorInstalling(QPersistentModelIndex) ) ); connect( m_accountProxy, SIGNAL( scrollTo( QModelIndex ) ), this, SLOT( scrollTo( QModelIndex ) ) ); ui->accountsView->setModel( m_accountProxy ); @@ -255,7 +264,7 @@ SettingsDialog::~SettingsDialog() } else qDebug() << "Settings dialog cancelled, NOT saving prefs."; - + delete ui; } @@ -450,15 +459,40 @@ SettingsDialog::installFromFile() if( !resolver.isEmpty() ) { + const QFileInfo resolverAbsoluteFilePath( resolver ); + TomahawkSettings::instance()->setScriptDefaultPath( resolverAbsoluteFilePath.absolutePath() ); + + if ( resolverAbsoluteFilePath.baseName() == "spotify_tomahawkresolver" ) + { + // HACK if this is a spotify resolver, we treat is specially. + // usually we expect the user to just download the spotify resolver from attica, + // however developers, those who build their own tomahawk, can't do that, or linux + // users can't do that. However, we have an already-existing SpotifyAccount that we + // know exists that we need to use this resolver path. + // + // Hence, we special-case the spotify resolver and directly set the path on it here. + SpotifyAccount* acct = 0; + foreach ( Account* account, AccountManager::instance()->accounts() ) + { + if ( SpotifyAccount* spotify = qobject_cast< SpotifyAccount* >( account ) ) + { + acct = spotify; + break; + } + } + + if ( acct ) + { + acct->setManualResolverPath( resolver ); + return; + } + } + Account* acct = AccountManager::instance()->accountFromPath( resolver ); AccountManager::instance()->addAccount( acct ); TomahawkSettings::instance()->addAccount( acct->accountId() ); AccountManager::instance()->enableAccount( acct ); - - - QFileInfo resolverAbsoluteFilePath( resolver ); - TomahawkSettings::instance()->setScriptDefaultPath( resolverAbsoluteFilePath.absolutePath() ); } } diff --git a/src/SocialWidget.cpp b/src/SocialWidget.cpp index 8c4d518ed..80f708939 100644 --- a/src/SocialWidget.cpp +++ b/src/SocialWidget.cpp @@ -25,6 +25,7 @@ #include "GlobalActionManager.h" #include "utils/Logger.h" +#include "Source.h" #define CORNER_ROUNDNESS 8.0 #define FADING_DURATION 500 @@ -63,7 +64,7 @@ SocialWidget::SocialWidget( QWidget* parent ) connect( ui->facebookButton, SIGNAL( clicked( bool ) ), SLOT( onChanged() ) ); connect( ui->twitterButton, SIGNAL( clicked( bool ) ), SLOT( onChanged() ) ); connect( GlobalActionManager::instance(), SIGNAL( shortLinkReady( QUrl, QUrl, QVariant ) ), SLOT( onShortLinkReady( QUrl, QUrl, QVariant ) ) ); - + onChanged(); } @@ -192,6 +193,9 @@ SocialWidget::paintEvent( QPaintEvent* event ) void SocialWidget::onShortLinkReady( const QUrl& longUrl, const QUrl& shortUrl, const QVariant& callbackObj ) { + Q_UNUSED( longUrl ); + Q_UNUSED( callbackObj ); + if ( m_query->album().isEmpty() ) ui->textEdit->setText( tr( "Listening to \"%1\" by %2 and loving it! %3" ).arg( m_query->track() ).arg( m_query->artist() ).arg( shortUrl.toString() ) ); else @@ -206,7 +210,7 @@ SocialWidget::setQuery( const Tomahawk::query_ptr& query ) ui->coverImage->setPixmap( query->cover( ui->coverImage->size() ) ); onShortLinkReady( QString(), QString(), QVariant() ); onChanged(); - + QUrl longUrl = GlobalActionManager::instance()->openLinkFromQuery( query ); GlobalActionManager::instance()->shortenLink( longUrl ); } @@ -235,7 +239,7 @@ SocialWidget::charsAvailable() const { if ( ui->twitterButton->isChecked() ) return 140; - + return 420; // facebook max length } @@ -262,6 +266,6 @@ SocialWidget::eventFilter( QObject* object, QEvent* event ) { onGeometryUpdate(); } - + return QObject::eventFilter( object, event ); } diff --git a/src/TomahawkApp.cpp b/src/TomahawkApp.cpp index 6243e11b5..baf919ee1 100644 --- a/src/TomahawkApp.cpp +++ b/src/TomahawkApp.cpp @@ -29,6 +29,7 @@ #include #include #include +#include #include "Artist.h" #include "Album.h" @@ -83,11 +84,6 @@ #include #endif -// should go to a plugin actually -#ifdef GLOOX_FOUND - #include "xmppbot/XmppBot.h" -#endif - #ifdef Q_WS_MAC #include "mac/MacShortcutHandler.h" @@ -134,6 +130,29 @@ TomahawkApp::TomahawkApp( int& argc, char *argv[] ) setApplicationVersion( QLatin1String( TOMAHAWK_VERSION ) ); registerMetaTypes(); + installTranslator(); +} + + +void +TomahawkApp::installTranslator() +{ + QString locale = QLocale::system().name(); + if ( locale == "C" ) + locale = "en"; + + QTranslator* translator = new QTranslator( this ); + if ( translator->load( QString( ":/lang/tomahawk_" ) + locale ) ) + { + tDebug() << "Using system locale:" << locale; + } + else + { + tDebug() << "Using default locale, system locale one not found:" << locale; + translator->load( QString( ":/lang/tomahawk_en" ) ); + } + + TOMAHAWK_APPLICATION::installTranslator( translator ); } @@ -290,6 +309,16 @@ TomahawkApp::init() PlaylistUpdaterInterface::registerUpdaterFactory( new XspfUpdaterFactory ); PlaylistUpdaterInterface::registerUpdaterFactory( new SpotifyUpdaterFactory ); + // Following work-around/fix taken from Clementine rev. 13e13ccd9a95 and courtesy of David Sansome + // A bug in Qt means the wheel_scroll_lines setting gets ignored and replaced + // with the default value of 3 in QApplicationPrivate::initialize. + { + QSettings qt_settings(QSettings::UserScope, "Trolltech"); + qt_settings.beginGroup("Qt"); + QApplication::setWheelScrollLines( + qt_settings.value("wheelScrollLines", QApplication::wheelScrollLines()).toInt()); + } + #ifndef ENABLE_HEADLESS // Make sure to init GAM in the gui thread GlobalActionManager::instance(); @@ -299,9 +328,9 @@ TomahawkApp::init() connect( r, SIGNAL( finished() ), this, SLOT( spotifyApiCheckFinished() ) ); #endif -#ifdef Q_WS_MAC +#ifdef Q_OS_MAC // Make sure to do this after main window is inited - Tomahawk::enableFullscreen(); + Tomahawk::enableFullscreen( m_mainwindow ); #endif } @@ -416,10 +445,13 @@ TomahawkApp::registerMetaTypes() qRegisterMetaType("Tomahawk::ModelMode"); // Extra definition for namespaced-versions of signals/slots required + qRegisterMetaType< Tomahawk::Resolver* >("Tomahawk::Resolver*"); qRegisterMetaType< Tomahawk::source_ptr >("Tomahawk::source_ptr"); qRegisterMetaType< Tomahawk::collection_ptr >("Tomahawk::collection_ptr"); qRegisterMetaType< Tomahawk::result_ptr >("Tomahawk::result_ptr"); qRegisterMetaType< Tomahawk::query_ptr >("Tomahawk::query_ptr"); + qRegisterMetaType< Tomahawk::album_ptr >("Tomahawk::album_ptr"); + qRegisterMetaType< Tomahawk::artist_ptr >("Tomahawk::artist_ptr"); qRegisterMetaType< Tomahawk::source_ptr >("Tomahawk::source_ptr"); qRegisterMetaType< Tomahawk::dyncontrol_ptr >("Tomahawk::dyncontrol_ptr"); qRegisterMetaType< Tomahawk::playlist_ptr >("Tomahawk::playlist_ptr"); @@ -459,7 +491,8 @@ TomahawkApp::registerMetaTypes() qRegisterMetaTypeStreamOperators< QList< Tomahawk::InfoSystem::InfoStringHash > >("QList< Tomahawk::InfoSystem::InfoStringHash > "); qRegisterMetaType< QPersistentModelIndex >( "QPersistentModelIndex" ); - qRegisterMetaType< Tomahawk::PlaylistInterface::LatchMode >( "Tomahawk::PlaylistInterface::LatchMode" ); + qRegisterMetaType< Tomahawk::PlaylistModes::LatchMode >( "Tomahawk::PlaylistModes::LatchMode" ); + qRegisterMetaType< Tomahawk::PlaylistModes::RepeatMode >( "Tomahawk::PlaylistModes::RepeatMode" ); qRegisterMetaType< TomahawkUtils::CacheData >( "TomahawkUtils::CacheData" ); qRegisterMetaTypeStreamOperators< TomahawkUtils::CacheData >( "TomahawkUtils::CacheData" ); @@ -580,10 +613,6 @@ TomahawkApp::initSIP() //FIXME: jabber autoconnect is really more, now that there is sip -- should be renamed and/or split out of jabber-specific settings if ( !arguments().contains( "--nosip" ) ) { -#ifdef GLOOX_FOUND - m_xmppBot = QWeakPointer( new XMPPBot( this ) ); -#endif - tDebug( LOGINFO ) << "Connecting SIP classes"; Accounts::AccountManager::instance()->initSIP(); } @@ -667,28 +696,29 @@ TomahawkApp::loadUrl( const QString& url ) void TomahawkApp::instanceStarted( KDSingleApplicationGuard::Instance instance ) { - tDebug( LOGINFO ) << "Instance started!" << instance.pid << instance.arguments; + tDebug( LOGINFO ) << "Instance started!" << instance.pid() << instance.arguments(); + const QStringList arguments = instance.arguments(); - if ( instance.arguments.size() < 2 ) + if ( arguments.size() < 2 ) return; - QString arg1 = instance.arguments[ 1 ]; + QString arg1 = arguments[ 1 ]; if ( loadUrl( arg1 ) ) { activate(); return; } - if ( instance.arguments.contains( "--next" ) ) + if ( arguments.contains( "--next" ) ) AudioEngine::instance()->next(); - else if ( instance.arguments.contains( "--prev" ) ) + else if ( arguments.contains( "--prev" ) ) AudioEngine::instance()->previous(); - else if ( instance.arguments.contains( "--playpause" ) ) + else if ( arguments.contains( "--playpause" ) ) AudioEngine::instance()->playPause(); - else if ( instance.arguments.contains( "--play" ) ) + else if ( arguments.contains( "--play" ) ) AudioEngine::instance()->play(); - else if ( instance.arguments.contains( "--pause" ) ) + else if ( arguments.contains( "--pause" ) ) AudioEngine::instance()->pause(); - else if ( instance.arguments.contains( "--stop" ) ) + else if ( arguments.contains( "--stop" ) ) AudioEngine::instance()->stop(); } diff --git a/src/TomahawkApp.h b/src/TomahawkApp.h index 09e911d15..41cfae3a9 100644 --- a/src/TomahawkApp.h +++ b/src/TomahawkApp.h @@ -47,6 +47,7 @@ class Servent; class SipHandler; class TomahawkSettings; class XMPPBot; +class AudioControls; namespace Tomahawk { @@ -114,6 +115,7 @@ private slots: void accountManagerReady(); private: + void installTranslator(); void registerMetaTypes(); void printHelp(); diff --git a/src/TomahawkTrayIcon.cpp b/src/TomahawkTrayIcon.cpp index 4e46f7a41..edcf2483c 100644 --- a/src/TomahawkTrayIcon.cpp +++ b/src/TomahawkTrayIcon.cpp @@ -27,8 +27,11 @@ #include "TomahawkApp.h" #include "TomahawkWindow.h" #include "Query.h" +#include "Source.h" +#include "Collection.h" #include "utils/Logger.h" +#include "utils/TomahawkUtilsGui.h" #include @@ -37,28 +40,35 @@ TomahawkTrayIcon::TomahawkTrayIcon( QObject* parent ) , m_currentAnimationFrame( 0 ) , m_showWindowAction( 0 ) , m_stopContinueAfterTrackAction( 0 ) + , m_loveTrackAction( 0 ) { +#ifdef Q_WS_MAC QIcon icon( RESPATH "icons/tomahawk-icon-128x128-grayscale.png" ); +#else + QIcon icon( RESPATH "icons/tomahawk-icon-128x128.png" ); +#endif + setIcon( icon ); refreshToolTip(); m_contextMenu = new QMenu(); setContextMenu( m_contextMenu ); - - m_stopContinueAfterTrackAction = new QAction( tr( "&Stop Playback after current Track" ), this ); + + m_loveTrackAction = new QAction( this ); + m_stopContinueAfterTrackAction = new QAction( this ); ActionCollection *ac = ActionCollection::instance(); m_contextMenu->addAction( ac->getAction( "playPause" ) ); m_contextMenu->addAction( ac->getAction( "stop" ) ); + m_contextMenu->addSeparator(); + m_contextMenu->addAction( m_loveTrackAction ); m_contextMenu->addAction( m_stopContinueAfterTrackAction ); m_contextMenu->addSeparator(); m_contextMenu->addAction( ac->getAction( "previousTrack" ) ); m_contextMenu->addAction( ac->getAction( "nextTrack" ) ); m_contextMenu->addSeparator(); m_contextMenu->addAction( ActionCollection::instance()->getAction( "togglePrivacy" ) ); - - connect( m_stopContinueAfterTrackAction, SIGNAL( triggered(bool) ), this, SLOT( stopContinueAfterTrackActionTriggered() ) ); #ifdef Q_WS_MAC // On mac you can close the windows while leaving the app open. We then need a way to show the main window again @@ -73,16 +83,20 @@ TomahawkTrayIcon::TomahawkTrayIcon( QObject* parent ) m_contextMenu->addSeparator(); m_contextMenu->addAction( ac->getAction( "quit" ) ); + connect( m_loveTrackAction, SIGNAL( triggered() ), SLOT( loveTrackTriggered() ) ); + connect( m_stopContinueAfterTrackAction, SIGNAL( triggered() ), SLOT( stopContinueAfterTrackActionTriggered() ) ); + connect( AudioEngine::instance(), SIGNAL( loading( Tomahawk::result_ptr ) ), SLOT( setResult( Tomahawk::result_ptr ) ) ); connect( AudioEngine::instance(), SIGNAL( started( Tomahawk::result_ptr ) ), SLOT( onPlay() ) ); - connect( AudioEngine::instance(), SIGNAL( resumed() ), this, SLOT( onResume() ) ); - connect( AudioEngine::instance(), SIGNAL( stopped() ), this, SLOT( onStop() ) ); - connect( AudioEngine::instance(), SIGNAL( paused() ), this, SLOT( onPause() ) ); - connect( AudioEngine::instance(), SIGNAL( stopAfterTrack_changed() ) , this, SLOT( stopContinueAfterTrack_StatusChanged() ) ); + connect( AudioEngine::instance(), SIGNAL( resumed() ), SLOT( onResume() ) ); + connect( AudioEngine::instance(), SIGNAL( stopped() ), SLOT( onStop() ) ); + connect( AudioEngine::instance(), SIGNAL( paused() ), SLOT( onPause() ) ); + connect( AudioEngine::instance(), SIGNAL( stopAfterTrackChanged() ), SLOT( onStopContinueAfterTrackChanged() ) ); connect( &m_animationTimer, SIGNAL( timeout() ), SLOT( onAnimationTimer() ) ); connect( this, SIGNAL( activated( QSystemTrayIcon::ActivationReason ) ), SLOT( onActivated( QSystemTrayIcon::ActivationReason ) ) ); + onStop(); show(); } @@ -142,8 +156,29 @@ TomahawkTrayIcon::menuAboutToShow() void TomahawkTrayIcon::setResult( const Tomahawk::result_ptr& result ) { + if ( m_currentTrack ) + { + disconnect( m_currentTrack->toQuery().data(), SIGNAL( socialActionsLoaded() ), this, SLOT( onSocialActionsLoaded() ) ); + } + m_currentTrack = result; refreshToolTip(); + + if ( result ) + connect( result->toQuery().data(), SIGNAL( socialActionsLoaded() ), SLOT( onSocialActionsLoaded() ), Qt::UniqueConnection ); + + onSocialActionsLoaded(); + onStopContinueAfterTrackChanged(); +} + + +void +TomahawkTrayIcon::onStopContinueAfterTrackChanged() +{ + if ( m_currentTrack && m_currentTrack->toQuery()->equals( AudioEngine::instance()->stopAfterTrack() ) ) + m_stopContinueAfterTrackAction->setText( tr( "&Continue Playback after current Track" ) ); + else + m_stopContinueAfterTrackAction->setText( tr( "&Stop Playback after current Track" ) ); } @@ -198,13 +233,13 @@ TomahawkTrayIcon::onActivated( QSystemTrayIcon::ActivationReason reason ) case QSystemTrayIcon::Trigger: { TomahawkWindow* mainwindow = APP->mainWindow(); - if ( mainwindow->isVisible() ) + if (mainwindow->isActiveWindow()) { mainwindow->hide(); } else { - mainwindow->show(); + TomahawkUtils::bringToFront(); } } break; @@ -231,16 +266,20 @@ TomahawkTrayIcon::onPause() void TomahawkTrayIcon::onPlay() { + m_loveTrackAction->setEnabled( true ); m_stopContinueAfterTrackAction->setEnabled( true ); + onResume(); - stopContinueAfterTrack_StatusChanged(); } void TomahawkTrayIcon::onStop() { + m_loveTrackAction->setEnabled( false ); m_stopContinueAfterTrackAction->setEnabled( false ); + + setResult( Tomahawk::result_ptr() ); onPause(); } @@ -253,26 +292,41 @@ TomahawkTrayIcon::onResume() void -TomahawkTrayIcon::stopContinueAfterTrack_StatusChanged() +TomahawkTrayIcon::loveTrackTriggered() { - if ( !AudioEngine::instance()->currentTrack().isNull() ) - { - if ( AudioEngine::instance()->currentTrack()->toQuery()->equals( AudioEngine::instance()->stopAfterTrack() ) ) - m_stopContinueAfterTrackAction->setText( tr( "&Continue Playback after current Track" ) ); - else - m_stopContinueAfterTrackAction->setText( tr( "&Stop Playback after current Track" ) ); - } + if ( !m_currentTrack ) + return; + + m_currentTrack->toQuery()->setLoved( !m_currentTrack->toQuery()->loved() ); } -void TomahawkTrayIcon::stopContinueAfterTrackActionTriggered() +void +TomahawkTrayIcon::stopContinueAfterTrackActionTriggered() { - if ( !AudioEngine::instance()->currentTrack().isNull() ) + if ( !m_currentTrack ) + return; + + if ( !m_currentTrack->toQuery()->equals( AudioEngine::instance()->stopAfterTrack() ) ) + AudioEngine::instance()->setStopAfterTrack( m_currentTrack->toQuery() ); + else + AudioEngine::instance()->setStopAfterTrack( Tomahawk::query_ptr() ); +} + + +void +TomahawkTrayIcon::onSocialActionsLoaded() +{ + m_loveTrackAction->setText( tr( "&Love" ) ); + m_loveTrackAction->setIcon( QIcon( RESPATH "images/loved.png" ) ); + + if ( !m_currentTrack ) + return; + + if ( m_currentTrack->toQuery()->loved() ) { - if ( !AudioEngine::instance()->currentTrack()->toQuery()->equals( AudioEngine::instance()->stopAfterTrack() ) ) - AudioEngine::instance()->setStopAfterTrack( AudioEngine::instance()->currentTrack()->toQuery() ); - else - AudioEngine::instance()->setStopAfterTrack( Tomahawk::query_ptr() ); + m_loveTrackAction->setText( tr( "Un-&Love" ) ); + m_loveTrackAction->setIcon( QIcon( RESPATH "images/not-loved.png" ) ); } } @@ -298,4 +352,3 @@ TomahawkTrayIcon::event( QEvent* e ) return QSystemTrayIcon::event( e ); } - diff --git a/src/TomahawkTrayIcon.h b/src/TomahawkTrayIcon.h index d1504e835..31984492b 100644 --- a/src/TomahawkTrayIcon.h +++ b/src/TomahawkTrayIcon.h @@ -49,11 +49,13 @@ private slots: void onStop(); void onResume(); - void stopContinueAfterTrack_StatusChanged(); - + void onSocialActionsLoaded(); + void onStopContinueAfterTrackChanged(); void stopContinueAfterTrackActionTriggered(); + void loveTrackTriggered(); void menuAboutToShow(); + private: void refreshToolTip(); ~TomahawkTrayIcon(); @@ -68,6 +70,7 @@ private: QAction* m_showWindowAction; QAction* m_stopContinueAfterTrackAction; + QAction* m_loveTrackAction; }; #endif // TOMAHAWK_TRAYICON_H diff --git a/src/TomahawkWindow.cpp b/src/TomahawkWindow.cpp index 1948c4b77..1a7734404 100644 --- a/src/TomahawkWindow.cpp +++ b/src/TomahawkWindow.cpp @@ -35,14 +35,12 @@ #include #include -#include "Playlist.h" -#include "Query.h" -#include "Artist.h" -#include "ViewManager.h" #include "accounts/AccountManager.h" #include "sourcetree/SourceTreeView.h" #include "network/Servent.h" +#include "utils/TomahawkUtilsGui.h" #include "utils/ProxyStyle.h" +#include "utils/WidgetDragFilter.h" #include "widgets/AnimatedSplitter.h" #include "widgets/NewPlaylistWidget.h" #include "widgets/SearchWidget.h" @@ -52,27 +50,31 @@ #include "playlist/PlaylistModel.h" #include "playlist/PlaylistView.h" #include "playlist/QueueView.h" +#include "jobview/JobStatusView.h" +#include "jobview/JobStatusModel.h" +#include "jobview/ErrorStatusMessage.h" +#include "jobview/JobStatusModel.h" +#include "Playlist.h" +#include "Query.h" +#include "Artist.h" +#include "ViewManager.h" +#include "ActionCollection.h" #include "AudioControls.h" #include "SettingsDialog.h" #include "DiagnosticsDialog.h" #include "TomahawkSettings.h" #include "SourceList.h" -#include "jobview/JobStatusView.h" -#include "jobview/JobStatusModel.h" -#include "jobview/ErrorStatusMessage.h" #include "TomahawkTrayIcon.h" #include "ScanManager.h" #include "TomahawkApp.h" +#include "LoadXSPFDialog.h" #ifdef Q_WS_WIN #include #endif #include "utils/Logger.h" -#include "jobview/JobStatusModel.h" -#include "LoadXSPFDialog.h" -#include using namespace Tomahawk; using namespace Accounts; @@ -102,6 +104,7 @@ TomahawkWindow::TomahawkWindow( QWidget* parent ) ui->centralWidget->setContentsMargins( 0, 0, 0, 0 ); TomahawkUtils::unmarginLayout( ui->centralWidget->layout() ); + setupToolBar(); setupSideBar(); statusBar()->addPermanentWidget( m_audioControls, 1 ); @@ -117,6 +120,8 @@ TomahawkWindow::TomahawkWindow( QWidget* parent ) // set initial state onAccountDisconnected(); + audioStopped(); + vm->setQueue( m_queueView ); vm->showWelcomePage(); } @@ -139,7 +144,7 @@ TomahawkWindow::loadSettings() // http://lists.qt.nokia.com/pipermail/qt-interest/2009-August/011491.html // for the 'fix' #ifdef QT_MAC_USE_COCOA - bool workaround = !isVisible(); + bool workaround = isVisible(); if ( workaround ) { // make "invisible" @@ -190,7 +195,7 @@ TomahawkWindow::applyPlatformTweaks() if ( !QString( qApp->style()->metaObject()->className() ).toLower().contains( "qtcurve" ) ) qApp->setStyle( new ProxyStyle() ); -#ifdef Q_WS_MAC +#ifdef Q_OS_MAC setUnifiedTitleAndToolBarOnMac( true ); delete ui->hline1; delete ui->hline2; @@ -201,6 +206,40 @@ TomahawkWindow::applyPlatformTweaks() } +void +TomahawkWindow::setupToolBar() +{ + QToolBar* toolbar = addToolBar( "TomahawkToolbar" ); + toolbar->setObjectName( "TomahawkToolbar" ); + toolbar->setMovable( false ); + toolbar->setFloatable( false ); + toolbar->setIconSize( QSize( 22, 22 ) ); + toolbar->setToolButtonStyle( Qt::ToolButtonIconOnly ); + toolbar->setStyleSheet( "border-bottom: 0px" ); + +#ifdef Q_OS_MAC + toolbar->installEventFilter( new WidgetDragFilter( toolbar ) ); +#endif + + m_backAction = toolbar->addAction( QIcon( RESPATH "images/back.png" ), tr( "Back" ), ViewManager::instance(), SLOT( historyBack() ) ); + m_backAction->setToolTip( tr( "Go back one page" ) ); + m_forwardAction = toolbar->addAction( QIcon( RESPATH "images/forward.png" ), tr( "Forward" ), ViewManager::instance(), SLOT( historyForward() ) ); + m_forwardAction->setToolTip( tr( "Go forward one page" ) ); + + QWidget* spacer = new QWidget( this ); + spacer->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Preferred ); + toolbar->addWidget( spacer ); + + m_searchWidget = new QSearchField( this ); + m_searchWidget->setPlaceholderText( tr( "Global Search..." ) ); + m_searchWidget->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Preferred ); + m_searchWidget->setMaximumWidth( 340 ); + connect( m_searchWidget, SIGNAL( returnPressed() ), this, SLOT( onFilterEdited() ) ); + + toolbar->addWidget( m_searchWidget ); +} + + void TomahawkWindow::setupSideBar() { @@ -215,39 +254,29 @@ TomahawkWindow::setupSideBar() m_sidebar->setOrientation( Qt::Vertical ); m_sidebar->setChildrenCollapsible( false ); - m_searchWidget = new QSearchField( m_sidebar ); - m_searchWidget->setPlaceholderText( tr( "Global Search..." ) ); - connect( m_searchWidget, SIGNAL( returnPressed() ), this, SLOT( onFilterEdited() ) ); - - m_sourcetree = new SourceTreeView(); + m_sourcetree = new SourceTreeView( this ); JobStatusView* jobsView = new JobStatusView( m_sidebar ); m_jobsModel = new JobStatusModel( jobsView ); jobsView->setModel( m_jobsModel ); m_queueView = new QueueView( m_sidebar ); - m_queueModel = new PlaylistModel( m_queueView ); - m_queueModel->setStyle( PlaylistModel::Short ); - m_queueView->queue()->setPlaylistModel( m_queueModel ); - m_queueView->queue()->playlistModel()->setReadOnly( false ); AudioEngine::instance()->setQueue( m_queueView->queue()->proxyModel()->playlistInterface() ); - m_sidebar->addWidget( m_searchWidget ); m_sidebar->addWidget( m_sourcetree ); m_sidebar->addWidget( jobsView ); m_sidebar->addWidget( m_queueView ); - m_sidebar->setGreedyWidget( 1 ); +// m_sidebar->setGreedyWidget( 1 ); m_sidebar->hide( 1, false ); m_sidebar->hide( 2, false ); m_sidebar->hide( 3, false ); - m_sidebar->hide( 4, false ); sidebarWidget->layout()->addWidget( m_sidebar ); sidebarWidget->setContentsMargins( 0, 0, 0, 0 ); sidebarWidget->layout()->setContentsMargins( 0, 0, 0, 0 ); sidebarWidget->layout()->setMargin( 0 ); -#ifndef Q_WS_MAC +#ifndef Q_OS_MAC sidebarWidget->layout()->setSpacing( 0 ); #endif @@ -262,11 +291,11 @@ TomahawkWindow::setupSideBar() void TomahawkWindow::setupUpdateCheck() { -#ifndef Q_WS_MAC +#ifndef Q_OS_MAC ui->menu_Help->insertSeparator( ui->actionAboutTomahawk ); #endif -#if defined( Q_WS_MAC ) && defined( HAVE_SPARKLE ) +#if defined( Q_OS_MAC ) && defined( HAVE_SPARKLE ) QAction* checkForUpdates = ui->menu_Help->addAction( tr( "Check For Updates..." ) ); checkForUpdates->setMenuRole( QAction::ApplicationSpecificRole ); connect( checkForUpdates, SIGNAL( triggered( bool ) ), SLOT( checkForUpdates() ) ); @@ -294,8 +323,8 @@ void TomahawkWindow::setupSignals() { // - connect( ViewManager::instance(), SIGNAL( repeatModeChanged( Tomahawk::PlaylistInterface::RepeatMode ) ), - m_audioControls, SLOT( onRepeatModeChanged( Tomahawk::PlaylistInterface::RepeatMode ) ) ); + connect( ViewManager::instance(), SIGNAL( repeatModeChanged( Tomahawk::PlaylistModes::RepeatMode ) ), + m_audioControls, SLOT( onRepeatModeChanged( Tomahawk::PlaylistModes::RepeatMode ) ) ); connect( ViewManager::instance(), SIGNAL( shuffleModeChanged( bool ) ), m_audioControls, SLOT( onShuffleModeChanged( bool ) ) ); @@ -304,7 +333,7 @@ TomahawkWindow::setupSignals() connect( AudioEngine::instance(), SIGNAL( loading( const Tomahawk::result_ptr& ) ), SLOT( onPlaybackLoading( const Tomahawk::result_ptr& ) ) ); connect( AudioEngine::instance(), SIGNAL( started( Tomahawk::result_ptr ) ), SLOT( audioStarted() ) ); connect( AudioEngine::instance(), SIGNAL( resumed()), SLOT( audioStarted() ) ); - connect( AudioEngine::instance(), SIGNAL( paused() ), SLOT( audioStopped() ) ); + connect( AudioEngine::instance(), SIGNAL( paused() ), SLOT( audioPaused() ) ); connect( AudioEngine::instance(), SIGNAL( stopped() ), SLOT( audioStopped() ) ); // @@ -325,7 +354,7 @@ TomahawkWindow::setupSignals() connect( ui->actionNext, SIGNAL( triggered() ), AudioEngine::instance(), SLOT( next() ) ); connect( ui->actionPrevious, SIGNAL( triggered() ), AudioEngine::instance(), SLOT( previous() ) ); -#if defined( Q_WS_MAC ) +#if defined( Q_OS_MAC ) connect( ui->actionMinimize, SIGNAL( triggered() ), SLOT( minimize() ) ); connect( ui->actionZoom, SIGNAL( triggered() ), SLOT( maximize() ) ); #else @@ -340,7 +369,7 @@ TomahawkWindow::setupSignals() // Menus for accounts that support them connect( AccountManager::instance(), SIGNAL( added( Tomahawk::Accounts::Account* ) ), this, SLOT( onAccountAdded( Tomahawk::Accounts::Account* ) ) ); - foreach( Account* account, AccountManager::instance()->accounts( Tomahawk::Accounts::SipType ) ) + foreach ( Account* account, AccountManager::instance()->accounts( Tomahawk::Accounts::SipType ) ) { if ( !account || !account->sipPlugin() ) continue; @@ -348,6 +377,9 @@ TomahawkWindow::setupSignals() connect( account->sipPlugin(), SIGNAL( addMenu( QMenu* ) ), this, SLOT( pluginMenuAdded( QMenu* ) ) ); connect( account->sipPlugin(), SIGNAL( removeMenu( QMenu* ) ), this, SLOT( pluginMenuRemoved( QMenu* ) ) ); } + + connect( ViewManager::instance(), SIGNAL( historyBackAvailable( bool ) ), SLOT( onHistoryBackAvailable( bool ) ) ); + connect( ViewManager::instance(), SIGNAL( historyForwardAvailable( bool ) ), SLOT( onHistoryForwardAvailable( bool ) ) ); } @@ -371,7 +403,7 @@ TomahawkWindow::changeEvent( QEvent* e ) void TomahawkWindow::closeEvent( QCloseEvent* e ) { -#ifndef Q_WS_MAC +#ifndef Q_OS_MAC if ( e->spontaneous() && QSystemTrayIcon::isSystemTrayAvailable() ) { hide(); @@ -391,7 +423,7 @@ TomahawkWindow::showEvent( QShowEvent* e ) { QMainWindow::showEvent( e ); -#if defined( Q_WS_MAC ) +#if defined( Q_OS_MAC ) ui->actionMinimize->setDisabled( false ); ui->actionZoom->setDisabled( false ); #endif @@ -403,7 +435,7 @@ TomahawkWindow::hideEvent( QHideEvent* e ) { QMainWindow::hideEvent( e ); -#if defined( Q_WS_MAC ) +#if defined( Q_OS_MAC ) ui->actionMinimize->setDisabled( true ); ui->actionZoom->setDisabled( true ); #endif @@ -414,8 +446,8 @@ void TomahawkWindow::keyPressEvent( QKeyEvent* e ) { bool accept = true; -#if ! defined ( Q_WS_MAC ) -#define KEY_PRESSED Q_FUNC_INFO << "Multimedia Key Pressed: " +#if ! defined ( Q_OS_MAC ) +#define KEY_PRESSED Q_FUNC_INFO << "Multimedia Key Pressed:" switch( e->key() ) { case Qt::Key_MediaPlay: @@ -450,13 +482,27 @@ TomahawkWindow::keyPressEvent( QKeyEvent* e ) accept = false; #endif - if(accept) + if ( accept ) e->accept(); QMainWindow::keyPressEvent( e ); } +void +TomahawkWindow::onHistoryBackAvailable( bool avail ) +{ + m_backAction->setEnabled( avail ); +} + + +void +TomahawkWindow::onHistoryForwardAvailable( bool avail ) +{ + m_forwardAction->setEnabled( avail ); +} + + void TomahawkWindow::showSettingsDialog() { @@ -529,9 +575,9 @@ TomahawkWindow::pluginMenuAdded( QMenu* menu ) void TomahawkWindow::pluginMenuRemoved( QMenu* menu ) { - foreach( QAction* action, ui->menuNetwork->actions() ) + foreach ( QAction* action, ui->menuNetwork->actions() ) { - if( action->menu() == menu ) + if ( action->menu() == menu ) { ui->menuNetwork->removeAction( action ); return; @@ -548,11 +594,25 @@ TomahawkWindow::showOfflineSources() } +void +TomahawkWindow::fullScreenEntered() +{ + statusBar()->setSizeGripEnabled( false ); +} + + +void +TomahawkWindow::fullScreenExited() +{ + statusBar()->setSizeGripEnabled( true ); +} + + void TomahawkWindow::loadSpiff() { LoadXSPFDialog* diag = new LoadXSPFDialog( this, Qt::Sheet ); -#ifdef Q_WS_MAC +#ifdef Q_OS_MAC connect( diag, SIGNAL( finished( int ) ), this, SLOT( loadXspfFinished( int ) ) ); diag->show(); #else @@ -638,18 +698,18 @@ TomahawkWindow::onAudioEngineError( AudioEngine::AudioErrorCode /* error */ ) void TomahawkWindow::createAutomaticPlaylist( QString playlistName ) { - QString name = playlistName; - - if ( name.isEmpty() ) + if ( playlistName.isEmpty() ) return; source_ptr author = SourceList::instance()->getLocal(); QString id = uuid(); QString info = ""; // FIXME QString creator = "someone"; // FIXME - dynplaylist_ptr playlist = DynamicPlaylist::create( author, id, name, info, creator, Static, false ); + + dynplaylist_ptr playlist = DynamicPlaylist::create( author, id, playlistName, info, creator, Static, false ); playlist->setMode( Static ); playlist->createNewRevision( uuid(), playlist->currentrevision(), playlist->type(), playlist->generator()->controls(), playlist->entries() ); + ViewManager::instance()->show( playlist ); } @@ -657,18 +717,36 @@ TomahawkWindow::createAutomaticPlaylist( QString playlistName ) void TomahawkWindow::createStation() { + QString title = tr( "Station" ); bool ok; - QString name = QInputDialog::getText( this, tr( "Create New Station" ), tr( "Name:" ), QLineEdit::Normal, tr( "New Station" ), &ok ); - if ( !ok || name.isEmpty() ) + QString playlistName = QInputDialog( this, Qt::Sheet ).getText( this, tr( "Create New Station" ), tr( "Name:" ), QLineEdit::Normal, title, &ok ); + if ( !ok ) return; + if ( playlistName.isEmpty() || playlistName == title ) + { + QList< dynplaylist_ptr > pls = SourceList::instance()->getLocal()->collection()->stations(); + QStringList titles; + foreach ( const playlist_ptr& pl, pls ) + titles << pl->title(); + + playlistName = title; + int i = 2; + while ( titles.contains( playlistName ) ) + { + playlistName = QString( "%1 (%2)" ).arg( title ).arg( i++ ); + } + } + source_ptr author = SourceList::instance()->getLocal(); QString id = uuid(); QString info = ""; // FIXME QString creator = "someone"; // FIXME - dynplaylist_ptr playlist = DynamicPlaylist::create( author, id, name, info, creator, OnDemand, false ); + + dynplaylist_ptr playlist = DynamicPlaylist::create( author, id, playlistName, info, creator, OnDemand, false ); playlist->setMode( OnDemand ); playlist->createNewRevision( uuid(), playlist->currentrevision(), playlist->type(), playlist->generator()->controls() ); + ViewManager::instance()->show( playlist ); } @@ -677,11 +755,12 @@ void TomahawkWindow::createPlaylist() { PlaylistTypeSelectorDlg* playlistSelectorDlg = new PlaylistTypeSelectorDlg( TomahawkApp::instance()->mainWindow(), Qt::Sheet ); -#ifndef Q_WS_MAC + +#ifndef Q_OS_MAC playlistSelectorDlg->setModal( true ); #endif - connect( playlistSelectorDlg, SIGNAL( finished( int ) ), this, SLOT( playlistCreateDialogFinished( int ) ) ); + connect( playlistSelectorDlg, SIGNAL( finished( int ) ), SLOT( playlistCreateDialogFinished( int ) ) ); playlistSelectorDlg->show(); } @@ -693,17 +772,50 @@ TomahawkWindow::playlistCreateDialogFinished( int ret ) Q_ASSERT( playlistSelectorDlg ); QString playlistName = playlistSelectorDlg->playlistName(); - if ( playlistName.isEmpty() ) - playlistName = tr( "New Playlist" ); - if ( !playlistSelectorDlg->playlistTypeIsAuto() && ret ) { + if ( !playlistSelectorDlg->playlistTypeIsAuto() && ret ) + { + if ( playlistName.isEmpty() ) + { + QList< playlist_ptr > pls = SourceList::instance()->getLocal()->collection()->playlists(); + QStringList titles; + foreach ( const playlist_ptr& pl, pls ) + titles << pl->title(); + + QString title = tr( "Playlist" ); + playlistName = title; + int i = 2; + while ( titles.contains( playlistName ) ) + { + playlistName = QString( "%1 (%2)" ).arg( title ).arg( i++ ); + } + } playlist_ptr playlist = Tomahawk::Playlist::create( SourceList::instance()->getLocal(), uuid(), playlistName, "", "", false, QList< query_ptr>() ); ViewManager::instance()->show( playlist ); - } else if ( playlistSelectorDlg->playlistTypeIsAuto() && ret ) { + } + else if ( playlistSelectorDlg->playlistTypeIsAuto() && ret ) + { // create Auto Playlist + if ( playlistName.isEmpty() ) + { + QList< dynplaylist_ptr > pls = SourceList::instance()->getLocal()->collection()->autoPlaylists(); + QStringList titles; + foreach ( const dynplaylist_ptr& pl, pls ) + titles << pl->title(); + + QString title = tr( "Automatic Playlist" ); + playlistName = title; + int i = 2; + while ( titles.contains( playlistName ) ) + { + playlistName = QString( "%1 (%2)" ).arg( title ).arg( i++ ); + } + } + createAutomaticPlaylist( playlistName ); } + playlistSelectorDlg->deleteLater(); } @@ -712,15 +824,27 @@ void TomahawkWindow::audioStarted() { m_audioRetryCounter = 0; + ui->actionPlay->setText( tr( "Pause" ) ); + ActionCollection::instance()->getAction( "stop" )->setEnabled( true ); +} + + +void +TomahawkWindow::audioPaused() +{ + ui->actionPlay->setText( tr( "Play" ) ); } void TomahawkWindow::audioStopped() { - - ui->actionPlay->setText( tr( "Play" ) ); + audioPaused(); + ActionCollection::instance()->getAction( "stop" )->setEnabled( false ); + + m_currentTrack = result_ptr(); + setWindowTitle( m_windowTitle ); } @@ -756,6 +880,7 @@ TomahawkWindow::onAccountAdded( Account* acc ) connect( acc->sipPlugin(), SIGNAL( removeMenu( QMenu* ) ), this, SLOT( pluginMenuRemoved( QMenu* ) ) ); } + void TomahawkWindow::onAccountError() { @@ -765,7 +890,7 @@ TomahawkWindow::onAccountError() // TODO real error message from plugin kthxbbq QMessageBox::warning( this, tr( "Authentication Error" ), - QString( "Error connecting to SIP: Authentication failed!" ), + tr( "Error connecting to SIP: Authentication failed!" ), QMessageBox::Ok ); } @@ -799,8 +924,13 @@ TomahawkWindow::showAboutTomahawk() .arg( TomahawkUtils::appFriendlyVersion() ); #endif - desc = tr( "Copyright 2010 - 2012
Christian Muehlhaeuser <muesli@tomahawk-player.org>

" - "Thanks to: Leo Franchi, Jeff Mitchell, Dominik Schmidt, Jason Herskowitz, Alejandro Wainzinger, Hugo Lindström, Michael Zanetti, Harald Sitter and Steve Robertson" ); + const QString copyright( tr( "Copyright 2010 - 2012" ) ); + const QString thanksto( tr( "Thanks to:" ) ); + + desc = QString( "%1
Christian Muehlhaeuser <muesli@tomahawk-player.org>

" + "%2 Leo Franchi, Jeff Mitchell, Dominik Schmidt, Jason Herskowitz, Alejandro Wainzinger, Hugo Lindström, Syd Lawrence, Michael Zanetti, Harald Sitter, Steve Robertson" ) + .arg( copyright ) + .arg( thanksto ); QMessageBox::about( this, tr( "About Tomahawk" ), head + desc ); } @@ -809,7 +939,7 @@ TomahawkWindow::showAboutTomahawk() void TomahawkWindow::checkForUpdates() { -#ifdef Q_WS_MAC +#ifdef Q_OS_MAC Tomahawk::checkForUpdates(); #endif } diff --git a/src/TomahawkWindow.h b/src/TomahawkWindow.h index cb2b91a0b..dee2122ff 100644 --- a/src/TomahawkWindow.h +++ b/src/TomahawkWindow.h @@ -88,12 +88,18 @@ public slots: void pluginMenuRemoved(QMenu*); void showOfflineSources(); + void fullScreenEntered(); + void fullScreenExited(); + private slots: void onAccountAdded( Tomahawk::Accounts::Account* account ); void onAccountConnected(); void onAccountDisconnected(); void onAccountError(); + void onHistoryBackAvailable( bool avail ); + void onHistoryForwardAvailable( bool avail ); + void onAudioEngineError( AudioEngine::AudioErrorCode error ); void onXSPFError( XSPFLoader::XSPFErrorCode error ); @@ -104,6 +110,7 @@ private slots: void onPlaybackLoading( const Tomahawk::result_ptr& result ); void audioStarted(); + void audioPaused(); void audioStopped(); void showAboutTomahawk(); @@ -130,6 +137,7 @@ private: void applyPlatformTweaks(); void setupSignals(); + void setupToolBar(); void setupSideBar(); void setupUpdateCheck(); @@ -140,11 +148,13 @@ private: SourceTreeView* m_sourcetree; QPushButton* m_statusButton; QPushButton* m_queueButton; - PlaylistModel* m_queueModel; QueueView* m_queueView; AnimatedSplitter* m_sidebar; JobStatusModel* m_jobsModel; + QAction* m_backAction; + QAction* m_forwardAction; + Tomahawk::result_ptr m_currentTrack; QString m_windowTitle; int m_audioRetryCounter; diff --git a/src/accounts/AccountDllMacro.h b/src/accounts/AccountDllMacro.h index 5acc5193c..9ae24818f 100644 --- a/src/accounts/AccountDllMacro.h +++ b/src/accounts/AccountDllMacro.h @@ -20,14 +20,14 @@ #ifndef ACCOUNTDLLMACRO_H #define ACCOUNTDLLMACRO_H -#ifdef Q_WS_WIN - #ifdef ACCOUNTDLLEXPORT_PRO - #define ACCOUNTDLLEXPORT __declspec(dllexport) - #else - #define ACCOUNTDLLEXPORT __declspec(dllimport) - #endif -#else - #define ACCOUNTDLLEXPORT +#include + +#ifndef ACCOUNTDLLEXPORT +# if defined (ACCOUNTDLLEXPORT_PRO) +# define ACCOUNTDLLEXPORT Q_DECL_EXPORT +# else +# define ACCOUNTDLLEXPORT Q_DECL_IMPORT +# endif #endif #endif diff --git a/src/accounts/CMakeLists.txt b/src/accounts/CMakeLists.txt index 5da89e6f5..f68da9e7b 100644 --- a/src/accounts/CMakeLists.txt +++ b/src/accounts/CMakeLists.txt @@ -1,3 +1,4 @@ +include(AddTomahawkPlugin) IF( JREEN_FOUND ) add_subdirectory( xmpp ) diff --git a/src/accounts/twitter/CMakeLists.txt b/src/accounts/twitter/CMakeLists.txt index c0a506395..558054b0a 100644 --- a/src/accounts/twitter/CMakeLists.txt +++ b/src/accounts/twitter/CMakeLists.txt @@ -1,58 +1,19 @@ -project( tomahawk ) -include( ${QT_USE_FILE} ) -add_definitions( ${QT_DEFINITIONS} ) -add_definitions( -DQT_PLUGIN ) -add_definitions( -DQT_SHARED ) -add_definitions( -DACCOUNTDLLEXPORT_PRO ) +include_directories(${QTWEETLIB_INCLUDE_DIR}) -set( twitterAccountSources - TwitterAccount.cpp - TwitterInfoPlugin.cpp - TwitterConfigWidget.cpp - TomahawkOAuthTwitter.cpp - sip/TwitterSip.cpp +add_tomahawk_plugin(twitter + TYPE account + EXPORT_MACRO ACCOUNTDLLEXPORT_PRO + SOURCES + TwitterAccount.cpp + TwitterInfoPlugin.cpp + TwitterConfigWidget.cpp + TomahawkOAuthTwitter.cpp + sip/TwitterSip.cpp + UI + TwitterConfigWidget.ui + LINK_LIBRARIES + ${TOMAHAWK_LIBRARIES} + ${QTWEETLIB_LIBRARIES} ) -set( twitterAccountHeaders - TwitterAccount.h - TwitterInfoPlugin.h - TwitterConfigWidget.h - TomahawkOAuthTwitter.h - sip/TwitterSip.h -) - -set( twitterAccountUI - TwitterConfigWidget.ui -) - -include_directories( . ${CMAKE_CURRENT_BINARY_DIR} .. - ${QT_INCLUDE_DIR} - ${QTWEETLIB_INCLUDE_DIR} -) - -qt4_wrap_cpp( twitterAccountMoc ${twitterAccountHeaders} ) -qt4_wrap_ui( twitterAccountUI_H ${twitterAccountUI} ) -qt4_add_resources( RC_SRCS "resources.qrc" ) -add_library( tomahawk_account_twitter SHARED ${twitterAccountUI_H} ${twitterAccountSources} ${twitterAccountMoc} ${RC_SRCS} ) - -IF( WIN32 ) -SET( OS_SPECIFIC_LINK_LIBRARIES - ${OS_SPECIFIC_LINK_LIBRARIES} - "winmm.dll" - "iphlpapi.a" -) -ENDIF( WIN32 ) - -target_link_libraries( tomahawk_account_twitter - ${TOMAHAWK_LIBRARIES} - ${QTWEETLIB_LIBRARIES} - ${QT_LIBRARIES} - ${OS_SPECIFIC_LINK_LIBRARIES} -) - -IF( APPLE ) -# SET( CMAKE_SHARED_LINKER_FLAGS ${CMAKE_SHARED_LINKER_FLAGS} "-undefined dynamic_lookup" ) -ENDIF( APPLE ) - -install( TARGETS tomahawk_account_twitter DESTINATION ${CMAKE_INSTALL_LIBDIR} ) diff --git a/src/accounts/twitter/TwitterAccount.cpp b/src/accounts/twitter/TwitterAccount.cpp index 91dce3ca1..a3b1dbe67 100644 --- a/src/accounts/twitter/TwitterAccount.cpp +++ b/src/accounts/twitter/TwitterAccount.cpp @@ -53,6 +53,8 @@ TwitterAccount::TwitterAccount( const QString &accountId ) setAccountServiceName( "Twitter" ); setTypes( AccountTypes( StatusPushType | SipType ) ); + connect( this, SIGNAL( credentialsChanged( QVariantHash ) ), this, SLOT( onCredentialsChanged( QVariantHash ) ) ); + qDebug() << "Got cached peers:" << configuration() << configuration()[ "cachedpeers" ]; m_configWidget = QWeakPointer< TwitterConfigWidget >( new TwitterConfigWidget( this, 0 ) ); @@ -73,13 +75,32 @@ TwitterAccount::configDialogAuthedSignalSlot( bool authed ) { tDebug() << Q_FUNC_INFO; m_isAuthenticated = authed; - if ( !credentials()[ "username" ].toString().isEmpty() ) - setAccountFriendlyName( QString( "@%1" ).arg( credentials()[ "username" ].toString() ) ); + if ( !m_credentials[ "username" ].toString().isEmpty() ) + setAccountFriendlyName( QString( "@%1" ).arg( m_credentials[ "username" ].toString() ) ); syncConfig(); emit configurationChanged(); } +void +TwitterAccount::onCredentialsChanged( const QVariantHash &credentials ) +{ + // Credentials loaded + bool reload = false; + if ( !credentials[ "oauthtoken" ].toString().isEmpty() && !credentials[ "oauthtokensecret" ].toString().isEmpty() && + ( m_credentials[ "oauthtoken" ] != credentials[ "oauthtoken"] || m_credentials[ "oauthtokensecret" ] != credentials[ "oauthtokensecret" ] ) ) + reload = true; + + m_credentials = credentials; + + if ( reload && enabled() ) + { + qDebug() << "Twitter account got async load of credentials, authenticating now!"; + authenticate(); + } +} + + Account::ConnectionState TwitterAccount::connectionState() const { @@ -130,7 +151,6 @@ TwitterAccount::authenticateSlot() { infoPlugin().data()->moveToThread( Tomahawk::InfoSystem::InfoSystem::instance()->workerThread().data() ); Tomahawk::InfoSystem::InfoSystem::instance()->addInfoPlugin( infoPlugin() ); - QMetaObject::invokeMethod( infoPlugin().data(), "init", Qt::QueuedConnection ); } } @@ -140,9 +160,9 @@ TwitterAccount::authenticateSlot() return; } - tDebug() << Q_FUNC_INFO << "credentials: " << credentials().keys(); + tDebug() << Q_FUNC_INFO << "credentials: " << m_credentials.keys(); - if ( credentials()[ "oauthtoken" ].toString().isEmpty() || credentials()[ "oauthtokensecret" ].toString().isEmpty() ) + if ( m_credentials[ "oauthtoken" ].toString().isEmpty() || m_credentials[ "oauthtokensecret" ].toString().isEmpty() ) { tDebug() << Q_FUNC_INFO << "TwitterSipPlugin has empty Twitter credentials; not connecting"; return; @@ -192,8 +212,8 @@ TwitterAccount::refreshTwitterAuth() if( m_twitterAuth.isNull() ) return false; - m_twitterAuth.data()->setOAuthToken( credentials()[ "oauthtoken" ].toString().toLatin1() ); - m_twitterAuth.data()->setOAuthTokenSecret( credentials()[ "oauthtokensecret" ].toString().toLatin1() ); + m_twitterAuth.data()->setOAuthToken( m_credentials[ "oauthtoken" ].toString().toLatin1() ); + m_twitterAuth.data()->setOAuthTokenSecret( m_credentials[ "oauthtokensecret" ].toString().toLatin1() ); return true; } diff --git a/src/accounts/twitter/TwitterAccount.h b/src/accounts/twitter/TwitterAccount.h index ec9e2bbbf..3c4550037 100644 --- a/src/accounts/twitter/TwitterAccount.h +++ b/src/accounts/twitter/TwitterAccount.h @@ -79,11 +79,14 @@ public: bool refreshTwitterAuth(); TomahawkOAuthTwitter* twitterAuth() const { return m_twitterAuth.data(); } + QVariantHash credentials() const { return m_credentials; } signals: void nowAuthenticated( const QWeakPointer< TomahawkOAuthTwitter >&, const QTweetUser &user ); void nowDeauthenticated(); private slots: + void onCredentialsChanged( const QVariantHash& credentials ); + void authenticateSlot(); void configDialogAuthedSignalSlot( bool authed ); void connectAuthVerifyReply( const QTweetUser &user ); @@ -92,6 +95,7 @@ private: QIcon m_icon; bool m_isAuthenticated; bool m_isAuthenticating; + QVariantHash m_credentials; QWeakPointer< TomahawkOAuthTwitter > m_twitterAuth; QWeakPointer< TwitterConfigWidget > m_configWidget; QWeakPointer< TwitterSipPlugin > m_twitterSipPlugin; diff --git a/src/accounts/twitter/TwitterConfigWidget.cpp b/src/accounts/twitter/TwitterConfigWidget.cpp index 720037b46..aef70c20b 100644 --- a/src/accounts/twitter/TwitterConfigWidget.cpp +++ b/src/accounts/twitter/TwitterConfigWidget.cpp @@ -24,6 +24,7 @@ #include "TomahawkSettings.h" #include "utils/TomahawkUtils.h" #include "database/Database.h" +#include "Source.h" #include "TomahawkOAuthTwitter.h" #include @@ -111,7 +112,7 @@ TwitterConfigWidget::authenticateTwitter() QVariantHash credentials = m_account->credentials(); credentials[ "oauthtoken" ] = twitAuth->oauthToken(); credentials[ "oauthtokensecret" ] = twitAuth->oauthTokenSecret(); - m_account->setCredentials( credentials ); + m_account->saveCredentials( credentials ); QTweetAccountVerifyCredentials *credVerifier = new QTweetAccountVerifyCredentials( twitAuth, this ); connect( credVerifier, SIGNAL( parsedUser( const QTweetUser & ) ), SLOT( authenticateVerifyReply( const QTweetUser & ) ) ); @@ -132,7 +133,7 @@ TwitterConfigWidget::authenticateVerifyReply( const QTweetUser &user ) QVariantHash credentials = m_account->credentials(); credentials[ "username" ] = user.screenName(); - m_account->setCredentials( credentials ); + m_account->saveCredentials( credentials ); QVariantHash configuration = m_account->configuration(); configuration[ "sipcachedfriendssinceid" ] = 0; @@ -164,11 +165,7 @@ void TwitterConfigWidget::deauthenticateTwitter() { qDebug() << Q_FUNC_INFO; - QVariantHash credentials = m_account->credentials(); - credentials[ "oauthtoken" ] = QString(); - credentials[ "oauthtokensecret" ] = QString(); - credentials[ "username" ] = QString(); - m_account->setCredentials( credentials ); + m_account->saveCredentials( QVariantHash() ); m_ui->twitterStatusLabel->setText(tr("Status: No saved credentials")); m_ui->twitterAuthenticateButton->setText( tr( "Authenticate" ) ); @@ -237,7 +234,7 @@ TwitterConfigWidget::postGotTomahawkStatusAuthVerifyReply( const QTweetUser &use return; } TomahawkOAuthTwitter *twitAuth = new TomahawkOAuthTwitter( TomahawkUtils::nam(), this ); - QVariantHash credentials = m_account->credentials(); + const QVariantHash credentials = m_account->credentials(); twitAuth->setOAuthToken( credentials[ "oauthtoken" ].toString().toLatin1() ); twitAuth->setOAuthTokenSecret( credentials[ "oauthtokensecret" ].toString().toLatin1() ); if ( m_postGTtype != "Direct Message" ) @@ -306,4 +303,4 @@ TwitterConfigWidget::showEvent(QShowEvent* event) } -} \ No newline at end of file +} diff --git a/src/accounts/twitter/TwitterInfoPlugin.cpp b/src/accounts/twitter/TwitterInfoPlugin.cpp index 89d8da763..39e375f0a 100644 --- a/src/accounts/twitter/TwitterInfoPlugin.cpp +++ b/src/accounts/twitter/TwitterInfoPlugin.cpp @@ -27,6 +27,7 @@ #include "GlobalActionManager.h" #include "utils/Logger.h" +#include "Source.h" namespace Tomahawk { @@ -49,7 +50,7 @@ TwitterInfoPlugin::init() tDebug() << "Failure: move to the worker thread before running init"; return; } - + QVariantHash credentials = m_account->credentials(); if ( credentials[ "oauthtoken" ].toString().isEmpty() || credentials[ "oauthtokensecret" ].toString().isEmpty() ) { @@ -128,7 +129,7 @@ TwitterInfoPlugin::pushInfo( Tomahawk::InfoSystem::InfoPushData pushData ) tLog() << Q_FUNC_INFO << "Failed to find QVariantMap!"; return; } - + QVariantMap map = pushInfoPair.second.toMap(); if ( !map.contains( "accountlist" ) || !map[ "accountlist" ].canConvert< QStringList >() ) @@ -142,13 +143,13 @@ TwitterInfoPlugin::pushInfo( Tomahawk::InfoSystem::InfoPushData pushData ) tLog( LOGVERBOSE ) << Q_FUNC_INFO << "Our account not in the list, not tweeting out"; return; } - + if ( !map.contains( "message" ) && ( !map.contains( "trackinfo" ) || !map[ "trackinfo" ].canConvert< Tomahawk::InfoSystem::InfoStringHash >() ) ) { tLog() << Q_FUNC_INFO << "Failed to find message or trackinfo"; return; } - + Tomahawk::InfoSystem::InfoStringHash info; QString msg; if ( !map.contains( "message" ) ) diff --git a/src/accounts/twitter/TwitterInfoPlugin.h b/src/accounts/twitter/TwitterInfoPlugin.h index e1b221105..0919515b7 100644 --- a/src/accounts/twitter/TwitterInfoPlugin.h +++ b/src/accounts/twitter/TwitterInfoPlugin.h @@ -44,7 +44,6 @@ namespace Tomahawk { virtual ~TwitterInfoPlugin(); public slots: - void init(); void notInCacheSlot( const Tomahawk::InfoSystem::InfoStringHash criteria, Tomahawk::InfoSystem::InfoRequestData requestData ) { Q_UNUSED( criteria ); @@ -52,6 +51,7 @@ namespace Tomahawk { } protected slots: + void init(); void pushInfo( Tomahawk::InfoSystem::InfoPushData pushData ); void getInfo( Tomahawk::InfoSystem::InfoRequestData requestData ) { diff --git a/src/accounts/twitter/sip/TwitterSip.cpp b/src/accounts/twitter/sip/TwitterSip.cpp index 075690f78..6bcdcba0d 100644 --- a/src/accounts/twitter/sip/TwitterSip.cpp +++ b/src/accounts/twitter/sip/TwitterSip.cpp @@ -37,6 +37,7 @@ #include #include #include +#include "Source.h" #include "utils/Logger.h" #include "accounts/twitter/TomahawkOAuthTwitter.h" diff --git a/src/accounts/xmpp/CMakeLists.txt b/src/accounts/xmpp/CMakeLists.txt index bcd68d85f..af2e67b16 100644 --- a/src/accounts/xmpp/CMakeLists.txt +++ b/src/accounts/xmpp/CMakeLists.txt @@ -1,66 +1,23 @@ -project( tomahawk ) +include_directories(${JREEN_INCLUDE_DIR}) -include( ${QT_USE_FILE} ) -add_definitions( ${QT_DEFINITIONS} ) -add_definitions( -DQT_PLUGIN ) -add_definitions( -DQT_SHARED ) -add_definitions( -DACCOUNTDLLEXPORT_PRO ) - -set( xmppAccountSources - XmppAccount.cpp - XmppConfigWidget.cpp - sip/XmppSip.cpp - sip/TomahawkXmppMessage.cpp - sip/TomahawkXmppMessageFactory.cpp - sip/AvatarManager.cpp - sip/XmlConsole.cpp - XmppInfoPlugin.cpp +add_tomahawk_plugin(xmpp + TYPE account + EXPORT_MACRO ACCOUNTDLLEXPORT_PRO + SOURCES + XmppAccount.cpp + XmppConfigWidget.cpp + sip/XmppSip.cpp + sip/TomahawkXmppMessage.cpp + sip/TomahawkXmppMessageFactory.cpp + sip/AvatarManager.cpp + sip/XmlConsole.cpp + XmppInfoPlugin.cpp + UI + XmppConfigWidget.ui + sip/XmlConsole.ui + LINK_LIBRARIES + ${TOMAHAWK_LIBRARIES} + ${JREEN_LIBRARIES} ) -set( xmppAccountHeaders - XmppAccount.h - XmppConfigWidget.h - sip/XmppSip.h - sip/AvatarManager.h - sip/XmlConsole.h - XmppInfoPlugin.h -) - -set( xmppAccountUI - XmppConfigWidget.ui - sip/XmlConsole.ui -) - -include_directories( . ${CMAKE_CURRENT_BINARY_DIR} .. - ${QT_INCLUDE_DIR} - ${JREEN_INCLUDE_DIR} -) - -qt4_wrap_cpp( xmppAccountMoc ${xmppAccountHeaders} ) -qt4_wrap_ui( xmppAccountUI_H ${xmppAccountUI} ) -qt4_add_resources( RC_SRCS "resources.qrc" ) -add_library( tomahawk_account_xmpp SHARED ${xmppAccountUI_H} ${xmppAccountSources} ${xmppAccountMoc} ${RC_SRCS} ) - -IF( WIN32 ) -SET( OS_SPECIFIC_LINK_LIBRARIES - ${OS_SPECIFIC_LINK_LIBRARIES} - "winmm.dll" - "iphlpapi.a" -) -ENDIF( WIN32 ) - -target_link_libraries( tomahawk_account_xmpp - ${TOMAHAWK_LIBRARIES} - ${JREEN_LIBRARIES} - ${QT_LIBRARIES} - ${OS_SPECIFIC_LINK_LIBRARIES} -) - -IF( APPLE ) -# SET( CMAKE_SHARED_LINKER_FLAGS ${CMAKE_SHARED_LINKER_FLAGS} "-undefined dynamic_lookup" ) -ENDIF( APPLE ) - -install( TARGETS tomahawk_account_xmpp DESTINATION ${CMAKE_INSTALL_LIBDIR} ) - add_subdirectory(googlewrapper) - diff --git a/src/accounts/xmpp/XmppAccount.cpp b/src/accounts/xmpp/XmppAccount.cpp index afe8d8f7f..e894e49c4 100644 --- a/src/accounts/xmpp/XmppAccount.cpp +++ b/src/accounts/xmpp/XmppAccount.cpp @@ -41,6 +41,8 @@ XmppAccountFactory::createAccount( const QString& accountId ) XmppAccount::XmppAccount( const QString &accountId ) : Account( accountId ) { + connect( this, SIGNAL( credentialsChanged( QVariantHash ) ), this, SLOT( onCredentialsChanged( QVariantHash ) ) ); + setAccountServiceName( "Jabber (XMPP)" ); setTypes( SipType ); @@ -90,6 +92,15 @@ XmppAccount::saveConfig() } +void +XmppAccount::onCredentialsChanged( const QVariantHash& credentials ) +{ + m_credentials = credentials; + if ( !m_xmppSipPlugin.isNull() ) + m_xmppSipPlugin.data()->configurationChanged(); +} + + InfoSystem::InfoPluginPtr XmppAccount::infoPlugin() { diff --git a/src/accounts/xmpp/XmppAccount.h b/src/accounts/xmpp/XmppAccount.h index 2de8852d1..23348b47d 100644 --- a/src/accounts/xmpp/XmppAccount.h +++ b/src/accounts/xmpp/XmppAccount.h @@ -77,12 +77,19 @@ public: QWidget* aclWidget() { return 0; } void saveConfig(); + QVariantHash credentials() const { return m_credentials; } + virtual Tomahawk::Accounts::Account::ConnectionState connectionState() const; +private slots: + void onCredentialsChanged( const QVariantHash& credentials ); + protected: QWeakPointer< QWidget > m_configWidget; // so the google wrapper can change the config dialog a bit QWeakPointer< XmppSipPlugin > m_xmppSipPlugin; QWeakPointer< Tomahawk::InfoSystem::XmppInfoPlugin > m_xmppInfoPlugin; + + QVariantHash m_credentials; }; }; diff --git a/src/accounts/xmpp/XmppConfigWidget.cpp b/src/accounts/xmpp/XmppConfigWidget.cpp index 7ad37e4b4..7ccd31071 100644 --- a/src/accounts/xmpp/XmppConfigWidget.cpp +++ b/src/accounts/xmpp/XmppConfigWidget.cpp @@ -65,10 +65,11 @@ XmppConfigWidget::saveConfig() configuration[ "enforcesecure"] = m_ui->xmppEnforceSecureCheckbox->isChecked(); m_account->setAccountFriendlyName( m_ui->xmppUsername->text() ); - m_account->setCredentials( credentials ); m_account->setConfiguration( configuration); m_account->sync(); + m_account->saveCredentials( credentials ); + static_cast< XmppSipPlugin* >( m_account->sipPlugin() )->checkSettings(); } @@ -96,16 +97,20 @@ XmppConfigWidget::loadFromConfig() void XmppConfigWidget::onCheckJidExists( QString jid ) { - QList< Tomahawk::Accounts::Account* > accounts = Tomahawk::Accounts::AccountManager::instance()->accounts( Tomahawk::Accounts::SipType ); + const QList< Tomahawk::Accounts::Account* > accounts = Tomahawk::Accounts::AccountManager::instance()->accounts( Tomahawk::Accounts::SipType ); foreach( Tomahawk::Accounts::Account* account, accounts ) { if ( account->accountId() == m_account->accountId() ) continue; + XmppAccount* xmppAccount = qobject_cast< XmppAccount* >( account ); + if ( !xmppAccount ) + continue; - QString savedUsername = account->credentials()[ "username" ].toString(); - QStringList savedSplitUsername = account->credentials()[ "username" ].toString().split("@"); - QString savedServer = account->configuration()[ "server" ].toString(); - int savedPort = account->configuration()[ "port" ].toInt(); + // Check if any other xmpp account already uses the given name/settings + QString savedUsername = xmppAccount->credentials()[ "username" ].toString(); + QStringList savedSplitUsername = xmppAccount->credentials()[ "username" ].toString().split("@"); + QString savedServer = xmppAccount->configuration()[ "server" ].toString(); + int savedPort = xmppAccount->configuration()[ "port" ].toInt(); if ( ( savedUsername == jid || savedSplitUsername.contains( jid ) ) && savedServer == m_ui->xmppServer->text() && diff --git a/src/accounts/xmpp/XmppInfoPlugin.cpp b/src/accounts/xmpp/XmppInfoPlugin.cpp index b46557cbf..afd637b4b 100644 --- a/src/accounts/xmpp/XmppInfoPlugin.cpp +++ b/src/accounts/xmpp/XmppInfoPlugin.cpp @@ -23,6 +23,7 @@ #include "GlobalActionManager.h" #include "sip/XmppSip.h" #include "utils/Logger.h" +#include "TomahawkSettings.h" // remove now playing status after PAUSE_TIMEOUT seconds @@ -58,7 +59,7 @@ Tomahawk::InfoSystem::XmppInfoPlugin::init() if ( m_sipPlugin.isNull() ) return; - + connect( this, SIGNAL( publishTune( QUrl, Tomahawk::InfoSystem::InfoStringHash ) ), m_sipPlugin.data(), SLOT( publishTune( QUrl, Tomahawk::InfoSystem::InfoStringHash ) ), Qt::QueuedConnection ); } @@ -96,20 +97,20 @@ Tomahawk::InfoSystem::XmppInfoPlugin::audioStarted( const Tomahawk::InfoSystem:: tDebug() << Q_FUNC_INFO << "Failed to convert data to a QVariantMap"; return; } - + QVariantMap map = pushInfoPair.second.toMap(); if ( map.contains( "private" ) && map[ "private" ] == TomahawkSettings::FullyPrivate ) { emit publishTune( QUrl(), Tomahawk::InfoSystem::InfoStringHash() ); return; } - + if ( !map.contains( "trackinfo" ) || !map[ "trackinfo" ].canConvert< Tomahawk::InfoSystem::InfoStringHash >() ) { tDebug() << Q_FUNC_INFO << "did not find an infostringhash"; return; } - + Tomahawk::InfoSystem::InfoStringHash info = map[ "trackinfo" ].value< Tomahawk::InfoSystem::InfoStringHash >(); QUrl url; diff --git a/src/accounts/xmpp/XmppInfoPlugin.h b/src/accounts/xmpp/XmppInfoPlugin.h index f553b43d0..cbb248e6d 100644 --- a/src/accounts/xmpp/XmppInfoPlugin.h +++ b/src/accounts/xmpp/XmppInfoPlugin.h @@ -42,10 +42,10 @@ namespace Tomahawk { void publishTune( QUrl url, Tomahawk::InfoSystem::InfoStringHash trackInfo ); public slots: - void init(); void notInCacheSlot( const Tomahawk::InfoSystem::InfoStringHash criteria, Tomahawk::InfoSystem::InfoRequestData requestData ); protected slots: + void init(); void pushInfo( Tomahawk::InfoSystem::InfoPushData pushData ); void getInfo( Tomahawk::InfoSystem::InfoRequestData requestData ); diff --git a/src/accounts/xmpp/googlewrapper/CMakeLists.txt b/src/accounts/xmpp/googlewrapper/CMakeLists.txt index d527e1125..973b70514 100644 --- a/src/accounts/xmpp/googlewrapper/CMakeLists.txt +++ b/src/accounts/xmpp/googlewrapper/CMakeLists.txt @@ -1,41 +1,21 @@ - -# fake google plugin - -set( googleHeaders - ../XmppAccount.h - ../XmppConfigWidget.h - ../sip/XmppSip.h - ../sip/AvatarManager.h - ../sip/XmlConsole.h - ../XmppInfoPlugin.h - GoogleWrapper.h ) - -set( googleSources - ../XmppAccount.cpp - ../XmppConfigWidget.cpp - ../sip/XmppSip.cpp - ../sip/TomahawkXmppMessage.cpp - ../sip/TomahawkXmppMessageFactory.cpp - ../sip/AvatarManager.cpp - ../sip/XmlConsole.cpp - ../XmppInfoPlugin.cpp - - GoogleWrapper.cpp ) - -add_definitions(-DGOOGLE_WRAPPER) - -qt4_add_resources( RCX_SRCS "resources.qrc" ) - -qt4_wrap_cpp( googleMoc ${googleHeaders} ) -add_library( tomahawk_account_google SHARED ${googleSources} ${googleMoc} ${googleMoc} ${RCX_SRCS} ) - include_directories( ${JREEN_INCLUDE_DIR} ) -target_link_libraries( tomahawk_account_google - ${QT_LIBRARIES} - ${JREEN_LIBRARIES} - ${OS_SPECIFIC_LINK_LIBRARIES} - tomahawklib +add_tomahawk_plugin(google + TYPE account + EXPORT_MACRO ACCOUNTDLLEXPORT_PRO + SOURCES + ../XmppAccount.cpp + ../XmppConfigWidget.cpp + ../sip/XmppSip.cpp + ../sip/TomahawkXmppMessage.cpp + ../sip/TomahawkXmppMessageFactory.cpp + ../sip/AvatarManager.cpp + ../sip/XmlConsole.cpp + ../XmppInfoPlugin.cpp + GoogleWrapper.cpp + LINK_LIBRARIES + ${TOMAHAWK_LIBRARIES} + ${JREEN_LIBRARIES} + COMPILE_DEFINITIONS + GOOGLE_WRAPPER ) - -install( TARGETS tomahawk_account_google DESTINATION ${CMAKE_INSTALL_LIBDIR} ) diff --git a/src/accounts/xmpp/googlewrapper/GoogleWrapper.cpp b/src/accounts/xmpp/googlewrapper/GoogleWrapper.cpp index cd4be9ddf..b0e1c8444 100644 --- a/src/accounts/xmpp/googlewrapper/GoogleWrapper.cpp +++ b/src/accounts/xmpp/googlewrapper/GoogleWrapper.cpp @@ -19,7 +19,7 @@ #include "GoogleWrapper.h" -#include "XmppConfigWidget.h" +#include "../XmppConfigWidget.h" #include "ui_XmppConfigWidget.h" #include "utils/TomahawkUtilsGui.h" @@ -43,7 +43,7 @@ GoogleWrapperFactory::icon() const return QPixmap( ":/gmail-logo.png" ); } -GoogleWrapperSip::GoogleWrapperSip( Account* account ) +GoogleWrapperSip::GoogleWrapperSip( GoogleWrapper *account ) : XmppSipPlugin( account ) { diff --git a/src/accounts/xmpp/googlewrapper/GoogleWrapper.h b/src/accounts/xmpp/googlewrapper/GoogleWrapper.h index da7211e38..e084fa329 100644 --- a/src/accounts/xmpp/googlewrapper/GoogleWrapper.h +++ b/src/accounts/xmpp/googlewrapper/GoogleWrapper.h @@ -20,7 +20,7 @@ #ifndef GOOGLEWRAPPER_H #define GOOGLEWRAPPER_H -#include "XmppAccount.h" +#include "../XmppAccount.h" namespace Tomahawk { @@ -44,11 +44,13 @@ public: virtual Account* createAccount( const QString& pluginId ); }; +class GoogleWrapper; + class ACCOUNTDLLEXPORT GoogleWrapperSip : public XmppSipPlugin { Q_OBJECT public: - GoogleWrapperSip( Tomahawk::Accounts::Account* account ); + GoogleWrapperSip( Tomahawk::Accounts::GoogleWrapper* account ); virtual ~GoogleWrapperSip(); public slots: diff --git a/src/accounts/xmpp/sip/AvatarManager.cpp b/src/accounts/xmpp/sip/AvatarManager.cpp index 2c1eff6d0..9ff1d26bf 100644 --- a/src/accounts/xmpp/sip/AvatarManager.cpp +++ b/src/accounts/xmpp/sip/AvatarManager.cpp @@ -33,47 +33,51 @@ #include "utils/Logger.h" -AvatarManager::AvatarManager(Jreen::Client *client) : - m_cacheDir(TomahawkUtils::appDataDir().absolutePath().append("/jreen/")) +AvatarManager::AvatarManager( Jreen::Client* client ) + : m_cacheDir( TomahawkUtils::appDataDir().absolutePath().append( "/jreen/" ) ) { m_client = client; - m_cachedAvatars = m_cacheDir.entryList(); - connect(m_client, SIGNAL(serverFeaturesReceived(QSet)), SLOT(onNewConnection())); - connect(m_client, SIGNAL(presenceReceived(Jreen::Presence)), SLOT(onNewPresence(Jreen::Presence))); - connect(m_client, SIGNAL(iqReceived(Jreen::IQ)), SLOT(onNewIq(Jreen::IQ))); + connect( m_client, SIGNAL( serverFeaturesReceived( QSet ) ), SLOT( onNewConnection() ) ); + connect( m_client, SIGNAL( presenceReceived( Jreen::Presence ) ), SLOT( onNewPresence( Jreen::Presence ) ) ); + connect( m_client, SIGNAL( iqReceived( Jreen::IQ ) ), SLOT( onNewIq( Jreen::IQ ) ) ); - connect(this, SIGNAL(newAvatar(QString)), SLOT(onNewAvatar(QString))); + connect( this, SIGNAL( newAvatar( QString ) ), SLOT( onNewAvatar( QString ) ) ); } + AvatarManager::~AvatarManager() { } -void AvatarManager::onNewConnection() + +void +AvatarManager::onNewConnection() { fetchVCard( m_client->jid().bare() ); } -void AvatarManager::fetchVCard(const QString &jid) +void +AvatarManager::fetchVCard( const QString& jid ) { -// qDebug() << Q_FUNC_INFO; + Jreen::IQ iq( Jreen::IQ::Get, jid ); + iq.addExtension( new Jreen::VCard() ); + Jreen::IQReply *reply = m_client->send( iq ); - Jreen::IQ iq(Jreen::IQ::Get, jid ); - iq.addExtension(new Jreen::VCard()); - Jreen::IQReply *reply = m_client->send(iq); - connect(reply, SIGNAL(received(Jreen::IQ)), SLOT(onNewIq(Jreen::IQ))); + connect( reply, SIGNAL( received( Jreen::IQ ) ), SLOT( onNewIq( Jreen::IQ ) ) ); } -void AvatarManager::onNewPresence(const Jreen::Presence& presence) + +void +AvatarManager::onNewPresence( const Jreen::Presence& presence ) { Jreen::VCardUpdate::Ptr update = presence.payload(); - if(update) + if ( update ) { // qDebug() << "vcard: found update for" << presence.from().full(); - if(!isCached(update->photoHash())) + if ( !isCached( update->photoHash() ) ) { // qDebug() << presence.from().full() << "vcard: photo not cached, starting request..." << update->photoHash(); fetchVCard( presence.from().bare() ); @@ -84,78 +88,72 @@ void AvatarManager::onNewPresence(const Jreen::Presence& presence) m_JidsAvatarHashes.insert( update->photoHash(), presence.from().bare() ); if ( !this->avatar( presence.from().bare() ).isNull() ) - emit newAvatar(presence.from().bare()); + emit newAvatar( presence.from().bare() ); } } else { -// qDebug() << Q_FUNC_INFO << presence.from().full() << "got no statusupdateextension"; - //TODO: do we want this? might fetch avatars for broken clients fetchVCard( presence.from().bare() ); } } -void AvatarManager::onNewIq(const Jreen::IQ& iq) + +void +AvatarManager::onNewIq( const Jreen::IQ& iq ) { Jreen::VCard::Ptr vcard = iq.payload(); - if(vcard) + if ( vcard ) { iq.accept(); - // qDebug() << Q_FUNC_INFO << "Got vcard from " << iq.from().full(); QString id = iq.from().full(); QString avatarHash; const Jreen::VCard::Photo &photo = vcard->photo(); - if (!photo.data().isEmpty()) { + if ( !photo.data().isEmpty() ) + { // qDebug() << "vcard: got photo data" << id; - avatarHash = QCryptographicHash::hash(photo.data(), QCryptographicHash::Sha1).toHex(); + avatarHash = QCryptographicHash::hash( photo.data(), QCryptographicHash::Sha1 ).toHex(); - if (!m_cacheDir.exists()) + if ( !m_cacheDir.exists() ) m_cacheDir.mkpath( avatarDir( avatarHash ).absolutePath() ); - - QFile file(avatarPath(avatarHash)); - if (file.open(QIODevice::WriteOnly)) { - file.write(photo.data()); + QFile file( avatarPath( avatarHash ) ); + if ( file.open( QIODevice::WriteOnly ) ) + { + file.write( photo.data() ); file.close(); } - m_cachedAvatars.append(avatarHash); + m_cachedAvatars.append( avatarHash ); m_JidsAvatarHashes.insert( avatarHash, iq.from().bare() ); - Q_ASSERT(!this->avatar(iq.from().bare()).isNull()); - emit newAvatar(iq.from().bare()); - } - else - { -// qDebug() << "vcard: got no photo data" << id; + Q_ASSERT( !this->avatar( iq.from().bare() ).isNull() ); + emit newAvatar( iq.from().bare() ); } // got own presence if ( m_client->jid().bare() == id ) { -// qDebug() << Q_FUNC_INFO << "got own vcard"; - Jreen::Presence presence = m_client->presence(); Jreen::VCardUpdate::Ptr update = presence.payload(); - if (update->photoHash() != avatarHash) + if ( update->photoHash() != avatarHash ) { - qDebug() << Q_FUNC_INFO << "Updating own presence..."; - - update->setPhotoHash(avatarHash); - m_client->send(presence); + update->setPhotoHash( avatarHash ); + m_client->send( presence ); } } } } -QPixmap AvatarManager::avatar(const QString &jid) const + +QPixmap +AvatarManager::avatar( const QString& jid ) const { - if( isCached( avatarHash( jid ) ) ) + if ( isCached( avatarHash( jid ) ) ) { return QPixmap( avatarPath( avatarHash( jid ) ) ); } @@ -165,29 +163,39 @@ QPixmap AvatarManager::avatar(const QString &jid) const } } -QString AvatarManager::avatarHash(const QString &jid) const + +QString +AvatarManager::avatarHash( const QString& jid ) const { - //qDebug() << Q_FUNC_INFO << jid << m_JidsAvatarHashes.key(jid); - return m_JidsAvatarHashes.key(jid); + //qDebug() << Q_FUNC_INFO << jid << m_JidsAvatarHashes.key( jid ); + return m_JidsAvatarHashes.key( jid ); } -QDir AvatarManager::avatarDir(const QString&) const + +QDir +AvatarManager::avatarDir( const QString& /* avatarHash */ ) const { return m_cacheDir; } -QString AvatarManager::avatarPath(const QString &avatarHash) const + +QString +AvatarManager::avatarPath( const QString& avatarHash ) const { - Q_ASSERT(!avatarHash.contains("@")); - return avatarDir(avatarHash).absoluteFilePath(avatarHash); + Q_ASSERT( !avatarHash.contains( "@" ) ); + return avatarDir( avatarHash ).absoluteFilePath( avatarHash ); } -bool AvatarManager::isCached(const QString &avatarHash) const + +bool +AvatarManager::isCached( const QString& avatarHash ) const { return m_cachedAvatars.contains( avatarHash ); } -void AvatarManager::onNewAvatar(const QString&) + +void +AvatarManager::onNewAvatar( const QString& /* jid */ ) { // qDebug() << Q_FUNC_INFO << "Found new Avatar..." << jid; } diff --git a/src/accounts/xmpp/sip/AvatarManager.h b/src/accounts/xmpp/sip/AvatarManager.h index 31ab900cf..a42cee08d 100644 --- a/src/accounts/xmpp/sip/AvatarManager.h +++ b/src/accounts/xmpp/sip/AvatarManager.h @@ -32,32 +32,32 @@ class ACCOUNTDLLEXPORT AvatarManager : public QObject Q_OBJECT public: - AvatarManager(Jreen::Client *client); + AvatarManager(Jreen::Client* client); virtual ~AvatarManager(); - QPixmap avatar(const QString &jid) const; + QPixmap avatar(const QString& jid) const; signals: - void newAvatar( const QString &jid ); + void newAvatar( const QString& jid ); private slots: void onNewPresence( const Jreen::Presence& presence ); - void onNewIq(const Jreen::IQ &iq); + void onNewIq(const Jreen::IQ& iq); void onNewConnection(); - void onNewAvatar( const QString &jid ); + void onNewAvatar( const QString& jid ); private: - void fetchVCard( const QString &jid); - QString avatarHash(const QString &jid) const; - QString avatarPath(const QString &avatarHash) const; + void fetchVCard( const QString& jid ); + QString avatarHash( const QString& jid ) const; + QString avatarPath( const QString& avatarHash ) const; - QDir avatarDir(const QString &avatarHash) const; - bool isCached(const QString &avatarHash) const; + QDir avatarDir( const QString& avatarHash ) const; + bool isCached( const QString& avatarHash ) const; - Jreen::Client *m_client; + Jreen::Client* m_client; QStringList m_cachedAvatars; QDir m_cacheDir; - QMap m_JidsAvatarHashes; + QMap< QString, QString > m_JidsAvatarHashes; }; #endif // AVATARMANAGER_H diff --git a/src/accounts/xmpp/sip/XmppSip.cpp b/src/accounts/xmpp/sip/XmppSip.cpp index a0cc9d64e..d2c6115fc 100644 --- a/src/accounts/xmpp/sip/XmppSip.cpp +++ b/src/accounts/xmpp/sip/XmppSip.cpp @@ -48,6 +48,9 @@ #include #include #include +#include + +#include "../XmppAccount.h" #ifndef ENABLE_HEADLESS #include @@ -57,7 +60,6 @@ #include #include "utils/Logger.h" -#include "XmppInfoPlugin.h" using namespace Tomahawk; using namespace Accounts; @@ -65,28 +67,34 @@ using namespace Accounts; // instead of simply copying this function for another thirdparty lib // please make it a meta-function or a macro and put it in Logger.h. kthxbbq #define JREEN_LOG_INFIX "Jreen" +#define TOMAHAWK_FEATURE QLatin1String( "tomahawk:sip:v1" ) +#define TOMAHAWK_CAP_NODE_NAME QLatin1String( "http://tomahawk-player.org/" ) + + void -JreenMessageHandler(QtMsgType type, const char *msg) +JreenMessageHandler( QtMsgType type, const char *msg ) { - switch (type) + switch ( type ) { case QtDebugMsg: - tDebug(LOGTHIRDPARTY).nospace() << JREEN_LOG_INFIX << ": " << "Debug: " << msg; + tDebug( LOGTHIRDPARTY ).nospace() << JREEN_LOG_INFIX << ": " << "Debug: " << msg; break; case QtWarningMsg: - tDebug(LOGTHIRDPARTY).nospace() << JREEN_LOG_INFIX << ": " << "Warning: " << msg; + tDebug( LOGTHIRDPARTY ).nospace() << JREEN_LOG_INFIX << ": " << "Warning: " << msg; break; case QtCriticalMsg: - tDebug(LOGTHIRDPARTY).nospace() << JREEN_LOG_INFIX << ": " << "Critical: " << msg; + tDebug( LOGTHIRDPARTY ).nospace() << JREEN_LOG_INFIX << ": " << "Critical: " << msg; break; case QtFatalMsg: - tDebug(LOGTHIRDPARTY).nospace() << JREEN_LOG_INFIX << ": " << "Fatal: " << msg; + tDebug( LOGTHIRDPARTY ).nospace() << JREEN_LOG_INFIX << ": " << "Fatal: " << msg; abort(); } } -XmppSipPlugin::XmppSipPlugin( Account *account ) + +XmppSipPlugin::XmppSipPlugin( XmppAccount *account ) : SipPlugin( account ) + , m_xmppAccount( account ) , m_state( Account::Disconnected ) #ifndef ENABLE_HEADLESS , m_menu( 0 ) @@ -97,7 +105,6 @@ XmppSipPlugin::XmppSipPlugin( Account *account ) Jreen::Logger::addHandler( JreenMessageHandler ); m_currentUsername = readUsername(); - m_currentServer = readServer(); m_currentPassword = readPassword(); m_currentPort = readPort(); @@ -122,6 +129,7 @@ XmppSipPlugin::XmppSipPlugin( Account *account ) m_xmlConsole->show(); } #endif + // add VCardUpdate extension to own presence m_client->presence().addExtension( new Jreen::VCardUpdate() ); @@ -148,19 +156,19 @@ XmppSipPlugin::XmppSipPlugin( Account *account ) qDebug() << "Our Port set to" << m_client->port(); // setup slots - connect(m_client, SIGNAL(serverFeaturesReceived(QSet)), SLOT(onConnect())); - connect(m_client, SIGNAL(disconnected(Jreen::Client::DisconnectReason)), SLOT(onDisconnect(Jreen::Client::DisconnectReason))); - connect(m_client, SIGNAL(messageReceived(Jreen::Message)), SLOT(onNewMessage(Jreen::Message))); + connect( m_client, SIGNAL( serverFeaturesReceived( QSet ) ), SLOT( onConnect() ) ); + connect( m_client, SIGNAL( disconnected( Jreen::Client::DisconnectReason ) ), SLOT( onDisconnect( Jreen::Client::DisconnectReason ) ) ); + connect( m_client, SIGNAL( messageReceived( Jreen::Message ) ), SLOT( onNewMessage( Jreen::Message ) ) ); - connect(m_client, SIGNAL(iqReceived(Jreen::IQ)), SLOT(onNewIq(Jreen::IQ))); + connect( m_client, SIGNAL( iqReceived( Jreen::IQ ) ), SLOT( onNewIq( Jreen::IQ ) ) ); - connect(m_roster, SIGNAL(presenceReceived(Jreen::RosterItem::Ptr,Jreen::Presence)), - SLOT(onPresenceReceived(Jreen::RosterItem::Ptr,Jreen::Presence))); - connect(m_roster, SIGNAL(subscriptionReceived(Jreen::RosterItem::Ptr,Jreen::Presence)), - SLOT(onSubscriptionReceived(Jreen::RosterItem::Ptr,Jreen::Presence))); + connect( m_roster, SIGNAL( presenceReceived( Jreen::RosterItem::Ptr, Jreen::Presence ) ), + SLOT( onPresenceReceived( Jreen::RosterItem::Ptr, Jreen::Presence ) ) ); + connect( m_roster, SIGNAL( subscriptionReceived( Jreen::RosterItem::Ptr, Jreen::Presence ) ), + SLOT( onSubscriptionReceived( Jreen::RosterItem::Ptr, Jreen::Presence ) ) ); #ifndef ENABLE_HEADLESS - connect(m_avatarManager, SIGNAL(newAvatar(QString)), SLOT(onNewAvatar(QString))); + connect( m_avatarManager, SIGNAL( newAvatar( QString ) ), SLOT( onNewAvatar( QString ) ) ); #endif m_pubSubManager = new Jreen::PubSub::Manager( m_client ); @@ -168,15 +176,16 @@ XmppSipPlugin::XmppSipPlugin( Account *account ) // Clear status Jreen::Tune::Ptr tune( new Jreen::Tune() ); - m_pubSubManager->publishItems(QList() << tune, Jreen::JID()); - + m_pubSubManager->publishItems( QList() << tune, Jreen::JID() ); } + XmppSipPlugin::~XmppSipPlugin() { //Note: the next two lines don't currently work, because the deletion wipes out internally posted events, need to talk to euro about a fix Jreen::Tune::Ptr tune( new Jreen::Tune() ); - m_pubSubManager->publishItems(QList() << tune, Jreen::JID()); + m_pubSubManager->publishItems( QList() << tune, Jreen::JID() ); + delete m_pubSubManager; delete m_avatarManager; delete m_roster; @@ -192,7 +201,7 @@ XmppSipPlugin::infoPlugin() { if ( m_infoPlugin.isNull() ) m_infoPlugin = QWeakPointer< Tomahawk::InfoSystem::XmppInfoPlugin >( new Tomahawk::InfoSystem::XmppInfoPlugin( this ) ); - + return InfoSystem::InfoPluginPtr( m_infoPlugin.data() ); } @@ -205,21 +214,20 @@ XmppSipPlugin::menu() } #endif + void XmppSipPlugin::connectPlugin() { - qDebug() << Q_FUNC_INFO; - - if( m_client->isConnected() ) + if ( m_client->isConnected() ) { qDebug() << Q_FUNC_INFO << "Already connected to server, not connecting again..."; return; } - if( m_account->configuration().contains("enforcesecure") && m_account->configuration().value("enforcesecure").toBool() ) + if ( m_xmppAccount->configuration().contains( "enforcesecure" ) && m_xmppAccount->configuration().value( "enforcesecure" ).toBool() ) { tLog() << Q_FUNC_INFO << "Enforcing secure connection..."; - m_client->setFeatureConfig(Jreen::Client::Encryption, Jreen::Client::Force); + m_client->setFeatureConfig( Jreen::Client::Encryption, Jreen::Client::Force ); } tDebug() << "Connecting to the Xmpp server..." << m_client->jid().full(); @@ -228,18 +236,17 @@ XmppSipPlugin::connectPlugin() QTimer::singleShot( 1000, m_client, SLOT( connectToServer() ) ); if ( m_client->connection() ) - connect(m_client->connection(), SIGNAL(error(Jreen::Connection::SocketError)), SLOT(onError(Jreen::Connection::SocketError))); + connect( m_client->connection(), SIGNAL( error( Jreen::Connection::SocketError ) ), SLOT( onError( Jreen::Connection::SocketError ) ) ); m_state = Account::Connecting; emit stateChanged( m_state ); - return; } void XmppSipPlugin::disconnectPlugin() { - if (!m_client->isConnected()) + if ( !m_client->isConnected() ) { if ( m_state != Account::Disconnected ) // might be Connecting { @@ -267,8 +274,6 @@ XmppSipPlugin::disconnectPlugin() void XmppSipPlugin::onConnect() { -// qDebug() << Q_FUNC_INFO; - // update jid resource, servers like gtalk use resource binding and may // have changed our requested /resource if ( m_client->jid().resource() != m_currentResource ) @@ -277,15 +282,9 @@ XmppSipPlugin::onConnect() emit jidChanged( m_client->jid().full() ); } - qDebug() << "Connected to xmpp as:" << m_client->jid().full(); - // set presence to least valid value - m_client->setPresence(Jreen::Presence::XA, "Got Tomahawk? http://gettomahawk.com", -127); - - // set ping timeout to 15 secs (TODO: verify if this works) - m_client->setPingInterval(1000); - - // load roster + m_client->setPresence( Jreen::Presence::XA, "Got Tomahawk? http://gettomahawk.com", -127 ); + m_client->setPingInterval( 1000 ); m_roster->load(); // load XmppInfoPlugin @@ -293,13 +292,12 @@ XmppSipPlugin::onConnect() { infoPlugin().data()->moveToThread( Tomahawk::InfoSystem::InfoSystem::instance()->workerThread().data() ); Tomahawk::InfoSystem::InfoSystem::instance()->addInfoPlugin( infoPlugin() ); - QMetaObject::invokeMethod( infoPlugin().data(), "init", Qt::QueuedConnection ); } //FIXME: this implementation is totally broken atm, so it's disabled to avoid harm :P // join MUC with bare jid as nickname //TODO: make the room a list of rooms and make that configurable - QString mucNickname = QString( "tomahawk@conference.qutim.org/" ).append( QString( m_client->jid().bare() ).replace( "@", "-" ) ); + // QString mucNickname = QString( "tomahawk@conference.qutim.org/" ).append( QString( m_client->jid().bare() ).replace( "@", "-" ) ); //m_room = new Jreen::MUCRoom(m_client, Jreen::JID( mucNickname ) ); //m_room->setHistorySeconds(0); //m_room->join(); @@ -318,8 +316,6 @@ XmppSipPlugin::onConnect() void XmppSipPlugin::onDisconnect( Jreen::Client::DisconnectReason reason ) { - qDebug() << Q_FUNC_INFO; - switch( reason ) { case Jreen::Client::User: @@ -346,7 +342,7 @@ XmppSipPlugin::onDisconnect( Jreen::Client::DisconnectReason reason ) default: qDebug() << "Not all Client::DisconnectReasons checked" << ( int ) reason; - Q_ASSERT(false); + Q_ASSERT( false ); break; } m_state = Account::Disconnected; @@ -354,9 +350,9 @@ XmppSipPlugin::onDisconnect( Jreen::Client::DisconnectReason reason ) removeMenuHelper(); - Q_FOREACH(const Jreen::JID &peer, m_peers.keys()) + Q_FOREACH( const Jreen::JID &peer, m_peers.keys() ) { - handlePeerStatus(peer, Jreen::Presence::Unavailable); + handlePeerStatus( peer, Jreen::Presence::Unavailable ); } if ( !m_infoPlugin.isNull() ) @@ -377,56 +373,51 @@ XmppSipPlugin::errorMessage( Jreen::Client::DisconnectReason reason ) switch( reason ) { case Jreen::Client::User: - return tr("User Interaction"); + return tr( "User Interaction" ); break; case Jreen::Client::HostUnknown: - return tr("Host is unknown"); + return tr( "Host is unknown" ); break; case Jreen::Client::ItemNotFound: - return tr("Item not found"); + return tr( "Item not found" ); break; case Jreen::Client::AuthorizationError: - return tr("Authorization Error"); + return tr( "Authorization Error" ); break; case Jreen::Client::RemoteStreamError: - return tr("Remote Stream Error"); + return tr( "Remote Stream Error" ); break; case Jreen::Client::RemoteConnectionFailed: - return tr("Remote Connection failed"); + return tr( "Remote Connection failed" ); break; case Jreen::Client::InternalServerError: - return tr("Internal Server Error"); + return tr( "Internal Server Error" ); break; case Jreen::Client::SystemShutdown: - return tr("System shutdown"); + return tr( "System shutdown" ); break; case Jreen::Client::Conflict: - return tr("Conflict"); + return tr( "Conflict" ); break; - - case Jreen::Client::Unknown: - return tr("Unknown"); - break; - case Jreen::Client::NoCompressionSupport: - return tr("No Compression Support"); + return tr( "No Compression Support" ); break; - case Jreen::Client::NoEncryptionSupport: - return tr("No Encryption Support"); + return tr( "No Encryption Support" ); break; - case Jreen::Client::NoAuthorizationSupport: - return tr("No Authorization Support"); + return tr( "No Authorization Support" ); break; - case Jreen::Client::NoSupportedFeature: - return tr("No Supported Feature"); + return tr( "No Supported Feature" ); + break; + case Jreen::Client::Unknown: + return tr( "Unknown" ); break; default: qDebug() << "Not all Client::DisconnectReasons checked"; - Q_ASSERT(false); + Q_ASSERT( false ); break; } @@ -442,9 +433,8 @@ XmppSipPlugin::sendMsg( const QString& to, const QString& msg ) { qDebug() << Q_FUNC_INFO << to << msg; - if ( !m_client ) { + if ( !m_client ) return; - } /******************************************************* * Obsolete this by a SipMessage class @@ -452,29 +442,24 @@ XmppSipPlugin::sendMsg( const QString& to, const QString& msg ) QJson::Parser parser; bool ok; QVariant v = parser.parse( msg.toAscii(), &ok ); - if ( !ok || v.type() != QVariant::Map ) + if ( !ok || v.type() != QVariant::Map ) { qDebug() << "Invalid JSON in Xmpp msg"; return; } + QVariantMap m = v.toMap(); /*******************************************************/ TomahawkXmppMessage *sipMessage; - if(m["visible"].toBool()) + if ( m["visible"].toBool() ) { - sipMessage = new TomahawkXmppMessage( m["ip"].toString(), - m["port"].toInt(), - m["uniqname"].toString(), - m["key"].toString() - ); + sipMessage = new TomahawkXmppMessage( m["ip"].toString(), m["port"].toInt(), m["uniqname"].toString(), m["key"].toString() ); } else - { sipMessage = new TomahawkXmppMessage(); - } - qDebug() << "Send sip messsage to " << to; + qDebug() << "Send sip messsage to" << to; Jreen::IQ iq( Jreen::IQ::Set, to ); iq.addExtension( sipMessage ); Jreen::IQReply *reply = m_client->send( iq ); @@ -486,12 +471,10 @@ XmppSipPlugin::sendMsg( const QString& to, const QString& msg ) void XmppSipPlugin::broadcastMsg( const QString& msg ) { - qDebug() << Q_FUNC_INFO; - if ( !m_client ) return; - foreach( const Jreen::JID& jid, m_peers.keys() ) + foreach ( const Jreen::JID& jid, m_peers.keys() ) { sendMsg( jid.full(), msg ); } @@ -502,14 +485,11 @@ void XmppSipPlugin::addContact( const QString& jid, const QString& msg ) { // Add contact to the Tomahawk group on the roster - QString realJid = jid; - if( !realJid.contains( '@' ) ) + if ( !realJid.contains( '@' ) ) realJid += defaultSuffix(); m_roster->subscribe( realJid, msg, realJid, QStringList() << "Tomahawk" ); - - return; } @@ -533,24 +513,24 @@ XmppSipPlugin::showAddFriendDialog() void XmppSipPlugin::publishTune( const QUrl& url, const InfoSystem::InfoStringHash& trackInfo ) { - if( m_account->configuration().value("publishtracks").toBool() == false ) + if ( m_xmppAccount->configuration().value("publishtracks").toBool() == false ) { - tDebug() << Q_FUNC_INFO << m_client->jid().full() << "Not publishing now playing info (disabled in account config)"; + tDebug() << Q_FUNC_INFO << m_client->jid().full() << "Not publishing now playing info (disabled in account config)"; return; } if ( trackInfo.isEmpty() ) { Jreen::Tune::Ptr tune( new Jreen::Tune() ); - m_pubSubManager->publishItems(QList() << tune, Jreen::JID()); + m_pubSubManager->publishItems( QList() << tune, Jreen::JID() ); } Jreen::Tune::Ptr tune( new Jreen::Tune() ); tune->setTitle( trackInfo.value( "title" ) ); tune->setArtist( trackInfo.value( "artist" ) ); - tune->setLength( trackInfo.value("duration").toInt() ); - tune->setTrack( trackInfo.value("albumpos") ); + tune->setLength( trackInfo.value( "duration" ).toInt() ); + tune->setTrack( trackInfo.value( "albumpos" ) ); //TODO: provide a rating once available in Tomahawk tune->setRating( 10 ); @@ -559,8 +539,8 @@ XmppSipPlugin::publishTune( const QUrl& url, const InfoSystem::InfoStringHash& t tune->setSource( "Tomahawk" ); tune->setUri( url ); - tDebug() << Q_FUNC_INFO << "Setting URI of " << tune->uri().toString(); - + tDebug() << Q_FUNC_INFO << "Setting URI of" << tune->uri().toString(); + m_pubSubManager->publishItems( QList() << tune, Jreen::JID() ); } @@ -625,10 +605,9 @@ XmppSipPlugin::configurationChanged() if ( !m_currentUsername.contains( '@' ) ) { m_currentUsername += defaultSuffix(); - QVariantHash credentials = m_account->credentials(); + QVariantHash credentials = m_xmppAccount->credentials(); credentials[ "username" ] = m_currentUsername; - m_account->setCredentials( credentials ); - m_account->sync(); + m_xmppAccount->saveCredentials( credentials ); } if ( reconnect ) @@ -643,13 +622,15 @@ XmppSipPlugin::configurationChanged() } } -void XmppSipPlugin::setupClientHelper() + +void +XmppSipPlugin::setupClientHelper() { Jreen::JID jid = Jreen::JID( m_currentUsername ); m_client->setJID( jid ); m_client->setPassword( m_currentPassword ); - if( !m_currentServer.isEmpty() ) + if ( !m_currentServer.isEmpty() ) { // set explicit server details m_client->setServer( m_currentServer ); @@ -663,20 +644,22 @@ void XmppSipPlugin::setupClientHelper() } } -void XmppSipPlugin::addMenuHelper() + +void +XmppSipPlugin::addMenuHelper() { #ifndef ENABLE_HEADLESS - if( !m_menu ) + if ( !m_menu ) { m_menu = new QMenu( QString( "%1 (" ).arg( friendlyName() ).append( readUsername() ).append(")" ) ); QAction* addFriendAction = m_menu->addAction( tr( "Add Friend..." ) ); - connect( addFriendAction, SIGNAL( triggered() ), this, SLOT( showAddFriendDialog() ) ); + connect( addFriendAction, SIGNAL( triggered() ), SLOT( showAddFriendDialog() ) ); - if( readXmlConsoleEnabled() ) + if ( readXmlConsoleEnabled() ) { - QAction* showXmlConsoleAction = m_menu->addAction( tr( "XML Console...") ); - connect( showXmlConsoleAction, SIGNAL( triggered() ), this, SLOT( showXmlConsole() ) ); + QAction* showXmlConsoleAction = m_menu->addAction( tr( "XML Console..." ) ); + connect( showXmlConsoleAction, SIGNAL( triggered() ), SLOT( showXmlConsole() ) ); } emit addMenu( m_menu ); @@ -684,10 +667,12 @@ void XmppSipPlugin::addMenuHelper() #endif } -void XmppSipPlugin::removeMenuHelper() + +void +XmppSipPlugin::removeMenuHelper() { #ifndef ENABLE_HEADLESS - if( m_menu ) + if ( m_menu ) { emit removeMenu( m_menu ); @@ -698,37 +683,35 @@ void XmppSipPlugin::removeMenuHelper() } -void XmppSipPlugin::onNewMessage( const Jreen::Message& message ) +void +XmppSipPlugin::onNewMessage( const Jreen::Message& message ) { if ( m_state != Account::Connected ) return; -// qDebug() << Q_FUNC_INFO << "message type" << message.subtype(); - QString from = message.from().full(); QString msg = message.body(); - if(msg.isEmpty()) + if ( msg.isEmpty() ) return; - if( message.subtype() == Jreen::Message::Error ) + if ( message.subtype() == Jreen::Message::Error ) { - tDebug() << Q_FUNC_INFO << "Received error message from " << from << ", not answering... (Condition: " + tDebug() << Q_FUNC_INFO << "Received error message from" << from << ", not answering... (Condition:" << ( message.error().isNull() ? -1 : message.error()->condition() ) << ")"; return; } SipInfo info = SipInfo::fromJson( msg ); - if ( !info.isValid() ) { QString to = from; - QString response = QString( tr("I'm sorry -- I'm just an automatic presence used by Tomahawk Player" - " (http://gettomahawk.com). If you are getting this message, the person you" - " are trying to reach is probably not signed on, so please try again later!") ); + QString response = QString( tr( "I'm sorry -- I'm just an automatic presence used by Tomahawk Player" + " (http://gettomahawk.com). If you are getting this message, the person you" + " are trying to reach is probably not signed on, so please try again later!" ) ); // this is not a sip message, so we send it directly through the client - m_client->send( Jreen::Message ( Jreen::Message::Error, Jreen::JID(to), response) ); + m_client->send( Jreen::Message ( Jreen::Message::Error, Jreen::JID( to ), response) ); emit msgReceived( from, msg ); return; @@ -739,7 +722,8 @@ void XmppSipPlugin::onNewMessage( const Jreen::Message& message ) } -void XmppSipPlugin::onPresenceReceived( const Jreen::RosterItem::Ptr &item, const Jreen::Presence& presence ) +void +XmppSipPlugin::onPresenceReceived( const Jreen::RosterItem::Ptr& item, const Jreen::Presence& presence ) { Q_UNUSED(item); if ( m_state != Account::Connected ) @@ -750,19 +734,20 @@ void XmppSipPlugin::onPresenceReceived( const Jreen::RosterItem::Ptr &item, cons qDebug() << Q_FUNC_INFO << "* New presence:" << fulljid << presence.subtype(); - if( jid == m_client->jid() ) + if ( jid == m_client->jid() ) return; - if ( presence.error() ) { + if ( presence.error() ) + { //qDebug() << Q_FUNC_INFO << fulljid << "Running tomahawk: no" << "presence error"; return; } // ignore anyone not Running tomahawk: Jreen::Capabilities::Ptr caps = presence.payload(); - if( caps ) + if ( caps ) { - qDebug() << Q_FUNC_INFO << fulljid << "Running tomahawk: maybe" << "caps " << caps->node() << "requesting disco..."; + qDebug() << Q_FUNC_INFO << fulljid << "Running tomahawk: maybe" << "caps" << caps->node() << "requesting disco..."; // request disco features QString node = caps->node() + '#' + caps->ver(); @@ -774,7 +759,7 @@ void XmppSipPlugin::onPresenceReceived( const Jreen::RosterItem::Ptr &item, cons reply->setData( RequestDisco ); connect( reply, SIGNAL( received( Jreen::IQ ) ), SLOT( onNewIq( Jreen::IQ ) ) ); } - else if( !caps ) + else if ( !caps ) { // qDebug() << Q_FUNC_INFO << "Running tomahawk: no" << "no caps"; if ( presenceMeansOnline( m_peers[ jid ] ) ) @@ -783,46 +768,40 @@ void XmppSipPlugin::onPresenceReceived( const Jreen::RosterItem::Ptr &item, cons } -void XmppSipPlugin::onSubscriptionReceived( const Jreen::RosterItem::Ptr& item, const Jreen::Presence& presence ) +void +XmppSipPlugin::onSubscriptionReceived( const Jreen::RosterItem::Ptr& item, const Jreen::Presence& presence ) { if ( m_state != Account::Connected ) return; -// qDebug() << Q_FUNC_INFO << "presence type:" << presence.subtype(); - if(item) + if ( item ) qDebug() << Q_FUNC_INFO << presence.from().full() << "subs" << item->subscription() << "ask" << item->ask(); else qDebug() << Q_FUNC_INFO << "item empty"; // don't do anything if the contact is already subscribed to us - if( presence.subtype() != Jreen::Presence::Subscribe || - ( - item && (item->subscription() == Jreen::RosterItem::From || item->subscription() == Jreen::RosterItem::Both) - ) - ) + if ( presence.subtype() != Jreen::Presence::Subscribe || + ( item && (item->subscription() == Jreen::RosterItem::From || item->subscription() == Jreen::RosterItem::Both ) ) ) { return; } // check if the requester is already on the roster - if(item && - ( - item->subscription() == Jreen::RosterItem::To || - ( item->subscription() == Jreen::RosterItem::None && !item->ask().isEmpty() ) - ) - ) + if ( item && + ( item->subscription() == Jreen::RosterItem::To || ( item->subscription() == Jreen::RosterItem::None && !item->ask().isEmpty() ) ) ) { qDebug() << Q_FUNC_INFO << presence.from().bare() << "already on the roster so we assume ack'ing subscription request is okay..."; - m_roster->allowSubscription(presence.from(), true); + m_roster->allowSubscription( presence.from(), true ); return; } + #ifndef ENABLE_HEADLESS // preparing the confirm box for the user QMessageBox *confirmBox = new QMessageBox( QMessageBox::Question, tr( "Authorize User" ), - QString( tr( "Do you want to grant %1 access to your Collection?" ) ).arg(presence.from().bare()), + QString( tr( "Do you want to grant %1 access to your Collection?" ) ).arg( presence.from().bare() ), QMessageBox::Yes | QMessageBox::No, TomahawkUtils::tomahawkWindow() ); @@ -858,11 +837,10 @@ XmppSipPlugin::onSubscriptionRequestConfirmed( int result ) sender()->deleteLater(); QMessageBox::StandardButton allowSubscription = static_cast< QMessageBox::StandardButton >( result ); - if ( allowSubscription == QMessageBox::Yes ) { qDebug() << Q_FUNC_INFO << jid.bare() << "accepted by user, adding to roster"; - addContact(jid, ""); + addContact( jid, "" ); } else { @@ -874,7 +852,8 @@ XmppSipPlugin::onSubscriptionRequestConfirmed( int result ) } -void XmppSipPlugin::onNewIq( const Jreen::IQ& iq ) +void +XmppSipPlugin::onNewIq( const Jreen::IQ& iq ) { if ( m_state != Account::Connected ) return; @@ -888,10 +867,9 @@ void XmppSipPlugin::onNewIq( const Jreen::IQ& iq ) Jreen::Disco::Info *discoInfo = iq.payload< Jreen::Disco::Info >().data(); if ( !discoInfo ) return; + iq.accept(); - Jreen::JID jid = iq.from(); - Jreen::DataForm::Ptr form = discoInfo->form(); if ( discoInfo->features().contains( TOMAHAWK_FEATURE ) ) @@ -907,8 +885,8 @@ void XmppSipPlugin::onNewIq( const Jreen::IQ& iq ) Jreen::SoftwareVersion::Ptr softwareVersion = iq.payload(); if ( softwareVersion ) { - QString versionString = QString("%1 %2 %3").arg( softwareVersion->name(), softwareVersion->os(), softwareVersion->version() ); - qDebug() << Q_FUNC_INFO << "Received software version for " << iq.from().full() << ":" << versionString; + QString versionString = QString( "%1 %2 %3" ).arg( softwareVersion->name(), softwareVersion->os(), softwareVersion->version() ); + qDebug() << Q_FUNC_INFO << "Received software version for" << iq.from().full() << ":" << versionString; emit softwareVersionReceived( iq.from().full(), versionString ); } } @@ -936,9 +914,8 @@ void XmppSipPlugin::onNewIq( const Jreen::IQ& iq ) SipInfo info; info.setVisible( sipMessage->visible() ); - if( sipMessage->visible() ) + if ( sipMessage->visible() ) { - QHostInfo hi; hi.setHostName( sipMessage->ip() ); info.setHost( hi ); @@ -956,7 +933,8 @@ void XmppSipPlugin::onNewIq( const Jreen::IQ& iq ) } -bool XmppSipPlugin::presenceMeansOnline( Jreen::Presence::Type p ) +bool +XmppSipPlugin::presenceMeansOnline( Jreen::Presence::Type p ) { switch( p ) { @@ -965,22 +943,21 @@ bool XmppSipPlugin::presenceMeansOnline( Jreen::Presence::Type p ) case Jreen::Presence::Error: return false; break; + default: return true; } } -void XmppSipPlugin::handlePeerStatus( const Jreen::JID& jid, Jreen::Presence::Type presenceType ) +void +XmppSipPlugin::handlePeerStatus( const Jreen::JID& jid, Jreen::Presence::Type presenceType ) { QString fulljid = jid.full(); // "going offline" event if ( !presenceMeansOnline( presenceType ) && - ( !m_peers.contains( jid ) || - presenceMeansOnline( m_peers.value( jid ) ) - ) - ) + ( !m_peers.contains( jid ) || presenceMeansOnline( m_peers.value( jid ) ) ) ) { m_peers[ jid ] = presenceType; qDebug() << Q_FUNC_INFO << "* Peer goes offline:" << fulljid; @@ -991,10 +968,7 @@ void XmppSipPlugin::handlePeerStatus( const Jreen::JID& jid, Jreen::Presence::Ty // "coming online" event if ( presenceMeansOnline( presenceType ) && - ( !m_peers.contains( jid ) || - !presenceMeansOnline( m_peers.value( jid ) ) - ) - ) + ( !m_peers.contains( jid ) || !presenceMeansOnline( m_peers.value( jid ) ) ) ) { m_peers[ jid ] = presenceType; qDebug() << Q_FUNC_INFO << "* Peer goes online:" << fulljid; @@ -1021,7 +995,8 @@ void XmppSipPlugin::handlePeerStatus( const Jreen::JID& jid, Jreen::Presence::Ty } -void XmppSipPlugin::onNewAvatar( const QString& jid ) +void +XmppSipPlugin::onNewAvatar( const QString& jid ) { #ifndef ENABLE_HEADLESS // qDebug() << Q_FUNC_INFO << jid; @@ -1032,20 +1007,20 @@ void XmppSipPlugin::onNewAvatar( const QString& jid ) // find peers for the jid QList< Jreen::JID > peers = m_peers.keys(); - foreach ( const Jreen::JID &peer, peers ) + foreach ( const Jreen::JID& peer, peers ) { - if( peer.bare() == jid ) + if ( peer.bare() == jid ) { - emit avatarReceived ( peer.full(), m_avatarManager->avatar( jid ) ); + emit avatarReceived( peer.full(), m_avatarManager->avatar( jid ) ); } } if ( jid == m_client->jid().bare() ) // own avatar - emit avatarReceived ( m_avatarManager->avatar( jid ) ); + emit avatarReceived( m_avatarManager->avatar( jid ) ); else // someone else's avatar - emit avatarReceived ( jid, m_avatarManager->avatar( jid ) ); + emit avatarReceived( jid, m_avatarManager->avatar( jid ) ); #endif } @@ -1062,22 +1037,21 @@ XmppSipPlugin::readXmlConsoleEnabled() QString XmppSipPlugin::readUsername() { - QVariantHash credentials = m_account->credentials(); - return credentials.contains( "username" ) ? credentials[ "username" ].toString() : QString(); + return m_xmppAccount->credentials().value( "username" ).toString(); } + QString XmppSipPlugin::readPassword() { - QVariantHash credentials = m_account->credentials(); - return credentials.contains( "password" ) ? credentials[ "password" ].toString() : QString(); + return m_xmppAccount->credentials().value( "password" ).toString(); } int XmppSipPlugin::readPort() { - QVariantHash configuration = m_account->configuration(); + QVariantHash configuration = m_xmppAccount->configuration(); return configuration.contains( "port" ) ? configuration[ "port" ].toInt() : 5222; } @@ -1085,7 +1059,7 @@ XmppSipPlugin::readPort() QString XmppSipPlugin::readServer() { - QVariantHash configuration = m_account->configuration(); + QVariantHash configuration = m_xmppAccount->configuration(); return configuration.contains( "server" ) ? configuration[ "server" ].toString() : QString(); } diff --git a/src/accounts/xmpp/sip/XmppSip.h b/src/accounts/xmpp/sip/XmppSip.h index 16eccffcd..3ddd3182a 100644 --- a/src/accounts/xmpp/sip/XmppSip.h +++ b/src/accounts/xmpp/sip/XmppSip.h @@ -48,12 +48,16 @@ #include #endif -#define TOMAHAWK_FEATURE QLatin1String( "tomahawk:sip:v1" ) -#define TOMAHAWK_CAP_NODE_NAME QLatin1String( "http://tomahawk-player.org/" ) - #include "accounts/AccountDllMacro.h" -#include "XmppInfoPlugin.h" +#include "../XmppInfoPlugin.h" + +namespace Tomahawk { +namespace Accounts { + class XmppAccount; +} +} + class ACCOUNTDLLEXPORT XmppSipPlugin : public SipPlugin { @@ -62,7 +66,7 @@ class ACCOUNTDLLEXPORT XmppSipPlugin : public SipPlugin friend class Tomahawk::InfoSystem::XmppInfoPlugin; public: - XmppSipPlugin( Tomahawk::Accounts::Account* account ); + XmppSipPlugin( Tomahawk::Accounts::XmppAccount* account ); virtual ~XmppSipPlugin(); //FIXME: Make this more correct @@ -90,10 +94,11 @@ public slots: virtual void checkSettings(); virtual void configurationChanged(); virtual void sendMsg( const QString& to, const QString& msg ); - void broadcastMsg( const QString &msg ); - virtual void addContact( const QString &jid, const QString& msg = QString() ); + virtual void addContact( const QString& jid, const QString& msg = QString() ); + + void broadcastMsg( const QString& msg ); void showAddFriendDialog(); - void publishTune( const QUrl &url, const Tomahawk::InfoSystem::InfoStringHash &trackInfo ); + void publishTune( const QUrl& url, const Tomahawk::InfoSystem::InfoStringHash& trackInfo ); protected: virtual QString defaultSuffix() const; @@ -103,14 +108,14 @@ private slots: void onConnect(); void onDisconnect( Jreen::Client::DisconnectReason reason ); - void onPresenceReceived( const Jreen::RosterItem::Ptr &item, const Jreen::Presence& presence ); - void onSubscriptionReceived( const Jreen::RosterItem::Ptr &item, const Jreen::Presence& presence ); + void onPresenceReceived( const Jreen::RosterItem::Ptr& item, const Jreen::Presence& presence ); + void onSubscriptionReceived( const Jreen::RosterItem::Ptr& item, const Jreen::Presence& presence ); void onSubscriptionRequestConfirmed( int result ); void onNewMessage( const Jreen::Message& message ); void onError( const Jreen::Connection::SocketError& e ); - void onNewIq( const Jreen::IQ &iq ); - void onNewAvatar( const QString &jid ); + void onNewIq( const Jreen::IQ& iq ); + void onNewAvatar( const QString& jid ); private: bool readXmlConsoleEnabled(); @@ -125,7 +130,7 @@ private: void removeMenuHelper(); bool presenceMeansOnline( Jreen::Presence::Type p ); - void handlePeerStatus( const Jreen::JID &jid, Jreen::Presence::Type presenceType ); + void handlePeerStatus( const Jreen::JID& jid, Jreen::Presence::Type presenceType ); QString m_currentUsername; QString m_currentPassword; @@ -133,22 +138,25 @@ private: int m_currentPort; QString m_currentResource; - QWeakPointer< Tomahawk::InfoSystem::XmppInfoPlugin> m_infoPlugin; + Tomahawk::Accounts::XmppAccount* m_xmppAccount; + QWeakPointer< Tomahawk::InfoSystem::XmppInfoPlugin > m_infoPlugin; Tomahawk::Accounts::Account::ConnectionState m_state; // sort out - Jreen::Client *m_client; + Jreen::Client* m_client; + + Jreen::MUCRoom* m_room; + Jreen::SimpleRoster* m_roster; + QHash< Jreen::JID, Jreen::Presence::Type > m_peers; - Jreen::MUCRoom *m_room; - Jreen::SimpleRoster *m_roster; - QHash m_peers; #ifndef ENABLE_HEADLESS - QHash m_subscriptionConfirmBoxes; + QHash< Jreen::JID, QMessageBox* > m_subscriptionConfirmBoxes; QMenu* m_menu; XmlConsole* m_xmlConsole; #endif + enum IqContext { NoContext, RequestDisco, RequestedDisco, SipMessageSent, RequestedVCard, RequestVersion, RequestedVersion }; - AvatarManager *m_avatarManager; + AvatarManager* m_avatarManager; Jreen::PubSub::Manager* m_pubSubManager; }; diff --git a/src/accounts/zeroconf/CMakeLists.txt b/src/accounts/zeroconf/CMakeLists.txt index de5239ac8..a41333c55 100644 --- a/src/accounts/zeroconf/CMakeLists.txt +++ b/src/accounts/zeroconf/CMakeLists.txt @@ -1,47 +1,13 @@ -project( tomahawk ) -include( ${QT_USE_FILE} ) -add_definitions( ${QT_DEFINITIONS} ) -add_definitions( -DQT_PLUGIN ) -add_definitions( -DQT_SHARED ) -add_definitions( -DACCOUNTDLLEXPORT_PRO ) - -set( zeroconfSources +add_tomahawk_plugin(zeroconf + TYPE account + EXPORT_MACRO ACCOUNTDLLEXPORT_PRO + SOURCES + TomahawkZeroconf.h Zeroconf.cpp ZeroconfAccount.cpp + UI + ConfigWidget.ui + LINK_LIBRARIES + ${TOMAHAWK_LIBRARIES} ) - -set( zeroconfHeaders - Zeroconf.h - TomahawkZeroconf.h - ZeroconfAccount.h -) - -include_directories( . ${CMAKE_CURRENT_BINARY_DIR} .. - ${QT_INCLUDE_DIR} -) - -qt4_wrap_ui( UI_SRCS ConfigWidget.ui ) -qt4_add_resources( RC_SRCS "resources.qrc" ) -qt4_wrap_cpp( zeroconfMoc ${zeroconfHeaders} ) -add_library( tomahawk_account_zeroconf SHARED ${zeroconfSources} ${zeroconfMoc} ${RC_SRCS} ${UI_SRCS} ) - -IF( WIN32 ) -SET( OS_SPECIFIC_LINK_LIBRARIES - ${OS_SPECIFIC_LINK_LIBRARIES} - "winmm.dll" - "iphlpapi.a" -) -ENDIF( WIN32 ) - -target_link_libraries( tomahawk_account_zeroconf - ${QT_LIBRARIES} - ${OS_SPECIFIC_LINK_LIBRARIES} - ${TOMAHAWK_LIBRARIES} -) - -IF( APPLE ) -# SET( CMAKE_SHARED_LINKER_FLAGS ${CMAKE_SHARED_LINKER_FLAGS} "-undefined dynamic_lookup" ) -ENDIF( APPLE ) - -install( TARGETS tomahawk_account_zeroconf DESTINATION ${CMAKE_INSTALL_LIBDIR} ) diff --git a/src/accounts/zeroconf/Zeroconf.cpp b/src/accounts/zeroconf/Zeroconf.cpp index cbe1edd19..dcc14709c 100644 --- a/src/accounts/zeroconf/Zeroconf.cpp +++ b/src/accounts/zeroconf/Zeroconf.cpp @@ -26,6 +26,7 @@ #include "TomahawkSettings.h" #include "utils/Logger.h" #include "ZeroconfAccount.h" +#include "Source.h" using namespace Tomahawk; using namespace Accounts; diff --git a/src/accounts/zeroconf/ZeroconfAccount.cpp b/src/accounts/zeroconf/ZeroconfAccount.cpp index c22515ce9..364022a37 100644 --- a/src/accounts/zeroconf/ZeroconfAccount.cpp +++ b/src/accounts/zeroconf/ZeroconfAccount.cpp @@ -20,6 +20,7 @@ #include "sip/SipPlugin.h" #include "Zeroconf.h" +#include "Source.h" #include @@ -85,7 +86,7 @@ void ZeroconfAccount::authenticate() { if ( !isAuthenticated() ) - static_cast< ZeroconfPlugin* >( m_sipPlugin.data() )->connectPlugin(); + sipPlugin()->connectPlugin(); } @@ -93,7 +94,7 @@ void ZeroconfAccount::deauthenticate() { if ( isAuthenticated() ) - static_cast< ZeroconfPlugin* >( m_sipPlugin.data() )->disconnectPlugin(); + sipPlugin()->disconnectPlugin(); } @@ -111,7 +112,7 @@ ZeroconfAccount::connectionState() const return Disconnected; // TODO can we get called before sipPlugin()? - return static_cast< ZeroconfPlugin* >( m_sipPlugin.data() )->connectionState(); + return m_sipPlugin.data()->connectionState(); } @@ -119,10 +120,10 @@ SipPlugin* ZeroconfAccount::sipPlugin() { if ( m_sipPlugin.isNull() ) - m_sipPlugin = QWeakPointer< SipPlugin >( new ZeroconfPlugin( this ) ); + m_sipPlugin = QWeakPointer< ZeroconfPlugin >( new ZeroconfPlugin( this ) ); return m_sipPlugin.data(); } -Q_EXPORT_PLUGIN2( Tomahawk::Accounts::AccountFactory, Tomahawk::Accounts::ZeroconfFactory ) \ No newline at end of file +Q_EXPORT_PLUGIN2( Tomahawk::Accounts::AccountFactory, Tomahawk::Accounts::ZeroconfFactory ) diff --git a/src/accounts/zeroconf/ZeroconfAccount.h b/src/accounts/zeroconf/ZeroconfAccount.h index 30d43e038..136c971c0 100644 --- a/src/accounts/zeroconf/ZeroconfAccount.h +++ b/src/accounts/zeroconf/ZeroconfAccount.h @@ -19,6 +19,7 @@ #ifndef ZEROCONF_ACCOUNTS_H #define ZEROCONF_ACCOUNTS_H +#include "Zeroconf.h" #include "accounts/Account.h" #include "../AccountDllMacro.h" @@ -71,7 +72,7 @@ public: QWidget* aclWidget() { return 0; } private: - QWeakPointer< SipPlugin > m_sipPlugin; + QWeakPointer< ZeroconfPlugin > m_sipPlugin; }; } diff --git a/src/breakpad/CrashReporter/CrashReporter.cpp b/src/breakpad/CrashReporter/CrashReporter.cpp index ede0aa2b5..42b6a4927 100644 --- a/src/breakpad/CrashReporter/CrashReporter.cpp +++ b/src/breakpad/CrashReporter/CrashReporter.cpp @@ -36,7 +36,6 @@ CrashReporter::CrashReporter( const QStringList& args ) setWindowIcon( QIcon( RESPATH "icons/tomahawk-icon-128x128.png" ) ); ui.setupUi( this ); - ui.logoLabel->setPixmap( QPixmap( RESPATH "icons/tomahawk-icon-128x128.png" ).scaled( QSize( 55, 55 ), Qt::KeepAspectRatio, Qt::SmoothTransformation ) ); ui.progressBar->setRange( 0, 100 ); ui.progressBar->setValue( 0 ); @@ -51,20 +50,11 @@ CrashReporter::CrashReporter( const QStringList& args ) ui.progressLabel->setIndent( 3 ); #else ui.vboxLayout->setSpacing( 16 ); + ui.hboxLayout1->setSpacing( 16 ); ui.progressBar->setTextVisible( false ); ui.progressLabel->setIndent( 1 ); ui.bottomLabel->setDisabled( true ); ui.bottomLabel->setIndent( 1 ); - - // adjust the spacer since we adjusted the spacing above - for ( int x = 0; x < ui.vboxLayout->count(); ++x ) - { - if ( QSpacerItem* spacer = ui.vboxLayout->itemAt( x )->spacerItem() ) - { - spacer->changeSize( 6, 2, QSizePolicy::Minimum, QSizePolicy::Fixed ); - break; - } - } #endif //Q_WS_MAC m_http = new QHttp( "oops.tomahawk-player.org", 80, this ); @@ -78,7 +68,18 @@ CrashReporter::CrashReporter( const QStringList& args ) setFixedSize( sizeHint() ); - QTimer::singleShot( 0, this, SLOT( send() ) ); + //hide until "send report" has been clicked + ui.progressBar->setVisible( false ); + ui.button->setVisible( false ); + ui.progressLabel->setVisible( false ); + connect( ui.sendButton, SIGNAL( clicked() ), SLOT( onSendButton() )); + + +} + +CrashReporter::~CrashReporter() +{ + delete m_http; } @@ -184,3 +185,14 @@ CrashReporter::onFail( int error, const QString& errorString ) ui.progressLabel->setText( tr( "Failed to send crash info." ) ); qDebug() << "Error:" << error << errorString; } + +void +CrashReporter::onSendButton() +{ + ui.progressBar->setVisible( true ); + ui.button->setVisible( true ); + ui.progressLabel->setVisible( true ); + ui.sendButton->setEnabled( false ); + ui.dontSendButton->setEnabled( false ); + QTimer::singleShot( 0, this, SLOT( send() ) ); +} diff --git a/src/breakpad/CrashReporter/CrashReporter.h b/src/breakpad/CrashReporter/CrashReporter.h index bb526a1a6..e6ee32c2f 100644 --- a/src/breakpad/CrashReporter/CrashReporter.h +++ b/src/breakpad/CrashReporter/CrashReporter.h @@ -31,6 +31,7 @@ class CrashReporter : public QDialog public: CrashReporter( const QStringList& argv ); + ~CrashReporter( ); private: Ui::CrashReporter ui; @@ -47,6 +48,7 @@ private slots: void onDone(); void onProgress( int done, int total ); void onFail( int error, const QString& errorString ); + void onSendButton(); }; #endif // CRASHREPORTER_H diff --git a/src/breakpad/CrashReporter/CrashReporter.ui b/src/breakpad/CrashReporter/CrashReporter.ui index 91e98b16b..8cfe575fb 100644 --- a/src/breakpad/CrashReporter/CrashReporter.ui +++ b/src/breakpad/CrashReporter/CrashReporter.ui @@ -10,7 +10,7 @@ 0 0 438 - 196 + 246 @@ -33,7 +33,7 @@ 9 - + 12 @@ -77,7 +77,7 @@ - <p><b>Sorry!</b>&nbsp;Tomahawk crashed. Information about the crash is now being sent to Tomahawk HQ so that we can fix the bug.</p> + <html><head/><body><p><span style=" font-weight:600;">Sorry!</span> Tomahawk crashed. Please tell us about it! Tomahawk has created an error report for you that can help improve the stability in the future. You can now send this report directly to the Tomahawk developers.</p></body></html> Qt::RichText @@ -106,23 +106,41 @@ - - - Qt::Vertical + + + -1 - - QSizePolicy::Fixed - - - - 20 - 16 - - - + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Send this report + + + + + + + Don't send + + + + - + 0 @@ -210,8 +228,8 @@ accept() - 424 - 154 + 426 + 203 247 @@ -219,5 +237,21 @@ + + dontSendButton + clicked() + CrashReporter + reject() + + + 380 + 117 + + + 218 + 122 + + + diff --git a/src/infoplugins/CMakeLists.txt b/src/infoplugins/CMakeLists.txt new file mode 100644 index 000000000..eadafe5a1 --- /dev/null +++ b/src/infoplugins/CMakeLists.txt @@ -0,0 +1,9 @@ +add_subdirectory( generic ) + +if(UNIX AND NOT APPLE) + add_subdirectory( linux ) +endif() + +if(APPLE) + add_subdirectory( mac ) +endif() diff --git a/src/libtomahawk/playlist/TrackHeader.h b/src/infoplugins/InfoPluginDllMacro.h similarity index 75% rename from src/libtomahawk/playlist/TrackHeader.h rename to src/infoplugins/InfoPluginDllMacro.h index 2c472dcfd..0ae9b7db6 100644 --- a/src/libtomahawk/playlist/TrackHeader.h +++ b/src/infoplugins/InfoPluginDllMacro.h @@ -17,24 +17,17 @@ * along with Tomahawk. If not, see . */ -#ifndef TRACKHEADER_H -#define TRACKHEADER_H +#ifndef INFOPLUGINDLLMACRO_H +#define INFOPLUGINDLLMACRO_H -#include "ViewHeader.h" -#include "DllMacro.h" +#include -class TrackView; - -class DLLEXPORT TrackHeader : public ViewHeader -{ -Q_OBJECT - -public: - explicit TrackHeader( TrackView* parent = 0 ); - ~TrackHeader(); - -private: - TrackView* m_parent; -}; +#ifndef INFOPLUGINDLLEXPORT +# if defined (INFOPLUGINDLLEXPORT_PRO) +# define INFOPLUGINDLLEXPORT Q_DECL_EXPORT +# else +# define INFOPLUGINDLLEXPORT Q_DECL_IMPORT +# endif +#endif #endif diff --git a/src/infoplugins/generic/CMakeLists.txt b/src/infoplugins/generic/CMakeLists.txt new file mode 100644 index 000000000..c077e1e77 --- /dev/null +++ b/src/infoplugins/generic/CMakeLists.txt @@ -0,0 +1,22 @@ +include_directories(${LIBECHONEST_INCLUDE_DIR}) + +list(APPEND simple_plugins + Echonest + Charts + NewReleases + Spotify + Hypem + MusixMatch + MusicBrainz + Rovi + Discogs +) + +foreach(simple_plugin ${simple_plugins}) + STRING(TOLOWER "${simple_plugin}" dir) + add_tomahawk_plugin(${dir} + TYPE infoplugin EXPORT_MACRO INFOPLUGINDLLEXPORT_PRO + SOURCES "${dir}/${simple_plugin}Plugin.cpp" + ) +endforeach() + diff --git a/src/infoplugins/generic/charts/CMakeLists.txt b/src/infoplugins/generic/charts/CMakeLists.txt new file mode 100644 index 000000000..36f52e154 --- /dev/null +++ b/src/infoplugins/generic/charts/CMakeLists.txt @@ -0,0 +1,43 @@ +project( tomahawk ) + +include( ${QT_USE_FILE} ) +add_definitions( ${QT_DEFINITIONS} ) +add_definitions( -DQT_PLUGIN ) +add_definitions( -DQT_SHARED ) +add_definitions( -DINFOPLUGINDLLEXPORT_PRO ) + +set( chartsInfoPluginSources + ChartsPlugin.cpp +) + +set( chartsInfoPluginHeaders + ChartsPlugin.h +) + +include_directories( + ${QT_INCLUDE_DIR} +) + +qt4_wrap_cpp( chartsInfoPluginMoc ${chartsInfoPluginHeaders} ) +add_library( tomahawk_infoplugin_charts SHARED ${chartsInfoPluginSources} ${chartsInfoPluginMoc} ${RC_SRCS} ) + +IF( WIN32 ) +SET( OS_SPECIFIC_LINK_LIBRARIES + ${OS_SPECIFIC_LINK_LIBRARIES} + "winmm.dll" + "iphlpapi.a" +) +ENDIF( WIN32 ) + +target_link_libraries( tomahawk_infoplugin_charts + ${TOMAHAWK_LIBRARIES} + ${QT_LIBRARIES} + ${OS_SPECIFIC_LINK_LIBRARIES} +) + +IF( APPLE ) +# SET( CMAKE_SHARED_LINKER_FLAGS ${CMAKE_SHARED_LINKER_FLAGS} "-undefined dynamic_lookup" ) +ENDIF( APPLE ) + +install( TARGETS tomahawk_infoplugin_charts DESTINATION ${CMAKE_INSTALL_LIBDIR} ) + diff --git a/src/libtomahawk/infosystem/infoplugins/generic/ChartsPlugin.cpp b/src/infoplugins/generic/charts/ChartsPlugin.cpp similarity index 95% rename from src/libtomahawk/infosystem/infoplugins/generic/ChartsPlugin.cpp rename to src/infoplugins/generic/charts/ChartsPlugin.cpp index 3eac27215..20123c997 100644 --- a/src/libtomahawk/infosystem/infoplugins/generic/ChartsPlugin.cpp +++ b/src/infoplugins/generic/charts/ChartsPlugin.cpp @@ -21,34 +21,54 @@ #include "ChartsPlugin.h" -#include -#include -#include -#include +#include +#include +#include +#include +#include #include "Album.h" -#include "ChartsPlugin_Data_p.h" +#include "CountryUtils.h" #include "Typedefs.h" #include "audio/AudioEngine.h" #include "TomahawkSettings.h" #include "utils/TomahawkUtils.h" #include "utils/Logger.h" #include "utils/TomahawkCache.h" +#include "Source.h" #define CHART_URL "http://charts.tomahawk-player.org/" //#define CHART_URL "http://localhost:8080/" #include #include -using namespace Tomahawk::InfoSystem; +namespace Tomahawk +{ +namespace InfoSystem +{ ChartsPlugin::ChartsPlugin() : InfoPlugin() , m_chartsFetchJobs( 0 ) { + tDebug( LOGVERBOSE ) << Q_FUNC_INFO << QThread::currentThread(); /// If you add resource, update version aswell m_chartVersion = "2.3"; + m_supportedGetTypes << InfoChart << InfoChartCapabilities; +} + + +ChartsPlugin::~ChartsPlugin() +{ + tDebug( LOGVERBOSE ) << Q_FUNC_INFO << QThread::currentThread(); +} + + +void +ChartsPlugin::init() +{ + tDebug( LOGVERBOSE ) << Q_FUNC_INFO << QThread::currentThread(); QVariantList source_qvarlist = TomahawkUtils::Cache::instance()->getData( "ChartsPlugin", "chart_sources" ).toList(); foreach( const QVariant & source, source_qvarlist ) { m_chartResources.append( source.toString() ); @@ -58,14 +78,6 @@ ChartsPlugin::ChartsPlugin() tDebug( LOGVERBOSE ) << Q_FUNC_INFO << "total sources" << m_chartResources.size() << source_qvarlist.size(); if( m_chartResources.size() == 0 ) fetchChartSourcesList( true ); - m_supportedGetTypes << InfoChart << InfoChartCapabilities; - -} - - -ChartsPlugin::~ChartsPlugin() -{ - tDebug( LOGVERBOSE ) << Q_FUNC_INFO; } @@ -214,7 +226,7 @@ ChartsPlugin::fetchChartSourcesList( bool fetchOnlySourceList ) reply->setProperty( "only_source_list", fetchOnlySourceList ); - tDebug() << "fetching:" << url; + tDebug() << Q_FUNC_INFO << "fetching:" << url; connect( reply, SIGNAL( finished() ), SLOT( chartSourcesList() ) ); } @@ -222,7 +234,7 @@ ChartsPlugin::fetchChartSourcesList( bool fetchOnlySourceList ) void ChartsPlugin::chartSourcesList() { - tDebug( LOGVERBOSE ) << "Got chart sources list"; + tDebug( LOGVERBOSE ) << Q_FUNC_INFO << "Got chart sources list"; QNetworkReply* reply = qobject_cast( sender() ); if ( reply->error() == QNetworkReply::NoError ) @@ -234,7 +246,7 @@ ChartsPlugin::chartSourcesList() if ( !ok ) { - tLog() << "Failed to parse sources" << p.errorString() << "On line" << p.errorLine(); + tLog() << Q_FUNC_INFO << "Failed to parse sources" << p.errorString() << "On line" << p.errorLine(); return; } @@ -248,6 +260,8 @@ ChartsPlugin::chartSourcesList() if( !reply->property("only_source_list" ).toBool() ) fetchAllChartSources(); } + else + tDebug() << Q_FUNC_INFO << "Encountered error fetching chart sources list"; } void ChartsPlugin::fetchAllChartSources() @@ -261,7 +275,7 @@ void ChartsPlugin::fetchAllChartSources() QNetworkReply* reply = TomahawkUtils::nam()->get( QNetworkRequest( url ) ); reply->setProperty( "chart_source", source); - tDebug() << "fetching:" << url; + tDebug() << Q_FUNC_INFO << "fetching:" << url; connect( reply, SIGNAL( finished() ), SLOT( chartsList() ) ); m_chartsFetchJobs++; @@ -286,7 +300,7 @@ void ChartsPlugin::fetchChart( Tomahawk::InfoSystem::InfoRequestData requestData void ChartsPlugin::chartsList() { - tDebug( LOGVERBOSE ) << "Got chart list result"; + tDebug( LOGVERBOSE ) << Q_FUNC_INFO << "Got chart list result"; QNetworkReply* reply = qobject_cast( sender() ); if ( reply->error() == QNetworkReply::NoError ) @@ -631,3 +645,9 @@ ChartsPlugin::chartReturned() tDebug( LOGVERBOSE ) << Q_FUNC_INFO << "Network error in fetching chart:" << reply->url().toString(); } + +} + +} + +Q_EXPORT_PLUGIN2( Tomahawk::InfoSystem::InfoPlugin, Tomahawk::InfoSystem::ChartsPlugin ) \ No newline at end of file diff --git a/src/libtomahawk/infosystem/infoplugins/generic/ChartsPlugin.h b/src/infoplugins/generic/charts/ChartsPlugin.h similarity index 93% rename from src/libtomahawk/infosystem/infoplugins/generic/ChartsPlugin.h rename to src/infoplugins/generic/charts/ChartsPlugin.h index 31cb1c696..bc0fbacfe 100644 --- a/src/libtomahawk/infosystem/infoplugins/generic/ChartsPlugin.h +++ b/src/infoplugins/generic/charts/ChartsPlugin.h @@ -20,10 +20,11 @@ #ifndef ChartsPlugin_H #define ChartsPlugin_H +#include "infoplugins/InfoPluginDllMacro.h" #include "infosystem/InfoSystem.h" #include "infosystem/InfoSystemWorker.h" -#include -#include +#include +#include class QNetworkReply; @@ -33,9 +34,10 @@ namespace Tomahawk namespace InfoSystem { -class ChartsPlugin : public InfoPlugin +class INFOPLUGINDLLEXPORT ChartsPlugin : public InfoPlugin { Q_OBJECT + Q_INTERFACES( Tomahawk::InfoSystem::InfoPlugin ) public: ChartsPlugin(); @@ -53,6 +55,7 @@ public: ChartType chartType() const { return m_chartType; } protected slots: + virtual void init(); virtual void getInfo( Tomahawk::InfoSystem::InfoRequestData requestData ); virtual void notInCacheSlot( Tomahawk::InfoSystem::InfoStringHash criteria, Tomahawk::InfoSystem::InfoRequestData requestData ); diff --git a/src/infoplugins/generic/discogs/DiscogsPlugin.cpp b/src/infoplugins/generic/discogs/DiscogsPlugin.cpp new file mode 100644 index 000000000..f4dc88652 --- /dev/null +++ b/src/infoplugins/generic/discogs/DiscogsPlugin.cpp @@ -0,0 +1,189 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2012 Leo Franchi + * + * Tomahawk is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Tomahawk is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Tomahawk. If not, see . + */ + +#include "DiscogsPlugin.h" + +#include +#include +#include + +#include "utils/TomahawkUtils.h" +#include "utils/Logger.h" +#include "utils/Closure.h" +#include + +using namespace Tomahawk::InfoSystem; + + +DiscogsPlugin::DiscogsPlugin() + : InfoPlugin() +{ + qDebug() << Q_FUNC_INFO; + m_supportedGetTypes << Tomahawk::InfoSystem::InfoAlbumSongs; +} + + +DiscogsPlugin::~DiscogsPlugin() {} + + +void +DiscogsPlugin::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; + } + + switch ( requestData.type ) + { + 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 +DiscogsPlugin::notInCacheSlot( InfoStringHash criteria, InfoRequestData requestData ) +{ + switch ( requestData.type ) + { + case InfoAlbumSongs: + { + QString requestString( "http://api.discogs.com/database/search" ); + QUrl url( requestString ); + url.addQueryItem( "type", "release" ); + url.addQueryItem( "release_title", criteria[ "album" ] ); + url.addQueryItem( "artist", criteria[ "artist" ] ); + QNetworkRequest req( url ); + req.setRawHeader( "User-Agent", "TomahawkPlayer/1.0 +http://tomahawk-player.org" ); + QNetworkReply* reply = TomahawkUtils::nam()->get( req ); + + NewClosure( reply, SIGNAL( finished() ), this, SLOT( albumSearchSlot( Tomahawk::InfoSystem::InfoRequestData, QNetworkReply* ) ), requestData, reply ); + break; + } + + default: + { + Q_ASSERT( false ); + break; + } + } +} + + +void +DiscogsPlugin::albumSearchSlot( const InfoRequestData &requestData, QNetworkReply *reply ) +{ + QJson::Parser p; + QVariantMap results = p.parse( reply ).toMap(); + + if ( !results.contains( "results" ) || results.value( "results" ).toList().isEmpty() ) + { + emit info( requestData, QVariant() ); + return; + } + + const QVariantMap result = results.value( "results" ).toList().first().toMap(); + if ( !result.contains( "id" ) ) + { + emit info( requestData, QVariant() ); + return; + } + + const int id = result.value( "id" ).toInt(); + QUrl url( QString( "http://api.discogs.com/release/%1" ).arg( id ) ); + QNetworkRequest req( url ); + req.setRawHeader( "User-Agent", "TomahawkPlayer/1.0 +http://tomahawk-player.org" ); + + QNetworkReply* reply2 = TomahawkUtils::nam()->get( req ); + NewClosure( reply2, SIGNAL( finished() ), this, SLOT( albumInfoSlot( Tomahawk::InfoSystem::InfoRequestData, QNetworkReply* ) ), requestData, reply2 ); +} + + +void +DiscogsPlugin::albumInfoSlot( const InfoRequestData& requestData, QNetworkReply* reply ) +{ + QJson::Parser p; + QVariantMap results = p.parse( reply ).toMap(); + + if ( !results.contains( "resp" ) ) + { + emit info( requestData, QVariant() ); + return; + } + + const QVariantMap resp = results[ "resp" ].toMap(); + if ( !resp.contains( "release" ) ) + { + emit info( requestData, QVariant() ); + return; + } + + const QVariantMap release = resp[ "release" ].toMap(); + if ( !release.contains( "tracklist" ) || release[ "tracklist" ].toList().isEmpty() ) + { + emit info( requestData, QVariant() ); + return; + } + + QStringList trackNameList; + foreach ( const QVariant& v, release[ "tracklist" ].toList() ) + { + const QVariantMap track = v.toMap(); + if ( track.contains( "title" ) ) + trackNameList << track[ "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 ); +} + + + +Q_EXPORT_PLUGIN2( Tomahawk::InfoSystem::InfoPlugin, Tomahawk::InfoSystem::DiscogsPlugin ) diff --git a/src/infoplugins/generic/discogs/DiscogsPlugin.h b/src/infoplugins/generic/discogs/DiscogsPlugin.h new file mode 100644 index 000000000..654fe7e52 --- /dev/null +++ b/src/infoplugins/generic/discogs/DiscogsPlugin.h @@ -0,0 +1,63 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2012 Leo Franchi + * + * Tomahawk is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Tomahawk is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Tomahawk. If not, see . + */ + +#ifndef DISCOGS_PLUGIN_H +#define DISCOGS_PLUGIN_H + +#include "Typedefs.h" +#include "infosystem/InfoSystem.h" +#include "infosystem/InfoSystemWorker.h" +#include "infoplugins/InfoPluginDllMacro.h" + +class QNetworkReply; + +namespace Tomahawk +{ + +namespace InfoSystem +{ + +class INFOPLUGINDLLEXPORT DiscogsPlugin : public InfoPlugin +{ + Q_OBJECT + Q_INTERFACES( Tomahawk::InfoSystem::InfoPlugin ) + +public: + DiscogsPlugin(); + virtual ~DiscogsPlugin(); + +protected slots: + virtual void init() {} + virtual void getInfo( Tomahawk::InfoSystem::InfoRequestData requestData ); + virtual void notInCacheSlot( InfoStringHash criteria, InfoRequestData requestData ); + + virtual void pushInfo( Tomahawk::InfoSystem::InfoPushData ) {} +private slots: + void albumSearchSlot( const Tomahawk::InfoSystem::InfoRequestData& , QNetworkReply* ); + void albumInfoSlot( const Tomahawk::InfoSystem::InfoRequestData& , QNetworkReply* ); + +private: + bool isValidTrackData( Tomahawk::InfoSystem::InfoRequestData requestData ); +}; + +} + +} + +Q_DECLARE_METATYPE( QNetworkReply* ) +#endif diff --git a/src/libtomahawk/infosystem/infoplugins/generic/EchonestPlugin.cpp b/src/infoplugins/generic/echonest/EchonestPlugin.cpp similarity index 87% rename from src/libtomahawk/infosystem/infoplugins/generic/EchonestPlugin.cpp rename to src/infoplugins/generic/echonest/EchonestPlugin.cpp index 128bea70e..b0e1ec65c 100644 --- a/src/libtomahawk/infosystem/infoplugins/generic/EchonestPlugin.cpp +++ b/src/infoplugins/generic/echonest/EchonestPlugin.cpp @@ -18,36 +18,45 @@ */ #include "EchonestPlugin.h" -#include #include #include "utils/TomahawkUtils.h" #include "utils/Logger.h" #include +#include -using namespace Tomahawk::InfoSystem; -using namespace Echonest; +namespace Tomahawk +{ + +namespace InfoSystem +{ // for internal neatness -EchoNestPlugin::EchoNestPlugin() +EchonestPlugin::EchonestPlugin() : InfoPlugin() { qDebug() << Q_FUNC_INFO; m_supportedGetTypes << Tomahawk::InfoSystem::InfoArtistBiography << Tomahawk::InfoSystem::InfoArtistFamiliarity << Tomahawk::InfoSystem::InfoArtistHotttness << Tomahawk::InfoSystem::InfoArtistTerms << Tomahawk::InfoSystem::InfoMiscTopTerms; - Echonest::Config::instance()->setNetworkAccessManager( TomahawkUtils::nam() ); } -EchoNestPlugin::~EchoNestPlugin() +EchonestPlugin::~EchonestPlugin() { qDebug() << Q_FUNC_INFO; } void -EchoNestPlugin::getInfo( Tomahawk::InfoSystem::InfoRequestData requestData ) +EchonestPlugin::init() +{ + Echonest::Config::instance()->setNetworkAccessManager( TomahawkUtils::nam() ); +} + + +void +EchonestPlugin::getInfo( Tomahawk::InfoSystem::InfoRequestData requestData ) { switch ( requestData.type ) { @@ -73,7 +82,7 @@ EchoNestPlugin::getInfo( Tomahawk::InfoSystem::InfoRequestData requestData ) void -EchoNestPlugin::getSongProfile( 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 ); @@ -91,7 +100,7 @@ EchoNestPlugin::getSongProfile( const Tomahawk::InfoSystem::InfoRequestData &req void -EchoNestPlugin::getArtistBiography( const Tomahawk::InfoSystem::InfoRequestData &requestData ) +EchonestPlugin::getArtistBiography( const Tomahawk::InfoSystem::InfoRequestData &requestData ) { if( !isValidArtistData( requestData ) ) return; @@ -105,7 +114,7 @@ EchoNestPlugin::getArtistBiography( const Tomahawk::InfoSystem::InfoRequestData void -EchoNestPlugin::getArtistFamiliarity( const Tomahawk::InfoSystem::InfoRequestData &requestData ) +EchonestPlugin::getArtistFamiliarity( const Tomahawk::InfoSystem::InfoRequestData &requestData ) { if( !isValidArtistData( requestData ) ) return; @@ -120,7 +129,7 @@ EchoNestPlugin::getArtistFamiliarity( const Tomahawk::InfoSystem::InfoRequestDat void -EchoNestPlugin::getArtistHotttnesss( const Tomahawk::InfoSystem::InfoRequestData &requestData ) +EchonestPlugin::getArtistHotttnesss( const Tomahawk::InfoSystem::InfoRequestData &requestData ) { if( !isValidArtistData( requestData ) ) return; @@ -134,7 +143,7 @@ EchoNestPlugin::getArtistHotttnesss( const Tomahawk::InfoSystem::InfoRequestData void -EchoNestPlugin::getArtistTerms( const Tomahawk::InfoSystem::InfoRequestData &requestData ) +EchonestPlugin::getArtistTerms( const Tomahawk::InfoSystem::InfoRequestData &requestData ) { if( !isValidArtistData( requestData ) ) return; @@ -148,7 +157,7 @@ EchoNestPlugin::getArtistTerms( const Tomahawk::InfoSystem::InfoRequestData &req void -EchoNestPlugin::getMiscTopTerms( const Tomahawk::InfoSystem::InfoRequestData &requestData ) +EchonestPlugin::getMiscTopTerms( const Tomahawk::InfoSystem::InfoRequestData &requestData ) { QNetworkReply* reply = Echonest::Artist::topTerms( 20 ); reply->setProperty( "requestData", QVariant::fromValue< Tomahawk::InfoSystem::InfoRequestData >( requestData ) ); @@ -157,13 +166,13 @@ EchoNestPlugin::getMiscTopTerms( const Tomahawk::InfoSystem::InfoRequestData &re void -EchoNestPlugin::getArtistBiographySlot() +EchonestPlugin::getArtistBiographySlot() { QNetworkReply* reply = qobject_cast( sender() ); Echonest::Artist artist = artistFromReply( reply ); - BiographyList biographies = artist.biographies(); + Echonest::BiographyList biographies = artist.biographies(); QVariantMap biographyMap; - Q_FOREACH( const Biography& biography, biographies ) + Q_FOREACH( const Echonest::Biography& biography, biographies ) { QVariantHash siteData; siteData[ "site" ] = biography.site(); @@ -181,7 +190,7 @@ EchoNestPlugin::getArtistBiographySlot() void -EchoNestPlugin::getArtistFamiliaritySlot() +EchonestPlugin::getArtistFamiliaritySlot() { QNetworkReply* reply = qobject_cast( sender() ); Echonest::Artist artist = artistFromReply( reply ); @@ -193,7 +202,7 @@ EchoNestPlugin::getArtistFamiliaritySlot() void -EchoNestPlugin::getArtistHotttnesssSlot() +EchonestPlugin::getArtistHotttnesssSlot() { QNetworkReply* reply = qobject_cast( sender() ); Echonest::Artist artist = artistFromReply( reply ); @@ -205,11 +214,11 @@ EchoNestPlugin::getArtistHotttnesssSlot() void -EchoNestPlugin::getArtistTermsSlot() +EchonestPlugin::getArtistTermsSlot() { QNetworkReply* reply = qobject_cast( sender() ); Echonest::Artist artist = artistFromReply( reply ); - TermList terms = artist.terms(); + Echonest::TermList terms = artist.terms(); QVariantMap termsMap; Q_FOREACH( const Echonest::Term& term, terms ) { QVariantHash termHash; @@ -224,10 +233,10 @@ EchoNestPlugin::getArtistTermsSlot() void -EchoNestPlugin::getMiscTopSlot() +EchonestPlugin::getMiscTopSlot() { QNetworkReply* reply = qobject_cast( sender() ); - TermList terms = Echonest::Artist::parseTopTerms( reply ); + Echonest::TermList terms = Echonest::Artist::parseTopTerms( reply ); QVariantMap termsMap; Q_FOREACH( const Echonest::Term& term, terms ) { QVariantHash termHash; @@ -242,7 +251,7 @@ EchoNestPlugin::getMiscTopSlot() bool -EchoNestPlugin::isValidArtistData( const Tomahawk::InfoSystem::InfoRequestData &requestData ) +EchonestPlugin::isValidArtistData( const Tomahawk::InfoSystem::InfoRequestData &requestData ) { if ( requestData.input.isNull() || !requestData.input.isValid() || !requestData.input.canConvert< QString >() ) { @@ -260,7 +269,7 @@ EchoNestPlugin::isValidArtistData( const Tomahawk::InfoSystem::InfoRequestData & bool -EchoNestPlugin::isValidTrackData( const Tomahawk::InfoSystem::InfoRequestData &requestData ) +EchonestPlugin::isValidTrackData( const Tomahawk::InfoSystem::InfoRequestData &requestData ) { if ( requestData.input.isNull() || !requestData.input.isValid() || !requestData.input.canConvert< QString >() ) { @@ -282,8 +291,8 @@ EchoNestPlugin::isValidTrackData( const Tomahawk::InfoSystem::InfoRequestData &r } -Artist -EchoNestPlugin::artistFromReply( QNetworkReply* reply ) +Echonest::Artist +EchonestPlugin::artistFromReply( QNetworkReply* reply ) { Echonest::Artist artist = reply->property("artist").value(); try { @@ -293,4 +302,9 @@ EchoNestPlugin::artistFromReply( QNetworkReply* reply ) } return artist; } -// + +} //ns InfoSystem + +} //ns Tomahawk + +Q_EXPORT_PLUGIN2( Tomahawk::InfoSystem::InfoPlugin, Tomahawk::InfoSystem::EchonestPlugin ) \ No newline at end of file diff --git a/src/libtomahawk/infosystem/infoplugins/generic/EchonestPlugin.h b/src/infoplugins/generic/echonest/EchonestPlugin.h similarity index 90% rename from src/libtomahawk/infosystem/infoplugins/generic/EchonestPlugin.h rename to src/infoplugins/generic/echonest/EchonestPlugin.h index 4497af432..bdd4e6a3a 100644 --- a/src/libtomahawk/infosystem/infoplugins/generic/EchonestPlugin.h +++ b/src/infoplugins/generic/echonest/EchonestPlugin.h @@ -20,33 +20,33 @@ #ifndef ECHONESTPLUGIN_H #define ECHONESTPLUGIN_H +#include "infoplugins/InfoPluginDllMacro.h" #include "infosystem/InfoSystem.h" #include "infosystem/InfoSystemWorker.h" +#include "echonest/Artist.h" #include class QNetworkReply; -namespace Echonest -{ - class Artist; -} - namespace Tomahawk { namespace InfoSystem { -class EchoNestPlugin : public InfoPlugin +class INFOPLUGINDLLEXPORT EchonestPlugin : public InfoPlugin { Q_OBJECT + Q_INTERFACES( Tomahawk::InfoSystem::InfoPlugin ) public: - EchoNestPlugin(); - virtual ~EchoNestPlugin(); + EchonestPlugin(); + virtual ~EchonestPlugin(); protected slots: + virtual void init(); + virtual void getInfo( Tomahawk::InfoSystem::InfoRequestData requestData ); virtual void pushInfo( Tomahawk::InfoSystem::InfoPushData pushData ) diff --git a/src/infoplugins/generic/hypem/CMakeLists.txt b/src/infoplugins/generic/hypem/CMakeLists.txt new file mode 100644 index 000000000..074399881 --- /dev/null +++ b/src/infoplugins/generic/hypem/CMakeLists.txt @@ -0,0 +1,43 @@ +project( tomahawk ) + +include( ${QT_USE_FILE} ) +add_definitions( ${QT_DEFINITIONS} ) +add_definitions( -DQT_PLUGIN ) +add_definitions( -DQT_SHARED ) +add_definitions( -DINFOPLUGINDLLEXPORT_PRO ) + +set( hypemInfoPluginSources + HypemPlugin.cpp +) + +set( hypemInfoPluginHeaders + HypemPlugin.h +) + +include_directories( + ${QT_INCLUDE_DIR} +) + +qt4_wrap_cpp( hypemInfoPluginMoc ${hypemInfoPluginHeaders} ) +add_library( tomahawk_infoplugin_hypem SHARED ${hypemInfoPluginSources} ${hypemInfoPluginMoc} ${RC_SRCS} ) + +IF( WIN32 ) +SET( OS_SPECIFIC_LINK_LIBRARIES + ${OS_SPECIFIC_LINK_LIBRARIES} + "winmm.dll" + "iphlpapi.a" +) +ENDIF( WIN32 ) + +target_link_libraries( tomahawk_infoplugin_hypem + ${TOMAHAWK_LIBRARIES} + ${QT_LIBRARIES} + ${OS_SPECIFIC_LINK_LIBRARIES} +) + +IF( APPLE ) +# SET( CMAKE_SHARED_LINKER_FLAGS ${CMAKE_SHARED_LINKER_FLAGS} "-undefined dynamic_lookup" ) +ENDIF( APPLE ) + +install( TARGETS tomahawk_infoplugin_hypem DESTINATION ${CMAKE_INSTALL_LIBDIR} ) + diff --git a/src/libtomahawk/infosystem/infoplugins/generic/hypemPlugin.cpp b/src/infoplugins/generic/hypem/HypemPlugin.cpp similarity index 91% rename from src/libtomahawk/infosystem/infoplugins/generic/hypemPlugin.cpp rename to src/infoplugins/generic/hypem/HypemPlugin.cpp index 074da3595..4827d425c 100644 --- a/src/libtomahawk/infosystem/infoplugins/generic/hypemPlugin.cpp +++ b/src/infoplugins/generic/hypem/HypemPlugin.cpp @@ -17,29 +17,36 @@ * along with Tomahawk. If not, see . */ -#include "hypemPlugin.h" +#include "HypemPlugin.h" #include #include #include #include #include +#include #include "Album.h" #include "Typedefs.h" #include "TomahawkSettings.h" #include "utils/TomahawkUtils.h" +#include "infosystem/InfoSystemWorker.h" #include "utils/Logger.h" +#include "Source.h" #define HYPEM_URL "http://hypem.com/playlist/" #define HYPEM_END_URL "json/1/data.js" #include #include -using namespace Tomahawk::InfoSystem; +namespace Tomahawk +{ + +namespace InfoSystem +{ -hypemPlugin::hypemPlugin() +HypemPlugin::HypemPlugin() : InfoPlugin() , m_chartsFetchJobs( 0 ) { @@ -88,21 +95,26 @@ hypemPlugin::hypemPlugin() << "Techno" << "Punk" << "New wave"; - chartTypes(); - } -hypemPlugin::~hypemPlugin() +HypemPlugin::~HypemPlugin() { qDebug() << Q_FUNC_INFO; } void -hypemPlugin::dataError( Tomahawk::InfoSystem::InfoRequestData requestData ) +HypemPlugin::init() +{ + chartTypes(); +} + + +void +HypemPlugin::dataError( Tomahawk::InfoSystem::InfoRequestData requestData ) { emit info( requestData, QVariant() ); return; @@ -110,7 +122,7 @@ hypemPlugin::dataError( Tomahawk::InfoSystem::InfoRequestData requestData ) void -hypemPlugin::getInfo( Tomahawk::InfoSystem::InfoRequestData requestData ) +HypemPlugin::getInfo( Tomahawk::InfoSystem::InfoRequestData requestData ) { qDebug() << Q_FUNC_INFO << requestData.caller; qDebug() << Q_FUNC_INFO << requestData.customData; @@ -140,7 +152,7 @@ hypemPlugin::getInfo( Tomahawk::InfoSystem::InfoRequestData requestData ) } void -hypemPlugin::fetchChart( Tomahawk::InfoSystem::InfoRequestData requestData ) +HypemPlugin::fetchChart( Tomahawk::InfoSystem::InfoRequestData requestData ) { if ( !requestData.input.canConvert< Tomahawk::InfoSystem::InfoStringHash >() ) @@ -168,7 +180,7 @@ hypemPlugin::fetchChart( Tomahawk::InfoSystem::InfoRequestData requestData ) } void -hypemPlugin::fetchChartCapabilities( Tomahawk::InfoSystem::InfoRequestData requestData ) +HypemPlugin::fetchChartCapabilities( Tomahawk::InfoSystem::InfoRequestData requestData ) { if ( !requestData.input.canConvert< Tomahawk::InfoSystem::InfoStringHash >() ) { @@ -181,8 +193,9 @@ hypemPlugin::fetchChartCapabilities( Tomahawk::InfoSystem::InfoRequestData reque } void -hypemPlugin::notInCacheSlot( QHash criteria, Tomahawk::InfoSystem::InfoRequestData requestData ) +HypemPlugin::notInCacheSlot( QHash criteria, Tomahawk::InfoSystem::InfoRequestData requestData ) { + tDebug( LOGVERBOSE ) << "HypemPlugin thread: " << QThread::currentThread() << ", InfoSystemWorker thread: " << Tomahawk::InfoSystem::InfoSystem::instance()->workerThread().data()->currentThread(); switch ( requestData.type ) { case InfoChart: @@ -225,9 +238,9 @@ hypemPlugin::notInCacheSlot( QHash criteria, Tomahawk::InfoSys void -hypemPlugin::chartTypes() +HypemPlugin::chartTypes() { - /// Get possible chart type for specifichypemPlugin: InfoChart types returned chart source + /// Get possible chart type for specificHypemPlugin: InfoChart types returned chart source tDebug() << Q_FUNC_INFO << "Got hypem types"; QVariantMap charts; @@ -295,13 +308,13 @@ hypemPlugin::chartTypes() m_allChartsMap.insert( "Hype Machine", QVariant::fromValue( charts ) ); - qDebug() << "hypemPlugin:Chartstype: " << m_allChartsMap; + qDebug() << "HypemPlugin:Chartstype: " << m_allChartsMap; } void -hypemPlugin::chartReturned() +HypemPlugin::chartReturned() { /// Chart request returned something! Woho @@ -387,3 +400,9 @@ hypemPlugin::chartReturned() qDebug() << "Network error in fetching chart:" << reply->url().toString(); } + +} + +} + +Q_EXPORT_PLUGIN2( Tomahawk::InfoSystem::InfoPlugin, Tomahawk::InfoSystem::HypemPlugin ) \ No newline at end of file diff --git a/src/libtomahawk/infosystem/infoplugins/generic/hypemPlugin.h b/src/infoplugins/generic/hypem/HypemPlugin.h similarity index 84% rename from src/libtomahawk/infosystem/infoplugins/generic/hypemPlugin.h rename to src/infoplugins/generic/hypem/HypemPlugin.h index f0b9867ab..63fc2f4c8 100644 --- a/src/libtomahawk/infosystem/infoplugins/generic/hypemPlugin.h +++ b/src/infoplugins/generic/hypem/HypemPlugin.h @@ -17,9 +17,10 @@ * along with Tomahawk. If not, see . */ -#ifndef hypemPlugin_H -#define hypemPlugin_H +#ifndef HYPEMPLUGIN_H +#define HYPEMPLUGIN_H +#include "infoplugins/InfoPluginDllMacro.h" #include "infosystem/InfoSystem.h" #include "infosystem/InfoSystemWorker.h" #include @@ -33,13 +34,14 @@ namespace Tomahawk namespace InfoSystem { -class hypemPlugin : public InfoPlugin +class INFOPLUGINDLLEXPORT HypemPlugin : public InfoPlugin { Q_OBJECT + Q_INTERFACES( Tomahawk::InfoSystem::InfoPlugin ) public: - hypemPlugin(); - virtual ~hypemPlugin(); + HypemPlugin(); + virtual ~HypemPlugin(); enum ChartType { None = 0x00, @@ -48,14 +50,15 @@ public: Artist = 0x04 }; - void setChartType( ChartType type ) { m_chartType = type; } - ChartType chartType() const { return m_chartType; } + void setChartType( ChartType type ) { m_chartType = type; } + ChartType chartType() const { return m_chartType; } public slots: void chartReturned(); void chartTypes(); protected slots: + virtual void init(); virtual void getInfo( Tomahawk::InfoSystem::InfoRequestData requestData ); virtual void notInCacheSlot( Tomahawk::InfoSystem::InfoStringHash criteria, Tomahawk::InfoSystem::InfoRequestData requestData ); virtual void pushInfo( Tomahawk::InfoSystem::InfoPushData pushData ) @@ -91,4 +94,4 @@ private: } -#endif // hypemPlugin_H +#endif // HYPEMPLUGIN_H diff --git a/src/libtomahawk/infosystem/infoplugins/generic/musicbrainzPlugin.cpp b/src/infoplugins/generic/musicbrainz/MusicBrainzPlugin.cpp similarity index 92% rename from src/libtomahawk/infosystem/infoplugins/generic/musicbrainzPlugin.cpp rename to src/infoplugins/generic/musicbrainz/MusicBrainzPlugin.cpp index a01b43930..3a52e3e52 100644 --- a/src/libtomahawk/infosystem/infoplugins/generic/musicbrainzPlugin.cpp +++ b/src/infoplugins/generic/musicbrainz/MusicBrainzPlugin.cpp @@ -17,10 +17,11 @@ * along with Tomahawk. If not, see . */ -#include "musicbrainzPlugin.h" +#include "MusicBrainzPlugin.h" #include #include +#include #include "utils/TomahawkUtils.h" #include "utils/Logger.h" @@ -89,6 +90,7 @@ MusicBrainzPlugin::getInfo( Tomahawk::InfoSystem::InfoRequestData requestData ) } } + void MusicBrainzPlugin::notInCacheSlot( InfoStringHash criteria, InfoRequestData requestData ) { @@ -127,32 +129,6 @@ MusicBrainzPlugin::notInCacheSlot( InfoStringHash criteria, InfoRequestData requ } -bool -MusicBrainzPlugin::isValidTrackData( Tomahawk::InfoSystem::InfoRequestData requestData ) -{ - if ( requestData.input.isNull() || !requestData.input.isValid() || !requestData.input.canConvert< QVariantMap >() ) - { - 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( requestData, QVariant() ); - qDebug() << Q_FUNC_INFO << "Track name is empty"; - return false; - } - if ( hash[ "artistName" ].toString().isEmpty() ) - { - emit info( requestData, QVariant() ); - qDebug() << Q_FUNC_INFO << "No artist name found"; - return false; - } - return true; -} - - void MusicBrainzPlugin::artistSearchSlot() { @@ -327,3 +303,6 @@ MusicBrainzPlugin::tracksFoundSlot() criteria["album"] = origData["album"]; emit updateCache( criteria, 0, requestData.type, returnedData ); } + + +Q_EXPORT_PLUGIN2( Tomahawk::InfoSystem::InfoPlugin, Tomahawk::InfoSystem::MusicBrainzPlugin ) diff --git a/src/libtomahawk/infosystem/infoplugins/generic/musicbrainzPlugin.h b/src/infoplugins/generic/musicbrainz/MusicBrainzPlugin.h similarity index 89% rename from src/libtomahawk/infosystem/infoplugins/generic/musicbrainzPlugin.h rename to src/infoplugins/generic/musicbrainz/MusicBrainzPlugin.h index 11a3bef4b..57ca6bc99 100644 --- a/src/libtomahawk/infosystem/infoplugins/generic/musicbrainzPlugin.h +++ b/src/infoplugins/generic/musicbrainz/MusicBrainzPlugin.h @@ -22,6 +22,7 @@ #include "infosystem/InfoSystem.h" #include "infosystem/InfoSystemWorker.h" +#include "infoplugins/InfoPluginDllMacro.h" class QNetworkReply; @@ -31,15 +32,17 @@ namespace Tomahawk namespace InfoSystem { -class MusicBrainzPlugin : public InfoPlugin +class INFOPLUGINDLLEXPORT MusicBrainzPlugin : public InfoPlugin { Q_OBJECT + Q_INTERFACES( Tomahawk::InfoSystem::InfoPlugin ) public: MusicBrainzPlugin(); virtual ~MusicBrainzPlugin(); protected slots: + virtual void init() {} virtual void getInfo( Tomahawk::InfoSystem::InfoRequestData requestData ); virtual void notInCacheSlot( InfoStringHash criteria, InfoRequestData requestData ); @@ -56,9 +59,6 @@ private slots: void albumFoundSlot(); void tracksFoundSlot(); - -private: - bool isValidTrackData( Tomahawk::InfoSystem::InfoRequestData requestData ); }; } diff --git a/src/libtomahawk/infosystem/infoplugins/generic/MusixMatchPlugin.cpp b/src/infoplugins/generic/musixmatch/MusixMatchPlugin.cpp similarity index 77% rename from src/libtomahawk/infosystem/infoplugins/generic/MusixMatchPlugin.cpp rename to src/infoplugins/generic/musixmatch/MusixMatchPlugin.cpp index 272ff5822..53223a2b9 100644 --- a/src/libtomahawk/infosystem/infoplugins/generic/MusixMatchPlugin.cpp +++ b/src/infoplugins/generic/musixmatch/MusixMatchPlugin.cpp @@ -21,6 +21,7 @@ #include #include +#include #include "utils/TomahawkUtils.h" #include "utils/Logger.h" @@ -32,32 +33,35 @@ using namespace Tomahawk::InfoSystem; MusixMatchPlugin::MusixMatchPlugin() : InfoPlugin() - , m_apiKey("61be4ea5aea7dd942d52b2f1311dd9fe") + , m_apiKey( "61be4ea5aea7dd942d52b2f1311dd9fe" ) { - qDebug() << Q_FUNC_INFO; + tDebug() << Q_FUNC_INFO; m_supportedGetTypes << Tomahawk::InfoSystem::InfoTrackLyrics; } + MusixMatchPlugin::~MusixMatchPlugin() { qDebug() << Q_FUNC_INFO; } + void MusixMatchPlugin::getInfo( Tomahawk::InfoSystem::InfoRequestData requestData ) { - qDebug() << Q_FUNC_INFO; - if( !isValidTrackData( requestData ) || !requestData.input.canConvert< QVariantMap >() || requestData.type != Tomahawk::InfoSystem::InfoTrackLyrics ) + tDebug() << Q_FUNC_INFO; + if( !isValidTrackData( requestData ) || requestData.type != Tomahawk::InfoSystem::InfoTrackLyrics ) return; - QVariantMap hash = requestData.input.value< QVariantMap >(); - QString artist = hash["artistName"].toString(); - QString track = hash["trackName"].toString(); + InfoStringHash hash = requestData.input.value< Tomahawk::InfoSystem::InfoStringHash >(); + + QString artist = hash["artist"]; + QString track = hash["track"]; if( artist.isEmpty() || track.isEmpty() ) { emit info( requestData, QVariant() ); return; } - qDebug() << "artist is " << artist << ", track is " << track; + tDebug() << "artist is " << artist << ", track is " << track; QString requestString( "http://api.musixmatch.com/ws/1.1/track.search?format=xml&page_size=1&f_has_lyrics=1" ); QUrl url( requestString ); url.addQueryItem( "apikey", m_apiKey ); @@ -69,36 +73,40 @@ MusixMatchPlugin::getInfo( Tomahawk::InfoSystem::InfoRequestData requestData ) connect( reply, SIGNAL( finished() ), SLOT( trackSearchSlot() ) ); } + bool MusixMatchPlugin::isValidTrackData( Tomahawk::InfoSystem::InfoRequestData requestData ) { - qDebug() << Q_FUNC_INFO; - if ( requestData.input.isNull() || !requestData.input.isValid() || !requestData.input.canConvert< QVariantMap >() ) + tDebug() << Q_FUNC_INFO; + + if ( !requestData.input.canConvert< Tomahawk::InfoSystem::InfoStringHash >() ) { emit info( requestData, QVariant() ); - qDebug() << "MusixMatchPlugin::isValidTrackData: Data null, invalid, or can't convert"; + tDebug() << "MusixMatchPlugin::isValidTrackData: Data null, invalid, or can't convert" << requestData.input.isNull() << requestData.input.isValid() << requestData.input.canConvert< QVariantMap >(); return false; } - QVariantMap hash = requestData.input.value< QVariantMap >(); - if ( hash[ "trackName" ].toString().isEmpty() ) + InfoStringHash hash = requestData.input.value< Tomahawk::InfoSystem::InfoStringHash >(); + + if ( hash[ "track" ].isEmpty() ) { emit info( requestData, QVariant() ); - qDebug() << "MusixMatchPlugin::isValidTrackData: Track name is empty"; + tDebug() << "MusixMatchPlugin::isValidTrackData: Track name is empty"; return false; } - if ( hash[ "artistName" ].toString().isEmpty() ) + if ( hash[ "artist" ].isEmpty() ) { emit info( requestData, QVariant() ); - qDebug() << "MusixMatchPlugin::isValidTrackData: No artist name found"; + tDebug() << "MusixMatchPlugin::isValidTrackData: No artist name found"; return false; } return true; } + void MusixMatchPlugin::trackSearchSlot() { - qDebug() << Q_FUNC_INFO; + tDebug() << Q_FUNC_INFO; QNetworkReply* oldReply = qobject_cast( sender() ); if ( !oldReply ) return; //timeout will handle it @@ -122,10 +130,11 @@ MusixMatchPlugin::trackSearchSlot() connect( newReply, SIGNAL( finished() ), SLOT( trackLyricsSlot() ) ); } + void MusixMatchPlugin::trackLyricsSlot() { - qDebug() << Q_FUNC_INFO; + tDebug() << Q_FUNC_INFO; QNetworkReply* reply = qobject_cast< QNetworkReply* >( sender() ); if ( !reply ) return; //timeout will handle it @@ -139,6 +148,8 @@ MusixMatchPlugin::trackLyricsSlot() return; } QString lyrics = domNodeList.at(0).toElement().text(); - qDebug() << "Emitting lyrics: " << lyrics; emit info( reply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >(), QVariant( lyrics ) ); } + + +Q_EXPORT_PLUGIN2( Tomahawk::InfoSystem::InfoPlugin, Tomahawk::InfoSystem::MusixMatchPlugin ) diff --git a/src/libtomahawk/infosystem/infoplugins/generic/MusixMatchPlugin.h b/src/infoplugins/generic/musixmatch/MusixMatchPlugin.h similarity index 90% rename from src/libtomahawk/infosystem/infoplugins/generic/MusixMatchPlugin.h rename to src/infoplugins/generic/musixmatch/MusixMatchPlugin.h index 038f74df6..5c17c0c75 100644 --- a/src/libtomahawk/infosystem/infoplugins/generic/MusixMatchPlugin.h +++ b/src/infoplugins/generic/musixmatch/MusixMatchPlugin.h @@ -22,6 +22,7 @@ #include "infosystem/InfoSystem.h" #include "infosystem/InfoSystemWorker.h" +#include "infoplugins/InfoPluginDllMacro.h" class QNetworkReply; @@ -31,9 +32,10 @@ namespace Tomahawk namespace InfoSystem { -class MusixMatchPlugin : public InfoPlugin +class INFOPLUGINDLLEXPORT MusixMatchPlugin : public InfoPlugin { Q_OBJECT + Q_INTERFACES( Tomahawk::InfoSystem::InfoPlugin ) public: MusixMatchPlugin(); @@ -44,6 +46,7 @@ public slots: void trackLyricsSlot(); protected slots: + virtual void init() {} virtual void getInfo( Tomahawk::InfoSystem::InfoRequestData requestData ); virtual void pushInfo( Tomahawk::InfoSystem::InfoPushData pushData ) diff --git a/src/libtomahawk/infosystem/infoplugins/generic/NewReleasesPlugin.cpp b/src/infoplugins/generic/newreleases/NewReleasesPlugin.cpp similarity index 98% rename from src/libtomahawk/infosystem/infoplugins/generic/NewReleasesPlugin.cpp rename to src/infoplugins/generic/newreleases/NewReleasesPlugin.cpp index 9781a2118..54910bf49 100644 --- a/src/libtomahawk/infosystem/infoplugins/generic/NewReleasesPlugin.cpp +++ b/src/infoplugins/generic/newreleases/NewReleasesPlugin.cpp @@ -4,15 +4,17 @@ #include #include #include +#include #include "Album.h" -#include "ChartsPlugin_Data_p.h" +#include "CountryUtils.h" #include "Typedefs.h" #include "audio/AudioEngine.h" #include "TomahawkSettings.h" #include "utils/TomahawkUtils.h" #include "utils/Logger.h" #include "utils/TomahawkCache.h" +#include "Source.h" #include #include @@ -28,6 +30,16 @@ NewReleasesPlugin::NewReleasesPlugin() { m_nrVersion = "0"; m_supportedGetTypes << InfoNewReleaseCapabilities << InfoNewRelease; +} + +NewReleasesPlugin::~NewReleasesPlugin() +{ + tDebug ( LOGVERBOSE ) << Q_FUNC_INFO; +} + +void +NewReleasesPlugin::init() +{ QVariantList source_qvarlist = TomahawkUtils::Cache::instance()->getData( "NewReleasesPlugin", "nr_sources" ).toList(); foreach( const QVariant & source, source_qvarlist ) { m_nrSources.append( source.toString() ); @@ -39,11 +51,6 @@ NewReleasesPlugin::NewReleasesPlugin() fetchNRSourcesList( true ); } -NewReleasesPlugin::~NewReleasesPlugin() -{ - tDebug ( LOGVERBOSE ) << Q_FUNC_INFO; -} - void NewReleasesPlugin::dataError ( InfoRequestData requestData ) { emit info ( requestData, QVariant() ); @@ -348,9 +355,4 @@ void NewReleasesPlugin::nrReturned() } - - - - - - +Q_EXPORT_PLUGIN2( Tomahawk::InfoSystem::InfoPlugin, Tomahawk::InfoSystem::NewReleasesPlugin ) diff --git a/src/libtomahawk/infosystem/infoplugins/generic/NewReleasesPlugin.h b/src/infoplugins/generic/newreleases/NewReleasesPlugin.h similarity index 94% rename from src/libtomahawk/infosystem/infoplugins/generic/NewReleasesPlugin.h rename to src/infoplugins/generic/newreleases/NewReleasesPlugin.h index d60c86299..85b24c31d 100644 --- a/src/libtomahawk/infosystem/infoplugins/generic/NewReleasesPlugin.h +++ b/src/infoplugins/generic/newreleases/NewReleasesPlugin.h @@ -21,6 +21,8 @@ #include "infosystem/InfoSystem.h" #include "infosystem/InfoSystemWorker.h" +#include "infoplugins/InfoPluginDllMacro.h" + #include #include @@ -32,15 +34,17 @@ namespace Tomahawk namespace InfoSystem { -class NewReleasesPlugin : public InfoPlugin +class INFOPLUGINDLLEXPORT NewReleasesPlugin : public InfoPlugin { Q_OBJECT + Q_INTERFACES( Tomahawk::InfoSystem::InfoPlugin ) public: NewReleasesPlugin(); virtual ~NewReleasesPlugin(); protected slots: + virtual void init(); virtual void getInfo( Tomahawk::InfoSystem::InfoRequestData requestData ); virtual void notInCacheSlot( Tomahawk::InfoSystem::InfoStringHash criteria, Tomahawk::InfoSystem::InfoRequestData requestData ); diff --git a/src/libtomahawk/infosystem/infoplugins/generic/RoviPlugin.cpp b/src/infoplugins/generic/rovi/RoviPlugin.cpp similarity index 98% rename from src/libtomahawk/infosystem/infoplugins/generic/RoviPlugin.cpp rename to src/infoplugins/generic/rovi/RoviPlugin.cpp index 914311e4e..99f3ed990 100644 --- a/src/libtomahawk/infosystem/infoplugins/generic/RoviPlugin.cpp +++ b/src/infoplugins/generic/rovi/RoviPlugin.cpp @@ -19,11 +19,12 @@ #include "RoviPlugin.h" -#include "utils/Logger.h" - #include #include +#include + #include +#include "utils/Logger.h" using namespace Tomahawk::InfoSystem; @@ -41,9 +42,9 @@ RoviPlugin::RoviPlugin() m_secret = "XUnYutaAW6"; } + RoviPlugin::~RoviPlugin() { - } @@ -69,6 +70,7 @@ RoviPlugin::getInfo( Tomahawk::InfoSystem::InfoRequestData requestData ) emit getCachedInfo( criteria, 0, requestData ); } + void RoviPlugin::notInCacheSlot( Tomahawk::InfoSystem::InfoStringHash criteria, Tomahawk::InfoSystem::InfoRequestData requestData ) { @@ -96,6 +98,7 @@ RoviPlugin::notInCacheSlot( Tomahawk::InfoSystem::InfoStringHash criteria, Tomah } } + void RoviPlugin::albumLookupError( QNetworkReply::NetworkError error ) { @@ -111,6 +114,7 @@ RoviPlugin::albumLookupError( QNetworkReply::NetworkError error ) } + void RoviPlugin::albumLookupFinished() { @@ -187,5 +191,7 @@ RoviPlugin::generateSig() const { QByteArray raw = m_apiKey + m_secret + QString::number( QDateTime::currentMSecsSinceEpoch() / 1000 ).toLatin1(); return TomahawkUtils::md5( raw ).toLatin1(); - } + + +Q_EXPORT_PLUGIN2( Tomahawk::InfoSystem::InfoPlugin, Tomahawk::InfoSystem::RoviPlugin ) diff --git a/src/libtomahawk/infosystem/infoplugins/generic/RoviPlugin.h b/src/infoplugins/generic/rovi/RoviPlugin.h similarity index 90% rename from src/libtomahawk/infosystem/infoplugins/generic/RoviPlugin.h rename to src/infoplugins/generic/rovi/RoviPlugin.h index aba88c010..b54c3577a 100644 --- a/src/libtomahawk/infosystem/infoplugins/generic/RoviPlugin.h +++ b/src/infoplugins/generic/rovi/RoviPlugin.h @@ -21,6 +21,7 @@ #define ROVIPLUGIN_H #include "infosystem/InfoSystem.h" +#include "infoplugins/InfoPluginDllMacro.h" #include @@ -32,14 +33,18 @@ namespace Tomahawk namespace InfoSystem { -class RoviPlugin : public InfoPlugin +class INFOPLUGINDLLEXPORT RoviPlugin : public InfoPlugin { Q_OBJECT + Q_INTERFACES( Tomahawk::InfoSystem::InfoPlugin ) + public: RoviPlugin(); virtual ~RoviPlugin(); protected: + virtual void init() {} + virtual void notInCacheSlot( Tomahawk::InfoSystem::InfoStringHash criteria, Tomahawk::InfoSystem::InfoRequestData requestData ); virtual void pushInfo( Tomahawk::InfoSystem::InfoPushData pushData ) diff --git a/src/libtomahawk/infosystem/infoplugins/generic/spotifyPlugin.cpp b/src/infoplugins/generic/spotify/SpotifyPlugin.cpp similarity index 98% rename from src/libtomahawk/infosystem/infoplugins/generic/spotifyPlugin.cpp rename to src/infoplugins/generic/spotify/SpotifyPlugin.cpp index e38ee5a9c..38e38db40 100644 --- a/src/libtomahawk/infosystem/infoplugins/generic/spotifyPlugin.cpp +++ b/src/infoplugins/generic/spotify/SpotifyPlugin.cpp @@ -17,13 +17,14 @@ * along with Tomahawk. If not, see . */ -#include "spotifyPlugin.h" +#include "SpotifyPlugin.h" #include #include #include #include #include +#include #include "Album.h" #include "Typedefs.h" @@ -31,7 +32,8 @@ #include "TomahawkSettings.h" #include "utils/TomahawkUtils.h" #include "utils/Logger.h" -#include "ChartsPlugin_Data_p.h" +#include "CountryUtils.h" +#include "Source.h" #define SPOTIFY_API_URL "http://spotikea.tomahawk-player.org/" #include @@ -383,3 +385,6 @@ SpotifyPlugin::chartReturned() else qDebug() << "Network error in fetching chart:" << reply->url().toString(); } + + +Q_EXPORT_PLUGIN2( Tomahawk::InfoSystem::InfoPlugin, Tomahawk::InfoSystem::SpotifyPlugin ) diff --git a/src/libtomahawk/infosystem/infoplugins/generic/spotifyPlugin.h b/src/infoplugins/generic/spotify/SpotifyPlugin.h similarity index 92% rename from src/libtomahawk/infosystem/infoplugins/generic/spotifyPlugin.h rename to src/infoplugins/generic/spotify/SpotifyPlugin.h index 5737fef42..c21600090 100644 --- a/src/libtomahawk/infosystem/infoplugins/generic/spotifyPlugin.h +++ b/src/infoplugins/generic/spotify/SpotifyPlugin.h @@ -22,6 +22,8 @@ #include "infosystem/InfoSystem.h" #include "infosystem/InfoSystemWorker.h" +#include "infoplugins/InfoPluginDllMacro.h" + #include #include @@ -33,9 +35,10 @@ namespace Tomahawk namespace InfoSystem { -class SpotifyPlugin : public InfoPlugin +class INFOPLUGINDLLEXPORT SpotifyPlugin : public InfoPlugin { Q_OBJECT + Q_INTERFACES( Tomahawk::InfoSystem::InfoPlugin ) public: SpotifyPlugin(); @@ -56,6 +59,7 @@ public slots: void chartTypes(); protected slots: + virtual void init() {} virtual void getInfo( Tomahawk::InfoSystem::InfoRequestData requestData ); virtual void notInCacheSlot( Tomahawk::InfoSystem::InfoStringHash criteria, Tomahawk::InfoSystem::InfoRequestData requestData ); virtual void pushInfo( Tomahawk::InfoSystem::InfoPushData pushData ) diff --git a/src/infoplugins/linux/CMakeLists.txt b/src/infoplugins/linux/CMakeLists.txt new file mode 100644 index 000000000..237b5038e --- /dev/null +++ b/src/infoplugins/linux/CMakeLists.txt @@ -0,0 +1,25 @@ +IF(BUILD_GUI AND X11_FOUND) + INCLUDE_DIRECTORIES( ${THIRDPARTY_DIR}/libqnetwm ) + SET(fdo_srcs + fdonotify/FdoNotifyPlugin.cpp + fdonotify/ImageConverter.cpp + ${THIRDPARTY_DIR}/libqnetwm/libqnetwm/netwm.cpp + ) + SET(FDO_LINK_LIBRARIES ${LINK_LIBRARIES} ${X11_LIBRARIES}) + + add_tomahawk_plugin(fdonotify + TYPE infoplugin EXPORT_MACRO INFOPLUGINDLLEXPORT_PRO + SOURCES "${fdo_srcs}" LINK_LIBRARIES "${FDO_LINK_LIBRARIES}" + ) +ENDIF() + +SET(mpris_srcs + mpris/MprisPluginRootAdaptor.cpp + mpris/MprisPluginPlayerAdaptor.cpp + mpris/MprisPlugin.cpp + ) + +add_tomahawk_plugin(mpris + TYPE infoplugin EXPORT_MACRO INFOPLUGINDLLEXPORT_PRO + SOURCES "${mpris_srcs}" + ) diff --git a/src/libtomahawk/infosystem/infoplugins/unix/FdoNotifyPlugin.cpp b/src/infoplugins/linux/fdonotify/FdoNotifyPlugin.cpp similarity index 96% rename from src/libtomahawk/infosystem/infoplugins/unix/FdoNotifyPlugin.cpp rename to src/infoplugins/linux/fdonotify/FdoNotifyPlugin.cpp index 113d7e7b6..44be76c35 100644 --- a/src/libtomahawk/infosystem/infoplugins/unix/FdoNotifyPlugin.cpp +++ b/src/infoplugins/linux/fdonotify/FdoNotifyPlugin.cpp @@ -44,10 +44,15 @@ #include #include #include +#include #include "utils/Logger.h" -using namespace Tomahawk::InfoSystem; +namespace Tomahawk +{ + +namespace InfoSystem +{ FdoNotifyPlugin::FdoNotifyPlugin() : InfoPlugin() @@ -162,3 +167,9 @@ FdoNotifyPlugin::nowPlaying( const QVariant &input ) if ( list.count() > 0 ) m_nowPlayingId = list.at( 0 ).toInt(); } + +} //ns InfoSystem + +} //ns Tomahawk + +Q_EXPORT_PLUGIN2( Tomahawk::InfoSystem::InfoPlugin, Tomahawk::InfoSystem::FdoNotifyPlugin ) diff --git a/src/libtomahawk/infosystem/infoplugins/unix/FdoNotifyPlugin.h b/src/infoplugins/linux/fdonotify/FdoNotifyPlugin.h similarity index 89% rename from src/libtomahawk/infosystem/infoplugins/unix/FdoNotifyPlugin.h rename to src/infoplugins/linux/fdonotify/FdoNotifyPlugin.h index 2679d7373..fc672c00a 100644 --- a/src/libtomahawk/infosystem/infoplugins/unix/FdoNotifyPlugin.h +++ b/src/infoplugins/linux/fdonotify/FdoNotifyPlugin.h @@ -20,6 +20,7 @@ #ifndef FDONOTIFYPLUGIN_H #define FDONOTIFYPLUGIN_H +#include "infoplugins/InfoPluginDllMacro.h" #include "infosystem/InfoSystem.h" namespace Tomahawk @@ -28,15 +29,18 @@ namespace Tomahawk namespace InfoSystem { -class FdoNotifyPlugin : public InfoPlugin +class INFOPLUGINDLLEXPORT FdoNotifyPlugin : public InfoPlugin { Q_OBJECT + Q_INTERFACES( Tomahawk::InfoSystem::InfoPlugin ) public: FdoNotifyPlugin(); virtual ~FdoNotifyPlugin(); protected slots: + virtual void init() {} + virtual void getInfo( Tomahawk::InfoSystem::InfoRequestData requestData ) { Q_UNUSED( requestData ); diff --git a/src/libtomahawk/infosystem/infoplugins/unix/ImageConverter.cpp b/src/infoplugins/linux/fdonotify/ImageConverter.cpp similarity index 100% rename from src/libtomahawk/infosystem/infoplugins/unix/ImageConverter.cpp rename to src/infoplugins/linux/fdonotify/ImageConverter.cpp diff --git a/src/libtomahawk/infosystem/infoplugins/unix/ImageConverter.h b/src/infoplugins/linux/fdonotify/ImageConverter.h similarity index 100% rename from src/libtomahawk/infosystem/infoplugins/unix/ImageConverter.h rename to src/infoplugins/linux/fdonotify/ImageConverter.h diff --git a/src/libtomahawk/infosystem/infoplugins/unix/MprisPlugin.cpp b/src/infoplugins/linux/mpris/MprisPlugin.cpp similarity index 92% rename from src/libtomahawk/infosystem/infoplugins/unix/MprisPlugin.cpp rename to src/infoplugins/linux/mpris/MprisPlugin.cpp index 542ce34c9..8f8f6610b 100644 --- a/src/libtomahawk/infosystem/infoplugins/unix/MprisPlugin.cpp +++ b/src/infoplugins/linux/mpris/MprisPlugin.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #include "audio/AudioEngine.h" #include "infosystem/InfoSystemWorker.h" @@ -30,12 +31,18 @@ #include "GlobalActionManager.h" #include "utils/Logger.h" #include "utils/TomahawkUtils.h" +#include "audio/AudioEngine.h" +#include "Source.h" #include "MprisPlugin.h" #include "MprisPluginRootAdaptor.h" #include "MprisPluginPlayerAdaptor.h" -using namespace Tomahawk::InfoSystem; +namespace Tomahawk +{ + +namespace InfoSystem +{ static QString s_mpInfoIdentifier = QString( "MPRISPLUGIN" ); @@ -47,7 +54,17 @@ MprisPlugin::MprisPlugin() // Types of pushInfo we care about m_supportedPushTypes << InfoNowPlaying << InfoNowPaused << InfoNowResumed << InfoNowStopped; +} + +MprisPlugin::~MprisPlugin() +{ +} + + +void +MprisPlugin::init() +{ // DBus connection new MprisPluginRootAdaptor( this ); new MprisPluginPlayerAdaptor( this ); @@ -77,11 +94,6 @@ MprisPlugin::MprisPlugin() } -MprisPlugin::~MprisPlugin() -{ -} - - // org.mpris.MediaPlayer2 bool @@ -193,7 +205,7 @@ MprisPlugin::canSeek() const Tomahawk::playlistinterface_ptr p = AudioEngine::instance()->playlist(); if ( p.isNull() ) return false; - return p->seekRestrictions() != PlaylistInterface::NoSeek; + return p->seekRestrictions() != PlaylistModes::NoSeek; } @@ -204,16 +216,16 @@ MprisPlugin::loopStatus() const Tomahawk::playlistinterface_ptr p = AudioEngine::instance()->playlist(); if ( p.isNull() ) return "None"; - PlaylistInterface::RepeatMode mode = p->repeatMode(); + PlaylistModes::RepeatMode mode = p->repeatMode(); switch( mode ) { - case PlaylistInterface::RepeatOne: + case PlaylistModes::RepeatOne: return "Track"; break; - case PlaylistInterface::RepeatAll: + case PlaylistModes::RepeatAll: return "Playlist"; break; - case PlaylistInterface::NoRepeat: + case PlaylistModes::NoRepeat: return "None"; break; default: @@ -232,11 +244,11 @@ MprisPlugin::setLoopStatus( const QString& value ) if ( p.isNull() ) return; if ( value == "Track" ) - p->setRepeatMode( PlaylistInterface::RepeatOne ); + p->setRepeatMode( PlaylistModes::RepeatOne ); else if ( value == "Playlist" ) - p->setRepeatMode( PlaylistInterface::RepeatAll ); + p->setRepeatMode( PlaylistModes::RepeatAll ); else if ( value == "None" ) - p->setRepeatMode( PlaylistInterface::NoRepeat ); + p->setRepeatMode( PlaylistModes::NoRepeat ); } @@ -254,8 +266,8 @@ MprisPlugin::metadata() const Tomahawk::result_ptr track = AudioEngine::instance()->currentTrack(); if ( track ) { - metadataMap.insert( "mpris:trackid", QString( "/track/" ) + track->id().replace( "-", "" ) ); - metadataMap.insert( "mpris:length", track->duration() ); + metadataMap.insert( "mpris:trackid", QVariant::fromValue(QDBusObjectPath(QString( "/track/" ) + track->id().replace( "-", "" ))) ); + metadataMap.insert( "mpris:length", static_cast(track->duration()) * 1000000 ); metadataMap.insert( "xesam:album", track->album()->name() ); metadataMap.insert( "xesam:artist", QStringList( track->artist()->name() ) ); metadataMap.insert( "xesam:title", track->track() ); @@ -332,14 +344,14 @@ MprisPlugin::setShuffle( bool value ) double MprisPlugin::volume() const { - return AudioEngine::instance()->volume(); + return static_cast(AudioEngine::instance()->volume()) / 100.0; } void MprisPlugin::setVolume( double value ) { - AudioEngine::instance()->setVolume( value ); + AudioEngine::instance()->setVolume( value * 100 ); } @@ -480,7 +492,7 @@ MprisPlugin::audioStarted( const QVariant& input ) return; QVariantMap map = input.toMap(); - + if ( !map.contains( "trackinfo" ) || !map[ "trackinfo" ].canConvert< Tomahawk::InfoSystem::InfoStringHash >() ) return; @@ -583,3 +595,8 @@ MprisPlugin::notifyPropertyChanged( const QString& interface, const QString& pro QDBusConnection::sessionBus().send(signal); } +} //ns InfoSystem + +} //ns Tomahawk + +Q_EXPORT_PLUGIN2( Tomahawk::InfoSystem::InfoPlugin, Tomahawk::InfoSystem::MprisPlugin ) diff --git a/src/libtomahawk/infosystem/infoplugins/unix/MprisPlugin.h b/src/infoplugins/linux/mpris/MprisPlugin.h similarity index 96% rename from src/libtomahawk/infosystem/infoplugins/unix/MprisPlugin.h rename to src/infoplugins/linux/mpris/MprisPlugin.h index e6b1fa573..8a11fc1d4 100644 --- a/src/libtomahawk/infosystem/infoplugins/unix/MprisPlugin.h +++ b/src/infoplugins/linux/mpris/MprisPlugin.h @@ -22,6 +22,7 @@ #include "audio/AudioEngine.h" #include "infosystem/InfoSystem.h" +#include "infoplugins/InfoPluginDllMacro.h" #include #include @@ -33,9 +34,10 @@ namespace Tomahawk namespace InfoSystem { -class MprisPlugin : public InfoPlugin +class INFOPLUGINDLLEXPORT MprisPlugin : public InfoPlugin { Q_OBJECT + Q_INTERFACES( Tomahawk::InfoSystem::InfoPlugin ) public: MprisPlugin(); @@ -140,6 +142,8 @@ public slots: void Stop(); protected slots: + virtual void init(); + virtual void getInfo( Tomahawk::InfoSystem::InfoRequestData requestData ) { Q_UNUSED( requestData ); diff --git a/src/libtomahawk/infosystem/infoplugins/unix/MprisPluginPlayerAdaptor.cpp b/src/infoplugins/linux/mpris/MprisPluginPlayerAdaptor.cpp similarity index 100% rename from src/libtomahawk/infosystem/infoplugins/unix/MprisPluginPlayerAdaptor.cpp rename to src/infoplugins/linux/mpris/MprisPluginPlayerAdaptor.cpp diff --git a/src/libtomahawk/infosystem/infoplugins/unix/MprisPluginPlayerAdaptor.h b/src/infoplugins/linux/mpris/MprisPluginPlayerAdaptor.h similarity index 100% rename from src/libtomahawk/infosystem/infoplugins/unix/MprisPluginPlayerAdaptor.h rename to src/infoplugins/linux/mpris/MprisPluginPlayerAdaptor.h diff --git a/src/libtomahawk/infosystem/infoplugins/unix/MprisPluginPlayerAdaptor.xml b/src/infoplugins/linux/mpris/MprisPluginPlayerAdaptor.xml similarity index 100% rename from src/libtomahawk/infosystem/infoplugins/unix/MprisPluginPlayerAdaptor.xml rename to src/infoplugins/linux/mpris/MprisPluginPlayerAdaptor.xml diff --git a/src/libtomahawk/infosystem/infoplugins/unix/MprisPluginRootAdaptor.cpp b/src/infoplugins/linux/mpris/MprisPluginRootAdaptor.cpp similarity index 100% rename from src/libtomahawk/infosystem/infoplugins/unix/MprisPluginRootAdaptor.cpp rename to src/infoplugins/linux/mpris/MprisPluginRootAdaptor.cpp diff --git a/src/libtomahawk/infosystem/infoplugins/unix/MprisPluginRootAdaptor.h b/src/infoplugins/linux/mpris/MprisPluginRootAdaptor.h similarity index 100% rename from src/libtomahawk/infosystem/infoplugins/unix/MprisPluginRootAdaptor.h rename to src/infoplugins/linux/mpris/MprisPluginRootAdaptor.h diff --git a/src/libtomahawk/infosystem/infoplugins/unix/MprisPluginRootAdaptor.xml b/src/infoplugins/linux/mpris/MprisPluginRootAdaptor.xml similarity index 100% rename from src/libtomahawk/infosystem/infoplugins/unix/MprisPluginRootAdaptor.xml rename to src/infoplugins/linux/mpris/MprisPluginRootAdaptor.xml diff --git a/src/infoplugins/mac/CMakeLists.txt b/src/infoplugins/mac/CMakeLists.txt new file mode 100644 index 000000000..8f75a22a9 --- /dev/null +++ b/src/infoplugins/mac/CMakeLists.txt @@ -0,0 +1,11 @@ +set(adium_srcs + adium/AdiumPlugin.cpp + adium/Adium.mm +) + + +add_tomahawk_plugin(adium + TYPE infoplugin EXPORT_MACRO INFOPLUGINDLLEXPORT_PRO + SOURCES "${adium_srcs}" +) + diff --git a/src/libtomahawk/infosystem/infoplugins/mac/Adium.h b/src/infoplugins/mac/adium/Adium.h similarity index 100% rename from src/libtomahawk/infosystem/infoplugins/mac/Adium.h rename to src/infoplugins/mac/adium/Adium.h diff --git a/src/libtomahawk/infosystem/infoplugins/mac/Adium.mm b/src/infoplugins/mac/adium/Adium.mm similarity index 100% rename from src/libtomahawk/infosystem/infoplugins/mac/Adium.mm rename to src/infoplugins/mac/adium/Adium.mm diff --git a/src/libtomahawk/infosystem/infoplugins/mac/AdiumPlugin.cpp b/src/infoplugins/mac/adium/AdiumPlugin.cpp similarity index 94% rename from src/libtomahawk/infosystem/infoplugins/mac/AdiumPlugin.cpp rename to src/infoplugins/mac/adium/AdiumPlugin.cpp index 1422eb710..920d4c188 100644 --- a/src/libtomahawk/infosystem/infoplugins/mac/AdiumPlugin.cpp +++ b/src/infoplugins/mac/adium/AdiumPlugin.cpp @@ -19,6 +19,7 @@ #include +#include #include #include "infosystem/InfoSystemWorker.h" @@ -46,7 +47,11 @@ static void setStatus(const QString &status) script( scriptstr ); } -using namespace Tomahawk::InfoSystem; +namespace Tomahawk +{ + +namespace InfoSystem +{ AdiumPlugin::AdiumPlugin() : InfoPlugin() @@ -141,11 +146,14 @@ AdiumPlugin::audioStarted( const Tomahawk::InfoSystem::PushInfoPair pushInfoPair QVariantMap map = pushInfoPair.second.toMap(); + if ( map.contains( "private" ) && map[ "private" ] == TomahawkSettings::FullyPrivate ) + return; + if ( !map.contains( "trackinfo" ) || !map[ "trackinfo" ].canConvert< Tomahawk::InfoSystem::InfoStringHash >() ) return; InfoStringHash hash = map[ "trackinfo" ].value< Tomahawk::InfoSystem::InfoStringHash >(); - + if ( !hash.contains( "title" ) || !hash.contains( "artist" ) ) return; @@ -158,7 +166,7 @@ AdiumPlugin::audioStarted( const Tomahawk::InfoSystem::PushInfoPair pushInfoPair QUrl shortUrl = m_currentLongUrl; if ( pushInfoPair.first.contains( "shortUrl" ) ) shortUrl = pushInfoPair.first[ "shortUrl" ].toUrl(); - + QString nowPlaying = ""; nowPlaying.append( m_currentArtist ); nowPlaying.append(" - "); @@ -205,3 +213,8 @@ AdiumPlugin::audioResumed( const Tomahawk::InfoSystem::PushInfoPair pushInfoPair audioStarted( pushInfoPair ); } +} //ns InfoSystem + +} //ns Tomahawk + +Q_EXPORT_PLUGIN2( Tomahawk::InfoSystem::InfoPlugin, Tomahawk::InfoSystem::AdiumPlugin ) diff --git a/src/libtomahawk/infosystem/infoplugins/mac/AdiumPlugin.h b/src/infoplugins/mac/adium/AdiumPlugin.h similarity index 92% rename from src/libtomahawk/infosystem/infoplugins/mac/AdiumPlugin.h rename to src/infoplugins/mac/adium/AdiumPlugin.h index 38d3aa149..a4d3d50bf 100644 --- a/src/libtomahawk/infosystem/infoplugins/mac/AdiumPlugin.h +++ b/src/infoplugins/mac/adium/AdiumPlugin.h @@ -20,6 +20,7 @@ #ifndef ADIUMPLUGIN_H #define ADIUMPLUGIN_H +#include "infoplugins/InfoPluginDllMacro.h" #include "infosystem/InfoSystem.h" #include @@ -33,20 +34,23 @@ namespace Tomahawk { namespace InfoSystem { -class AdiumPlugin : public InfoPlugin +class INFOPLUGINDLLEXPORT AdiumPlugin : public InfoPlugin { Q_OBJECT + Q_INTERFACES( Tomahawk::InfoSystem::InfoPlugin ) public: AdiumPlugin(); virtual ~AdiumPlugin(); protected slots: + virtual void init() {} + virtual void getInfo( Tomahawk::InfoSystem::InfoRequestData requestData ) { Q_UNUSED( requestData ); } - + virtual void pushInfo( Tomahawk::InfoSystem::InfoPushData pushData ); public slots: diff --git a/src/libtomahawk/AclRegistry.cpp b/src/libtomahawk/AclRegistry.cpp index 78f370ee5..5ae2262c9 100644 --- a/src/libtomahawk/AclRegistry.cpp +++ b/src/libtomahawk/AclRegistry.cpp @@ -24,6 +24,7 @@ #include "TomahawkSettings.h" #include "TomahawkApp.h" +#include "Source.h" #include "utils/Logger.h" #include "jobview/AclJobItem.h" @@ -72,7 +73,7 @@ ACLRegistry::isAuthorizedUser( const QString& dbid, const QString &username, ACL //FIXME: Remove when things are working emit aclResult( dbid, username, ACLRegistry::Stream ); return ACLRegistry::NotFound; - + bool found = false; QMutableListIterator< ACLRegistry::User > i( m_cache ); while ( i.hasNext() ) @@ -87,7 +88,7 @@ ACLRegistry::isAuthorizedUser( const QString& dbid, const QString &username, ACL found = true; } } - + foreach ( QString knownaccountid, user.knownAccountIds ) { if ( username == knownaccountid ) @@ -109,7 +110,7 @@ ACLRegistry::isAuthorizedUser( const QString& dbid, const QString &username, ACL if ( skipEmission ) return ACLRegistry::NotFound; - + // User was not found, create a new user entry ACLRegistry::User user; user.knownDbids.append( dbid ); @@ -158,7 +159,7 @@ ACLRegistry::queueNextJob() { if ( m_jobCount != 0 ) return; - + if ( !m_jobQueue.isEmpty() ) { AclJobItem* job = m_jobQueue.dequeue(); diff --git a/src/libtomahawk/AclRegistry.h b/src/libtomahawk/AclRegistry.h index 1530a843f..373d673a2 100644 --- a/src/libtomahawk/AclRegistry.h +++ b/src/libtomahawk/AclRegistry.h @@ -26,6 +26,9 @@ #include #include #include +#include +#include +#include #include "HeadlessCheck.h" #include "DllMacro.h" @@ -71,7 +74,7 @@ public: signals: void aclResult( QString nodeid, QString username, ACLRegistry::ACL peerStatus ); - + public slots: /** * @brief Checks if peer is authorized; optionally, can authorize peer with given type if not found @@ -90,7 +93,7 @@ public slots: private slots: void userDecision( ACLRegistry::User user ); void queueNextJob(); - + private: /** * @brief Saves the cache. diff --git a/src/libtomahawk/ActionCollection.cpp b/src/libtomahawk/ActionCollection.cpp index b72ae63b2..bc6908e30 100644 --- a/src/libtomahawk/ActionCollection.cpp +++ b/src/libtomahawk/ActionCollection.cpp @@ -22,6 +22,8 @@ #include "TomahawkSettings.h" #include "audio/AudioEngine.h" #include "utils/TomahawkUtils.h" +#include "utils/Logger.h" +#include "Source.h" ActionCollection* ActionCollection::s_instance = 0; ActionCollection* ActionCollection::instance() @@ -30,7 +32,7 @@ ActionCollection* ActionCollection::instance() } -ActionCollection::ActionCollection( QObject *parent ) +ActionCollection::ActionCollection( QObject* parent ) : QObject( parent ) { s_instance = this; diff --git a/src/libtomahawk/ActionCollection.h b/src/libtomahawk/ActionCollection.h index 2eb327622..378b13cba 100644 --- a/src/libtomahawk/ActionCollection.h +++ b/src/libtomahawk/ActionCollection.h @@ -38,7 +38,7 @@ public: static ActionCollection* instance(); - ActionCollection( QObject *parent); + ActionCollection( QObject *parent ); ~ActionCollection(); void initActions(); diff --git a/src/libtomahawk/Album.cpp b/src/libtomahawk/Album.cpp index 4e3dc61fd..909f17fe2 100644 --- a/src/libtomahawk/Album.cpp +++ b/src/libtomahawk/Album.cpp @@ -24,6 +24,7 @@ #include "database/Database.h" #include "database/DatabaseImpl.h" #include "Query.h" +#include "Source.h" #include "utils/Logger.h" @@ -32,6 +33,8 @@ using namespace Tomahawk; Album::~Album() { + m_ownRef.clear(); + #ifndef ENABLE_HEADLESS delete m_cover; #endif @@ -65,6 +68,8 @@ Album::get( unsigned int id, const QString& name, const Tomahawk::artist_ptr& ar } album_ptr a = album_ptr( new Album( id, name, artist ), &QObject::deleteLater ); + a->setWeakRef( a.toWeakRef() ); + if ( id > 0 ) s_albums.insert( id, a ); @@ -77,23 +82,20 @@ Album::Album( unsigned int id, const QString& name, const Tomahawk::artist_ptr& , m_id( id ) , m_name( name ) , m_artist( artist ) - , m_infoLoaded( false ) - , m_infoLoading( false ) + , m_coverLoaded( false ) + , m_coverLoading( false ) #ifndef ENABLE_HEADLESS , m_cover( 0 ) #endif { + m_sortname = DatabaseImpl::sortname( name ); } void -Album::onTracksAdded( const QList& tracks ) +Album::onTracksLoaded( Tomahawk::ModelMode mode, const Tomahawk::collection_ptr& collection ) { - Tomahawk::AlbumPlaylistInterface* api = dynamic_cast< Tomahawk::AlbumPlaylistInterface* >( playlistInterface().data() ); - if ( api ) - api->addQueries( tracks ); - - emit tracksAdded( tracks ); + emit tracksAdded( playlistInterface( mode, collection )->tracks(), mode, collection ); } @@ -108,19 +110,17 @@ Album::artist() const QPixmap Album::cover( const QSize& size, bool forceLoad ) const { - if ( !m_infoLoaded && !m_infoLoading ) + if ( !m_coverLoaded && !m_coverLoading ) { if ( !forceLoad ) return QPixmap(); - m_uuid = uuid(); - Tomahawk::InfoSystem::InfoStringHash trackInfo; trackInfo["artist"] = artist()->name(); trackInfo["album"] = name(); Tomahawk::InfoSystem::InfoRequestData requestData; - requestData.caller = m_uuid; + requestData.caller = infoid(); requestData.type = Tomahawk::InfoSystem::InfoAlbumCoverArt; requestData.input = QVariant::fromValue< Tomahawk::InfoSystem::InfoStringHash >( trackInfo ); requestData.customData = QVariantMap(); @@ -135,7 +135,7 @@ Album::cover( const QSize& size, bool forceLoad ) const Tomahawk::InfoSystem::InfoSystem::instance()->getInfo( requestData ); - m_infoLoading = true; + m_coverLoading = true; } if ( !m_cover && !m_coverBuffer.isEmpty() ) @@ -168,7 +168,7 @@ Album::cover( const QSize& size, bool forceLoad ) const void Album::infoSystemInfo( const Tomahawk::InfoSystem::InfoRequestData& requestData, const QVariant& output ) { - if ( requestData.caller != m_uuid || + if ( requestData.caller != infoid() || requestData.type != Tomahawk::InfoSystem::InfoAlbumCoverArt ) { return; @@ -191,7 +191,7 @@ Album::infoSystemInfo( const Tomahawk::InfoSystem::InfoRequestData& requestData, void Album::infoSystemFinished( const QString& target ) { - if ( target != m_uuid ) + if ( target != infoid() ) return; disconnect( Tomahawk::InfoSystem::InfoSystem::instance(), SIGNAL( info( Tomahawk::InfoSystem::InfoRequestData, QVariant ) ), @@ -200,18 +200,41 @@ Album::infoSystemFinished( const QString& target ) disconnect( Tomahawk::InfoSystem::InfoSystem::instance(), SIGNAL( finished( QString ) ), this, SLOT( infoSystemFinished( QString ) ) ); - m_infoLoaded = true; + m_coverLoaded = true; emit updated(); } Tomahawk::playlistinterface_ptr -Album::playlistInterface() +Album::playlistInterface( ModelMode mode, const Tomahawk::collection_ptr& collection ) { - if ( m_playlistInterface.isNull() ) + playlistinterface_ptr pli = m_playlistInterface[ mode ][ collection ]; + + if ( pli.isNull() ) { - m_playlistInterface = Tomahawk::playlistinterface_ptr( new Tomahawk::AlbumPlaylistInterface( this ) ); + pli = Tomahawk::playlistinterface_ptr( new Tomahawk::AlbumPlaylistInterface( this, mode, collection ) ); + connect( pli.data(), SIGNAL( tracksLoaded( Tomahawk::ModelMode, Tomahawk::collection_ptr ) ), + SLOT( onTracksLoaded( Tomahawk::ModelMode, Tomahawk::collection_ptr ) ) ); + + m_playlistInterface[ mode ][ collection ] = pli; } - return m_playlistInterface; + return pli; } + + +QList +Album::tracks( ModelMode mode, const Tomahawk::collection_ptr& collection ) +{ + return playlistInterface( mode, collection )->tracks(); +} + + +QString +Album::infoid() const +{ + if ( m_uuid.isEmpty() ) + m_uuid = uuid(); + + return m_uuid; +} \ No newline at end of file diff --git a/src/libtomahawk/Album.h b/src/libtomahawk/Album.h index a7f4f8902..89c6ad09f 100644 --- a/src/libtomahawk/Album.h +++ b/src/libtomahawk/Album.h @@ -31,6 +31,7 @@ #include "Typedefs.h" #include "PlaylistInterface.h" #include "DllMacro.h" +#include "Collection.h" #include "infosystem/InfoSystem.h" namespace Tomahawk @@ -49,34 +50,44 @@ public: unsigned int id() const { return m_id; } QString name() const { return m_name; } + QString sortname() const { return m_sortname; } + artist_ptr artist() const; #ifndef ENABLE_HEADLESS QPixmap cover( const QSize& size, bool forceLoad = true ) const; #endif - bool infoLoaded() const { return m_infoLoaded; } + bool coverLoaded() const { return m_coverLoaded; } - Tomahawk::playlistinterface_ptr playlistInterface(); + QList tracks( ModelMode mode = Mixed, const Tomahawk::collection_ptr& collection = Tomahawk::collection_ptr() ); + Tomahawk::playlistinterface_ptr playlistInterface( ModelMode mode, const Tomahawk::collection_ptr& collection = Tomahawk::collection_ptr() ); + + QWeakPointer< Tomahawk::Album > weakRef() { return m_ownRef; } + void setWeakRef( QWeakPointer< Tomahawk::Album > weakRef ) { m_ownRef = weakRef; } signals: - void tracksAdded( const QList& tracks ); + void tracksAdded( const QList& tracks, Tomahawk::ModelMode mode, const Tomahawk::collection_ptr& collection ); void updated(); void coverChanged(); private slots: - void onTracksAdded( const QList& tracks ); + void onTracksLoaded(Tomahawk::ModelMode mode, const Tomahawk::collection_ptr& collection ); void infoSystemInfo( const Tomahawk::InfoSystem::InfoRequestData& requestData, const QVariant& output ); void infoSystemFinished( const QString& target ); private: Q_DISABLE_COPY( Album ) + QString infoid() const; unsigned int m_id; QString m_name; + QString m_sortname; + artist_ptr m_artist; QByteArray m_coverBuffer; - bool m_infoLoaded; - mutable bool m_infoLoading; + + bool m_coverLoaded; + mutable bool m_coverLoading; mutable QString m_uuid; #ifndef ENABLE_HEADLESS @@ -84,9 +95,13 @@ private: mutable QHash< int, QPixmap > m_coverCache; #endif - Tomahawk::playlistinterface_ptr m_playlistInterface; + QHash< Tomahawk::ModelMode, QHash< Tomahawk::collection_ptr, Tomahawk::playlistinterface_ptr > > m_playlistInterface; + + QWeakPointer< Tomahawk::Album > m_ownRef; }; } // ns +Q_DECLARE_METATYPE( Tomahawk::album_ptr ) + #endif diff --git a/src/libtomahawk/AlbumPlaylistInterface.cpp b/src/libtomahawk/AlbumPlaylistInterface.cpp index 35b24cdbd..355409079 100644 --- a/src/libtomahawk/AlbumPlaylistInterface.cpp +++ b/src/libtomahawk/AlbumPlaylistInterface.cpp @@ -23,18 +23,24 @@ #include "database/Database.h" #include "database/DatabaseImpl.h" #include "database/DatabaseCommand_AllTracks.h" +#include "Pipeline.h" #include "Query.h" +#include "Source.h" +#include "SourceList.h" #include "utils/Logger.h" using namespace Tomahawk; -AlbumPlaylistInterface::AlbumPlaylistInterface() {} -AlbumPlaylistInterface::AlbumPlaylistInterface( Tomahawk::Album *album ) +AlbumPlaylistInterface::AlbumPlaylistInterface( Tomahawk::Album* album, Tomahawk::ModelMode mode, const Tomahawk::collection_ptr& collection ) : Tomahawk::PlaylistInterface() , m_currentItem( 0 ) , m_currentTrack( 0 ) + , m_infoSystemLoaded( false ) + , m_databaseLoaded( false ) + , m_mode( mode ) + , m_collection( collection ) , m_album( QWeakPointer< Tomahawk::Album >( album ) ) { } @@ -58,6 +64,9 @@ AlbumPlaylistInterface::siblingItem( int itemsAway ) if ( p >= m_queries.count() ) return Tomahawk::result_ptr(); + if ( !m_queries.at( p )->numResults() ) + return siblingItem( itemsAway + 1 ); + m_currentTrack = p; m_currentItem = m_queries.at( p )->results().first(); return m_currentItem; @@ -88,15 +97,35 @@ AlbumPlaylistInterface::tracks() { if ( m_queries.isEmpty() && m_album ) { - DatabaseCommand_AllTracks* cmd = new DatabaseCommand_AllTracks(); - cmd->setAlbum( m_album ); - cmd->setSortOrder( DatabaseCommand_AllTracks::AlbumPosition ); - //this takes discnumber into account as well + if ( ( m_mode == Mixed || m_mode == InfoSystemMode ) && !m_infoSystemLoaded ) + { + Tomahawk::InfoSystem::InfoStringHash artistInfo; + artistInfo["artist"] = m_album.data()->artist()->name(); + artistInfo["album"] = m_album.data()->name(); - connect( cmd, SIGNAL( tracks( QList, QVariant ) ), - m_album.data(), SLOT( onTracksAdded( QList ) ) ); + Tomahawk::InfoSystem::InfoRequestData requestData; + requestData.caller = id(); + 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 ); - Database::instance()->enqueue( QSharedPointer( cmd ) ); + connect( Tomahawk::InfoSystem::InfoSystem::instance(), + SIGNAL( info( Tomahawk::InfoSystem::InfoRequestData, QVariant ) ), + SLOT( infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData, QVariant ) ) ); + } + else if ( m_mode == DatabaseMode && !m_databaseLoaded ) + { + DatabaseCommand_AllTracks* cmd = new DatabaseCommand_AllTracks( m_collection ); + cmd->setAlbum( m_album ); + cmd->setSortOrder( DatabaseCommand_AllTracks::AlbumPosition ); + + connect( cmd, SIGNAL( tracks( QList, QVariant ) ), + SLOT( onTracksLoaded( QList ) ) ); + + Database::instance()->enqueue( QSharedPointer( cmd ) ); + } } return m_queries; @@ -104,7 +133,83 @@ AlbumPlaylistInterface::tracks() void -AlbumPlaylistInterface::addQueries( const QList< query_ptr >& tracks ) +AlbumPlaylistInterface::infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData requestData, QVariant output ) { - m_queries << tracks; + if ( requestData.caller != id() ) + return; + + switch ( requestData.type ) + { + case Tomahawk::InfoSystem::InfoAlbumSongs: + { + QVariantMap returnedData = output.value< QVariantMap >(); + if ( !returnedData.isEmpty() ) + { + Tomahawk::InfoSystem::InfoStringHash inputInfo; + inputInfo = requestData.input.value< Tomahawk::InfoSystem::InfoStringHash >(); + + QStringList tracks = returnedData[ "tracks" ].toStringList(); + QList ql; + + //TODO: Figure out how to do this with a multi-disk album without breaking the + // current behaviour. I just know too little about InfoSystem to deal with + // it right now, I've only taken the liberty of adding Query::setDiscNumber + // which should make this easier. --Teo 11/2011 + unsigned int trackNo = 1; + + foreach ( const QString& trackName, tracks ) + { + query_ptr query = Query::get( inputInfo[ "artist" ], trackName, inputInfo[ "album" ] ); + query->setAlbumPos( trackNo++ ); + ql << query; + } + Pipeline::instance()->resolve( ql ); + + m_queries << ql; + } + + break; + } + + default: + { + Q_ASSERT( false ); + break; + } + } + + m_infoSystemLoaded = true; + disconnect( Tomahawk::InfoSystem::InfoSystem::instance(), SIGNAL( info( Tomahawk::InfoSystem::InfoRequestData, QVariant ) ), + this, SLOT( infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData, QVariant ) ) ); + + if ( m_queries.isEmpty() && m_mode == Mixed ) + { + DatabaseCommand_AllTracks* cmd = new DatabaseCommand_AllTracks( m_collection ); + cmd->setAlbum( m_album ); + //this takes discnumber into account as well + cmd->setSortOrder( DatabaseCommand_AllTracks::AlbumPosition ); + + connect( cmd, SIGNAL( tracks( QList, QVariant ) ), + SLOT( onTracksLoaded( QList ) ) ); + + Database::instance()->enqueue( QSharedPointer( cmd ) ); + } + else + { + emit tracksLoaded( m_mode, m_collection ); + } +} + + +void +AlbumPlaylistInterface::onTracksLoaded( const QList< query_ptr >& tracks ) +{ + m_databaseLoaded = true; + + if ( m_collection.isNull() ) + m_queries << filterTracks( tracks ); + else + m_queries << tracks; + + emit tracksLoaded( m_mode, m_collection ); } diff --git a/src/libtomahawk/AlbumPlaylistInterface.h b/src/libtomahawk/AlbumPlaylistInterface.h index 89c1e347b..14d6efd19 100644 --- a/src/libtomahawk/AlbumPlaylistInterface.h +++ b/src/libtomahawk/AlbumPlaylistInterface.h @@ -26,6 +26,7 @@ #include "Album.h" #include "Typedefs.h" #include "PlaylistInterface.h" +#include "infosystem/InfoSystem.h" #include "DllMacro.h" namespace Tomahawk @@ -36,7 +37,7 @@ class DLLEXPORT AlbumPlaylistInterface : public Tomahawk::PlaylistInterface Q_OBJECT public: - AlbumPlaylistInterface( Tomahawk::Album *album ); + AlbumPlaylistInterface( Tomahawk::Album* album, Tomahawk::ModelMode mode, const Tomahawk::collection_ptr& collection ); virtual ~AlbumPlaylistInterface(); QList tracks(); @@ -49,32 +50,32 @@ public: virtual bool hasNextItem(); virtual Tomahawk::result_ptr currentItem() const; - virtual PlaylistInterface::RepeatMode repeatMode() const { return PlaylistInterface::NoRepeat; } + virtual PlaylistModes::RepeatMode repeatMode() const { return PlaylistModes::NoRepeat; } virtual bool shuffled() const { return false; } - virtual void setRepeatMode( PlaylistInterface::RepeatMode ) {} + virtual void setRepeatMode( PlaylistModes::RepeatMode ) {} virtual void setShuffled( bool ) {} virtual void setFilter( const QString& /*pattern*/ ) {} - virtual void addQueries( const QList& tracks ); - signals: - void repeatModeChanged( Tomahawk::PlaylistInterface::RepeatMode mode ); - void shuffleModeChanged( bool enabled ); + void tracksLoaded( Tomahawk::ModelMode mode, const Tomahawk::collection_ptr& collection ); - void trackCountChanged( unsigned int tracks ); - void sourceTrackCountChanged( unsigned int tracks ); - - void nextTrackReady(); +private slots: + void onTracksLoaded( const QList< Tomahawk::query_ptr >& tracks ); + void infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData requestData, QVariant output ); private: - AlbumPlaylistInterface(); - QList< Tomahawk::query_ptr > m_queries; result_ptr m_currentItem; unsigned int m_currentTrack; + bool m_infoSystemLoaded; + bool m_databaseLoaded; + + Tomahawk::ModelMode m_mode; + Tomahawk::collection_ptr m_collection; + QWeakPointer< Tomahawk::Album > m_album; }; diff --git a/src/libtomahawk/Artist.cpp b/src/libtomahawk/Artist.cpp index b0d79c1e8..ba44cda76 100644 --- a/src/libtomahawk/Artist.cpp +++ b/src/libtomahawk/Artist.cpp @@ -25,6 +25,7 @@ #include "database/DatabaseImpl.h" #include "database/DatabaseCommand_AllAlbums.h" #include "database/DatabaseCommand_TrackStats.h" +#include "Source.h" #include "utils/Logger.h" @@ -81,9 +82,10 @@ Artist::Artist( unsigned int id, const QString& name ) : QObject() , m_id( id ) , m_name( name ) - , m_infoLoaded( false ) - , m_infoLoading( false ) + , m_coverLoaded( false ) + , m_coverLoading( false ) , m_simArtistsLoaded( false ) + , m_biographyLoaded( false ) , m_infoJobs( 0 ) #ifndef ENABLE_HEADLESS , m_cover( 0 ) @@ -94,13 +96,9 @@ Artist::Artist( unsigned int id, const QString& name ) void -Artist::onTracksAdded( const QList& tracks ) +Artist::onTracksLoaded( Tomahawk::ModelMode mode, const Tomahawk::collection_ptr& collection ) { - Tomahawk::ArtistPlaylistInterface* api = dynamic_cast< Tomahawk::ArtistPlaylistInterface* >( playlistInterface().data() ); - if ( api ) - api->addQueries( tracks ); - - emit tracksAdded( tracks ); + emit tracksAdded( playlistInterface( mode, collection )->tracks(), mode, collection ); } @@ -114,9 +112,6 @@ Artist::albums( ModelMode mode, const Tomahawk::collection_ptr& collection ) con if ( !collection.isNull() ) dbLoaded = false; - m_uuid = uuid(); - tDebug() << Q_FUNC_INFO << mode; - if ( ( mode == DatabaseMode || mode == Mixed ) && !dbLoaded ) { DatabaseCommand_AllAlbums* cmd = new DatabaseCommand_AllAlbums( collection, artist ); @@ -134,10 +129,10 @@ Artist::albums( ModelMode mode, const Tomahawk::collection_ptr& collection ) con artistInfo["artist"] = name(); Tomahawk::InfoSystem::InfoRequestData requestData; - requestData.caller = m_uuid; + requestData.caller = infoid(); requestData.input = QVariant::fromValue< Tomahawk::InfoSystem::InfoStringHash >( artistInfo ); requestData.type = Tomahawk::InfoSystem::InfoArtistReleases; - + connect( Tomahawk::InfoSystem::InfoSystem::instance(), SIGNAL( info( Tomahawk::InfoSystem::InfoRequestData, QVariant ) ), SLOT( infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData, QVariant ) ), Qt::UniqueConnection ); @@ -174,13 +169,13 @@ Artist::similarArtists() const artistInfo["artist"] = name(); Tomahawk::InfoSystem::InfoRequestData requestData; - requestData.caller = m_uuid; + requestData.caller = infoid(); requestData.customData = QVariantMap(); requestData.input = QVariant::fromValue< Tomahawk::InfoSystem::InfoStringHash >( artistInfo ); requestData.type = Tomahawk::InfoSystem::InfoArtistSimilars; requestData.requestId = TomahawkUtils::infosystemRequestId(); - + connect( Tomahawk::InfoSystem::InfoSystem::instance(), SIGNAL( info( Tomahawk::InfoSystem::InfoRequestData, QVariant ) ), SLOT( infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData, QVariant ) ), Qt::UniqueConnection ); @@ -192,11 +187,40 @@ Artist::similarArtists() const m_infoJobs++; Tomahawk::InfoSystem::InfoSystem::instance()->getInfo( requestData ); } - + return m_similarArtists; } +QString +Artist::biography() const +{ + if ( !m_biographyLoaded ) + { + Tomahawk::InfoSystem::InfoRequestData requestData; + requestData.caller = infoid(); + requestData.customData = QVariantMap(); + + requestData.input = name(); + requestData.type = Tomahawk::InfoSystem::InfoArtistBiography; + requestData.requestId = TomahawkUtils::infosystemRequestId(); + + connect( Tomahawk::InfoSystem::InfoSystem::instance(), + SIGNAL( info( Tomahawk::InfoSystem::InfoRequestData, QVariant ) ), + SLOT( infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData, QVariant ) ), Qt::UniqueConnection ); + + connect( Tomahawk::InfoSystem::InfoSystem::instance(), + SIGNAL( finished( QString ) ), + SLOT( infoSystemFinished( QString ) ), Qt::UniqueConnection ); + + m_infoJobs++; + Tomahawk::InfoSystem::InfoSystem::instance()->getInfo( requestData ); + } + + return m_biography; +} + + void Artist::loadStats() { @@ -219,7 +243,7 @@ Artist::playbackHistory( const Tomahawk::source_ptr& source ) const history << log; } } - + return history; } @@ -241,7 +265,7 @@ Artist::playbackCount( const source_ptr& source ) if ( source.isNull() || log.source == source ) count++; } - + return count; } @@ -254,7 +278,7 @@ Artist::onAlbumsFound( const QList< album_ptr >& albums, const QVariant& data ) m_databaseAlbums << albums; m_albumsLoaded.insert( DatabaseMode, true ); } - + emit albumsAdded( albums, DatabaseMode ); } @@ -262,7 +286,7 @@ Artist::onAlbumsFound( const QList< album_ptr >& albums, const QVariant& data ) void Artist::infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData requestData, QVariant output ) { - if ( requestData.caller != m_uuid ) + if ( requestData.caller != infoid() ) return; QVariantMap returnedData = output.value< QVariantMap >(); @@ -277,7 +301,6 @@ Artist::infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData requestData, QVari QList< album_ptr > albums; foreach ( const QString& albumName, albumNames ) { - tDebug() << Q_FUNC_INFO << albumName; Tomahawk::album_ptr album = Tomahawk::Album::get( m_ownRef.toStrongRef(), albumName, false ); m_officialAlbums << album; albums << album; @@ -289,7 +312,7 @@ Artist::infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData requestData, QVari break; } - + case Tomahawk::InfoSystem::InfoArtistImages: { if ( !output.isNull() && output.isValid() ) @@ -298,11 +321,11 @@ Artist::infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData requestData, QVari if ( ba.length() ) { m_coverBuffer = ba; - m_infoLoaded = true; + m_coverLoaded = true; emit coverChanged(); } } - + break; } @@ -313,13 +336,29 @@ Artist::infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData requestData, QVari { m_similarArtists << Artist::get( artist ); } - + m_simArtistsLoaded = true; emit similarArtistsLoaded(); break; } + case InfoSystem::InfoArtistBiography: + { + QVariantMap bmap = output.toMap(); + + foreach ( const QString& source, bmap.keys() ) + { + if ( source == "last.fm" ) + m_biography = bmap[ source ].toHash()[ "text" ].toString(); + } + + m_biographyLoaded = true; + emit biographyLoaded(); + + break; + } + default: Q_ASSERT( false ); } @@ -331,7 +370,7 @@ Artist::infoSystemFinished( QString target ) { Q_UNUSED( target ); - if ( target != m_uuid ) + if ( target != infoid() ) return; if ( --m_infoJobs == 0 ) @@ -351,17 +390,16 @@ Artist::infoSystemFinished( QString target ) QPixmap Artist::cover( const QSize& size, bool forceLoad ) const { - if ( !m_infoLoaded && !m_infoLoading ) + if ( !m_coverLoaded && !m_coverLoading ) { if ( !forceLoad ) return QPixmap(); - m_uuid = uuid(); Tomahawk::InfoSystem::InfoStringHash trackInfo; trackInfo["artist"] = name(); Tomahawk::InfoSystem::InfoRequestData requestData; - requestData.caller = m_uuid; + requestData.caller = infoid(); requestData.type = Tomahawk::InfoSystem::InfoArtistImages; requestData.input = QVariant::fromValue< Tomahawk::InfoSystem::InfoStringHash >( trackInfo ); requestData.customData = QVariantMap(); @@ -377,7 +415,7 @@ Artist::cover( const QSize& size, bool forceLoad ) const m_infoJobs++; Tomahawk::InfoSystem::InfoSystem::instance()->getInfo( requestData ); - m_infoLoading = true; + m_coverLoading = true; } if ( !m_cover && !m_coverBuffer.isEmpty() ) @@ -409,12 +447,35 @@ Artist::cover( const QSize& size, bool forceLoad ) const Tomahawk::playlistinterface_ptr -Artist::playlistInterface() +Artist::playlistInterface( ModelMode mode, const Tomahawk::collection_ptr& collection ) { - if ( m_playlistInterface.isNull() ) + playlistinterface_ptr pli = m_playlistInterface[ mode ][ collection ]; + + if ( pli.isNull() ) { - m_playlistInterface = Tomahawk::playlistinterface_ptr( new Tomahawk::ArtistPlaylistInterface( this ) ); + pli = Tomahawk::playlistinterface_ptr( new Tomahawk::ArtistPlaylistInterface( this, mode, collection ) ); + connect( pli.data(), SIGNAL( tracksLoaded( Tomahawk::ModelMode, Tomahawk::collection_ptr ) ), + SLOT( onTracksLoaded( Tomahawk::ModelMode, Tomahawk::collection_ptr ) ) ); + + m_playlistInterface[ mode ][ collection ] = pli; } - return m_playlistInterface; + return pli; +} + + +QList +Artist::tracks( ModelMode mode, const Tomahawk::collection_ptr& collection ) +{ + return playlistInterface( mode, collection )->tracks(); +} + + +QString +Artist::infoid() const +{ + if ( m_uuid.isEmpty() ) + m_uuid = uuid(); + + return m_uuid; } diff --git a/src/libtomahawk/Artist.h b/src/libtomahawk/Artist.h index eee1da918..3eef077a1 100644 --- a/src/libtomahawk/Artist.h +++ b/src/libtomahawk/Artist.h @@ -23,7 +23,6 @@ #include "config.h" #include -#include #ifndef ENABLE_HEADLESS #include #endif @@ -31,7 +30,6 @@ #include "Typedefs.h" #include "DllMacro.h" #include "Query.h" -#include "infosystem/InfoSystem.h" namespace Tomahawk { @@ -51,19 +49,23 @@ public: QString name() const { return m_name; } QString sortname() const { return m_sortname; } - bool infoLoaded() const { return m_infoLoaded; } - QList albums( ModelMode mode = Mixed, const Tomahawk::collection_ptr& collection = Tomahawk::collection_ptr() ) const; QList similarArtists() const; + QList tracks( ModelMode mode = Mixed, const Tomahawk::collection_ptr& collection = Tomahawk::collection_ptr() ); + Tomahawk::playlistinterface_ptr playlistInterface( ModelMode mode, const Tomahawk::collection_ptr& collection = Tomahawk::collection_ptr() ); + void loadStats(); QList< Tomahawk::PlaybackLog > playbackHistory( const Tomahawk::source_ptr& source = Tomahawk::source_ptr() ) const; void setPlaybackHistory( const QList< Tomahawk::PlaybackLog >& playbackData ); unsigned int playbackCount( const Tomahawk::source_ptr& source = Tomahawk::source_ptr() ); + + QString biography() const; #ifndef ENABLE_HEADLESS QPixmap cover( const QSize& size, bool forceLoad = true ) const; #endif + bool coverLoaded() const { return m_coverLoaded; } Tomahawk::playlistinterface_ptr playlistInterface(); @@ -71,32 +73,35 @@ public: void setWeakRef( QWeakPointer< Tomahawk::Artist > weakRef ) { m_ownRef = weakRef; } signals: - void tracksAdded( const QList& tracks ); + void tracksAdded( const QList& tracks, Tomahawk::ModelMode mode, const Tomahawk::collection_ptr& collection ); void albumsAdded( const QList& albums, Tomahawk::ModelMode mode ); void updated(); void coverChanged(); void similarArtistsLoaded(); + void biographyLoaded(); void statsLoaded(); private slots: - void onTracksAdded( const QList& tracks ); + void onTracksLoaded(Tomahawk::ModelMode mode, const Tomahawk::collection_ptr& collection ); void onAlbumsFound( const QList& albums, const QVariant& data ); void infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData requestData, QVariant output ); void infoSystemFinished( QString target ); private: - Q_DISABLE_COPY( Artist ) + Artist(); + QString infoid() const; unsigned int m_id; QString m_name; QString m_sortname; - bool m_infoLoaded; - mutable bool m_infoLoading; + bool m_coverLoaded; + mutable bool m_coverLoading; QHash m_albumsLoaded; bool m_simArtistsLoaded; + bool m_biographyLoaded; mutable QString m_uuid; mutable int m_infoJobs; @@ -104,7 +109,8 @@ private: QList m_databaseAlbums; QList m_officialAlbums; QList m_similarArtists; - + QString m_biography; + bool m_playbackHistoryLoaded; QList< PlaybackLog > m_playbackHistory; @@ -114,11 +120,13 @@ private: mutable QHash< int, QPixmap > m_coverCache; #endif - Tomahawk::playlistinterface_ptr m_playlistInterface; + QHash< Tomahawk::ModelMode, QHash< Tomahawk::collection_ptr, Tomahawk::playlistinterface_ptr > > m_playlistInterface; QWeakPointer< Tomahawk::Artist > m_ownRef; }; } // ns +Q_DECLARE_METATYPE( Tomahawk::artist_ptr ) + #endif diff --git a/src/libtomahawk/ArtistPlaylistInterface.cpp b/src/libtomahawk/ArtistPlaylistInterface.cpp index 4568076bd..c3c5a1164 100644 --- a/src/libtomahawk/ArtistPlaylistInterface.cpp +++ b/src/libtomahawk/ArtistPlaylistInterface.cpp @@ -24,16 +24,22 @@ #include "Query.h" #include "database/Database.h" #include "database/DatabaseCommand_AllTracks.h" +#include "Source.h" +#include "Pipeline.h" #include "utils/Logger.h" using namespace Tomahawk; -ArtistPlaylistInterface::ArtistPlaylistInterface( Tomahawk::Artist *artist ) +ArtistPlaylistInterface::ArtistPlaylistInterface( Tomahawk::Artist* artist, Tomahawk::ModelMode mode, const Tomahawk::collection_ptr& collection ) : Tomahawk::PlaylistInterface() , m_currentItem( 0 ) , m_currentTrack( 0 ) + , m_infoSystemLoaded( false ) + , m_databaseLoaded( false ) + , m_mode( mode ) + , m_collection( collection ) , m_artist( QWeakPointer< Tomahawk::Artist >( artist ) ) { } @@ -57,6 +63,9 @@ ArtistPlaylistInterface::siblingItem( int itemsAway ) if ( p >= m_queries.count() ) return Tomahawk::result_ptr(); + if ( !m_queries.at( p )->numResults() ) + return siblingItem( itemsAway + 1 ); + m_currentTrack = p; m_currentItem = m_queries.at( p )->results().first(); return m_currentItem; @@ -87,14 +96,34 @@ ArtistPlaylistInterface::tracks() { if ( m_queries.isEmpty() && m_artist ) { - DatabaseCommand_AllTracks* cmd = new DatabaseCommand_AllTracks(); - cmd->setArtist( m_artist ); - cmd->setSortOrder( DatabaseCommand_AllTracks::Album ); + if ( ( m_mode == Mixed || m_mode == InfoSystemMode ) && !m_infoSystemLoaded ) + { + Tomahawk::InfoSystem::InfoStringHash artistInfo; + artistInfo["artist"] = m_artist.data()->name(); - connect( cmd, SIGNAL( tracks( QList, QVariant ) ), - m_artist.data(), SLOT( onTracksAdded( QList ) ) ); + Tomahawk::InfoSystem::InfoRequestData requestData; + requestData.caller = id(); + requestData.input = QVariant::fromValue< Tomahawk::InfoSystem::InfoStringHash >( artistInfo ); + requestData.type = Tomahawk::InfoSystem::InfoArtistSongs; + requestData.timeoutMillis = 0; + requestData.allSources = true; + Tomahawk::InfoSystem::InfoSystem::instance()->getInfo( requestData ); - Database::instance()->enqueue( QSharedPointer< DatabaseCommand >( cmd ) ); + connect( Tomahawk::InfoSystem::InfoSystem::instance(), + SIGNAL( info( Tomahawk::InfoSystem::InfoRequestData, QVariant ) ), + SLOT( infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData, QVariant ) ) ); + } + else if ( m_mode == DatabaseMode && !m_databaseLoaded ) + { + DatabaseCommand_AllTracks* cmd = new DatabaseCommand_AllTracks( m_collection ); + cmd->setArtist( m_artist ); + cmd->setSortOrder( DatabaseCommand_AllTracks::AlbumPosition ); + + connect( cmd, SIGNAL( tracks( QList, QVariant ) ), + SLOT( onTracksLoaded( QList ) ) ); + + Database::instance()->enqueue( QSharedPointer( cmd ) ); + } } return m_queries; @@ -102,7 +131,83 @@ ArtistPlaylistInterface::tracks() void -ArtistPlaylistInterface::addQueries( const QList< query_ptr >& tracks ) +ArtistPlaylistInterface::infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData requestData, QVariant output ) { - m_queries << tracks; + if ( requestData.caller != id() ) + return; + + switch ( requestData.type ) + { + case Tomahawk::InfoSystem::InfoArtistSongs: + { + QVariantMap returnedData = output.value< QVariantMap >(); + if ( !returnedData.isEmpty() ) + { + Tomahawk::InfoSystem::InfoStringHash inputInfo; + inputInfo = requestData.input.value< Tomahawk::InfoSystem::InfoStringHash >(); + + QStringList tracks = returnedData[ "tracks" ].toStringList(); + QList ql; + + //TODO: Figure out how to do this with a multi-disk album without breaking the + // current behaviour. I just know too little about InfoSystem to deal with + // it right now, I've only taken the liberty of adding Query::setDiscNumber + // which should make this easier. --Teo 11/2011 + unsigned int trackNo = 1; + + foreach ( const QString& trackName, tracks ) + { + query_ptr query = Query::get( inputInfo[ "artist" ], trackName, inputInfo[ "album" ] ); + query->setAlbumPos( trackNo++ ); + ql << query; + } + Pipeline::instance()->resolve( ql ); + + m_queries << ql; + } + + break; + } + + default: + { + Q_ASSERT( false ); + break; + } + } + + m_infoSystemLoaded = true; + disconnect( Tomahawk::InfoSystem::InfoSystem::instance(), SIGNAL( info( Tomahawk::InfoSystem::InfoRequestData, QVariant ) ), + this, SLOT( infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData, QVariant ) ) ); + + if ( m_queries.isEmpty() && m_mode == Mixed ) + { + DatabaseCommand_AllTracks* cmd = new DatabaseCommand_AllTracks( m_collection ); + cmd->setArtist( m_artist ); + //this takes discnumber into account as well + cmd->setSortOrder( DatabaseCommand_AllTracks::AlbumPosition ); + + connect( cmd, SIGNAL( tracks( QList, QVariant ) ), + SLOT( onTracksLoaded( QList ) ) ); + + Database::instance()->enqueue( QSharedPointer( cmd ) ); + } + else + { + emit tracksLoaded( m_mode, m_collection ); + } +} + + +void +ArtistPlaylistInterface::onTracksLoaded( const QList< query_ptr >& tracks ) +{ + m_databaseLoaded = true; + + if ( m_collection.isNull() ) + m_queries << filterTracks( tracks ); + else + m_queries << tracks; + + emit tracksLoaded( m_mode, m_collection ); } diff --git a/src/libtomahawk/ArtistPlaylistInterface.h b/src/libtomahawk/ArtistPlaylistInterface.h index c575de55c..03b1b36bd 100644 --- a/src/libtomahawk/ArtistPlaylistInterface.h +++ b/src/libtomahawk/ArtistPlaylistInterface.h @@ -36,12 +36,12 @@ class DLLEXPORT ArtistPlaylistInterface : public Tomahawk::PlaylistInterface Q_OBJECT public: - ArtistPlaylistInterface( Tomahawk::Artist *artist ); + ArtistPlaylistInterface( Tomahawk::Artist* artist, Tomahawk::ModelMode mode, const Tomahawk::collection_ptr& collection ); virtual ~ArtistPlaylistInterface(); virtual QList tracks(); - virtual int trackCount() const { return 0; } + virtual int trackCount() const { return m_queries.count(); } virtual int unfilteredTrackCount() const { return m_queries.count(); } virtual Tomahawk::result_ptr siblingItem( int itemsAway ); @@ -49,15 +49,20 @@ public: virtual bool hasNextItem(); virtual Tomahawk::result_ptr currentItem() const; - virtual PlaylistInterface::RepeatMode repeatMode() const { return PlaylistInterface::NoRepeat; } + virtual PlaylistModes::RepeatMode repeatMode() const { return PlaylistModes::NoRepeat; } virtual bool shuffled() const { return false; } - virtual void setRepeatMode( PlaylistInterface::RepeatMode ) {} + virtual void setRepeatMode( PlaylistModes::RepeatMode ) {} virtual void setShuffled( bool ) {} virtual void setFilter( const QString& /*pattern*/ ) {} - virtual void addQueries( const QList& tracks ); +signals: + void tracksLoaded( Tomahawk::ModelMode mode, const Tomahawk::collection_ptr& collection ); + +private slots: + void onTracksLoaded( const QList< Tomahawk::query_ptr >& tracks ); + void infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData requestData, QVariant output ); private: Q_DISABLE_COPY( ArtistPlaylistInterface ) @@ -66,6 +71,12 @@ private: result_ptr m_currentItem; unsigned int m_currentTrack; + bool m_infoSystemLoaded; + bool m_databaseLoaded; + + Tomahawk::ModelMode m_mode; + Tomahawk::collection_ptr m_collection; + QWeakPointer< Tomahawk::Artist > m_artist; }; diff --git a/src/libtomahawk/AtticaManager.cpp b/src/libtomahawk/AtticaManager.cpp index 0dd491e8f..f0782b4d3 100644 --- a/src/libtomahawk/AtticaManager.cpp +++ b/src/libtomahawk/AtticaManager.cpp @@ -21,11 +21,12 @@ #include "utils/TomahawkUtils.h" #include "TomahawkSettingsGui.h" #include "Pipeline.h" +#include "Source.h" +#include "config.h" #include -#include -#include +#include #include #include #include @@ -34,19 +35,40 @@ #include "utils/Logger.h" #include "accounts/ResolverAccount.h" #include "accounts/AccountManager.h" +#include "utils/BinaryInstallerHelper.h" +#include "utils/Closure.h" using namespace Attica; AtticaManager* AtticaManager::s_instance = 0; +// Sort binary resolvers above script resolvers, and script resolvers by download count +bool +resolverSort( const Attica::Content& first, const Attica::Content& second ) +{ + if ( !first.attribute( "typeid" ).isEmpty() && second.attribute( "typeid" ).isEmpty() ) + return true; + + return first.downloads() > second.downloads(); +} + + AtticaManager::AtticaManager( QObject* parent ) : QObject( parent ) + , m_resolverJobsLoaded( 0 ) { connect( &m_manager, SIGNAL( providerAdded( Attica::Provider ) ), this, SLOT( providerAdded( Attica::Provider ) ) ); // resolvers - m_manager.addProviderFile( QUrl( "http://bakery.tomahawk-player.org/resolvers/providers.xml" ) ); +// m_manager.addProviderFile( QUrl( "http://bakery.tomahawk-player.org/resolvers/providers.xml" ) ); + + const QString url = QString( "http://bakery.tomahawk-player.org/resolvers/providers.xml?version=%1" ).arg( TomahawkUtils::appFriendlyVersion() ); + QNetworkReply* reply = TomahawkUtils::nam()->get( QNetworkRequest( QUrl( url ) ) ); + NewClosure( reply, SIGNAL( finished() ), this, SLOT( providerFetched( QNetworkReply* ) ), reply ); + connect( reply, SIGNAL( error( QNetworkReply::NetworkError ) ), this, SLOT( providerError( QNetworkReply::NetworkError ) ) ); + +// m_manager.addProviderFile( QUrl( "http://lycophron/resolvers/providers.xml" ) ); qRegisterMetaType< Attica::Content >( "Attica::Content" ); } @@ -55,6 +77,15 @@ AtticaManager::AtticaManager( QObject* parent ) AtticaManager::~AtticaManager() { savePixmapsToCache(); + + + foreach( const QString& id, m_resolverStates.keys() ) + { + if ( !m_resolverStates[ id ].pixmap ) + continue; + + delete m_resolverStates[ id ].pixmap; + } } @@ -215,11 +246,7 @@ AtticaManager::userHasRated( const Content& c ) const bool AtticaManager::hasCustomAccountForAttica( const QString &id ) const { - // Only last.fm at the moment contains a custom account - if ( id == "lastfm" ) - return true; - - return false; + return m_customAccounts.keys().contains( id ); } @@ -244,15 +271,57 @@ AtticaManager::resolverData(const QString &atticaId) const } +void +AtticaManager::providerError( QNetworkReply::NetworkError err ) +{ + // So those who care know + emit resolversLoaded( Content::List() ); +} + + +void +AtticaManager::providerFetched( QNetworkReply* reply ) +{ + Q_ASSERT( reply ); + if ( !reply ) + return; + + m_manager.addProviderFromXml( reply->readAll() ); +} + + void AtticaManager::providerAdded( const Provider& provider ) { if ( provider.name() == "Tomahawk Resolvers" ) { m_resolverProvider = provider; + m_resolvers.clear(); + + m_resolverStates = TomahawkSettingsGui::instanceGui()->atticaResolverStates(); + + ListJob* job = m_resolverProvider.requestCategories(); + connect( job, SIGNAL( finished( Attica::BaseJob* ) ), this, SLOT( categoriesReturned( Attica::BaseJob* ) ) ); + job->start(); + } +} + + +void +AtticaManager::categoriesReturned( BaseJob* j ) +{ + ListJob< Category >* job = static_cast< ListJob< Category >* >( j ); + + Category::List categories = job->itemList(); + foreach ( const Category& category, categories ) + { + ListJob< Content >* job = m_resolverProvider.searchContents( Category::List() << category, QString(), Provider::Downloads, 0, 50 ); + + if ( category.name() == "Resolver" ) + connect( job, SIGNAL( finished( Attica::BaseJob* ) ), this, SLOT( resolversList( Attica::BaseJob* ) ) ); + else if ( category.name() == "BinaryResolver" ) + connect( job, SIGNAL( finished( Attica::BaseJob* ) ), this, SLOT( binaryResolversList( Attica::BaseJob* ) ) ); - ListJob< Content >* job = m_resolverProvider.searchContents( Category::List(), QString(), Provider::Downloads, 0, 30 ); - connect( job, SIGNAL( finished( Attica::BaseJob* ) ), this, SLOT( resolversList( Attica::BaseJob* ) ) ); job->start(); } } @@ -263,8 +332,7 @@ AtticaManager::resolversList( BaseJob* j ) { ListJob< Content >* job = static_cast< ListJob< Content >* >( j ); - m_resolvers = job->itemList(); - m_resolverStates = TomahawkSettingsGui::instanceGui()->atticaResolverStates(); + m_resolvers.append( job->itemList() ); // Sanity check. if any resolvers are installed that don't exist on the hd, remove them. foreach ( const QString& rId, m_resolverStates.keys() ) @@ -272,6 +340,9 @@ AtticaManager::resolversList( BaseJob* j ) if ( m_resolverStates[ rId ].state == Installed || m_resolverStates[ rId ].state == NeedsUpgrade ) { + if ( m_resolverStates[ rId ].binary ) + continue; + // Guess location on disk QDir dir( QString( "%1/atticaresolvers/%2" ).arg( TomahawkUtils::appDataDir().absolutePath() ).arg( rId ) ); if ( !dir.exists() ) @@ -303,7 +374,65 @@ AtticaManager::resolversList( BaseJob* j ) syncServerData(); - emit resolversLoaded( m_resolvers ); + if ( ++m_resolverJobsLoaded == 2 ) + { + qSort( m_resolvers.begin(), m_resolvers.end(), resolverSort ); + emit resolversLoaded( m_resolvers ); + } +} + + +void +AtticaManager::binaryResolversList( BaseJob* j ) +{ + ListJob< Content >* job = static_cast< ListJob< Content >* >( j ); + + Content::List binaryResolvers = job->itemList(); + + // NOTE: No binary support for linux distros + QString platform; +#if defined(Q_OS_MAC) + platform = "osx"; +#elif defined(Q_OS_WIN) + platform = "win"; +#elif defined(Q_OS_LINUX) && defined(__GNUC__) && defined(__x86_64__) + platform = "linux-x64"; +#elif defined(Q_OS_LINUX) // Horrible assumption here... + platform = "linux-x86"; +#endif + + // Override if no binary resolvers were requested +#ifndef WITH_BINARY_ATTICA + platform = QString(); +#endif + + foreach ( const Content& c, binaryResolvers ) + { + if ( !c.attribute( "typeid" ).isEmpty() && c.attribute( "typeid" ) == platform ) + { + // We have a binary resolver for this platform + qDebug() << "WE GOT A BINARY RESOLVER:" << c.id() << c.name() << c.attribute( "signature" ); + m_resolvers.append( c ); + if ( !m_resolverStates.contains( c.id() ) ) + { + Resolver r; + r.binary = true; + m_resolverStates.insert( c.id(), r ); + } + else if ( m_resolverStates[ c.id() ].binary != true ) + { // HACK workaround... why is this not set in the first place sometimes? Migration issue? + m_resolverStates[ c.id() ].binary = true; + } + + + } + } + + if ( ++m_resolverJobsLoaded == 2 ) + { + qSort( m_resolvers.begin(), m_resolvers.end(), resolverSort ); + emit resolversLoaded( m_resolvers ); + } } @@ -367,10 +496,25 @@ AtticaManager::syncServerData() void -AtticaManager::installResolver( const Content& resolver, bool autoCreateAccount ) +AtticaManager::installResolver( const Content& resolver, bool autoCreate ) +{ + doInstallResolver( resolver, autoCreate, 0 ); +} + + +void +AtticaManager::installResolverWithHandler( const Content& resolver, Tomahawk::Accounts::AtticaResolverAccount* handler ) +{ + doInstallResolver( resolver, false, handler ); +} + + +void AtticaManager::doInstallResolver( const Content& resolver, bool autoCreate, Tomahawk::Accounts::AtticaResolverAccount* handler ) { Q_ASSERT( !resolver.id().isNull() ); + emit startedInstalling( resolver.id() ); + if ( m_resolverStates[ resolver.id() ].state != Upgrading ) m_resolverStates[ resolver.id() ].state = Installing; @@ -381,7 +525,9 @@ AtticaManager::installResolver( const Content& resolver, bool autoCreateAccount ItemJob< DownloadItem >* job = m_resolverProvider.downloadLink( resolver.id() ); connect( job, SIGNAL( finished( Attica::BaseJob* ) ), this, SLOT( resolverDownloadFinished( Attica::BaseJob* ) ) ); job->setProperty( "resolverId", resolver.id() ); - job->setProperty( "createAccount", autoCreateAccount ); + job->setProperty( "createAccount", autoCreate ); + job->setProperty( "handler", QVariant::fromValue< QObject* >( handler ) ); + job->setProperty( "binarySignature", resolver.attribute("signature")); job->start(); } @@ -418,6 +564,8 @@ AtticaManager::resolverDownloadFinished ( BaseJob* j ) connect( reply, SIGNAL( finished() ), this, SLOT( payloadFetched() ) ); reply->setProperty( "resolverId", job->property( "resolverId" ) ); reply->setProperty( "createAccount", job->property( "createAccount" ) ); + reply->setProperty( "handler", job->property( "handler" ) ); + reply->setProperty( "binarySignature", job->property( "binarySignature" ) ); } else { @@ -432,114 +580,88 @@ AtticaManager::payloadFetched() QNetworkReply* reply = qobject_cast< QNetworkReply* >( sender() ); Q_ASSERT( reply ); + bool installedSuccessfully = false; + const QString resolverId = reply->property( "resolverId" ).toString(); + // we got a zip file, save it to a temporary file, then unzip it to our destination data dir if ( reply->error() == QNetworkReply::NoError ) { - QTemporaryFile f( QDir::tempPath() + QDir::separator() + "tomahawkattica_XXXXXX.zip" ); - if ( !f.open() ) + QTemporaryFile* f = new QTemporaryFile( QDir::tempPath() + QDir::separator() + "tomahawkattica_XXXXXX.zip" ); + if ( !f->open() ) { - tLog() << "Failed to write zip file to temp file:" << f.fileName(); + tLog() << "Failed to write zip file to temp file:" << f->fileName(); return; } - f.write( reply->readAll() ); - f.close(); + f->write( reply->readAll() ); + f->close(); - QString resolverId = reply->property( "resolverId" ).toString(); - QDir dir( extractPayload( f.fileName(), resolverId ) ); - QString resolverPath = dir.absoluteFilePath( m_resolverStates[ resolverId ].scriptPath ); - - if ( !resolverPath.isEmpty() ) + if ( m_resolverStates[ resolverId ].binary ) { - // update with absolute, not relative, path - m_resolverStates[ resolverId ].scriptPath = resolverPath; - - if ( reply->property( "createAccount" ).toBool() ) + // First ensure the signature matches. If we can't verify it, abort! + const QString signature = reply->property( "binarySignature" ).toString(); + // Must have a signature for binary resolvers... + Q_ASSERT( !signature.isEmpty() ); + if ( signature.isEmpty() ) + return; + if ( !TomahawkUtils::verifyFile( f->fileName(), signature ) ) { - // Do the install / add to tomahawk - Tomahawk::Accounts::Account* resolver = Tomahawk::Accounts::ResolverAccountFactory::createFromPath( resolverPath, "resolveraccount", true ); - Tomahawk::Accounts::AccountManager::instance()->addAccount( resolver ); - TomahawkSettings::instance()->addAccount( resolver->accountId() ); + qWarning() << "FILE SIGNATURE FAILED FOR BINARY RESOLVER! WARNING! :" << f->fileName() << signature; + } + else + { + TomahawkUtils::extractBinaryResolver( f->fileName(), new BinaryInstallerHelper( f, resolverId, reply->property( "createAccount" ).toBool(), this ) ); + // Don't emit success or failed yet, helper will do that. + return; } - - m_resolverStates[ resolverId ].state = Installed; - TomahawkSettingsGui::instanceGui()->setAtticaResolverStates( m_resolverStates ); - emit resolverInstalled( resolverId ); - emit resolverStateChanged( resolverId ); } + else + { + QDir dir( TomahawkUtils::extractScriptPayload( f->fileName(), resolverId ) ); + QString resolverPath = dir.absoluteFilePath( m_resolverStates[ resolverId ].scriptPath ); + + if ( !resolverPath.isEmpty() ) + { + // update with absolute, not relative, path + m_resolverStates[ resolverId ].scriptPath = resolverPath; + + Tomahawk::Accounts::AtticaResolverAccount* handlerAccount = qobject_cast< Tomahawk::Accounts::AtticaResolverAccount* >( reply->property( "handler" ).value< QObject* >() ); + const bool createAccount = reply->property( "createAccount" ).toBool(); + if ( handlerAccount ) + { + handlerAccount->setPath( resolverPath ); + Tomahawk::Accounts::AccountManager::instance()->enableAccount( handlerAccount ); + } + else if ( createAccount ) + { + // Do the install / add to tomahawk + Tomahawk::Accounts::Account* resolver = Tomahawk::Accounts::ResolverAccountFactory::createFromPath( resolverPath, "resolveraccount", true ); + Tomahawk::Accounts::AccountManager::instance()->addAccount( resolver ); + TomahawkSettings::instance()->addAccount( resolver->accountId() ); + } + + installedSuccessfully = true; + } + } + + delete f; } else { tLog() << "Failed to download attica payload...:" << reply->errorString(); } -} -QString -AtticaManager::extractPayload( const QString& filename, const QString& resolverId ) const -{ - // uses QuaZip to extract the temporary zip file to the user's tomahawk data/resolvers directory - QuaZip zipFile( filename ); - if ( !zipFile.open( QuaZip::mdUnzip ) ) + if ( installedSuccessfully ) { - tLog() << "Failed to QuaZip open:" << zipFile.getZipError(); - return QString(); + m_resolverStates[ resolverId ].state = Installed; + TomahawkSettingsGui::instanceGui()->setAtticaResolverStates( m_resolverStates ); + emit resolverInstalled( resolverId ); + emit resolverStateChanged( resolverId ); } - - if ( !zipFile.goToFirstFile() ) + else { - tLog() << "Failed to go to first file in zip archive: " << zipFile.getZipError(); - return QString(); + emit resolverInstallationFailed( resolverId ); } - - QDir resolverDir = TomahawkUtils::appDataDir(); - if ( !resolverDir.mkpath( QString( "atticaresolvers/%1" ).arg( resolverId ) ) ) - { - tLog() << "Failed to mkdir resolver save dir: " << TomahawkUtils::appDataDir().absoluteFilePath( QString( "atticaresolvers/%1" ).arg( resolverId ) ); - return QString(); - } - resolverDir.cd( QString( "atticaresolvers/%1" ).arg( resolverId ) ); - tDebug() << "Installing resolver to:" << resolverDir.absolutePath(); - - QuaZipFile fileInZip( &zipFile ); - do - { - QuaZipFileInfo info; - zipFile.getCurrentFileInfo( &info ); - - if ( !fileInZip.open( QIODevice::ReadOnly ) ) - { - tLog() << "Failed to open file inside zip archive:" << info.name << zipFile.getZipName() << "with error:" << zipFile.getZipError(); - continue; - } - - QFile out( resolverDir.absoluteFilePath( fileInZip.getActualFileName() ) ); - - QStringList parts = fileInZip.getActualFileName().split( "/" ); - if ( parts.size() > 1 ) - { - QStringList dirs = parts.mid( 0, parts.size() - 1 ); - QString dirPath = dirs.join( "/" ); // QDir translates / to \ internally if necessary - resolverDir.mkpath( dirPath ); - } - - // make dir if there is one needed - QDir d( fileInZip.getActualFileName() ); - - tDebug() << "Writing to output file..." << out.fileName(); - if ( !out.open( QIODevice::WriteOnly ) ) - { - tLog() << "Failed to open resolver extract file:" << out.errorString() << info.name; - continue; - } - - - out.write( fileInZip.readAll() ); - out.close(); - fileInZip.close(); - - } while ( zipFile.goToNextFile() ); - - return resolverDir.absolutePath(); } diff --git a/src/libtomahawk/AtticaManager.h b/src/libtomahawk/AtticaManager.h index 27bcacda6..bca1d7440 100644 --- a/src/libtomahawk/AtticaManager.h +++ b/src/libtomahawk/AtticaManager.h @@ -24,6 +24,7 @@ #include #include #include +#include #include "DllMacro.h" #include "accounts/Account.h" @@ -32,6 +33,13 @@ #include #include +namespace Tomahawk { +namespace Accounts { +class AtticaResolverAccount; +} +} + +class BinaryInstallerHelper; class DLLEXPORT AtticaManager : public QObject { @@ -51,13 +59,17 @@ public: int userRating; // 0-100 ResolverState state; QPixmap* pixmap; + bool binary; // internal bool pixmapDirty; - Resolver( const QString& v, const QString& path, int userR, ResolverState s ) - : version( v ), scriptPath( path ), userRating( userR ), state( s ), pixmap( 0 ), pixmapDirty( false ) {} - Resolver() : userRating( -1 ), state( Uninstalled ), pixmap( 0 ), pixmapDirty( false ) {} + Resolver( const QString& v, const QString& path, int userR, ResolverState s, bool resolver ) + : version( v ), scriptPath( path ), userRating( userR ), state( s ), pixmap( 0 ), binary( false ), pixmapDirty( false ) + { + Q_UNUSED( resolver ); + } + Resolver() : userRating( -1 ), state( Uninstalled ), pixmap( 0 ), binary( false ), pixmapDirty( false ) {} }; typedef QHash< QString, AtticaManager::Resolver > StateHash; @@ -90,7 +102,7 @@ public: /** If the resolver coming from libattica has a native custom c++ account - as well. For example the last.fm account. + as well. For example the last.fm & spotify accounts. */ bool hasCustomAccountForAttica( const QString& id ) const; Tomahawk::Accounts::Account* customAccountForAttica( const QString& id ) const; @@ -100,6 +112,8 @@ public: public slots: void installResolver( const Attica::Content& resolver, bool autoCreateAccount = true ); + void installResolverWithHandler( const Attica::Content& resolver, Tomahawk::Accounts::AtticaResolverAccount* handler ); + void upgradeResolver( const Attica::Content& resolver ); signals: @@ -108,10 +122,17 @@ signals: void resolverStateChanged( const QString& resolverId ); void resolverInstalled( const QString& resolverId ); void resolverUninstalled( const QString& resolverId ); + void resolverInstallationFailed( const QString& resolverId ); + + void startedInstalling( const QString& resolverId ); private slots: + void providerFetched( QNetworkReply* reply ); + void providerError( QNetworkReply::NetworkError ); void providerAdded( const Attica::Provider& ); + void categoriesReturned( Attica::BaseJob* ); void resolversList( Attica::BaseJob* ); + void binaryResolversList( Attica::BaseJob* ); void resolverDownloadFinished( Attica::BaseJob* ); void payloadFetched(); @@ -122,8 +143,8 @@ private slots: void syncServerData(); private: - QString extractPayload( const QString& filename, const QString& resolverId ) const; void doResolverRemove( const QString& id ) const; + void doInstallResolver( const Attica::Content& resolver, bool autoCreate, Tomahawk::Accounts::AtticaResolverAccount* handler ); Attica::ProviderManager m_manager; @@ -131,9 +152,12 @@ private: Attica::Content::List m_resolvers; StateHash m_resolverStates; + int m_resolverJobsLoaded; QMap< QString, Tomahawk::Accounts::Account* > m_customAccounts; static AtticaManager* s_instance; + + friend class ::BinaryInstallerHelper; }; class DLLEXPORT CustomAtticaAccount : public Tomahawk::Accounts::Account @@ -150,5 +174,5 @@ protected: }; Q_DECLARE_METATYPE( Attica::Content ); - +Q_DECLARE_METATYPE( QNetworkReply* ); #endif // ATTICAMANAGER_H diff --git a/src/libtomahawk/CMakeLists.txt b/src/libtomahawk/CMakeLists.txt index c88e2f0c7..a9d309d64 100644 --- a/src/libtomahawk/CMakeLists.txt +++ b/src/libtomahawk/CMakeLists.txt @@ -48,40 +48,27 @@ set( libGuiSources playlist/TreeModel.cpp playlist/TreeProxyModel.cpp playlist/TreeProxyModelPlaylistInterface.cpp - playlist/TreeHeader.cpp playlist/TreeItemDelegate.cpp - playlist/CollectionProxyModel.cpp - playlist/CollectionProxyModelPlaylistInterface.cpp - playlist/CollectionFlatModel.cpp - playlist/CollectionView.cpp playlist/PlaylistModel.cpp - playlist/PlaylistProxyModel.cpp - playlist/PlaylistProxyModelPlaylistInterface.cpp playlist/PlaylistView.cpp playlist/PlaylistItemDelegate.cpp playlist/QueueProxyModel.cpp - playlist/QueueProxyModelPlaylistInterface.cpp playlist/QueueView.cpp - playlist/TrackModel.cpp - playlist/TrackModelItem.cpp - playlist/TrackProxyModel.cpp - playlist/TrackProxyModelPlaylistInterface.cpp + playlist/PlayableModel.cpp + playlist/PlayableProxyModel.cpp + playlist/PlayableProxyModelPlaylistInterface.cpp playlist/TrackView.cpp - playlist/TrackHeader.cpp - playlist/TreeModelItem.cpp - playlist/AlbumItem.cpp playlist/AlbumModel.cpp - playlist/AlbumProxyModel.cpp - playlist/AlbumProxyModelPlaylistInterface.cpp - playlist/AlbumItemDelegate.cpp - playlist/AlbumView.cpp - playlist/ArtistView.cpp + playlist/GridItemDelegate.cpp + playlist/GridView.cpp + playlist/TreeView.cpp playlist/CustomPlaylistView.cpp playlist/ViewHeader.cpp playlist/RecentlyAddedModel.cpp playlist/RecentlyPlayedModel.cpp playlist/PlaylistLargeItemDelegate.cpp playlist/PlaylistChartItemDelegate.cpp + playlist/PlayableItem.cpp playlist/dynamic/DynamicPlaylist.cpp playlist/dynamic/DynamicView.cpp @@ -123,6 +110,9 @@ set( libGuiSources utils/PixmapDelegateFader.cpp utils/SmartPointerList.h utils/AnimatedSpinner.cpp + utils/BinaryInstallerHelper.cpp + utils/BinaryExtractWorker.cpp + utils/SharedTimeLine.cpp widgets/AnimatedCounterLabel.cpp widgets/CheckDirTree.cpp @@ -190,13 +180,25 @@ set( libSources accounts/AccountModel.cpp accounts/AccountModelFilterProxy.cpp accounts/ResolverAccount.cpp + accounts/AccountDelegate.cpp + accounts/DelegateConfigWrapper.cpp + accounts/AccountFactoryWrapper.cpp + accounts/AccountFactoryWrapperDelegate.cpp + + accounts/spotify/SpotifyAccount.cpp + accounts/spotify/SpotifyAccountConfig.cpp + accounts/spotify/SpotifyPlaylistUpdater.cpp + + accounts/lastfm/LastFmAccount.cpp + accounts/lastfm/LastFmConfig.cpp + accounts/lastfm/LastFmInfoPlugin.cpp + sip/SipPlugin.cpp sip/SipHandler.cpp sip/SipInfo.cpp audio/AudioEngine.cpp - database/Database.cpp database/FuzzyIndex.cpp database/DatabaseCollection.cpp @@ -249,20 +251,12 @@ set( libSources database/DatabaseCommand_TrackAttributes.cpp database/DatabaseCommand_SetTrackAttributes.cpp database/Database.cpp + database/TomahawkSqlQuery.cpp infosystem/InfoSystem.cpp infosystem/InfoSystemCache.cpp infosystem/InfoSystemWorker.cpp - infosystem/infoplugins/generic/EchonestPlugin.cpp - infosystem/infoplugins/generic/ChartsPlugin.cpp - infosystem/infoplugins/generic/NewReleasesPlugin.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 - network/BufferIoDevice.cpp network/MsgProcessor.cpp network/StreamConnection.cpp @@ -296,6 +290,7 @@ set( libSources utils/Qnr_IoDeviceStream.cpp utils/XspfLoader.cpp utils/TomahawkCache.cpp + utils/GuiHelpers.cpp thirdparty/kdsingleapplicationguard/kdsingleapplicationguard.cpp thirdparty/kdsingleapplicationguard/kdsharedmemorylocker.cpp @@ -319,6 +314,9 @@ set( libUI ${libUI} playlist/QueueView.ui context/ContextWidget.ui infobar/InfoBar.ui + accounts/AccountFactoryWrapper.ui + accounts/spotify/SpotifyAccountConfig.ui + accounts/lastfm/LastFmConfig.ui ) include_directories( . ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_BINARY_DIR}/.. .. @@ -351,19 +349,6 @@ IF(LIBATTICA_FOUND) ENDIF(LIBATTICA_FOUND) IF( UNIX AND NOT APPLE ) - LIST(APPEND libGuiSources - infosystem/infoplugins/unix/MprisPluginRootAdaptor.cpp - infosystem/infoplugins/unix/MprisPluginPlayerAdaptor.cpp - infosystem/infoplugins/unix/MprisPlugin.cpp - infosystem/infoplugins/unix/FdoNotifyPlugin.cpp - infosystem/infoplugins/unix/ImageConverter.cpp ) - - LIST(APPEND libGuiHeaders - infosystem/infoplugins/unix/MprisPluginRootAdaptor.h - infosystem/infoplugins/unix/MprisPluginPlayerAdaptor.h - infosystem/infoplugins/unix/MprisPlugin.h - infosystem/infoplugins/unix/FdoNotifyPlugin.h ) - IF( BUILD_GUI AND X11_FOUND ) INCLUDE_DIRECTORIES( ${THIRDPARTY_DIR}/libqnetwm ) SET( libSources ${libSources} ${THIRDPARTY_DIR}/libqnetwm/libqnetwm/netwm.cpp ) @@ -393,9 +378,8 @@ IF( APPLE ) MARK_AS_ADVANCED( COREAUDIO_LIBRARY COREFOUNDATION_LIBRARY FOUNDATION_LIBRARY SCRIPTINGBRIDGE_LIBRARY ) SET( libSources ${libSources} - infosystem/infoplugins/mac/Adium.mm - infosystem/infoplugins/mac/AdiumPlugin.cpp utils/TomahawkUtils_Mac.mm + mac/FileHelpers.mm thirdparty/Qocoa/qsearchfield_mac.mm ) SET_SOURCE_FILES_PROPERTIES(utils/TomahawkUtils_Mac.mm PROPERTIES COMPILE_FLAGS "-fvisibility=default") @@ -409,6 +393,7 @@ IF( APPLE ) ${SCRIPTINGBRIDGE_LIBRARY} /System/Library/Frameworks/AppKit.framework + /System/Library/Frameworks/Security.framework ) ELSE( APPLE ) SET( libGuiSources ${libGuiSources} thirdparty/Qocoa/qsearchfield.cpp ) diff --git a/src/libtomahawk/ContextMenu.cpp b/src/libtomahawk/ContextMenu.cpp index c5716bdb6..1b562b093 100644 --- a/src/libtomahawk/ContextMenu.cpp +++ b/src/libtomahawk/ContextMenu.cpp @@ -27,6 +27,7 @@ #include "Artist.h" #include "Album.h" #include "utils/Logger.h" +#include "audio/AudioEngine.h" using namespace Tomahawk; @@ -38,7 +39,7 @@ ContextMenu::ContextMenu( QWidget* parent ) m_sigmap = new QSignalMapper( this ); connect( m_sigmap, SIGNAL( mapped( int ) ), SLOT( onTriggered( int ) ) ); - m_supportedActions = ActionPlay | ActionQueue | ActionCopyLink | ActionLove | ActionStopAfter; + m_supportedActions = ActionPlay | ActionQueue | ActionCopyLink | ActionLove | ActionStopAfter | ActionPage; } @@ -97,13 +98,15 @@ ContextMenu::setQueries( const QList& queries ) m_sigmap->setMapping( m_loveAction, ActionLove ); connect( queries.first().data(), SIGNAL( socialActionsLoaded() ), SLOT( onSocialActionsLoaded() ) ); - m_queries.first()->loadSocialActions(); onSocialActionsLoaded(); } if ( m_supportedActions & ActionCopyLink && itemCount() == 1 ) m_sigmap->setMapping( addAction( tr( "&Copy Track Link" ) ), ActionCopyLink ); + if ( m_supportedActions & ActionPage && itemCount() == 1 ) + m_sigmap->setMapping( addAction( tr( "&Show Track Page" ) ), ActionPage ); + addSeparator(); if ( m_supportedActions & ActionDelete ) @@ -135,12 +138,15 @@ ContextMenu::setAlbums( const QList& albums ) m_albums.clear(); m_albums << albums; - if ( m_supportedActions & ActionPlay && itemCount() == 1 ) - m_sigmap->setMapping( addAction( tr( "Show &Album page" ) ), ActionPlay ); +/* if ( m_supportedActions & ActionPlay && itemCount() == 1 ) + m_sigmap->setMapping( addAction( tr( "Show &Album Page" ) ), ActionPlay );*/ if ( m_supportedActions & ActionQueue ) m_sigmap->setMapping( addAction( tr( "Add to &Queue" ) ), ActionQueue ); + if ( m_supportedActions & ActionPage && itemCount() == 1 ) + m_sigmap->setMapping( addAction( tr( "&Show Album Page" ) ), ActionPage ); + //m_sigmap->setMapping( addAction( tr( "&Add to Playlist" ) ), ActionAddToPlaylist ); addSeparator(); @@ -174,12 +180,15 @@ ContextMenu::setArtists( const QList& artists ) m_artists.clear(); m_artists << artists; - if ( m_supportedActions & ActionPlay && itemCount() == 1 ) - m_sigmap->setMapping( addAction( tr( "Show &Artist page" ) ), ActionPlay ); +/* if ( m_supportedActions & ActionPlay && itemCount() == 1 ) + m_sigmap->setMapping( addAction( tr( "Show &Artist Page" ) ), ActionPlay );*/ if ( m_supportedActions & ActionQueue ) m_sigmap->setMapping( addAction( tr( "Add to &Queue" ) ), ActionQueue ); + if ( m_supportedActions & ActionPage && itemCount() == 1 ) + m_sigmap->setMapping( addAction( tr( "&Show Artist Page" ) ), ActionPage ); + //m_sigmap->setMapping( addAction( tr( "&Add to Playlist" ) ), ActionAddToPlaylist ); addSeparator(); @@ -215,6 +224,10 @@ ContextMenu::onTriggered( int action ) case ActionCopyLink: copyLink(); break; + + case ActionPage: + openPage(); + break; case ActionLove: m_queries.first()->setLoved( !m_queries.first()->loved() ); @@ -264,6 +277,24 @@ ContextMenu::copyLink() } +void +ContextMenu::openPage() +{ + if ( m_queries.count() ) + { + ViewManager::instance()->show( m_queries.first() ); + } + else if ( m_artists.count() ) + { + ViewManager::instance()->show( m_artists.first() ); + } + else if ( m_albums.count() ) + { + ViewManager::instance()->show( m_albums.first() ); + } +} + + void ContextMenu::onSocialActionsLoaded() { diff --git a/src/libtomahawk/ContextMenu.h b/src/libtomahawk/ContextMenu.h index 2e695a675..9f1e28def 100644 --- a/src/libtomahawk/ContextMenu.h +++ b/src/libtomahawk/ContextMenu.h @@ -41,7 +41,8 @@ public: ActionDelete = 4, ActionCopyLink = 8, ActionLove = 16, - ActionStopAfter = 32 + ActionStopAfter = 32, + ActionPage = 64 }; explicit ContextMenu( QWidget* parent = 0 ); @@ -69,6 +70,7 @@ signals: private slots: void onTriggered( int action ); void copyLink(); + void openPage(); void addToQueue(); void onSocialActionsLoaded(); diff --git a/src/libtomahawk/infosystem/infoplugins/generic/ChartsPlugin_Data_p.h b/src/libtomahawk/CountryUtils.h similarity index 100% rename from src/libtomahawk/infosystem/infoplugins/generic/ChartsPlugin_Data_p.h rename to src/libtomahawk/CountryUtils.h diff --git a/src/libtomahawk/DropJob.cpp b/src/libtomahawk/DropJob.cpp index bfabf75cb..674a8840d 100644 --- a/src/libtomahawk/DropJob.cpp +++ b/src/libtomahawk/DropJob.cpp @@ -32,7 +32,6 @@ #include "utils/Logger.h" #include "utils/TomahawkUtils.h" #include "GlobalActionManager.h" -#include "infosystem/InfoSystem.h" #include "utils/XspfLoader.h" #include "jobview/JobStatusView.h" #include "jobview/JobStatusModel.h" @@ -57,7 +56,6 @@ DropJob::DropJob( QObject *parent ) , m_getWholeAlbums( false ) , m_top10( false ) , m_dropAction( Default ) - , m_dropJob( 0 ) { } @@ -294,11 +292,11 @@ DropJob::tracksFromQueryList( const QMimeData* data ) query_ptr* query = reinterpret_cast(qptr); if ( query && !query->isNull() ) { - tDebug() << "Dropped query item:" << query->data()->artist() << "-" << query->data()->track(); + tDebug() << "Dropped query item:" << query->data()->toString(); if ( m_top10 ) { - getTopTen( query->data()->artist() ); + queries << getTopTen( query->data()->artist() ); } else if ( m_getWholeArtists ) { @@ -376,7 +374,7 @@ DropJob::tracksFromAlbumMetaData( const QMimeData *data ) stream >> album; if ( m_top10 ) - getTopTen( artist ); + queries << getTopTen( artist ); else if ( m_getWholeArtists ) queries << getArtist( artist ); else @@ -405,7 +403,7 @@ DropJob::tracksFromArtistMetaData( const QMimeData *data ) } else { - getTopTen( artist ); + queries << getTopTen( artist ); } } return queries; @@ -569,6 +567,7 @@ DropJob::handleRdioUrls( const QString& urlsRaw ) rdio->parse( urls ); } + void DropJob::handleGroovesharkUrls ( const QString& urlsRaw ) { @@ -594,7 +593,6 @@ DropJob::handleGroovesharkUrls ( const QString& urlsRaw ) } - void DropJob::handleAllUrls( const QString& urls ) { @@ -672,22 +670,28 @@ DropJob::expandedUrls( QStringList urls ) void DropJob::onTracksAdded( const QList& tracksList ) { - qDebug() << Q_FUNC_INFO; - if ( m_dropJob ) + tDebug() << Q_FUNC_INFO << tracksList.count(); + +/* if ( results.isEmpty() ) { - m_dropJob->setFinished(); - m_dropJob = 0; + const QString which = album.isEmpty() ? "artist" : "album"; + JobStatusView::instance()->model()->addJob( new ErrorStatusMessage( tr( "No tracks found for given %1" ).arg( which ), 5 ) ); + }*/ + + if ( !m_dropJob.isEmpty() ) + { + m_dropJob.takeFirst()->setFinished(); } m_resultList.append( tracksList ); if ( --m_queryCount == 0 ) { - if ( m_onlyLocal ) +/* if ( m_onlyLocal ) removeRemoteSources(); if ( !m_allowDuplicates ) - removeDuplicates(); + removeDuplicates();*/ emit tracks( m_resultList ); deleteLater(); @@ -695,67 +699,6 @@ DropJob::onTracksAdded( const QList& tracksList ) } -void -DropJob::tracksFromDB( const QList< query_ptr >& tracks ) -{ - // Tracks that we get from databasecommand_alltracks are resolved only against the database and explicitly marked - // as finished. if the source they resolve to is offline they will not resolve against any resolver. - // explicitly resolve them if they fall in that case first - foreach( const query_ptr& track, tracks ) - { - if ( !track->playable() && !track->solved() && track->results().size() ) // we have offline results - { - track->setResolveFinished( false ); - Pipeline::instance()->resolve( track ); - } - } - - album_ptr albumPtr; - artist_ptr artistPtr; - if ( Tomahawk::Album* album = qobject_cast< Tomahawk::Album* >( sender() ) ) - { - foreach ( const album_ptr& ptr, m_albumsToKeep ) - if ( ptr.data() == album ) - { - albumPtr = ptr; - m_albumsToKeep.remove( ptr ); - } - } - else if ( Tomahawk::Artist* artist = qobject_cast< Tomahawk::Artist* >( sender() ) ) - { - foreach ( const artist_ptr& ptr, m_artistsToKeep ) - if ( ptr.data() == artist ) - { - artistPtr = ptr; - m_artistsToKeep.remove( ptr ); - } - } - - // If we have no tracks, this means no sources in our network have the give request (artist or album) - // Since we really do want to try to drop them, we ask the infosystem as well. - if ( tracks.isEmpty() ) - { - if ( !albumPtr.isNull() && !albumPtr->artist().isNull() ) - { - Q_ASSERT( artistPtr.isNull() ); - --m_queryCount; // This query is done. New query is infosystem query - getAlbumFromInfoystem( albumPtr->artist()->name(), albumPtr->name() ); - } - else if ( !artistPtr.isNull() ) - { - Q_ASSERT( albumPtr.isNull() ); - --m_queryCount; - getTopTen( artistPtr->name() ); - } - } - else - { - onTracksAdded( tracks ); - } - -} - - void DropJob::removeDuplicates() { @@ -803,59 +746,29 @@ DropJob::removeRemoteSources() } -void -DropJob::infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData requestData, QVariant output ) -{ - if ( requestData.caller == s_dropJobInfoId ) - { - const Tomahawk::InfoSystem::InfoStringHash info = requestData.input.value< Tomahawk::InfoSystem::InfoStringHash >(); - - const QString artist = info["artist"]; - const QString album = info["album"]; - - qDebug() << "Got requestData response for artist" << artist << "and album:" << album << output; - - QList< query_ptr > results; - - int i = 0; - foreach ( const QVariant& title, output.toMap().value( "tracks" ).toList() ) - { - results << Query::get( artist, title.toString(), QString(), uuid() ); - - if ( ++i == 10 ) // Only getting top ten for now. Would make sense to make it configurable - break; - } - - if ( results.isEmpty() ) - { - const QString which = album.isEmpty() ? "artist" : "album"; - JobStatusView::instance()->model()->addJob( new ErrorStatusMessage( tr( "No tracks found for given %1" ).arg( which ), 5 ) ); - } - onTracksAdded( results ); - } -} - - QList< query_ptr > -DropJob::getArtist( const QString &artist ) +DropJob::getArtist( const QString &artist, Tomahawk::ModelMode mode ) { artist_ptr artistPtr = Artist::get( artist ); - if ( artistPtr->playlistInterface()->tracks().isEmpty() ) + if ( artistPtr->playlistInterface( Mixed )->tracks().isEmpty() ) { m_artistsToKeep.insert( artistPtr ); - connect( artistPtr.data(), SIGNAL( tracksAdded( QList ) ), - SLOT( tracksFromDB( QList ) ) ); + connect( artistPtr.data(), SIGNAL( tracksAdded( QList, Tomahawk::ModelMode, Tomahawk::collection_ptr ) ), + SLOT( onTracksAdded( QList ) ) ); + + m_dropJob << new DropJobNotifier( QPixmap( RESPATH "images/album-icon.png" ), Album ); + JobStatusView::instance()->model()->addJob( m_dropJob.last() ); + m_queryCount++; - return QList< query_ptr >(); } - else - return artistPtr->playlistInterface()->tracks(); + + return artistPtr->playlistInterface( Mixed )->tracks(); } QList< query_ptr > -DropJob::getAlbum(const QString &artist, const QString &album) +DropJob::getAlbum( const QString& artist, const QString& album ) { artist_ptr artistPtr = Artist::get( artist ); album_ptr albumPtr = Album::get( artistPtr, album ); @@ -863,69 +776,29 @@ DropJob::getAlbum(const QString &artist, const QString &album) if ( albumPtr.isNull() ) return QList< query_ptr >(); - if ( albumPtr->playlistInterface()->tracks().isEmpty() ) + //FIXME: should check tracksLoaded() + if ( albumPtr->playlistInterface( Mixed )->tracks().isEmpty() ) { // For albums that don't exist until this moment, we are the main shared pointer holding on. // fetching the tracks is asynchronous, so the resulting signal is queued. when we go out of scope we delete // the artist_ptr which means we never get the signal delivered. so we hold on to the album pointer till we're done m_albumsToKeep.insert( albumPtr ); - m_dropJob = new DropJobNotifier( QPixmap( RESPATH "images/album-icon.png" ), Album ); - connect( albumPtr.data(), SIGNAL( tracksAdded( QList ) ), - SLOT( tracksFromDB( QList ) ) ); - JobStatusView::instance()->model()->addJob( m_dropJob ); + connect( albumPtr.data(), SIGNAL( tracksAdded( QList, Tomahawk::ModelMode, Tomahawk::collection_ptr ) ), + SLOT( onTracksAdded( QList ) ) ); + + m_dropJob << new DropJobNotifier( QPixmap( RESPATH "images/album-icon.png" ), Album ); + JobStatusView::instance()->model()->addJob( m_dropJob.last() ); m_queryCount++; - return QList< query_ptr >(); } - else - return albumPtr->playlistInterface()->tracks(); + + return albumPtr->playlistInterface( Mixed )->tracks(); } -void -DropJob::getTopTen( const QString &artist ) +QList< query_ptr > +DropJob::getTopTen( const QString& artist ) { - connect( Tomahawk::InfoSystem::InfoSystem::instance(), - SIGNAL( info( Tomahawk::InfoSystem::InfoRequestData, QVariant ) ), - SLOT( infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData, QVariant ) ) ); - - Tomahawk::InfoSystem::InfoStringHash artistInfo; - artistInfo["artist"] = artist; - - Tomahawk::InfoSystem::InfoRequestData requestData; - requestData.caller = s_dropJobInfoId; - requestData.customData = QVariantMap(); - - requestData.input = QVariant::fromValue< Tomahawk::InfoSystem::InfoStringHash >( artistInfo ); - - requestData.type = Tomahawk::InfoSystem::InfoArtistSongs; - Tomahawk::InfoSystem::InfoSystem::instance()->getInfo( requestData ); - - m_queryCount++; + return getArtist( artist, Tomahawk::InfoSystemMode ); } - - -void -DropJob::getAlbumFromInfoystem( const QString& artist, const QString& album ) -{ - connect( Tomahawk::InfoSystem::InfoSystem::instance(), - SIGNAL( info( Tomahawk::InfoSystem::InfoRequestData, QVariant ) ), - SLOT( infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData, QVariant ) ) ); - - Tomahawk::InfoSystem::InfoStringHash artistInfo; - artistInfo["artist"] = artist; - artistInfo["album"] = album; - - Tomahawk::InfoSystem::InfoRequestData requestData; - requestData.caller = s_dropJobInfoId; - requestData.customData = QVariantMap(); - - requestData.input = QVariant::fromValue< Tomahawk::InfoSystem::InfoStringHash >( artistInfo ); - - requestData.type = Tomahawk::InfoSystem::InfoAlbumSongs; - Tomahawk::InfoSystem::InfoSystem::instance()->getInfo( requestData ); - - m_queryCount++; -} - diff --git a/src/libtomahawk/DropJob.h b/src/libtomahawk/DropJob.h index 8ae990352..272a6d338 100644 --- a/src/libtomahawk/DropJob.h +++ b/src/libtomahawk/DropJob.h @@ -22,9 +22,6 @@ #include "Query.h" -#include "infosystem/InfoSystem.h" -#include "utils/XspfLoader.h" - #include #include #include @@ -120,9 +117,7 @@ signals: private slots: void expandedUrls( QStringList ); void onTracksAdded( const QList& ); - void infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData requestData, QVariant output ); - void tracksFromDB( const QList< Tomahawk::query_ptr >& ); private: /// handle parsing mime data void handleAllUrls( const QString& urls ); @@ -133,11 +128,9 @@ private: QList< Tomahawk::query_ptr > tracksFromAlbumMetaData( const QMimeData* d ); QList< Tomahawk::query_ptr > tracksFromMixedData( const QMimeData* d ); - QList< Tomahawk::query_ptr > getArtist( const QString& artist ); + QList< Tomahawk::query_ptr > getArtist( const QString& artist, Tomahawk::ModelMode mode = Tomahawk::Mixed ); QList< Tomahawk::query_ptr > getAlbum( const QString& artist, const QString& album ); - - void getTopTen( const QString& artist ); - void getAlbumFromInfoystem( const QString& artist, const QString& album ); + QList< Tomahawk::query_ptr > getTopTen( const QString& artist ); void removeDuplicates(); void removeRemoteSources(); @@ -151,7 +144,7 @@ private: DropTypes m_dropTypes; DropAction m_dropAction; - Tomahawk::DropJobNotifier* m_dropJob; + QList m_dropJob; QList< Tomahawk::query_ptr > m_resultList; QSet< Tomahawk::album_ptr > m_albumsToKeep; diff --git a/src/libtomahawk/ExternalResolver.cpp b/src/libtomahawk/ExternalResolver.cpp index f01fc3e5b..639c51da0 100644 --- a/src/libtomahawk/ExternalResolver.cpp +++ b/src/libtomahawk/ExternalResolver.cpp @@ -19,6 +19,7 @@ #include "ExternalResolver.h" #include "utils/Logger.h" +#include "Source.h" Tomahawk::ExternalResolver::ErrorState Tomahawk::ExternalResolver::error() const diff --git a/src/libtomahawk/ExternalResolverGui.cpp b/src/libtomahawk/ExternalResolverGui.cpp index 744b2ab5f..1204a0c29 100644 --- a/src/libtomahawk/ExternalResolverGui.cpp +++ b/src/libtomahawk/ExternalResolverGui.cpp @@ -28,6 +28,8 @@ #include #include "utils/Logger.h" +#include "Source.h" + Tomahawk::ExternalResolverGui::ExternalResolverGui(const QString& filePath) : Tomahawk::ExternalResolver(filePath) { diff --git a/src/libtomahawk/GlobalActionManager.cpp b/src/libtomahawk/GlobalActionManager.cpp index d7c6cfbe2..a35d4d77c 100644 --- a/src/libtomahawk/GlobalActionManager.cpp +++ b/src/libtomahawk/GlobalActionManager.cpp @@ -87,20 +87,9 @@ GlobalActionManager::~GlobalActionManager() QUrl GlobalActionManager::openLinkFromQuery( const query_ptr& query ) const { - QString title, artist, album; - - if ( !query->results().isEmpty() && !query->results().first().isNull() ) - { - title = query->results().first()->track(); - artist = query->results().first()->artist().isNull() ? QString() : query->results().first()->artist()->name(); - album = query->results().first()->album().isNull() ? QString() : query->results().first()->album()->name(); - } - else - { - title = query->track(); - artist = query->artist(); - album = query->album(); - } + QString title = query->displayQuery()->track(); + QString artist = query->displayQuery()->artist(); + QString album = query->displayQuery()->album(); return openLink( title, artist, album ); } @@ -1166,11 +1155,7 @@ GlobalActionManager::doBookmark( const playlist_ptr& pl, const query_ptr& q ) plentry_ptr e( new PlaylistEntry ); e->setGuid( uuid() ); - if ( q->results().count() ) - e->setDuration( q->results().at( 0 )->duration() ); - else - e->setDuration( 0 ); - + e->setDuration( q->displayQuery()->duration() ); e->setLastmodified( 0 ); QString annotation = ""; if ( !q->property( "annotation" ).toString().isEmpty() ) diff --git a/src/libtomahawk/LatchManager.cpp b/src/libtomahawk/LatchManager.cpp index a5a0c772b..ec18dfde7 100644 --- a/src/libtomahawk/LatchManager.cpp +++ b/src/libtomahawk/LatchManager.cpp @@ -85,7 +85,7 @@ LatchManager::playlistChanged( Tomahawk::playlistinterface_ptr ) QAction *latchOnAction = ActionCollection::instance()->getAction( "latchOn" ); latchOnAction->setText( tr( "&Catch Up" ) ); latchOnAction->setIcon( QIcon() ); - + // If not, then keep waiting return; } @@ -152,7 +152,7 @@ LatchManager::latchModeChangeRequest( const Tomahawk::source_ptr& source, bool r if ( !isLatched( source ) ) return; - source->playlistInterface()->setLatchMode( realtime ? Tomahawk::PlaylistInterface::RealTime : Tomahawk::PlaylistInterface::StayOnSong ); + source->playlistInterface()->setLatchMode( realtime ? Tomahawk::PlaylistModes::RealTime : Tomahawk::PlaylistModes::StayOnSong ); if ( realtime ) catchUpRequest(); } diff --git a/src/libtomahawk/Pipeline.cpp b/src/libtomahawk/Pipeline.cpp index 4adce9a12..6e12e5734 100644 --- a/src/libtomahawk/Pipeline.cpp +++ b/src/libtomahawk/Pipeline.cpp @@ -25,6 +25,7 @@ #include "ExternalResolver.h" #include "resolvers/ScriptResolver.h" #include "resolvers/QtScriptResolver.h" +#include "Source.h" #include "utils/Logger.h" @@ -105,6 +106,7 @@ Pipeline::removeResolver( Resolver* r ) { QMutexLocker lock( &m_mut ); + tDebug() << "Removed resolver:" << r->name(); m_resolvers.removeAll( r ); emit resolverRemoved( r ); } @@ -129,7 +131,7 @@ Pipeline::addExternalResolverFactory( ResolverFactoryFunc resolverFactory ) Tomahawk::ExternalResolver* -Pipeline::addScriptResolver( const QString& path, bool start ) +Pipeline::addScriptResolver( const QString& path ) { ExternalResolver* res = 0; @@ -140,8 +142,6 @@ Pipeline::addScriptResolver( const QString& path, bool start ) continue; m_scriptResolvers << QWeakPointer< ExternalResolver >( res ); - if ( start ) - res->start(); break; } @@ -202,14 +202,20 @@ Pipeline::resolve( const QList& qlist, bool prioritized, bool tempora QMutexLocker lock( &m_mut ); int i = 0; - foreach( const query_ptr& q, qlist ) + foreach ( const query_ptr& q, qlist ) { if ( q->resolvingFinished() ) continue; - if ( m_queries_pending.contains( q ) ) - continue; if ( m_qidsState.contains( q->id() ) ) continue; + if ( m_queries_pending.contains( q ) ) + { + if ( prioritized ) + { + m_queries_pending.insert( i++, m_queries_pending.takeAt( m_queries_pending.indexOf( q ) ) ); + } + continue; + } if ( !m_qids.contains( q->id() ) ) m_qids.insert( q->id(), q ); @@ -267,7 +273,7 @@ Pipeline::reportResults( QID qid, const QList< result_ptr >& results ) const query_ptr& q = m_qids.value( qid ); QList< result_ptr > cleanResults; - foreach( const result_ptr& r, results ) + foreach ( const result_ptr& r, results ) { float score = q->howSimilar( r ); r->setScore( score ); @@ -280,12 +286,12 @@ Pipeline::reportResults( QID qid, const QList< result_ptr >& results ) if ( !cleanResults.isEmpty() ) { q->addResults( cleanResults ); - foreach( const result_ptr& r, cleanResults ) + foreach ( const result_ptr& r, cleanResults ) { m_rids.insert( r->id(), r ); } - if ( q->playable() && !q->isFullTextQuery() ) + if ( q->solved() && !q->isFullTextQuery() ) { setQIDState( q, 0 ); return; @@ -311,7 +317,7 @@ Pipeline::reportAlbums( QID qid, const QList< album_ptr >& albums ) Q_ASSERT( q->isFullTextQuery() ); QList< album_ptr > cleanAlbums; - foreach( const album_ptr& r, albums ) + foreach ( const album_ptr& r, albums ) { // float score = q->howSimilar( r ); @@ -340,7 +346,7 @@ Pipeline::reportArtists( QID qid, const QList< artist_ptr >& artists ) Q_ASSERT( q->isFullTextQuery() ); QList< artist_ptr > cleanArtists; - foreach( const artist_ptr& r, artists ) + foreach ( const artist_ptr& r, artists ) { // float score = q->howSimilar( r ); diff --git a/src/libtomahawk/Pipeline.h b/src/libtomahawk/Pipeline.h index c142bb646..818c22d16 100644 --- a/src/libtomahawk/Pipeline.h +++ b/src/libtomahawk/Pipeline.h @@ -58,7 +58,7 @@ public: void reportArtists( QID qid, const QList< artist_ptr >& artists ); void addExternalResolverFactory( ResolverFactoryFunc resolverFactory ); - Tomahawk::ExternalResolver* addScriptResolver( const QString& scriptPath, bool start = true ); + Tomahawk::ExternalResolver* addScriptResolver( const QString& scriptPath ); void stopScriptResolver( const QString& scriptPath ); void removeScriptResolver( const QString& scriptPath ); QList< QWeakPointer< ExternalResolver > > scriptResolvers() const { return m_scriptResolvers; } @@ -90,8 +90,8 @@ signals: void idle(); void resolving( const Tomahawk::query_ptr& query ); - void resolverAdded( Resolver* ); - void resolverRemoved( Resolver* ); + void resolverAdded( Tomahawk::Resolver* ); + void resolverRemoved( Tomahawk::Resolver* ); private slots: void timeoutShunt( const query_ptr& q ); diff --git a/src/libtomahawk/Playlist.cpp b/src/libtomahawk/Playlist.cpp index bbda7ed5f..8b3d8942e 100644 --- a/src/libtomahawk/Playlist.cpp +++ b/src/libtomahawk/Playlist.cpp @@ -469,6 +469,9 @@ Playlist::setNewRevision( const QString& rev, bool is_newest_rev, const QMap< QString, Tomahawk::plentry_ptr >& addedmap ) { + Q_UNUSED( oldorderedguids ); + Q_UNUSED( is_newest_rev ); + // build up correctly ordered new list of plentry_ptrs from // existing ones, and the ones that have been added QMap entriesmap; @@ -622,11 +625,7 @@ Playlist::entriesFromQueries( const QList& queries, bool cl plentry_ptr e( new PlaylistEntry() ); e->setGuid( uuid() ); - if ( query->results().count() ) - e->setDuration( query->results().at( 0 )->duration() ); - else - e->setDuration( 0 ); - + e->setDuration( query->displayQuery()->duration() ); e->setLastmodified( 0 ); QString annotation = ""; if ( !query->property( "annotation" ).toString().isEmpty() ) diff --git a/src/libtomahawk/PlaylistInterface.cpp b/src/libtomahawk/PlaylistInterface.cpp index f965b8613..a8b24ebb8 100644 --- a/src/libtomahawk/PlaylistInterface.cpp +++ b/src/libtomahawk/PlaylistInterface.cpp @@ -20,29 +20,71 @@ #include "PlaylistInterface.h" #include "utils/Logger.h" #include "Result.h" +#include "Pipeline.h" +#include "Source.h" using namespace Tomahawk; + PlaylistInterface::PlaylistInterface () : QObject() - , m_latchMode( StayOnSong ) + , m_latchMode( PlaylistModes::StayOnSong ) { m_id = uuid(); - qRegisterMetaType( "Tomahawk::PlaylistInterface::RepeatMode" ); } + PlaylistInterface::~PlaylistInterface() { } + result_ptr PlaylistInterface::previousItem() { return siblingItem( -1 ); } + result_ptr PlaylistInterface::nextItem() { return siblingItem( 1 ); } + + +QList +PlaylistInterface::filterTracks( const QList& queries ) +{ + QList result; + + for ( int i = 0; i < queries.count(); i++ ) + { + bool picked = true; + const query_ptr q1 = queries.at( i ); + + for ( int j = 0; j < result.count(); j++ ) + { + if ( !picked ) + break; + + const query_ptr& q2 = result.at( j ); + + if ( q1->track() == q2->track() ) + { + picked = false; + } + } + + if ( picked ) + { + query_ptr q = Query::get( q1->artist(), q1->track(), q1->album(), uuid(), false ); + q->setAlbumPos( q1->results().first()->albumpos() ); + q->setDiscNumber( q1->discnumber() ); + result << q; + } + } + + Pipeline::instance()->resolve( result ); + return result; +} diff --git a/src/libtomahawk/PlaylistInterface.h b/src/libtomahawk/PlaylistInterface.h index e6b405575..fa18a4de7 100644 --- a/src/libtomahawk/PlaylistInterface.h +++ b/src/libtomahawk/PlaylistInterface.h @@ -34,19 +34,11 @@ class DLLEXPORT PlaylistInterface : public QObject Q_OBJECT public: - enum RepeatMode { NoRepeat, RepeatOne, RepeatAll }; - Q_ENUMS( RepeatMode ) - enum ViewMode { Unknown, Tree, Flat, Album }; - enum SeekRestrictions { NoSeekRestrictions, NoSeek }; - enum SkipRestrictions { NoSkipRestrictions, NoSkipForwards, NoSkipBackwards, NoSkip }; - enum RetryMode { NoRetry, Retry }; - enum LatchMode { StayOnSong, RealTime }; - explicit PlaylistInterface(); virtual ~PlaylistInterface(); const QString id() { return m_id; } - + virtual QList< Tomahawk::query_ptr > tracks() = 0; virtual int unfilteredTrackCount() const = 0; @@ -58,21 +50,21 @@ public: virtual Tomahawk::result_ptr nextItem(); virtual Tomahawk::result_ptr siblingItem( int itemsAway ) = 0; - virtual PlaylistInterface::RepeatMode repeatMode() const = 0; + virtual PlaylistModes::RepeatMode repeatMode() const = 0; virtual bool shuffled() const = 0; - virtual PlaylistInterface::ViewMode viewMode() const { return Unknown; } - - virtual PlaylistInterface::SeekRestrictions seekRestrictions() const { return NoSeekRestrictions; } - virtual PlaylistInterface::SkipRestrictions skipRestrictions() const { return NoSkipRestrictions; } + virtual PlaylistModes::ViewMode viewMode() const { return PlaylistModes::Unknown; } - virtual PlaylistInterface::RetryMode retryMode() const { return NoRetry; } + virtual PlaylistModes::SeekRestrictions seekRestrictions() const { return PlaylistModes::NoSeekRestrictions; } + virtual PlaylistModes::SkipRestrictions skipRestrictions() const { return PlaylistModes::NoSkipRestrictions; } + + virtual PlaylistModes::RetryMode retryMode() const { return PlaylistModes::NoRetry; } virtual quint32 retryInterval() const { return 30000; } - virtual PlaylistInterface::LatchMode latchMode() const { return m_latchMode; } - virtual void setLatchMode( PlaylistInterface::LatchMode latchMode ) { m_latchMode = latchMode; } - + virtual PlaylistModes::LatchMode latchMode() const { return m_latchMode; } + virtual void setLatchMode( PlaylistModes::LatchMode latchMode ) { m_latchMode = latchMode; } + virtual QString filter() const { return m_filter; } virtual void setFilter( const QString& pattern ) { m_filter = pattern; } @@ -84,27 +76,32 @@ public: virtual bool hasChildInterface( Tomahawk::playlistinterface_ptr ) { return false; } public slots: - virtual void setRepeatMode( RepeatMode mode ) = 0; + virtual void setRepeatMode( PlaylistModes::RepeatMode mode ) = 0; virtual void setShuffled( bool enabled ) = 0; signals: - void repeatModeChanged( Tomahawk::PlaylistInterface::RepeatMode mode ); + void repeatModeChanged( Tomahawk::PlaylistModes::RepeatMode mode ); void shuffleModeChanged( bool enabled ); void trackCountChanged( unsigned int tracks ); void sourceTrackCountChanged( unsigned int tracks ); - void latchModeChanged( Tomahawk::PlaylistInterface::LatchMode mode ); + void latchModeChanged( Tomahawk::PlaylistModes::LatchMode mode ); void nextTrackReady(); protected: - LatchMode m_latchMode; + virtual QList filterTracks( const QList& queries ); + + PlaylistModes::LatchMode m_latchMode; private: Q_DISABLE_COPY( PlaylistInterface ) +private: QString m_id; QString m_filter; }; -}; +} + +Q_DECLARE_METATYPE( Tomahawk::playlistinterface_ptr ) #endif // PLAYLISTINTERFACE_H diff --git a/src/libtomahawk/PlaylistPlaylistInterface.h b/src/libtomahawk/PlaylistPlaylistInterface.h index 5881c50ef..4db326431 100644 --- a/src/libtomahawk/PlaylistPlaylistInterface.h +++ b/src/libtomahawk/PlaylistPlaylistInterface.h @@ -52,13 +52,13 @@ public: virtual Tomahawk::result_ptr siblingItem( int /*itemsAway*/ ) { return result_ptr(); } - virtual PlaylistInterface::RepeatMode repeatMode() const { return PlaylistInterface::NoRepeat; } + virtual PlaylistModes::RepeatMode repeatMode() const { return PlaylistModes::NoRepeat; } virtual bool shuffled() const { return false; } virtual void setFilter( const QString& /*pattern*/ ) {} public slots: - virtual void setRepeatMode( PlaylistInterface::RepeatMode ) {} + virtual void setRepeatMode( PlaylistModes::RepeatMode ) {} virtual void setShuffled( bool ) {} private: diff --git a/src/libtomahawk/Query.cpp b/src/libtomahawk/Query.cpp index 6e9c284a3..4d803f776 100644 --- a/src/libtomahawk/Query.cpp +++ b/src/libtomahawk/Query.cpp @@ -39,6 +39,46 @@ using namespace Tomahawk; + +SocialAction::SocialAction() {} +SocialAction::~SocialAction() {} + +SocialAction& SocialAction::operator=( const SocialAction& other ) +{ + action = other.action; + value = other.value; + timestamp = other.timestamp; + source = other.source; + + return *this; +} + + +SocialAction::SocialAction( const SocialAction& other ) +{ + *this = other; +} + + +PlaybackLog::PlaybackLog() {} +PlaybackLog::~PlaybackLog() {} + +PlaybackLog& PlaybackLog::operator=( const PlaybackLog& other ) +{ + source = other.source; + timestamp = other.timestamp; + secsPlayed = other.secsPlayed; + + return *this; +} + + +PlaybackLog::PlaybackLog( const PlaybackLog& other ) +{ + *this = other; +} + + query_ptr Query::get( const QString& artist, const QString& track, const QString& album, const QID& qid, bool autoResolve ) { @@ -75,6 +115,7 @@ Query::Query( const QString& artist, const QString& track, const QString& album, , m_track( track ) , m_socialActionsLoaded( false ) , m_simTracksLoaded( false ) + , m_lyricsLoaded( false ) , m_infoJobs( 0 ) { init(); @@ -84,10 +125,7 @@ Query::Query( const QString& artist, const QString& track, const QString& album, connect( Database::instance(), SIGNAL( indexReady() ), SLOT( refreshResults() ), Qt::QueuedConnection ); } - connect( Pipeline::instance(), SIGNAL( resolverAdded( Resolver* ) ), - SLOT( onResolverAdded() ), Qt::QueuedConnection ); - connect( Pipeline::instance(), SIGNAL( resolverRemoved( Resolver* ) ), - SLOT( onResolverRemoved() ), Qt::QueuedConnection ); + connect( Pipeline::instance(), SIGNAL( resolverAdded( Tomahawk::Resolver* ) ), SLOT( onResolverAdded() ), Qt::QueuedConnection ); } @@ -146,6 +184,16 @@ Query::updateSortNames() } +query_ptr +Query::displayQuery() const +{ + if ( !results().isEmpty() ) + return results().first()->toQuery(); + + return m_ownRef.toStrongRef(); +} + + void Query::addResults( const QList< Tomahawk::result_ptr >& newresults ) { @@ -165,7 +213,6 @@ Query::addResults( const QList< Tomahawk::result_ptr >& newresults ) m_results << newresults; qStableSort( m_results.begin(), m_results.end(), Query::resultSorter ); - query_ptr q = m_ownRef.toStrongRef(); // hook up signals, and check solved status foreach( const result_ptr& rp, newresults ) @@ -267,16 +314,6 @@ Query::onResolverAdded() } -void -Query::onResolverRemoved() -{ - if ( !solved() ) - { - refreshResults(); - } -} - - QList< result_ptr > Query::results() const { @@ -396,7 +433,7 @@ Query::checkResults() } } - if ( m_playable && !playable ) + if ( m_solved && !solved ) { refreshResults(); } @@ -729,7 +766,7 @@ Query::cover( const QSize& size, bool forceLoad ) const } m_albumPtr->cover( size, forceLoad ); - if ( m_albumPtr->infoLoaded() ) + if ( m_albumPtr->coverLoaded() ) { if ( !m_albumPtr->cover( size ).isNull() ) return m_albumPtr->cover( size ); @@ -742,6 +779,19 @@ Query::cover( const QSize& size, bool forceLoad ) const #endif +bool +Query::coverLoaded() const +{ + if ( m_albumPtr.isNull() ) + return false; + + if ( m_albumPtr->coverLoaded() && !m_albumPtr->cover( QSize( 0, 0 ) ).isNull() ) + return true; + + return m_artistPtr->coverLoaded(); +} + + QList Query::similarTracks() const { @@ -775,6 +825,39 @@ Query::similarTracks() const } +QStringList +Query::lyrics() const +{ + if ( !m_lyricsLoaded ) + { + Tomahawk::InfoSystem::InfoStringHash trackInfo; + trackInfo["artist"] = artist(); + trackInfo["track"] = track(); + + Tomahawk::InfoSystem::InfoRequestData requestData; + requestData.caller = id(); + requestData.customData = QVariantMap(); + + requestData.input = QVariant::fromValue< Tomahawk::InfoSystem::InfoStringHash >( trackInfo ); + requestData.type = Tomahawk::InfoSystem::InfoTrackLyrics; + requestData.requestId = TomahawkUtils::infosystemRequestId(); + + connect( Tomahawk::InfoSystem::InfoSystem::instance(), + SIGNAL( info( Tomahawk::InfoSystem::InfoRequestData, QVariant ) ), + SLOT( infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData, QVariant ) ), Qt::UniqueConnection ); + + connect( Tomahawk::InfoSystem::InfoSystem::instance(), + SIGNAL( finished( QString ) ), + SLOT( infoSystemFinished( QString ) ), Qt::UniqueConnection ); + + m_infoJobs++; + Tomahawk::InfoSystem::InfoSystem::instance()->getInfo( requestData ); + } + + return m_lyrics; +} + + void Query::infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData requestData, QVariant output ) { @@ -784,6 +867,15 @@ Query::infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData requestData, QVaria QVariantMap returnedData = output.value< QVariantMap >(); switch ( requestData.type ) { + case InfoSystem::InfoTrackLyrics: + { + m_lyrics = output.value< QVariant >().toString().split( "\n" ); + + m_lyricsLoaded = true; + emit lyricsLoaded(); + break; + } + case InfoSystem::InfoTrackSimilars: { const QStringList artists = returnedData["artists"].toStringList(); @@ -791,8 +883,9 @@ Query::infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData requestData, QVaria for ( int i = 0; i < tracks.count() && i < 50; i++ ) { - m_similarTracks << Query::get( artists.at( i ), tracks.at( i ), QString(), uuid(), true ); + m_similarTracks << Query::get( artists.at( i ), tracks.at( i ), QString(), uuid(), false ); } + Pipeline::instance()->resolve( m_similarTracks ); m_simTracksLoaded = true; emit similarTracksLoaded(); @@ -809,9 +902,6 @@ Query::infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData requestData, QVaria void Query::infoSystemFinished( QString target ) { - tDebug() << Q_FUNC_INFO; - Q_UNUSED( target ); - if ( target != id() ) return; diff --git a/src/libtomahawk/Query.h b/src/libtomahawk/Query.h index a2656684e..6eb60088c 100644 --- a/src/libtomahawk/Query.h +++ b/src/libtomahawk/Query.h @@ -46,6 +46,13 @@ struct SocialAction QVariant value; QVariant timestamp; Tomahawk::source_ptr source; + + // Make explicit so compiler won't auto-generate, since destructor of + // source_ptr is not defined yet (only typedef included in header) + SocialAction(); + ~SocialAction(); + SocialAction& operator=( const SocialAction& other ); + SocialAction( const SocialAction& other ); }; struct PlaybackLog @@ -53,6 +60,13 @@ struct PlaybackLog Tomahawk::source_ptr source; unsigned int timestamp; unsigned int secsPlayed; + + // Make explicit so compiler won't auto-generate, since destructor of + // source_ptr is not defined yet (only typedef included in header) + PlaybackLog(); + ~PlaybackLog(); + PlaybackLog& operator=( const PlaybackLog& other ); + PlaybackLog( const PlaybackLog& other ); }; @@ -117,18 +131,21 @@ public: QString artistSortname() const { return m_artistSortname; } QString albumSortname() const { return m_albumSortname; } QString trackSortname() const { return m_trackSortname; } + QString artist() const { return m_artist; } QString composer() const { return m_composer; } QString album() const { return m_album; } QString track() const { return m_track; } - int duration() const { return m_duration; } unsigned int albumpos() const { return m_albumpos; } unsigned int discnumber() const { return m_discnumber; } + + query_ptr displayQuery() const; #ifndef ENABLE_HEADLESS QPixmap cover( const QSize& size, bool forceLoad = true ) const; #endif + bool coverLoaded() const; void setResolveFinished( bool resolved ) { m_resolveFinished = resolved; } void setPlayedBy( const Tomahawk::source_ptr& source, unsigned int playtime ); @@ -147,6 +164,7 @@ public: QString socialActionDescription( const QString& action, DescriptionMode mode ) const; QList similarTracks() const; + QStringList lyrics() const; QWeakPointer< Tomahawk::Query > weakRef() { return m_ownRef; } void setWeakRef( QWeakPointer< Tomahawk::Query > weakRef ) { m_ownRef = weakRef; } @@ -168,6 +186,8 @@ signals: void socialActionsLoaded(); void statsLoaded(); void similarTracksLoaded(); + void lyricsLoaded(); + void updated(); public slots: @@ -179,10 +199,7 @@ public slots: void addArtists( const QList< Tomahawk::artist_ptr >& ); void onResolvingFinished(); - - // resolve if not solved() void onResolverAdded(); - void onResolverRemoved(); private slots: void infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData requestData, QVariant output ); @@ -250,11 +267,14 @@ private: bool m_simTracksLoaded; QList m_similarTracks; + bool m_lyricsLoaded; + QStringList m_lyrics; + mutable int m_infoJobs; }; }; //ns -Q_DECLARE_METATYPE(Tomahawk::query_ptr); +Q_DECLARE_METATYPE( Tomahawk::query_ptr ); #endif // QUERY_H diff --git a/src/libtomahawk/Resolver.cpp b/src/libtomahawk/Resolver.cpp index 6311a8d53..60c70af9f 100644 --- a/src/libtomahawk/Resolver.cpp +++ b/src/libtomahawk/Resolver.cpp @@ -17,3 +17,5 @@ */ #include "Resolver.h" + +#include "Source.h" \ No newline at end of file diff --git a/src/libtomahawk/Result.cpp b/src/libtomahawk/Result.cpp index ffe79d1e2..62e204058 100644 --- a/src/libtomahawk/Result.cpp +++ b/src/libtomahawk/Result.cpp @@ -20,7 +20,9 @@ #include "Album.h" #include "Collection.h" +#include "Resolver.h" #include "Source.h" +#include "Pipeline.h" #include "database/Database.h" #include "database/DatabaseCommand_Resolve.h" #include "database/DatabaseCommand_AllTracks.h" @@ -72,6 +74,7 @@ Result::Result( const QString& url ) , m_trackId( 0 ) , m_fileId( 0 ) { + connect( Pipeline::instance(), SIGNAL( resolverRemoved( Tomahawk::Resolver* ) ), SLOT( onResolverRemoved( Tomahawk::Resolver* ) ), Qt::QueuedConnection ); } @@ -94,12 +97,24 @@ Result::deleteLater() } +void +Result::onResolverRemoved( Tomahawk::Resolver* resolver ) +{ + if ( m_resolvedBy.data() == resolver ) + { + m_resolvedBy.clear(); + emit statusChanged(); + } +} + + artist_ptr Result::artist() const { return m_artist; } + artist_ptr Result::composer() const { @@ -124,18 +139,10 @@ Result::collection() const float Result::score() const { - if ( !collection().isNull() && collection()->source()->isOnline() ) - { + if ( isOnline() ) return m_score; - } else - { - // check if this a valid collection-less result (e.g. from youtube, but ignore offline sources still) - if ( collection().isNull() ) - return m_score; - else - return 0.0; - } + return 0.0; } @@ -152,7 +159,14 @@ Result::id() const bool Result::isOnline() const { - return ( ( !collection().isNull() && collection()->source()->isOnline() ) || collection().isNull() ); + if ( !collection().isNull() ) + { + return collection()->source()->isOnline(); + } + else + { + return !m_resolvedBy.isNull(); + } } @@ -163,12 +177,7 @@ Result::toVariant() const m.insert( "artist", artist()->name() ); m.insert( "album", album()->name() ); m.insert( "track", track() ); - - if ( !collection().isNull() ) - m.insert( "source", collection()->source()->friendlyName() ); - else - m.insert( "source", friendlySource() ); - + m.insert( "source", friendlySource() ); m.insert( "mimetype", mimetype() ); m.insert( "size", size() ); m.insert( "bitrate", bitrate() ); @@ -176,6 +185,7 @@ Result::toVariant() const m.insert( "score", score() ); m.insert( "sid", id() ); m.insert( "discnumber", discnumber() ); + m.insert( "albumpos", albumpos() ); if ( !composer().isNull() ) m.insert( "composer", composer()->name() ); @@ -187,7 +197,7 @@ Result::toVariant() const QString Result::toString() const { - return QString( "Result(%1 %2\t%3 - %4 %5" ).arg( id() ).arg( score() ).arg( artist().isNull() ? QString() : artist()->name() ).arg( track() ).arg( url() ); + return QString( "Result(%1) %2\t%3 - %4 %5" ).arg( id() ).arg( score() ).arg( artist().isNull() ? QString() : artist()->name() ).arg( track() ).arg( url() ); } @@ -197,6 +207,12 @@ Result::toQuery() if ( m_query.isNull() ) { m_query = Tomahawk::Query::get( artist()->name(), track(), album()->name() ); + m_query->setAlbumPos( albumpos() ); + m_query->setDiscNumber( discnumber() ); + m_query->setDuration( duration() ); + if ( !composer().isNull() ) + m_query->setComposer( composer()->name() ); + QList rl; rl << Result::get( m_url ); @@ -272,3 +288,20 @@ Result::friendlySource() const else return collection()->source()->friendlyName(); } + + +Tomahawk::Resolver* +Result::resolvedBy() const +{ + if ( m_resolvedBy.isNull() ) + return 0; + + return m_resolvedBy.data(); +} + + +void +Result::setResolvedBy( Tomahawk::Resolver* resolver ) +{ + m_resolvedBy = QWeakPointer< Tomahawk::Resolver >( resolver ); +} diff --git a/src/libtomahawk/Result.h b/src/libtomahawk/Result.h index bfa6ded64..89a799e4e 100644 --- a/src/libtomahawk/Result.h +++ b/src/libtomahawk/Result.h @@ -36,6 +36,8 @@ class DatabaseCommand_LoadFile; namespace Tomahawk { +class Resolver; + class DLLEXPORT Result : public QObject { Q_OBJECT @@ -54,6 +56,9 @@ public: QString toString() const; Tomahawk::query_ptr toQuery(); + Tomahawk::Resolver* resolvedBy() const; + void setResolvedBy( Tomahawk::Resolver* resolver ); + float score() const; RID id() const; bool isOnline() const; @@ -110,6 +115,8 @@ signals: private slots: void onOffline(); void onOnline(); + + void onResolverRemoved( Tomahawk::Resolver* resolver ); private: // private constructor @@ -121,6 +128,7 @@ private: mutable RID m_rid; collection_ptr m_collection; Tomahawk::query_ptr m_query; + QWeakPointer< Tomahawk::Resolver > m_resolvedBy; Tomahawk::artist_ptr m_artist; Tomahawk::album_ptr m_album; diff --git a/src/libtomahawk/Source.cpp b/src/libtomahawk/Source.cpp index 00fe429f0..db331385f 100644 --- a/src/libtomahawk/Source.cpp +++ b/src/libtomahawk/Source.cpp @@ -23,6 +23,7 @@ #include "SourceList.h" #include "SourcePlaylistInterface.h" +#include "accounts/AccountManager.h" #include "network/ControlConnection.h" #include "database/DatabaseCommand_AddSource.h" #include "database/DatabaseCommand_CollectionStats.h" @@ -31,9 +32,11 @@ #include "database/Database.h" #include +#include #include "utils/Logger.h" #include "utils/TomahawkUtilsGui.h" +#include "utils/TomahawkCache.h" #include "database/DatabaseCommand_SocialAction.h" using namespace Tomahawk; @@ -46,6 +49,7 @@ Source::Source( int id, const QString& username ) , m_username( username ) , m_id( id ) , m_updateIndexWhenSynced( false ) + , m_avatarUpdated( true ) , m_state( DBSyncConnection::UNKNOWN ) , m_cc( 0 ) , m_commandCount( 0 ) @@ -55,13 +59,16 @@ Source::Source( int id, const QString& username ) m_scrubFriendlyName = qApp->arguments().contains( "--demo" ); if ( id == 0 ) - { m_isLocal = true; - m_online = true; - } m_currentTrackTimer.setSingleShot( true ); connect( &m_currentTrackTimer, SIGNAL( timeout() ), this, SLOT( trackTimerFired() ) ); + + if ( m_isLocal ) + { + connect( Accounts::AccountManager::instance(), SIGNAL( connected( Tomahawk::Accounts::Account* ) ), SLOT( setOnline() ) ); + connect( Accounts::AccountManager::instance(), SIGNAL( disconnected( Tomahawk::Accounts::Account* ) ), SLOT( setOffline() ) ); + } } @@ -125,12 +132,36 @@ Source::setAvatar( const QPixmap& avatar ) { delete m_avatar; m_avatar = new QPixmap( avatar ); + m_fancyAvatar = 0; + + QByteArray ba; + QBuffer buffer( &ba ); + buffer.open( QIODevice::WriteOnly ); + avatar.save( &buffer, "PNG" ); + + TomahawkUtils::Cache::instance()->putData( "Sources", 7776000000 /* 90 days */, m_username, ba ); + m_avatarUpdated = true; } QPixmap -Source::avatar( AvatarStyle style, const QSize& size ) const +Source::avatar( AvatarStyle style, const QSize& size ) { + if ( !m_avatar && m_avatarUpdated ) + { + m_avatar = new QPixmap(); + QByteArray ba = TomahawkUtils::Cache::instance()->getData( "Sources", m_username ).toByteArray(); + + if ( ba.count() ) + m_avatar->loadFromData( ba ); + if ( m_avatar->isNull() ) + { + delete m_avatar; + m_avatar = 0; + } + m_avatarUpdated = false; + } + if ( style == FancyStyle && m_avatar && !m_fancyAvatar ) m_fancyAvatar = new QPixmap( TomahawkUtils::createAvatarFrame( QPixmap( *m_avatar ) ) ); @@ -166,7 +197,10 @@ Source::setFriendlyName( const QString& fname ) m_friendlyname = fname; if ( m_scrubFriendlyName ) - m_friendlyname = m_friendlyname.split( "@" ).first(); + { + if ( m_friendlyname.indexOf( "@" ) > 0 ) + m_friendlyname = m_friendlyname.split( "@" ).first(); + } } @@ -198,12 +232,15 @@ Source::setOffline() m_online = false; emit offline(); - m_currentTrack.clear(); - emit stateChanged(); + if ( !isLocal() ) + { + m_currentTrack.clear(); + emit stateChanged(); - m_cc = 0; - DatabaseCommand_SourceOffline* cmd = new DatabaseCommand_SourceOffline( id() ); - Database::instance()->enqueue( QSharedPointer< DatabaseCommand >( cmd ) ); + m_cc = 0; + DatabaseCommand_SourceOffline* cmd = new DatabaseCommand_SourceOffline( id() ); + Database::instance()->enqueue( QSharedPointer< DatabaseCommand >( cmd ) ); + } } @@ -217,11 +254,14 @@ Source::setOnline() m_online = true; emit online(); - // ensure username is in the database - DatabaseCommand_addSource* cmd = new DatabaseCommand_addSource( m_username, friendlyName() ); - connect( cmd, SIGNAL( done( unsigned int, QString ) ), - SLOT( dbLoaded( unsigned int, const QString& ) ) ); - Database::instance()->enqueue( QSharedPointer(cmd) ); + if ( !isLocal() ) + { + // ensure username is in the database + DatabaseCommand_addSource* cmd = new DatabaseCommand_addSource( m_username, friendlyName() ); + connect( cmd, SIGNAL( done( unsigned int, QString ) ), + SLOT( dbLoaded( unsigned int, const QString& ) ) ); + Database::instance()->enqueue( QSharedPointer(cmd) ); + } } @@ -332,24 +372,27 @@ Source::playlistInterface() void Source::onPlaybackStarted( const Tomahawk::query_ptr& query, unsigned int duration ) { - qDebug() << Q_FUNC_INFO << query->toString(); + tLog( LOGVERBOSE ) << Q_FUNC_INFO << query->toString(); m_currentTrack = query; m_currentTrackTimer.start( duration * 1000 + 900000 ); // duration comes in seconds if ( m_playlistInterface.isNull() ) playlistInterface(); + emit playbackStarted( query ); + emit stateChanged(); } void Source::onPlaybackFinished( const Tomahawk::query_ptr& query ) { - qDebug() << Q_FUNC_INFO << query->toString(); + tDebug() << Q_FUNC_INFO << query->toString(); emit playbackFinished( query ); - m_currentTrackTimer.start(); + m_currentTrack.clear(); + emit stateChanged(); } @@ -357,7 +400,6 @@ void Source::trackTimerFired() { m_currentTrack.clear(); - emit stateChanged(); } @@ -479,3 +521,26 @@ Source::updateIndexWhenSynced() { m_updateIndexWhenSynced = true; } + + +QString +Source::textStatus() const +{ + if ( !m_textStatus.isEmpty() ) + return m_textStatus; + + if ( !currentTrack().isNull() ) + { + return currentTrack()->artist() + " - " + currentTrack()->track(); + } + + // do not use isOnline() here - it will always return true for the local source + if ( m_online ) + { + return tr( "Online" ); + } + else + { + return tr( "Offline" ); + } +} diff --git a/src/libtomahawk/Source.h b/src/libtomahawk/Source.h index 1c3dc82ee..bd463ae2a 100644 --- a/src/libtomahawk/Source.h +++ b/src/libtomahawk/Source.h @@ -58,7 +58,7 @@ public: virtual ~Source(); bool isLocal() const { return m_isLocal; } - bool isOnline() const { return m_online; } + bool isOnline() const { return m_online || m_isLocal; } QString userName() const { return m_username; } QString friendlyName() const; @@ -66,7 +66,7 @@ public: #ifndef ENABLE_HEADLESS void setAvatar( const QPixmap& avatar ); - QPixmap avatar( AvatarStyle style = Original, const QSize& size = QSize() ) const; + QPixmap avatar( AvatarStyle style = Original, const QSize& size = QSize() ); #endif collection_ptr collection() const; @@ -83,7 +83,7 @@ public: unsigned int trackCount() const; Tomahawk::query_ptr currentTrack() const { return m_currentTrack; } - QString textStatus() const { return m_textStatus; } + QString textStatus() const; DBSyncConnection::State state() const { return m_state; } Tomahawk::playlistinterface_ptr playlistInterface(); @@ -147,6 +147,7 @@ private: int m_id; bool m_scrubFriendlyName; bool m_updateIndexWhenSynced; + bool m_avatarUpdated; Tomahawk::query_ptr m_currentTrack; QString m_textStatus; @@ -157,7 +158,7 @@ private: QList< QSharedPointer > m_cmds; int m_commandCount; - QPixmap* m_avatar; + mutable QPixmap* m_avatar; mutable QPixmap* m_fancyAvatar; mutable QHash< int, QPixmap > m_coverCache; diff --git a/src/libtomahawk/SourcePlaylistInterface.cpp b/src/libtomahawk/SourcePlaylistInterface.cpp index c2d818bcc..6b3caf048 100644 --- a/src/libtomahawk/SourcePlaylistInterface.cpp +++ b/src/libtomahawk/SourcePlaylistInterface.cpp @@ -28,14 +28,14 @@ using namespace Tomahawk; -SourcePlaylistInterface::SourcePlaylistInterface( Tomahawk::Source *source, Tomahawk::PlaylistInterface::LatchMode latchMode ) +SourcePlaylistInterface::SourcePlaylistInterface( Tomahawk::Source *source, Tomahawk::PlaylistModes::LatchMode latchMode ) : PlaylistInterface() , m_source( source ) , m_currentItem( 0 ) , m_gotNextItem( false ) { setLatchMode( latchMode ); - + if ( !m_source.isNull() ) connect( m_source.data(), SIGNAL( playbackStarted( const Tomahawk::query_ptr& ) ), SLOT( onSourcePlaybackStarted( const Tomahawk::query_ptr& ) ) ); @@ -75,7 +75,7 @@ SourcePlaylistInterface::nextItem() } m_gotNextItem = false; - + if ( m_source.data()->currentTrack()->numResults() ) m_currentItem = m_source.data()->currentTrack()->results().first(); else @@ -108,7 +108,7 @@ SourcePlaylistInterface::hasNextItem() { if ( !sourceValid() ) return false; - + return m_gotNextItem; } diff --git a/src/libtomahawk/SourcePlaylistInterface.h b/src/libtomahawk/SourcePlaylistInterface.h index dc967510c..cc09e4dc9 100644 --- a/src/libtomahawk/SourcePlaylistInterface.h +++ b/src/libtomahawk/SourcePlaylistInterface.h @@ -36,7 +36,7 @@ class DLLEXPORT SourcePlaylistInterface : public Tomahawk::PlaylistInterface Q_OBJECT public: - SourcePlaylistInterface( Tomahawk::Source *source, Tomahawk::PlaylistInterface::LatchMode latchMode = PlaylistInterface::StayOnSong ); + SourcePlaylistInterface( Tomahawk::Source *source, Tomahawk::PlaylistModes::LatchMode latchMode = PlaylistModes::StayOnSong ); virtual ~SourcePlaylistInterface(); QList tracks(); @@ -50,14 +50,14 @@ public: virtual Tomahawk::result_ptr nextItem(); virtual Tomahawk::result_ptr currentItem() const; - virtual PlaylistInterface::RepeatMode repeatMode() const { return PlaylistInterface::NoRepeat; } - virtual PlaylistInterface::SeekRestrictions seekRestrictions() const { return PlaylistInterface::NoSeek; } - virtual PlaylistInterface::SkipRestrictions skipRestrictions() const { return PlaylistInterface::NoSkipBackwards; } - virtual PlaylistInterface::RetryMode retryMode() const { return Retry; } + virtual PlaylistModes::RepeatMode repeatMode() const { return PlaylistModes::NoRepeat; } + virtual PlaylistModes::SeekRestrictions seekRestrictions() const { return PlaylistModes::NoSeek; } + virtual PlaylistModes::SkipRestrictions skipRestrictions() const { return PlaylistModes::NoSkipBackwards; } + virtual PlaylistModes::RetryMode retryMode() const { return PlaylistModes::Retry; } virtual quint32 retryInterval() const { return 5000; } - virtual void setLatchMode( PlaylistInterface::LatchMode latchMode ) { m_latchMode = latchMode; emit latchModeChanged( latchMode ); } - + virtual void setLatchMode( PlaylistModes::LatchMode latchMode ) { m_latchMode = latchMode; emit latchModeChanged( latchMode ); } + virtual bool shuffled() const { return false; } virtual void setFilter( const QString& /*pattern*/ ) {} @@ -66,9 +66,9 @@ public: virtual void reset(); public slots: - virtual void setRepeatMode( PlaylistInterface::RepeatMode ) {} + virtual void setRepeatMode( PlaylistModes::RepeatMode ) {} virtual void setShuffled( bool ) {} - virtual void audioPaused() { setLatchMode( PlaylistInterface::StayOnSong ); } + virtual void audioPaused() { setLatchMode( PlaylistModes::StayOnSong ); } private slots: void onSourcePlaybackStarted( const Tomahawk::query_ptr& query ); diff --git a/src/libtomahawk/TomahawkSettings.cpp b/src/libtomahawk/TomahawkSettings.cpp index c83faccb7..405df9411 100644 --- a/src/libtomahawk/TomahawkSettings.cpp +++ b/src/libtomahawk/TomahawkSettings.cpp @@ -21,9 +21,9 @@ #include "TomahawkSettings.h" #include - #include +#include "Source.h" #include "sip/SipHandler.h" #include "PlaylistInterface.h" @@ -129,6 +129,21 @@ TomahawkSettings::TomahawkSettings( QObject* parent ) // insert upgrade code here as required setValue( "configversion", TOMAHAWK_SETTINGS_VERSION ); } + + // Ensure last.fm and spotify accounts always exist + QString spotifyAcct, lastfmAcct; + foreach ( const QString& acct, value( "accounts/allaccounts" ).toStringList() ) + { + if ( acct.startsWith( "lastfmaccount_" ) ) + lastfmAcct = acct; + else if ( acct.startsWith( "spotifyaccount_" ) ) + spotifyAcct = acct; + } + + if ( spotifyAcct.isEmpty() ) + createSpotifyAccount(); + if ( lastfmAcct.isEmpty() ) + createLastFmAccount(); } @@ -144,6 +159,14 @@ TomahawkSettings::doInitialSetup() // by default we add a local network resolver addAccount( "sipzeroconf_autocreated" ); + createLastFmAccount(); + createSpotifyAccount(); +} + + +void +TomahawkSettings::createLastFmAccount() +{ // Add a last.fm account for scrobbling and infosystem const QString accountKey = QString( "lastfmaccount_%1" ).arg( QUuid::createUuid().toString().mid( 1, 8 ) ); addAccount( accountKey ); @@ -153,6 +176,27 @@ TomahawkSettings::doInitialSetup() setValue( "autoconnect", true ); setValue( "types", QStringList() << "ResolverType" << "StatusPushType" ); endGroup(); + + QStringList allAccounts = value( "accounts/allaccounts" ).toStringList(); + allAccounts << accountKey; + setValue( "accounts/allaccounts", allAccounts ); +} + + +void +TomahawkSettings::createSpotifyAccount() +{ + const QString accountKey = QString( "spotifyaccount_%1" ).arg( QUuid::createUuid().toString().mid( 1, 8 ) ); + beginGroup( "accounts/" + accountKey ); + setValue( "enabled", false ); + setValue( "types", QStringList() << "ResolverType" ); + setValue( "credentials", QVariantHash() ); + setValue( "configuration", QVariantHash() ); + endGroup(); + + QStringList allAccounts = value( "accounts/allaccounts" ).toStringList(); + allAccounts << accountKey; + setValue( "accounts/allaccounts", allAccounts ); } @@ -514,18 +558,65 @@ TomahawkSettings::doUpgrade( int oldVersion, int newVersion ) endGroup(); - setPlaylistUpdaters( updaters ); +// setPlaylistUpdaters( updaters ); remove( "playlistupdaters" ); } - else if ( oldVersion == 10 ) + else if ( oldVersion == 11 ) { - const QStringList accounts = childGroups(); + // If the user doesn't have a spotify account, create one, since now it + // is like the last.fm account and always exists + QStringList allAccounts = value( "accounts/allaccounts" ).toStringList(); + QString acct; + foreach ( const QString& account, allAccounts ) + { + if ( account.startsWith( "spotifyaccount_" ) ) + { + acct = account; + break; + } + } + + if ( !acct.isEmpty() ) + { + beginGroup( "accounts/" + acct ); + QVariantHash conf = value( "configuration" ).toHash(); + foreach ( const QString& key, conf.keys() ) + qDebug() << key << conf[ key ].toString(); + endGroup(); + } + else + { + createSpotifyAccount(); + } + } + else if ( oldVersion == 12 ) + { + const QStringList accounts = value( "accounts/allaccounts" ).toStringList(); //Move storage of Credentials from QSettings to QtKeychain foreach ( const QString& account, accounts ) { - //TODO: migration code to be written - + beginGroup( QString( "accounts/%1" ).arg( account ) ); + const QVariantHash creds = value( "credentials" ).toHash(); + + if ( !creds.isEmpty() ) + { + QKeychain::WritePasswordJob* j = new QKeychain::WritePasswordJob( QLatin1String( "tomahawkaccounts" ), this ); + j->setKey( account ); + j->setAutoDelete( false ); + + QByteArray data; + QDataStream ds( &data, QIODevice::WriteOnly ); + ds << creds; + + j->setBinaryData( data ); +// connect( j, SIGNAL( finished( QKeychain::Job* ) ), this, SLOT( keychainJobFinished( QKeychain::Job* ) ) ); + j->start(); + + qDebug() << "Migrating account credentials for account:" << account; + } + + endGroup(); } } } @@ -650,6 +741,20 @@ TomahawkSettings::setCrashReporterEnabled( bool enable ) } +unsigned int +TomahawkSettings::volume() const +{ + return value( "audio/volume", 75 ).toUInt(); +} + + +void +TomahawkSettings::setVolume( unsigned int volume ) +{ + setValue( "audio/volume", volume ); +} + + QString TomahawkSettings::proxyHost() const { @@ -887,33 +992,16 @@ TomahawkSettings::removePlaylistSettings( const QString& playlistid ) void -TomahawkSettings::setRepeatMode( const QString& playlistid, Tomahawk::PlaylistInterface::RepeatMode mode ) +TomahawkSettings::setRepeatMode( const QString& playlistid, Tomahawk::PlaylistModes::RepeatMode mode ) { setValue( QString( "ui/playlist/%1/repeatMode" ).arg( playlistid ), (int)mode ); } -Tomahawk::PlaylistInterface::RepeatMode +Tomahawk::PlaylistModes::RepeatMode TomahawkSettings::repeatMode( const QString& playlistid ) { - return (PlaylistInterface::RepeatMode)value( QString( "ui/playlist/%1/repeatMode" ).arg( playlistid )).toInt(); -} - - -QList -TomahawkSettings::recentlyPlayedPlaylists() const -{ - QStringList playlist_guids = value( "playlists/recentlyPlayed" ).toStringList(); - - QList playlists; - foreach( const QString& guid, playlist_guids ) - { - playlist_ptr pl = Playlist::load( guid ); - if ( !pl.isNull() ) - playlists << pl; - } - - return playlists; + return (PlaylistModes::RepeatMode)value( QString( "ui/playlist/%1/repeatMode" ).arg( playlistid )).toInt(); } @@ -930,16 +1018,16 @@ TomahawkSettings::recentlyPlayedPlaylistGuids( unsigned int amount ) const void -TomahawkSettings::appendRecentlyPlayedPlaylist( const Tomahawk::playlist_ptr& playlist ) +TomahawkSettings::appendRecentlyPlayedPlaylist( const QString& playlistguid, int sourceId ) { QStringList playlist_guids = value( "playlists/recentlyPlayed" ).toStringList(); - playlist_guids.removeAll( playlist->guid() ); - playlist_guids.append( playlist->guid() ); + playlist_guids.removeAll( playlistguid ); + playlist_guids.append( playlistguid ); setValue( "playlists/recentlyPlayed", playlist_guids ); - emit recentlyPlayedPlaylistAdded( playlist ); + emit recentlyPlayedPlaylistAdded( playlistguid, sourceId ); } diff --git a/src/libtomahawk/TomahawkSettings.h b/src/libtomahawk/TomahawkSettings.h index e7c6156e7..b529d9c4a 100644 --- a/src/libtomahawk/TomahawkSettings.h +++ b/src/libtomahawk/TomahawkSettings.h @@ -1,7 +1,7 @@ /* === This file is part of Tomahawk Player - === * * Copyright 2010-2011, Christian Muehlhaeuser - * Copyright 2010-2011 Leo Franchi + * Copyright 2010-2012 Leo Franchi * Copyright 2010-2012, Jeff Mitchell * * Tomahawk is free software: you can redistribute it and/or modify @@ -21,16 +21,14 @@ #ifndef TOMAHAWK_SETTINGS_H #define TOMAHAWK_SETTINGS_H -#include "Playlist.h" - -#include "playlist/PlaylistUpdaterInterface.h" +#include "DllMacro.h" +#include "Typedefs.h" #include #include +#include -#include "DllMacro.h" - -#define TOMAHAWK_SETTINGS_VERSION 10 +#define TOMAHAWK_SETTINGS_VERSION 13 /** * Convenience wrapper around QSettings for tomahawk-specific config @@ -49,11 +47,13 @@ public: /// General settings virtual QString storageCacheLocation() const; + virtual QStringList scannerPaths() const; /// QDesktopServices::MusicLocation in TomahawkSettingsGui void setScannerPaths( const QStringList& paths ); bool hasScannerPaths() const; uint scannerTime() const; void setScannerTime( uint time ); + uint infoSystemCacheVersion() const; void setInfoSystemCacheVersion( uint version ); @@ -83,18 +83,21 @@ public: bool enableEchonestCatalogs() const; void setEnableEchonestCatalogs( bool enable ); + /// Audio stuff + unsigned int volume() const; + void setVolume( unsigned int volume ); + /// Playlist stuff QByteArray playlistColumnSizes( const QString& playlistid ) const; void setPlaylistColumnSizes( const QString& playlistid, const QByteArray& state ); - QList recentlyPlayedPlaylists() const; QStringList recentlyPlayedPlaylistGuids( unsigned int amount = 0 ) const; - void appendRecentlyPlayedPlaylist( const Tomahawk::playlist_ptr& playlist ); + void appendRecentlyPlayedPlaylist( const QString& playlistGuid, int sourceId ); bool shuffleState( const QString& playlistid ) const; void setShuffleState( const QString& playlistid, bool state ); - Tomahawk::PlaylistInterface::RepeatMode repeatMode( const QString& playlistid ); - void setRepeatMode( const QString& playlistid, Tomahawk::PlaylistInterface::RepeatMode mode ); + Tomahawk::PlaylistModes::RepeatMode repeatMode( const QString& playlistid ); + void setRepeatMode( const QString& playlistid, Tomahawk::PlaylistModes::RepeatMode mode ); // remove shuffle state and repeat state void removePlaylistSettings( const QString& playlistid ); @@ -207,13 +210,15 @@ public: signals: void changed(); - void recentlyPlayedPlaylistAdded( const Tomahawk::playlist_ptr& playlist ); + void recentlyPlayedPlaylistAdded( const QString& playlistId, int sourceId ); private slots: void updateIndex(); private: void doInitialSetup(); + void createLastFmAccount(); + void createSpotifyAccount(); void doUpgrade( int oldVersion, int newVersion ); static TomahawkSettings* s_instance; diff --git a/src/libtomahawk/TomahawkSettingsGui.cpp b/src/libtomahawk/TomahawkSettingsGui.cpp index dea0c5236..2e6932fda 100644 --- a/src/libtomahawk/TomahawkSettingsGui.cpp +++ b/src/libtomahawk/TomahawkSettingsGui.cpp @@ -30,7 +30,7 @@ inline QDataStream& operator<<(QDataStream& out, const AtticaManager::StateHash& foreach( const QString& key, states.keys() ) { AtticaManager::Resolver resolver = states[ key ]; - out << key << resolver.version << resolver.scriptPath << (qint32)resolver.state << resolver.userRating; + out << key << resolver.version << resolver.scriptPath << (qint32)resolver.state << resolver.userRating << resolver.binary; } return out; } @@ -38,23 +38,30 @@ inline QDataStream& operator<<(QDataStream& out, const AtticaManager::StateHash& inline QDataStream& operator>>(QDataStream& in, AtticaManager::StateHash& states) { - quint32 count = 0, version = 0; - in >> version; + quint32 count = 0, configVersion = 0; + in >> configVersion; in >> count; for ( uint i = 0; i < count; i++ ) { QString key, version, scriptPath; qint32 state, userRating; + bool binary = false; in >> key; in >> version; in >> scriptPath; in >> state; in >> userRating; - states[ key ] = AtticaManager::Resolver( version, scriptPath, userRating, (AtticaManager::ResolverState)state ); + if ( configVersion > 10 ) + { + // V11 includes 'bool binary' flag + in >> binary; + } + states[ key ] = AtticaManager::Resolver( version, scriptPath, userRating, (AtticaManager::ResolverState)state, binary ); } return in; } + TomahawkSettingsGui* TomahawkSettingsGui::instanceGui() { @@ -103,12 +110,14 @@ TomahawkSettingsGui::setAtticaResolverState( const QString& resolver, AtticaMana sync(); } + AtticaManager::StateHash TomahawkSettingsGui::atticaResolverStates() const { return value( "script/atticaresolverstates" ).value< AtticaManager::StateHash >(); } + void TomahawkSettingsGui::setAtticaResolverStates( const AtticaManager::StateHash states ) { diff --git a/src/libtomahawk/Typedefs.h b/src/libtomahawk/Typedefs.h index cce94d236..e442f2328 100644 --- a/src/libtomahawk/Typedefs.h +++ b/src/libtomahawk/Typedefs.h @@ -77,6 +77,128 @@ namespace Tomahawk class ExternalResolver; typedef boost::function ResolverFactoryFunc; + namespace PlaylistModes { + enum RepeatMode { NoRepeat, RepeatOne, RepeatAll }; + enum ViewMode { Unknown, Tree, Flat, Album }; + enum SeekRestrictions { NoSeekRestrictions, NoSeek }; + enum SkipRestrictions { NoSkipRestrictions, NoSkipForwards, NoSkipBackwards, NoSkip }; + enum RetryMode { NoRetry, Retry }; + enum LatchMode { StayOnSong, RealTime }; + } + + + struct SerializedUpdater { + QString type; + QVariantHash customData; + + SerializedUpdater( const QString& t, const QVariantHash cd = QVariantHash() ) : type( t ), customData( cd ) {} + SerializedUpdater() {} + }; + + typedef QMultiHash< QString, SerializedUpdater > SerializedUpdaters; + typedef QList< SerializedUpdater > SerializedUpdaterList; + + + namespace InfoSystem { + + + enum InfoType { // as items are saved in cache, mark them here to not change them + 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, + InfoTrackSimilars = 23, // cached -- do not change + + InfoArtistID = 25, + InfoArtistName = 26, + InfoArtistBiography = 27, + InfoArtistImages = 28, //cached -- do not change + InfoArtistBlog = 29, + InfoArtistFamiliarity = 30, + InfoArtistHotttness = 31, + InfoArtistSongs = 32, //cached -- do not change + InfoArtistSimilars = 33, //cached -- do not change + InfoArtistNews = 34, + InfoArtistProfile = 35, + InfoArtistReviews = 36, + InfoArtistTerms = 37, + InfoArtistLinks = 38, + InfoArtistVideos = 39, + InfoArtistReleases = 40, + + InfoAlbumID = 42, + InfoAlbumCoverArt = 43, //cached -- do not change + InfoAlbumName = 44, + InfoAlbumArtist = 45, + InfoAlbumDate = 46, + InfoAlbumGenre = 47, + InfoAlbumComposer = 48, + InfoAlbumSongs = 49, + + /** \var Tomahawk::InfoSystem::InfoType Tomahawk::InfoSystem::InfoType::InfoChartCapabilities + * Documentation for InfoChartCapabilities + * + * Clients of this InfoType expect a QVariant + * + */ + InfoChartCapabilities = 50, + /** + * Documentation for InfoChartArtists + */ + InfoChart = 51, + + InfoNewReleaseCapabilities = 52, + InfoNewRelease = 53, + + InfoMiscTopHotttness = 60, + InfoMiscTopTerms = 61, + + InfoSubmitNowPlaying = 70, + InfoSubmitScrobble = 71, + + InfoNowPlaying = 80, + InfoNowPaused = 81, + InfoNowResumed = 82, + InfoNowStopped = 83, + InfoTrackUnresolved = 84, + + InfoLove = 90, + InfoUnLove = 91, + InfoShareTrack = 92, + + InfoNotifyUser = 100, + + InfoLastInfo = 101 //WARNING: *ALWAYS* keep this last! + }; + + class InfoPlugin; + + typedef QMap< InfoType, QVariant > InfoTypeMap; + typedef QMap< InfoType, uint > InfoTimeoutMap; + typedef QHash< QString, QString > InfoStringHash; + typedef QPair< QVariantMap, QVariant > PushInfoPair; + + typedef QWeakPointer< InfoPlugin > InfoPluginPtr; + } }; // ns typedef int AudioErrorCode; @@ -93,6 +215,7 @@ inline static QString uuid() return q; } +Q_DECLARE_METATYPE( QModelIndex ) Q_DECLARE_METATYPE( QPersistentModelIndex ) #endif // TYPEDEFS_H diff --git a/src/libtomahawk/ViewManager.cpp b/src/libtomahawk/ViewManager.cpp index f8aa166e3..445eea592 100644 --- a/src/libtomahawk/ViewManager.cpp +++ b/src/libtomahawk/ViewManager.cpp @@ -28,15 +28,12 @@ #include "topbar/TopBar.h" #include "TreeModel.h" -#include "CollectionFlatModel.h" -#include "CollectionView.h" #include "PlaylistModel.h" #include "PlaylistView.h" -#include "TrackProxyModel.h" -#include "TrackModel.h" -#include "ArtistView.h" -#include "AlbumView.h" -#include "AlbumProxyModel.h" +#include "PlayableProxyModel.h" +#include "PlayableModel.h" +#include "TreeView.h" +#include "GridView.h" #include "AlbumModel.h" #include "SourceList.h" #include "TomahawkSettings.h" @@ -46,6 +43,7 @@ #include "RecentlyPlayedModel.h" #include "dynamic/widgets/DynamicWidget.h" +#include "widgets/NewReleasesWidget.h" #include "widgets/WelcomeWidget.h" #include "widgets/WhatsHotWidget.h" #include "widgets/infowidgets/SourceInfoWidget.h" @@ -79,7 +77,8 @@ ViewManager::ViewManager( QObject* parent ) , m_newReleasesWidget( new NewReleasesWidget() ) , m_topLovedWidget( 0 ) , m_recentPlaysWidget( 0 ) - , m_currentMode( PlaylistInterface::Tree ) + , m_currentPage( 0 ) + , m_currentMode( PlaylistModes::Tree ) , m_loaded( false ) { s_instance = this; @@ -94,19 +93,15 @@ ViewManager::ViewManager( QObject* parent ) m_widget->layout()->addWidget( m_stack ); m_widget->layout()->addWidget( m_contextWidget ); - m_superCollectionView = new ArtistView(); + m_superCollectionView = new TreeView(); m_superCollectionModel = new TreeModel( m_superCollectionView ); m_superCollectionView->setTreeModel( m_superCollectionModel ); - m_superCollectionView->setFrameShape( QFrame::NoFrame ); - m_superCollectionView->setAttribute( Qt::WA_MacShowFocusRect, 0 ); m_superCollectionView->setShowModes( false ); // m_superCollectionView->proxyModel()->setShowOfflineResults( false ); - m_superAlbumView = new AlbumView(); - m_superAlbumModel = new AlbumModel( m_superAlbumView ); - m_superAlbumView->setAlbumModel( m_superAlbumModel ); - m_superAlbumView->setFrameShape( QFrame::NoFrame ); - m_superAlbumView->setAttribute( Qt::WA_MacShowFocusRect, 0 ); + m_superGridView = new GridView(); + m_superAlbumModel = new AlbumModel( m_superGridView ); + m_superGridView->setPlayableModel( m_superAlbumModel ); m_stack->setContentsMargins( 0, 0, 0, 0 ); m_widget->setContentsMargins( 0, 0, 0, 0 ); @@ -149,8 +144,6 @@ ViewManager::createPageForPlaylist( const playlist_ptr& pl ) PlaylistModel* model = new PlaylistModel(); view->setPlaylistModel( model ); model->loadPlaylist( pl ); - view->setFrameShape( QFrame::NoFrame ); - view->setAttribute( Qt::WA_MacShowFocusRect, 0 ); pl->resolve(); m_playlistViews.insert( pl, view ); @@ -239,12 +232,12 @@ ViewManager::show( const Tomahawk::artist_ptr& artist ) Tomahawk::ViewPage* -ViewManager::show( const Tomahawk::album_ptr& album, Tomahawk::ModelMode initialMode ) +ViewManager::show( const Tomahawk::album_ptr& album ) { AlbumInfoWidget* swidget; if ( !m_albumViews.contains( album ) || m_albumViews.value( album ).isNull() ) { - swidget = new AlbumInfoWidget( album, initialMode ); + swidget = new AlbumInfoWidget( album ); m_albumViews.insert( album, swidget ); } else @@ -282,16 +275,14 @@ ViewManager::show( const Tomahawk::collection_ptr& collection ) qDebug() << Q_FUNC_INFO << m_currentMode; m_currentCollection = collection; ViewPage* shown = 0; - if ( m_currentMode == PlaylistInterface::Flat ) + if ( m_currentMode == PlaylistModes::Flat ) { - CollectionView* view; +/* CollectionView* view; if ( !m_collectionViews.contains( collection ) || m_collectionViews.value( collection ).isNull() ) { view = new CollectionView(); CollectionFlatModel* model = new CollectionFlatModel(); - view->setTrackModel( model ); - view->setFrameShape( QFrame::NoFrame ); - view->setAttribute( Qt::WA_MacShowFocusRect, 0 ); + view->setPlayableModel( model ); model->addCollection( collection ); @@ -303,19 +294,22 @@ ViewManager::show( const Tomahawk::collection_ptr& collection ) } shown = view; - setPage( view ); + setPage( view );*/ } - if ( m_currentMode == PlaylistInterface::Tree ) + if ( m_currentMode == PlaylistModes::Tree ) { - ArtistView* view; + TreeView* view; if ( !m_treeViews.contains( collection ) || m_treeViews.value( collection ).isNull() ) { - view = new ArtistView(); + view = new TreeView(); TreeModel* model = new TreeModel(); view->setTreeModel( model ); - view->setFrameShape( QFrame::NoFrame ); - view->setAttribute( Qt::WA_MacShowFocusRect, 0 ); + + if ( collection && collection->source()->isLocal() ) + view->setEmptyTip( tr( "After you have scanned your music collection you will find your tracks right here." ) ); + else + view->setEmptyTip( tr( "This collection is empty." ) ); model->addCollection( collection ); @@ -330,23 +324,21 @@ ViewManager::show( const Tomahawk::collection_ptr& collection ) setPage( view ); } - if ( m_currentMode == PlaylistInterface::Album ) + if ( m_currentMode == PlaylistModes::Album ) { - AlbumView* aview; - if ( !m_collectionAlbumViews.contains( collection ) || m_collectionAlbumViews.value( collection ).isNull() ) + GridView* aview; + if ( !m_collectionGridViews.contains( collection ) || m_collectionGridViews.value( collection ).isNull() ) { - aview = new AlbumView(); + aview = new GridView(); AlbumModel* amodel = new AlbumModel( aview ); - aview->setAlbumModel( amodel ); - aview->setFrameShape( QFrame::NoFrame ); - aview->setAttribute( Qt::WA_MacShowFocusRect, 0 ); + aview->setPlayableModel( amodel ); amodel->addCollection( collection ); - m_collectionAlbumViews.insert( collection, aview ); + m_collectionGridViews.insert( collection, aview ); } else { - aview = m_collectionAlbumViews.value( collection ).data(); + aview = m_collectionGridViews.value( collection ).data(); } shown = aview; @@ -407,20 +399,20 @@ ViewManager::showSuperCollection() m_superAlbumModel->setTitle( tr( "All available albums" ) ); ViewPage* shown = 0; - if ( m_currentMode == PlaylistInterface::Tree ) + if ( m_currentMode == PlaylistModes::Tree ) { shown = m_superCollectionView; setPage( m_superCollectionView ); } - else if ( m_currentMode == PlaylistInterface::Flat ) + else if ( m_currentMode == PlaylistModes::Flat ) { shown = m_superCollectionView; setPage( m_superCollectionView ); } - else if ( m_currentMode == PlaylistInterface::Album ) + else if ( m_currentMode == PlaylistModes::Album ) { - shown = m_superAlbumView; - setPage( m_superAlbumView ); + shown = m_superGridView; + setPage( m_superGridView ); } emit numSourcesChanged( m_superCollections.count() ); @@ -435,13 +427,13 @@ ViewManager::playlistInterfaceChanged( Tomahawk::playlistinterface_ptr interface playlist_ptr pl = playlistForInterface( interface ); if ( !pl.isNull() ) { - TomahawkSettings::instance()->appendRecentlyPlayedPlaylist( pl ); + TomahawkSettings::instance()->appendRecentlyPlayedPlaylist( pl->guid(), pl->author()->id() ); } else { pl = dynamicPlaylistForInterface( interface ); if ( !pl.isNull() ) - TomahawkSettings::instance()->appendRecentlyPlayedPlaylist( pl ); + TomahawkSettings::instance()->appendRecentlyPlayedPlaylist( pl->guid(), pl->author()->id() ); } } @@ -490,13 +482,11 @@ ViewManager::showRecentPlaysPage() if ( !m_recentPlaysWidget ) { PlaylistView* pv = new PlaylistView( m_widget ); - pv->setFrameShape( QFrame::NoFrame ); - pv->setAttribute( Qt::WA_MacShowFocusRect, 0 ); RecentlyPlayedModel* raModel = new RecentlyPlayedModel( source_ptr(), pv ); raModel->setTitle( tr( "Recently Played Tracks" ) ); raModel->setDescription( tr( "Recently played tracks from all your friends" ) ); - raModel->setStyle( TrackModel::Large ); + raModel->setStyle( PlayableModel::Large ); PlaylistLargeItemDelegate* del = new PlaylistLargeItemDelegate( PlaylistLargeItemDelegate::RecentlyPlayed, pv, pv->proxyModel() ); connect( del, SIGNAL( updateIndex( QModelIndex ) ), pv, SLOT( update( QModelIndex ) ) ); @@ -516,7 +506,7 @@ ViewManager::setTableMode() { qDebug() << Q_FUNC_INFO; - m_currentMode = PlaylistInterface::Flat; + m_currentMode = PlaylistModes::Flat; if ( isSuperCollectionVisible() ) showSuperCollection(); @@ -530,7 +520,7 @@ ViewManager::setTreeMode() { qDebug() << Q_FUNC_INFO; - m_currentMode = PlaylistInterface::Tree; + m_currentMode = PlaylistModes::Tree; if ( isSuperCollectionVisible() ) showSuperCollection(); @@ -544,7 +534,7 @@ ViewManager::setAlbumMode() { qDebug() << Q_FUNC_INFO; - m_currentMode = PlaylistInterface::Album; + m_currentMode = PlaylistModes::Album; if ( isSuperCollectionVisible() ) showSuperCollection(); @@ -553,35 +543,6 @@ ViewManager::setAlbumMode() } -void -ViewManager::historyBack() -{ - ViewPage* oldPage = m_pageHistory.takeFirst(); - - ViewPage* newPage = m_pageHistory.first(); - qDebug() << "Showing page after moving backwards in history:" << newPage->widget()->metaObject()->className(); - setPage( newPage, false ); - - delete oldPage; -} - - -void -ViewManager::removeFromHistory ( ViewPage* p ) -{ - if ( currentPage() == p ) - { - historyBack(); - } - else - { - m_pageHistory.removeAll( p ); - delete p; - } - -} - - void ViewManager::setFilter( const QString& filter ) { @@ -597,13 +558,80 @@ ViewManager::setFilter( const QString& filter ) void ViewManager::applyFilter() { - qDebug() << Q_FUNC_INFO; - if ( currentPlaylistInterface() && currentPlaylistInterface()->filter() != m_filter ) currentPlaylistInterface()->setFilter( m_filter ); } +void +ViewManager::historyBack() +{ + if ( !m_pageHistoryBack.count() ) + return; + + ViewPage* page = m_pageHistoryBack.takeLast(); + + if ( m_currentPage ) + { + m_pageHistoryFwd << m_currentPage; + tDebug() << "Moved to forward history:" << m_currentPage->widget()->metaObject()->className(); + } + + tDebug() << "Showing page after moving backwards in history:" << page->widget()->metaObject()->className(); + setPage( page, false ); +} + + +void +ViewManager::historyForward() +{ + if ( !m_pageHistoryFwd.count() ) + return; + + ViewPage* page = m_pageHistoryFwd.takeLast(); + + if ( m_currentPage ) + { + m_pageHistoryBack << m_currentPage; + tDebug() << "Moved to backward history:" << m_currentPage->widget()->metaObject()->className(); + } + + tDebug() << "Showing page after moving forwards in history:" << page->widget()->metaObject()->className(); + setPage( page, false ); +} + + +QList +ViewManager::historyPages() const +{ + return m_pageHistoryBack + m_pageHistoryFwd; +} + + +void +ViewManager::destroyPage( ViewPage* page ) +{ + if ( m_currentPage == page ) + { + m_currentPage = 0; + historyBack(); + return; + } + + QList< Tomahawk::ViewPage* > p = historyPages(); + if ( p.contains( page ) ) + { + m_pageHistoryBack.removeAll( page ); + m_pageHistoryFwd.removeAll( page ); + + emit historyBackAvailable( m_pageHistoryBack.count() ); + emit historyForwardAvailable( m_pageHistoryFwd.count() ); + + delete page; + } +} + + void ViewManager::setPage( ViewPage* page, bool trackHistory ) { @@ -614,20 +642,20 @@ ViewManager::setPage( ViewPage* page, bool trackHistory ) saveCurrentPlaylistSettings(); unlinkPlaylist(); - if ( !m_pageHistory.contains( page ) ) + if ( m_stack->indexOf( page->widget() ) < 0 ) { m_stack->addWidget( page->widget() ); } - else - { - if ( trackHistory ) - m_pageHistory.removeAll( page ); - } - if ( trackHistory ) + if ( m_currentPage && trackHistory ) { - m_pageHistory.insert( 0, page ); + m_pageHistoryBack << m_currentPage; + m_pageHistoryFwd.clear(); } + m_currentPage = page; + + emit historyBackAvailable( m_pageHistoryBack.count() ); + emit historyForwardAvailable( m_pageHistoryFwd.count() ); qDebug() << "View page shown:" << page->title(); emit viewPageActivated( page ); @@ -688,8 +716,8 @@ ViewManager::unlinkPlaylist() disconnect( currentPlaylistInterface().data(), SIGNAL( trackCountChanged( unsigned int ) ), this, SIGNAL( numShownChanged( unsigned int ) ) ); - disconnect( currentPlaylistInterface().data(), SIGNAL( repeatModeChanged( Tomahawk::PlaylistInterface::RepeatMode ) ), - this, SIGNAL( repeatModeChanged( Tomahawk::PlaylistInterface::RepeatMode ) ) ); + disconnect( currentPlaylistInterface().data(), SIGNAL( repeatModeChanged( Tomahawk::PlaylistModes::RepeatMode ) ), + this, SIGNAL( repeatModeChanged( Tomahawk::PlaylistModes::RepeatMode ) ) ); disconnect( currentPlaylistInterface().data(), SIGNAL( shuffleModeChanged( bool ) ), this, SIGNAL( shuffleModeChanged( bool ) ) ); @@ -727,8 +755,8 @@ ViewManager::updateView() connect( currentPlaylistInterface().data(), SIGNAL( trackCountChanged( unsigned int ) ), SIGNAL( numShownChanged( unsigned int ) ) ); - connect( currentPlaylistInterface().data(), SIGNAL( repeatModeChanged( Tomahawk::PlaylistInterface::RepeatMode ) ), - SIGNAL( repeatModeChanged( Tomahawk::PlaylistInterface::RepeatMode ) ) ); + connect( currentPlaylistInterface().data(), SIGNAL( repeatModeChanged( Tomahawk::PlaylistModes::RepeatMode ) ), + SIGNAL( repeatModeChanged( Tomahawk::PlaylistModes::RepeatMode ) ) ); connect( currentPlaylistInterface().data(), SIGNAL( shuffleModeChanged( bool ) ), SIGNAL( shuffleModeChanged( bool ) ) ); @@ -815,13 +843,14 @@ ViewManager::loadCurrentPlaylistSettings() void ViewManager::onWidgetDestroyed( QWidget* widget ) { - qDebug() << "Destroyed child:" << widget << widget->metaObject()->className(); + tDebug() << "Destroyed child:" << widget << widget->metaObject()->className(); bool resetWidget = ( m_stack->currentWidget() == widget ); - for ( int i = 0; i < m_pageHistory.count(); i++ ) + QList< Tomahawk::ViewPage* > p = historyPages(); + for ( int i = 0; i < p.count(); i++ ) { - ViewPage* page = m_pageHistory.at( i ); + ViewPage* page = p.at( i ); if ( page->widget() != widget ) continue; @@ -833,11 +862,9 @@ ViewManager::onWidgetDestroyed( QWidget* widget ) { m_dynamicWidgets.remove( dynamicPlaylistForInterface( page->playlistInterface() ) ); } - - if ( page->widget() == widget && !resetWidget ) - { - m_pageHistory.removeAt( i ); - } + + m_pageHistoryBack.removeAll( page ); + m_pageHistoryFwd.removeAll( page ); } m_stack->removeWidget( widget ); @@ -850,7 +877,7 @@ ViewManager::onWidgetDestroyed( QWidget* widget ) void -ViewManager::setRepeatMode( Tomahawk::PlaylistInterface::RepeatMode mode ) +ViewManager::setRepeatMode( Tomahawk::PlaylistModes::RepeatMode mode ) { if ( currentPlaylistInterface() ) currentPlaylistInterface()->setRepeatMode( mode ); @@ -891,14 +918,6 @@ ViewManager::setTomahawkLoaded() } - -ViewPage* -ViewManager::pageForCollection( const collection_ptr& col ) const -{ - return m_collectionViews.value( col ).data(); -} - - ViewPage* ViewManager::pageForDynPlaylist(const dynplaylist_ptr& pl) const { @@ -916,14 +935,14 @@ ViewManager::pageForPlaylist(const playlist_ptr& pl) const ViewPage* ViewManager::pageForInterface( Tomahawk::playlistinterface_ptr interface ) const { - for ( int i = 0; i < m_pageHistory.count(); i++ ) +/* for ( int i = 0; i < m_pageHistory.count(); i++ ) { ViewPage* page = m_pageHistory.at( i ); if ( page->playlistInterface() == interface ) return page; if ( page->playlistInterface() && page->playlistInterface()->hasChildInterface( interface ) ) return page; - } + }*/ return 0; } @@ -942,7 +961,7 @@ ViewManager::currentPlaylistInterface() const Tomahawk::ViewPage* ViewManager::currentPage() const { - return m_pageHistory.isEmpty() ? 0 : m_pageHistory.front(); + return m_currentPage; } @@ -979,18 +998,11 @@ ViewManager::dynamicPlaylistForInterface( Tomahawk::playlistinterface_ptr interf Tomahawk::collection_ptr ViewManager::collectionForInterface( Tomahawk::playlistinterface_ptr interface ) const { - foreach ( QWeakPointer view, m_collectionViews.values() ) + foreach ( QWeakPointer view, m_collectionGridViews.values() ) { if ( view.data()->playlistInterface() == interface ) { - return m_collectionViews.key( view ); - } - } - foreach ( QWeakPointer view, m_collectionAlbumViews.values() ) - { - if ( view.data()->playlistInterface() == interface ) - { - return m_collectionAlbumViews.key( view ); + return m_collectionGridViews.key( view ); } } @@ -1001,9 +1013,9 @@ ViewManager::collectionForInterface( Tomahawk::playlistinterface_ptr interface ) bool ViewManager::isSuperCollectionVisible() const { - return ( m_pageHistory.count() && + return ( currentPage() != 0 && ( currentPage()->playlistInterface() == m_superCollectionView->playlistInterface() || - currentPage()->playlistInterface() == m_superAlbumView->playlistInterface() ) ); + currentPage()->playlistInterface() == m_superGridView->playlistInterface() ) ); } @@ -1019,16 +1031,57 @@ ViewManager::showCurrentTrack() // reset the correct mode, if the user has changed it since - if ( dynamic_cast< CollectionView* >( page ) ) - m_currentMode = PlaylistInterface::Flat; - else if ( dynamic_cast< AlbumView* >( page ) ) - m_currentMode = PlaylistInterface::Album; - else if ( dynamic_cast< ArtistView* >( page ) ) - m_currentMode = PlaylistInterface::Tree; + if ( dynamic_cast< TrackView* >( page ) ) + m_currentMode = PlaylistModes::Flat; + else if ( dynamic_cast< GridView* >( page ) ) + m_currentMode = PlaylistModes::Album; + else if ( dynamic_cast< TreeView* >( page ) ) + m_currentMode = PlaylistModes::Tree; else return; - emit modeChanged( (PlaylistInterface::ViewMode)m_currentMode ); + emit modeChanged( (PlaylistModes::ViewMode)m_currentMode ); } } + +Tomahawk::ViewPage* +ViewManager::welcomeWidget() const +{ + return m_welcomeWidget; +} + + +Tomahawk::ViewPage* +ViewManager::whatsHotWidget() const +{ + return m_whatsHotWidget; +} + + +Tomahawk::ViewPage* +ViewManager::newReleasesWidget() const +{ + return m_newReleasesWidget; +} + + +Tomahawk::ViewPage* +ViewManager::topLovedWidget() const +{ + return m_topLovedWidget; +} + + +Tomahawk::ViewPage* +ViewManager::recentPlaysWidget() const +{ + return m_recentPlaysWidget; +} + + +TreeView* +ViewManager::superCollectionView() const +{ + return m_superCollectionView; +} diff --git a/src/libtomahawk/ViewManager.h b/src/libtomahawk/ViewManager.h index 31d86012e..d4fefe1bf 100644 --- a/src/libtomahawk/ViewManager.h +++ b/src/libtomahawk/ViewManager.h @@ -29,21 +29,16 @@ #include "PlaylistInterface.h" #include "playlist/QueueView.h" #include "ViewPage.h" -#include "widgets/WelcomeWidget.h" -#include "widgets/WhatsHotWidget.h" -#include "widgets/NewReleasesWidget.h" #include "DllMacro.h" class AnimatedSplitter; class AlbumModel; -class AlbumView; +class GridView; class AlbumInfoWidget; class ArtistInfoWidget; -class ArtistView; +class TreeView; class CollectionModel; -class CollectionFlatModel; -class CollectionView; class ContextWidget; class PlaylistModel; class PlaylistView; @@ -56,6 +51,7 @@ class SourceInfoWidget; class InfoBar; class TopBar; class TrackInfoWidget; +class NewReleasesWidget; class WelcomeWidget; class WhatsHotWidget; class QPushButton; @@ -91,17 +87,16 @@ public: Tomahawk::ViewPage* show( Tomahawk::ViewPage* page ); - Tomahawk::ViewPage* welcomeWidget() const { return m_welcomeWidget; } - Tomahawk::ViewPage* whatsHotWidget() const { return m_whatsHotWidget; } - Tomahawk::ViewPage* newReleasesWidget() const { return m_newReleasesWidget; } - Tomahawk::ViewPage* topLovedWidget() const { return m_topLovedWidget; } - Tomahawk::ViewPage* recentPlaysWidget() const { return m_recentPlaysWidget; } - ArtistView* superCollectionView() const { return m_superCollectionView; } + Tomahawk::ViewPage* welcomeWidget() const; + Tomahawk::ViewPage* whatsHotWidget() const; + Tomahawk::ViewPage* newReleasesWidget() const; + Tomahawk::ViewPage* topLovedWidget() const; + Tomahawk::ViewPage* recentPlaysWidget() const; + TreeView* superCollectionView() const; /// Get the view page for the given item. Not pretty... Tomahawk::ViewPage* pageForPlaylist( const Tomahawk::playlist_ptr& pl ) const; Tomahawk::ViewPage* pageForDynPlaylist( const Tomahawk::dynplaylist_ptr& pl ) const; - Tomahawk::ViewPage* pageForCollection( const Tomahawk::collection_ptr& pl ) const; /// Get a playlist (or dynamic playlist ) from a ViewPage* if the page is PlaylistView or DynamicWidget. /// Lives here but used by SourcesModel @@ -119,13 +114,13 @@ signals: void numArtistsChanged( unsigned int artists ); void numShownChanged( unsigned int shown ); - void repeatModeChanged( Tomahawk::PlaylistInterface::RepeatMode mode ); + void repeatModeChanged( Tomahawk::PlaylistModes::RepeatMode mode ); void shuffleModeChanged( bool enabled ); void statsAvailable( bool b ); void modesAvailable( bool b ); void filterAvailable( bool b ); - void modeChanged( Tomahawk::PlaylistInterface::ViewMode mode ); + void modeChanged( Tomahawk::PlaylistModes::ViewMode mode ); void playClicked(); void pauseClicked(); @@ -137,6 +132,9 @@ signals: void hideQueueRequested(); void tomahawkLoaded(); + + void historyBackAvailable( bool avail ); + void historyForwardAvailable( bool avail ); public slots: Tomahawk::ViewPage* showSuperCollection(); @@ -151,13 +149,16 @@ 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::ModelMode withInitialMode = Tomahawk::InfoSystemMode ); + Tomahawk::ViewPage* show( const Tomahawk::album_ptr& album ); Tomahawk::ViewPage* show( const Tomahawk::query_ptr& query ); Tomahawk::ViewPage* show( const Tomahawk::collection_ptr& collection ); Tomahawk::ViewPage* show( const Tomahawk::source_ptr& source ); void historyBack(); - void removeFromHistory( Tomahawk::ViewPage* p ); + void historyForward(); + + QList< Tomahawk::ViewPage* > historyPages() const; + void destroyPage( Tomahawk::ViewPage* page ); void showQueue() { emit showQueueRequested(); } void hideQueue() { emit hideQueueRequested(); } @@ -166,7 +167,7 @@ public slots: void setTableMode(); void setAlbumMode(); - void setRepeatMode( Tomahawk::PlaylistInterface::RepeatMode mode ); + void setRepeatMode( Tomahawk::PlaylistModes::RepeatMode mode ); void setShuffled( bool enabled ); void playlistInterfaceChanged( Tomahawk::playlistinterface_ptr ); @@ -201,9 +202,9 @@ private: AnimatedSplitter* m_splitter; AlbumModel* m_superAlbumModel; - AlbumView* m_superAlbumView; + GridView* m_superGridView; TreeModel* m_superCollectionModel; - ArtistView* m_superCollectionView; + TreeView* m_superCollectionView; QueueView* m_queue; WelcomeWidget* m_welcomeWidget; WhatsHotWidget* m_whatsHotWidget; @@ -214,16 +215,17 @@ private: QList< Tomahawk::collection_ptr > m_superCollections; QHash< Tomahawk::dynplaylist_ptr, QWeakPointer > m_dynamicWidgets; - QHash< Tomahawk::collection_ptr, QWeakPointer > m_collectionViews; - QHash< Tomahawk::collection_ptr, QWeakPointer > m_treeViews; - QHash< Tomahawk::collection_ptr, QWeakPointer > m_collectionAlbumViews; + QHash< Tomahawk::collection_ptr, QWeakPointer > m_treeViews; + QHash< Tomahawk::collection_ptr, QWeakPointer > m_collectionGridViews; QHash< Tomahawk::artist_ptr, QWeakPointer > m_artistViews; QHash< Tomahawk::album_ptr, QWeakPointer > m_albumViews; QHash< Tomahawk::query_ptr, QWeakPointer > m_trackViews; QHash< Tomahawk::playlist_ptr, QWeakPointer > m_playlistViews; QHash< Tomahawk::source_ptr, QWeakPointer > m_sourceViews; - QList m_pageHistory; + QList m_pageHistoryBack; + QList m_pageHistoryFwd; + Tomahawk::ViewPage* m_currentPage; Tomahawk::collection_ptr m_currentCollection; int m_currentMode; diff --git a/src/libtomahawk/ViewPage.cpp b/src/libtomahawk/ViewPage.cpp index 2a1733ba9..6ff2f7ccd 100644 --- a/src/libtomahawk/ViewPage.cpp +++ b/src/libtomahawk/ViewPage.cpp @@ -19,6 +19,7 @@ #include "ViewPage.h" #include "utils/Logger.h" +#include "Source.h" using namespace Tomahawk; diff --git a/src/libtomahawk/accounts/Account.cpp b/src/libtomahawk/accounts/Account.cpp index 6eb2c1754..be93ef641 100644 --- a/src/libtomahawk/accounts/Account.cpp +++ b/src/libtomahawk/accounts/Account.cpp @@ -19,6 +19,9 @@ #include "Account.h" +#include "TomahawkSettings.h" +#include "utils/Logger.h" + #include namespace Tomahawk @@ -116,50 +119,46 @@ Account::onError( int errorCode, const QString& error ) void -Account::keychainJobFinished(QKeychain::Job* j ) +Account::keychainJobFinished( QKeychain::Job* j ) { if ( j->error() == QKeychain::NoError ) { - QKeychain::ReadPasswordJob* readJob = qobject_cast< QKeychain::ReadPasswordJob* >( j ); - if ( readJob != 0 ) + if ( QKeychain::ReadPasswordJob* readJob = qobject_cast< QKeychain::ReadPasswordJob* >( j ) ) { tLog() << Q_FUNC_INFO << "readJob finished without errors"; - deserializeCredentials( readJob->binaryData() ); + + QVariantHash credentials; + QDataStream dataStream( readJob->binaryData() ); + dataStream >> credentials; + tLog() << Q_FUNC_INFO << readJob->key(); - tLog() << Q_FUNC_INFO << m_credentials; - emit credentialsChanged(); + + emit credentialsChanged( credentials ); } - else + else if ( QKeychain::WritePasswordJob* writeJob = qobject_cast< QKeychain::WritePasswordJob* >( j ) ) { - QKeychain::WritePasswordJob* writeJob = qobject_cast< QKeychain::WritePasswordJob* >( j ); - if ( writeJob != 0 ) - tLog() << Q_FUNC_INFO << "writeJob finished"; - else - { - QKeychain::DeletePasswordJob* deleteJob = qobject_cast< QKeychain::DeletePasswordJob* >( j ); - tLog() << Q_FUNC_INFO << "deleteJob finished"; - } + tLog() << Q_FUNC_INFO << "writeJob finished"; + } + else if ( QKeychain::DeletePasswordJob* deleteJob = qobject_cast< QKeychain::DeletePasswordJob* >( j ) ) + { + tLog() << Q_FUNC_INFO << "deleteJob finished"; } } + else + { tLog() << Q_FUNC_INFO << "Job finished with error: " << j->error() << " " << j->errorString(); + } + j->deleteLater(); -} - -void -Account::serializeCredentials( QByteArray& data ) -{ - QDataStream ds(&data, QIODevice::WriteOnly); - ds << m_credentials; -} - - -void -Account::deserializeCredentials( const QByteArray& data ) -{ - QDataStream ds2(data); - ds2 >> m_credentials; + m_workingKeychainJobs.removeAll( j ); + if ( !m_queuedKeychainJobs.isEmpty() ) + { + QKeychain::Job* j = m_queuedKeychainJobs.dequeue(); + j->start(); + m_workingKeychainJobs << j; + } } @@ -177,20 +176,11 @@ Account::syncConfig() s->beginGroup( "accounts/" + m_accountId ); s->setValue( "accountfriendlyname", m_accountFriendlyName ); s->setValue( "enabled", m_enabled ); - //s->setValue( "credentials", m_credentials ); s->setValue( "configuration", m_configuration ); s->setValue( "acl", m_acl ); s->setValue( "types", m_types ); s->endGroup(); s->sync(); - QKeychain::WritePasswordJob* j = new QKeychain::WritePasswordJob( QLatin1String( "tomahawkaccounts" ), this ); - j->setKey( m_accountId ); - j->setAutoDelete( false ); - QByteArray bData; - serializeCredentials( bData ); - j->setBinaryData( bData ); - connect( j, SIGNAL( finished( QKeychain::Job* ) ), this, SLOT( keychainJobFinished( QKeychain::Job* ) ) ); - j->start(); } @@ -205,6 +195,46 @@ Account::syncType() } +void +Account::saveCredentials( const QVariantHash &creds ) +{ + QByteArray data; + { + QDataStream ds( &data, QIODevice::WriteOnly ); + ds << creds; + } + + QKeychain::WritePasswordJob* j = new QKeychain::WritePasswordJob( QLatin1String( "tomahawkaccounts" ), this ); + j->setKey( m_accountId ); + j->setAutoDelete( false ); + j->setBinaryData( data ); + connect( j, SIGNAL( finished( QKeychain::Job* ) ), this, SLOT( keychainJobFinished( QKeychain::Job* ) ) ); + j->start(); + + m_workingKeychainJobs << j; +} + + +void +Account::loadCredentials() const +{ + QKeychain::ReadPasswordJob* j = new QKeychain::ReadPasswordJob( QLatin1String( "tomahawkaccounts" ), const_cast( this ) ); + j->setKey( m_accountId ); + j->setAutoDelete( false ); + connect( j, SIGNAL( finished( QKeychain::Job* ) ), this, SLOT( keychainJobFinished( QKeychain::Job* ) ) ); + + + if ( !m_workingKeychainJobs.isEmpty() ) + { + m_queuedKeychainJobs.enqueue( j ); + } + else + { + j->start(); + m_workingKeychainJobs << j; + } +} + void Account::loadFromConfig( const QString& accountId ) { @@ -213,16 +243,12 @@ Account::loadFromConfig( const QString& accountId ) s->beginGroup( "accounts/" + m_accountId ); m_accountFriendlyName = s->value( "accountfriendlyname", QString() ).toString(); m_enabled = s->value( "enabled", false ).toBool(); - //m_credentials = s->value( "credentials", QVariantHash() ).toHash(); m_configuration = s->value( "configuration", QVariantHash() ).toHash(); m_acl = s->value( "acl", QVariantMap() ).toMap(); m_types = s->value( "types", QStringList() ).toStringList(); s->endGroup(); - QKeychain::ReadPasswordJob* j = new QKeychain::ReadPasswordJob( QLatin1String( "tomahawk" ), this ); - j->setKey( m_accountId ); - j->setAutoDelete( false ); - connect( j, SIGNAL( finished( QKeychain::Job* ) ), this, SLOT( keychainJobFinished( QKeychain::Job* ) ) ); - j->start(); + + loadCredentials(); } @@ -239,11 +265,14 @@ Account::removeFromConfig() s->remove( "types" ); s->endGroup(); s->remove( "accounts/" + m_accountId ); + QKeychain::DeletePasswordJob* j = new QKeychain::DeletePasswordJob( QLatin1String( "tomahawk" ), this ); j->setKey( m_accountId ); j->setAutoDelete( false ); connect( j, SIGNAL( finished( QKeychain::Job* ) ), this, SLOT( keychainJobFinished( QKeychain::Job* ) ) ); j->start(); + + m_workingKeychainJobs << j; } diff --git a/src/libtomahawk/accounts/Account.h b/src/libtomahawk/accounts/Account.h index 096357d70..1ff2e9d59 100644 --- a/src/libtomahawk/accounts/Account.h +++ b/src/libtomahawk/accounts/Account.h @@ -20,18 +20,19 @@ #ifndef ACCOUNT_H #define ACCOUNT_H -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include +#include +#include #include "Typedefs.h" #include "DllMacro.h" -#include "TomahawkSettings.h" -#include "libtomahawk/infosystem/InfoSystem.h" +// #include "libtomahawk/infosystem/InfoSystem.h" class SipPlugin; @@ -84,6 +85,13 @@ public: QVariantHash configuration() const { QMutexLocker locker( &m_mutex ); return m_configuration; } + // Starts the asynchronous loading of the credentials. + // Will emit credentialsChanged() when they are successfully loaded + void loadCredentials() const; + + // Asynchronously save credentials to storage + void saveCredentials( const QVariantHash& credentials ); + /** * Configuration widgets can have a "dataError( bool )" signal to enable/disable the OK button in their wrapper dialogs. */ @@ -92,8 +100,6 @@ public: virtual void saveConfig() {} // called when the widget has been edited. save values from config widget, call sync() to write to disk account generic settings - QVariantHash credentials() const { QMutexLocker locker( &m_mutex ); return m_credentials; } - QVariantMap acl() const { QMutexLocker locker( &m_mutex ); return m_acl; } virtual QWidget* aclWidget() = 0; @@ -107,18 +113,21 @@ public: virtual Tomahawk::InfoSystem::InfoPluginPtr infoPlugin() = 0; virtual SipPlugin* sipPlugin() = 0; + // Some accounts cannot be enabled if authentication fails. Return true after failing to authenticate + // if this is the case, and the account will not be enabled + virtual bool preventEnabling() const { return false; } + AccountTypes types() const; void setAccountServiceName( const QString &serviceName ) { QMutexLocker locker( &m_mutex ); m_accountServiceName = serviceName; } void setAccountFriendlyName( const QString &friendlyName ) { QMutexLocker locker( &m_mutex ); m_accountFriendlyName = friendlyName; } void setEnabled( bool enabled ) { QMutexLocker locker( &m_mutex ); m_enabled = enabled; } void setAccountId( const QString &accountId ) { QMutexLocker locker( &m_mutex ); m_accountId = accountId; } - void setCredentials( const QVariantHash &credentialHash ) { QMutexLocker locker( &m_mutex ); m_credentials = credentialHash; } void setConfiguration( const QVariantHash &configuration ) { QMutexLocker locker( &m_mutex ); m_configuration = configuration; } void setAcl( const QVariantMap &acl ) { QMutexLocker locker( &m_mutex ); m_acl = acl; } void setTypes( AccountTypes types ); - void sync() { QMutexLocker locker( &m_mutex ); syncConfig(); }; + void sync() { QMutexLocker locker( &m_mutex ); syncConfig(); } /** * Removes all the settings held in the config file for this account instance @@ -137,7 +146,7 @@ signals: void configurationChanged(); - void credentialsChanged(); + void credentialsChanged( const QVariantHash& credentials ); protected: virtual void loadFromConfig( const QString &accountId ); @@ -151,18 +160,18 @@ private slots: void keychainJobFinished( QKeychain::Job* ); private: - void serializeCredentials( QByteArray& data ); - void deserializeCredentials( const QByteArray& data ); - QString m_accountServiceName; QString m_accountFriendlyName; QString m_cachedError; bool m_enabled; QString m_accountId; - QVariantHash m_credentials; QVariantHash m_configuration; QVariantMap m_acl; QStringList m_types; + + mutable QList< QKeychain::Job* > m_workingKeychainJobs; + mutable QQueue< QKeychain::Job* > m_queuedKeychainJobs; + mutable QMutex m_mutex; }; diff --git a/src/AccountDelegate.cpp b/src/libtomahawk/accounts/AccountDelegate.cpp similarity index 95% rename from src/AccountDelegate.cpp rename to src/libtomahawk/accounts/AccountDelegate.cpp index 95e5ce418..8b77aabca 100644 --- a/src/AccountDelegate.cpp +++ b/src/libtomahawk/accounts/AccountDelegate.cpp @@ -1,20 +1,20 @@ -/* - Copyright (C) 2011 Leo Franchi - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ - +/* === This file is part of Tomahawk Player - === + * + * Copyright 2011-2012 Leo Franchi + * + * Tomahawk is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Tomahawk is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Tomahawk. If not, see . + */ #include "AccountDelegate.h" @@ -30,6 +30,7 @@ #include "utils/Logger.h" #include "utils/AnimatedSpinner.h" #include "utils/Closure.h" +#include "Source.h" #define CHILD_ACCOUNT_HEIGHT 24 @@ -58,7 +59,6 @@ AccountDelegate::AccountDelegate( QObject* parent ) , m_accountRowHeight( -1 ) , m_model( 0 ) { - m_defaultCover.load( RESPATH "images/sipplugin-online.png" ); m_ratingStarPositive.load( RESPATH "images/starred.png" ); m_ratingStarNegative.load( RESPATH "images/star-unstarred.png" ); @@ -270,7 +270,7 @@ AccountDelegate::paint ( QPainter* painter, const QStyleOptionViewItem& option, } else if ( canDelete ) { - const QString btnText = tr( "Remove Account" ); + const QString btnText = tr( "Remove" ); const int btnWidth = installMetrics.width( btnText ) + 2*PADDING; QRect btnRect; @@ -280,7 +280,10 @@ AccountDelegate::paint ( QPainter* painter, const QStyleOptionViewItem& option, btnRect = QRect( opt.rect.right() - PADDING - btnWidth, center - ( installMetrics.height() + 4 ) / 2, btnWidth, installMetrics.height() + 2*PADDING ); #ifdef Q_WS_MAC - btnRect.adjust( -4, 2, 4, -2 ); + btnRect.adjust( -4, 0, 4, 0 ); + + if ( hasConfigWrench ) + btnRect.moveTop( btnRect.top() + 2 ); #endif leftEdge = btnRect.left(); m_cachedButtonRects[ index ] = btnRect; @@ -385,7 +388,7 @@ AccountDelegate::paint ( QPainter* painter, const QStyleOptionViewItem& option, const int countW = painter->fontMetrics().width( count ); const QRect countRect( runningEdge + 50, starsTop, countW, painter->fontMetrics().height() ); count = painter->fontMetrics().elidedText( count, Qt::ElideRight, rightEdge - PADDING - countRect.left() ); - painter->drawText( countRect, Qt::AlignLeft | Qt::TextWordWrap, count ); + painter->drawText( countRect, Qt::AlignLeft, count ); // runningEdge = authorRect.x(); } @@ -601,6 +604,7 @@ AccountDelegate::drawStatus( QPainter* painter, const QPointF& rightTopEdge, Acc { QPixmap p; QString statusText; + const Account::ConnectionState state = acct->connectionState(); if ( state == Account::Connected ) { @@ -724,7 +728,6 @@ void AccountDelegate::doneInstalling ( const QPersistentModelIndex& idx ) { qDebug() << "STOP INSTALLING:" << idx.data( Qt::DisplayRole ).toString(); - Q_ASSERT( m_loadingSpinners.contains( idx ) ); if ( !m_loadingSpinners.contains( idx ) ) return; @@ -734,6 +737,15 @@ AccountDelegate::doneInstalling ( const QPersistentModelIndex& idx ) } +void +AccountDelegate::errorInstalling( const QPersistentModelIndex& idx ) +{ + // Just hide the loading spinner as we do after a successful install + qDebug() << "ERROR INSTALLING index:" << idx; + doneInstalling( idx ); +} + + void AccountDelegate::doUpdateIndex( const QPersistentModelIndex& idx ) { diff --git a/src/AccountDelegate.h b/src/libtomahawk/accounts/AccountDelegate.h similarity index 73% rename from src/AccountDelegate.h rename to src/libtomahawk/accounts/AccountDelegate.h index 2028eb09b..1b70a7df6 100644 --- a/src/AccountDelegate.h +++ b/src/libtomahawk/accounts/AccountDelegate.h @@ -1,24 +1,25 @@ -/* - Copyright (C) 2011 Leo Franchi - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ - +/* === This file is part of Tomahawk Player - === + * + * Copyright 2011-2012 Leo Franchi + * + * Tomahawk is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Tomahawk is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Tomahawk. If not, see . + */ #ifndef ACCOUNTDELEGATE_H #define ACCOUNTDELEGATE_H +#include "DllMacro.h" #include #include "accounts/AccountModel.h" @@ -31,7 +32,7 @@ namespace Accounts class Account; -class AccountDelegate : public QStyledItemDelegate +class DLLEXPORT AccountDelegate : public QStyledItemDelegate { Q_OBJECT public: @@ -43,6 +44,8 @@ public: public slots: void startInstalling( const QPersistentModelIndex& idx ); void doneInstalling ( const QPersistentModelIndex& idx ); + void errorInstalling ( const QPersistentModelIndex& idx ); + void doUpdateIndex( const QPersistentModelIndex& idx ); diff --git a/src/AccountFactoryWrapper.cpp b/src/libtomahawk/accounts/AccountFactoryWrapper.cpp similarity index 98% rename from src/AccountFactoryWrapper.cpp rename to src/libtomahawk/accounts/AccountFactoryWrapper.cpp index 72e5ac418..4b1214b1c 100644 --- a/src/AccountFactoryWrapper.cpp +++ b/src/libtomahawk/accounts/AccountFactoryWrapper.cpp @@ -20,10 +20,11 @@ #include "accounts/Account.h" #include "accounts/AccountManager.h" -#include "GuiHelpers.h" +#include "utils/GuiHelpers.h" #include "AccountFactoryWrapperDelegate.h" #include "DelegateConfigWrapper.h" #include "ui_AccountFactoryWrapper.h" +#include "Source.h" using namespace Tomahawk::Accounts; AccountFactoryWrapper::AccountFactoryWrapper( AccountFactory* factory, QWidget* parent ) diff --git a/src/AccountFactoryWrapper.h b/src/libtomahawk/accounts/AccountFactoryWrapper.h similarity index 95% rename from src/AccountFactoryWrapper.h rename to src/libtomahawk/accounts/AccountFactoryWrapper.h index 00e31e672..ac8bf195d 100644 --- a/src/AccountFactoryWrapper.h +++ b/src/libtomahawk/accounts/AccountFactoryWrapper.h @@ -19,6 +19,8 @@ #ifndef ACCOUNTFACTORYWRAPPER_H #define ACCOUNTFACTORYWRAPPER_H +#include "DllMacro.h" + #include #include @@ -33,7 +35,7 @@ class Account; class Ui_AccountFactoryWrapper; // class AccountFactoryWrapper_ -class AccountFactoryWrapper : public QDialog +class DLLEXPORT AccountFactoryWrapper : public QDialog { Q_OBJECT public: diff --git a/src/AccountFactoryWrapper.ui b/src/libtomahawk/accounts/AccountFactoryWrapper.ui similarity index 100% rename from src/AccountFactoryWrapper.ui rename to src/libtomahawk/accounts/AccountFactoryWrapper.ui diff --git a/src/AccountFactoryWrapperDelegate.cpp b/src/libtomahawk/accounts/AccountFactoryWrapperDelegate.cpp similarity index 99% rename from src/AccountFactoryWrapperDelegate.cpp rename to src/libtomahawk/accounts/AccountFactoryWrapperDelegate.cpp index 833166b84..2b61599c5 100644 --- a/src/AccountFactoryWrapperDelegate.cpp +++ b/src/libtomahawk/accounts/AccountFactoryWrapperDelegate.cpp @@ -20,6 +20,7 @@ #include "accounts/Account.h" #include "AccountFactoryWrapper.h" #include "utils/TomahawkUtils.h" +#include "Source.h" #include #include diff --git a/src/AccountFactoryWrapperDelegate.h b/src/libtomahawk/accounts/AccountFactoryWrapperDelegate.h similarity index 100% rename from src/AccountFactoryWrapperDelegate.h rename to src/libtomahawk/accounts/AccountFactoryWrapperDelegate.h diff --git a/src/libtomahawk/accounts/AccountManager.cpp b/src/libtomahawk/accounts/AccountManager.cpp index 0c9e3d190..75db452ee 100644 --- a/src/libtomahawk/accounts/AccountManager.cpp +++ b/src/libtomahawk/accounts/AccountManager.cpp @@ -20,6 +20,7 @@ #include "AccountManager.h" #include "config.h" #include "SourceList.h" +#include "TomahawkSettings.h" #include "ResolverAccount.h" #include @@ -202,6 +203,9 @@ AccountManager::enableAccount( Account* account ) account->authenticate(); + if ( account->preventEnabling() ) + return; + account->setEnabled( true ); m_enabledAccounts << account; @@ -230,8 +234,11 @@ AccountManager::connectAll() tDebug( LOGVERBOSE ) << Q_FUNC_INFO; foreach( Account* acc, m_accounts ) { - acc->authenticate(); - m_enabledAccounts << acc; + if ( acc->enabled() ) + { + acc->authenticate(); + m_enabledAccounts << acc; + } } m_connected = true; @@ -401,6 +408,8 @@ AccountManager::hookupAccount( Account* account ) const void AccountManager::hookupAndEnable( Account* account, bool startup ) { + Q_UNUSED( startup ); + tDebug( LOGVERBOSE ) << Q_FUNC_INFO; SipPlugin* p = account->sipPlugin(); if ( p ) diff --git a/src/libtomahawk/accounts/AccountModel.cpp b/src/libtomahawk/accounts/AccountModel.cpp index f58014435..2f263b9ac 100644 --- a/src/libtomahawk/accounts/AccountModel.cpp +++ b/src/libtomahawk/accounts/AccountModel.cpp @@ -23,6 +23,11 @@ #include "AccountManager.h" #include "AtticaManager.h" #include "ResolverAccount.h" +#include "TomahawkSettings.h" + +#ifndef ENABLE_HEADLESS +#include +#endif #include @@ -35,6 +40,9 @@ AccountModel::AccountModel( QObject* parent ) : QAbstractListModel( parent ) { connect( AtticaManager::instance(), SIGNAL( resolversLoaded( Attica::Content::List ) ), this, SLOT( loadData() ) ); + connect( AtticaManager::instance(), SIGNAL( startedInstalling( QString ) ), this, SLOT( onStartedInstalling( QString ) ) ); + connect( AtticaManager::instance(), SIGNAL( resolverInstalled( QString ) ), this, SLOT( onFinishedInstalling( QString ) ) ); + connect( AtticaManager::instance(), SIGNAL( resolverInstallationFailed( QString ) ), this, SLOT( resolverInstallFailed( QString ) ) ); connect( AccountManager::instance(), SIGNAL( added( Tomahawk::Accounts::Account* ) ), this, SLOT( accountAdded( Tomahawk::Accounts::Account* ) ) ); connect( AccountManager::instance(), SIGNAL( removed( Tomahawk::Accounts::Account* ) ), this, SLOT( accountRemoved( Tomahawk::Accounts::Account* ) ) ); @@ -93,6 +101,8 @@ AccountModel::loadData() qDebug() << "All accounts after remove:"; foreach ( Account* acct, allAccounts ) qDebug() << acct->accountFriendlyName() << "\t" << acct->accountId(); // All other accounts we haven't dealt with yet +#else + Q_UNUSED( removed ); #endif } } else @@ -101,10 +111,14 @@ AccountModel::loadData() foreach ( Account* acct, AccountManager::instance()->accounts( Accounts::ResolverType ) ) { -// qDebug() << "Found ResolverAccount" << acct->accountFriendlyName(); +#if ACCOUNTMODEL_DEBUG + qDebug() << "Found ResolverAccount" << acct->accountFriendlyName(); +#endif if ( AtticaResolverAccount* resolver = qobject_cast< AtticaResolverAccount* >( acct ) ) { -// qDebug() << "Which is an attica resolver with id:" << resolver->atticaId(); +#if ACCOUNTMODEL_DEBUG + qDebug() << "Which is an attica resolver with id:" << resolver->atticaId(); +#endif if ( resolver->atticaId() == content.id() ) { allAccounts.removeAll( acct ); @@ -121,7 +135,7 @@ AccountModel::loadData() #endif foreach ( Account* acct, allAccounts ) { - qDebug() << "Resolver is left over:" << acct->accountFriendlyName(); + qDebug() << "Resolver is left over:" << acct->accountFriendlyName() << acct->accountId(); // Q_ASSERT( !qobject_cast< AtticaResolverAccount* >( acct ) ); // This should be caught above in the attica list if ( qobject_cast< ResolverAccount* >( acct ) && !qobject_cast< AtticaResolverAccount* >( acct ) ) @@ -337,12 +351,15 @@ AccountModel::data( const QModelIndex& index, int role ) const Q_ASSERT( node->customAccount ); Q_ASSERT( node->factory ); - Account* account = node->customAccount; - // This is sort of ugly. CustomAccounts are pure Account*, but we know that + Attica::Content content = node->atticaContent; + // This is ugly. CustomAccounts are pure Account*, but we know that // some might also be linked to attica resolvers (not always). If that is the case // they have a Attica::Content set on the node, so we use that to display some // extra metadata and rating - const bool hasAttica = !node->atticaContent.id().isEmpty(); + Account* account = node->customAccount; + if ( node->type == AccountModelNode::CustomAccountType && qobject_cast< CustomAtticaAccount* >( account ) ) + content = qobject_cast< CustomAtticaAccount* >( node->customAccount )->atticaContent(); + const bool hasAttica = !content.id().isNull(); switch ( role ) { @@ -354,15 +371,15 @@ AccountModel::data( const QModelIndex& index, int role ) const return ShippedWithTomahawk; case Qt::ToolTipRole: case DescriptionRole: - return hasAttica ? node->atticaContent.description() : node->factory->description(); + return hasAttica ? content.description() : node->factory->description(); case CanRateRole: return hasAttica; case AuthorRole: - return hasAttica ? node->atticaContent.author() : QString(); + return hasAttica ? content.author() : QString(); case RatingRole: - return hasAttica ? node->atticaContent.rating() / 20 : 0; // rating is out of 100 + return hasAttica ? content.rating() / 20 : 0; // rating is out of 100 case DownloadCounterRole: - return hasAttica ? node->atticaContent.downloads() : QVariant(); + return hasAttica ? content.downloads() : QVariant(); case RowTypeRole: return CustomAccount; case AccountData: @@ -375,6 +392,8 @@ AccountModel::data( const QModelIndex& index, int role ) const return account->enabled() ? Qt::Checked : Qt::Unchecked; case ConnectionStateRole: return account->connectionState(); + case UserHasRatedRole: + return hasAttica ? AtticaManager::instance()->userHasRated( content ) : false; default: return QVariant(); } @@ -451,8 +470,11 @@ AccountModel::setData( const QModelIndex& index, const QVariant& value, int role qDebug() << "Kicked off fetch+install, now waiting"; m_waitingForAtticaInstall.insert( resolver.id() ); - emit startInstalling( index ); - AtticaManager::instance()->installResolver( resolver ); + if ( node->atticaAccount ) + AtticaManager::instance()->installResolverWithHandler( resolver, node->atticaAccount ); + else + AtticaManager::instance()->installResolver( resolver, true ); + return true; } @@ -491,6 +513,21 @@ AccountModel::setData( const QModelIndex& index, const QVariant& value, int role else if( state == Qt::Unchecked ) AccountManager::instance()->disableAccount( acct ); +#if defined(Q_OS_LINUX) && !defined(ENABLE_HEADLESS) + if ( acct->preventEnabling() ) + { + // Can't install from attica yet on linux, so show a warning if the user tries to turn it on. + // TODO make a prettier display + QMessageBox box; + box.setWindowTitle( tr( "Manual Install Required" ) ); + box.setTextFormat( Qt::RichText ); + box.setIcon( QMessageBox::Information ); + box.setText( tr( "Unfortunately, automatic installation of this resolver is not available or disabled for your platform.

" + "Please use \"Install from file\" above, by fetching it from your distribution or compiling it yourself. Further instructions can be found here:

http://www.tomahawk-player.org/resolvers/%1" ).arg( acct->accountServiceName() ) ); + box.setStandardButtons( QMessageBox::Ok ); + box.exec(); + } +#endif emit dataChanged( index, index ); return true; @@ -520,16 +557,29 @@ AccountModel::setData( const QModelIndex& index, const QVariant& value, int role if ( role == RatingRole ) { // We only support rating Attica resolvers for the moment. - Q_ASSERT( node->type == AccountModelNode::AtticaType ); + Attica::Content content; + if ( node->type == AccountModelNode::AtticaType ) + { + content = node->atticaContent; - AtticaManager::ResolverState state = AtticaManager::instance()->resolverState( node->atticaContent ); - // For now only allow rating if a resolver is installed! - if ( state != AtticaManager::Installed && state != AtticaManager::NeedsUpgrade ) + AtticaManager::ResolverState state = AtticaManager::instance()->resolverState( content ); + // For now only allow rating if a resolver is installed! + if ( state != AtticaManager::Installed && state != AtticaManager::NeedsUpgrade ) + return false; + } // Allow rating custom attica accounts regardless as user may have installed manually + else if ( node->type == AccountModelNode::CustomAccountType && qobject_cast< CustomAtticaAccount* >( node->customAccount ) ) + content = qobject_cast< CustomAtticaAccount* >( node->customAccount )->atticaContent(); + + Q_ASSERT( !content.id().isNull() ); + + if ( AtticaManager::instance()->userHasRated( content ) ) return false; - if ( AtticaManager::instance()->userHasRated( node->atticaContent ) ) - return false; - node->atticaContent.setRating( value.toInt() * 20 ); - AtticaManager::instance()->uploadRating( node->atticaContent ); + + content.setRating( value.toInt() * 20 ); + AtticaManager::instance()->uploadRating( content ); + + if ( node->type == AccountModelNode::AtticaType ) + node->atticaContent = content; emit dataChanged( index, index ); @@ -572,10 +622,6 @@ AccountModel::accountAdded( Account* account ) AccountManager::instance()->enableAccount( account ); m_waitingForAtticaInstall.remove( attica->atticaId() ); - - // find index to emit doneInstalling for - const QModelIndex idx = index( i, 0, QModelIndex() ); - emit doneInstalling( idx ); } if ( thisIsTheOne ) @@ -615,7 +661,8 @@ AccountModel::accountStateChanged( Account* account , Account::ConnectionState ) // For each type that this node could be, check the corresponding data if ( ( n->type == AccountModelNode::UniqueFactoryType && n->accounts.size() && n->accounts.first() == account ) || ( n->type == AccountModelNode::AtticaType && n->atticaAccount && n->atticaAccount == account ) || - ( n->type == AccountModelNode::ManualResolverType && n->resolverAccount && n->resolverAccount == account ) ) + ( n->type == AccountModelNode::ManualResolverType && n->resolverAccount && n->resolverAccount == account ) || + ( n->type == AccountModelNode::CustomAccountType && n->customAccount && n->customAccount == account ) ) { const QModelIndex idx = index( i, 0, QModelIndex() ); emit dataChanged( idx, idx ); @@ -689,6 +736,63 @@ AccountModel::accountRemoved( Account* account ) } +void +AccountModel::resolverInstallFailed( const QString& resolverId ) +{ + const QModelIndex idx = indexForAtticaId( resolverId ); + if ( idx.isValid() ) + { + qDebug() << "Got failed attica install in account mode, emitting signal!"; + emit errorInstalling( idx ); + } +} + + +QModelIndex +AccountModel::indexForAtticaId( const QString& resolverId ) const +{ + for ( int i = 0; i < m_accounts.size(); i++ ) + { + if ( m_accounts[ i ]->type == AccountModelNode::AtticaType && m_accounts[ i ]->atticaContent.id() == resolverId ) + { + return index( i, 0, QModelIndex() ); + } + else if ( m_accounts[ i ]->type == AccountModelNode::CustomAccountType && qobject_cast< CustomAtticaAccount* >( m_accounts[ i ]->customAccount ) ) + { + const CustomAtticaAccount* atticaAcct = qobject_cast< CustomAtticaAccount* >( m_accounts[ i ]->customAccount ); + if ( atticaAcct->atticaContent().id() == resolverId ) + return index( i, 0, QModelIndex() ); + } + } + + return QModelIndex(); +} + + +void +AccountModel::onStartedInstalling( const QString& resolverId ) +{ + const QModelIndex idx = indexForAtticaId( resolverId ); + if ( idx.isValid() ) + { + qDebug() << "Got resolver that is beginning to install, emitting signal"; + emit startInstalling( idx ); + } +} + + +void +AccountModel::onFinishedInstalling( const QString& resolverId ) +{ + const QModelIndex idx = indexForAtticaId( resolverId ); + if ( idx.isValid() ) + { + qDebug() << "Got resolver that is beginning to install, emitting signal"; + emit doneInstalling( idx ); + } +} + + int AccountModel::rowCount( const QModelIndex& ) const { diff --git a/src/libtomahawk/accounts/AccountModel.h b/src/libtomahawk/accounts/AccountModel.h index 68fcb89f6..13de779f1 100644 --- a/src/libtomahawk/accounts/AccountModel.h +++ b/src/libtomahawk/accounts/AccountModel.h @@ -24,6 +24,7 @@ #include "Account.h" #include +#include namespace Tomahawk { @@ -96,6 +97,8 @@ signals: void startInstalling( const QPersistentModelIndex& idx ); void doneInstalling( const QPersistentModelIndex& idx ); + void errorInstalling( const QPersistentModelIndex& idx ); + private slots: void loadData(); @@ -103,7 +106,12 @@ private slots: void accountRemoved( Tomahawk::Accounts::Account* ); void accountStateChanged( Account*, Accounts::Account::ConnectionState ); + void onStartedInstalling( const QString& resolverId ); + void onFinishedInstalling( const QString& resolverId ); + void resolverInstallFailed( const QString& resolverId ); private: + QModelIndex indexForAtticaId( const QString& resolverId ) const; + QList< AccountModelNode* > m_accounts; QSet< QString > m_waitingForAtticaInstall; }; diff --git a/src/libtomahawk/accounts/AccountModelFilterProxy.cpp b/src/libtomahawk/accounts/AccountModelFilterProxy.cpp index 1a559faed..fee5516f3 100644 --- a/src/libtomahawk/accounts/AccountModelFilterProxy.cpp +++ b/src/libtomahawk/accounts/AccountModelFilterProxy.cpp @@ -38,6 +38,7 @@ AccountModelFilterProxy::setSourceModel( QAbstractItemModel* sourceModel ) connect( sourceModel, SIGNAL( scrollTo( QModelIndex ) ), this, SLOT( onScrollTo( QModelIndex ) ) ); connect( sourceModel, SIGNAL( startInstalling( QPersistentModelIndex ) ), this, SLOT( onStartInstalling( QPersistentModelIndex ) ) ); connect( sourceModel, SIGNAL( doneInstalling( QPersistentModelIndex ) ), this, SLOT( onDoneInstalling( QPersistentModelIndex ) ) ); + connect( sourceModel, SIGNAL( errorInstalling( QPersistentModelIndex ) ), this, SLOT( onErrorInstalling( QPersistentModelIndex ) ) ); QSortFilterProxyModel::setSourceModel( sourceModel ); } @@ -86,3 +87,11 @@ AccountModelFilterProxy::onStartInstalling( const QPersistentModelIndex& idx ) { emit startInstalling( mapFromSource( idx ) ); } + + +void +AccountModelFilterProxy::onErrorInstalling( const QPersistentModelIndex& idx ) +{ + emit errorInstalling( mapFromSource( idx ) ); +} + diff --git a/src/libtomahawk/accounts/AccountModelFilterProxy.h b/src/libtomahawk/accounts/AccountModelFilterProxy.h index 75021c6b1..2a8868fd8 100644 --- a/src/libtomahawk/accounts/AccountModelFilterProxy.h +++ b/src/libtomahawk/accounts/AccountModelFilterProxy.h @@ -42,6 +42,7 @@ signals: void startInstalling( const QPersistentModelIndex& idx ); void doneInstalling( const QPersistentModelIndex& idx ); + void errorInstalling( const QPersistentModelIndex& idx ); protected: virtual bool filterAcceptsRow ( int sourceRow, const QModelIndex& sourceParent ) const; @@ -51,6 +52,7 @@ private slots: void onStartInstalling( const QPersistentModelIndex& idx ); void onDoneInstalling( const QPersistentModelIndex& idx ); + void onErrorInstalling( const QPersistentModelIndex& idx ); private: Tomahawk::Accounts::AccountType m_filterType; diff --git a/src/libtomahawk/accounts/AccountModelNode.h b/src/libtomahawk/accounts/AccountModelNode.h index 857d57402..18486ef8b 100644 --- a/src/libtomahawk/accounts/AccountModelNode.h +++ b/src/libtomahawk/accounts/AccountModelNode.h @@ -139,9 +139,6 @@ struct AccountModelNode { init(); customAccount = account; factory = AccountManager::instance()->factoryForAccount( account ); - - if ( CustomAtticaAccount* customAtticaAccount = qobject_cast< CustomAtticaAccount* >( account ) ) - atticaContent = customAtticaAccount->atticaContent(); } void init() diff --git a/src/DelegateConfigWrapper.cpp b/src/libtomahawk/accounts/DelegateConfigWrapper.cpp similarity index 99% rename from src/DelegateConfigWrapper.cpp rename to src/libtomahawk/accounts/DelegateConfigWrapper.cpp index abd1de5ad..fccd60606 100644 --- a/src/DelegateConfigWrapper.cpp +++ b/src/libtomahawk/accounts/DelegateConfigWrapper.cpp @@ -124,12 +124,9 @@ DelegateConfigWrapper::rejected() void DelegateConfigWrapper::updateSizeHint() { - hide(); setSizeGripEnabled( false ); setMinimumSize( sizeHint() ); setMaximumSize( sizeHint() ); - - show(); } diff --git a/src/DelegateConfigWrapper.h b/src/libtomahawk/accounts/DelegateConfigWrapper.h similarity index 100% rename from src/DelegateConfigWrapper.h rename to src/libtomahawk/accounts/DelegateConfigWrapper.h diff --git a/src/libtomahawk/accounts/ResolverAccount.cpp b/src/libtomahawk/accounts/ResolverAccount.cpp index 4446e94e3..30a3800e8 100644 --- a/src/libtomahawk/accounts/ResolverAccount.cpp +++ b/src/libtomahawk/accounts/ResolverAccount.cpp @@ -21,6 +21,9 @@ #include "ExternalResolver.h" #include "ExternalResolverGui.h" #include "AccountManager.h" +#include "TomahawkSettings.h" +#include "Source.h" + #include #include #include @@ -69,20 +72,13 @@ ResolverAccountFactory::createFromPath( const QString& path, const QString& fact ResolverAccount::ResolverAccount( const QString& accountId ) : Account( accountId ) { - const QString path = configuration()[ "path" ].toString(); + setTypes( AccountType( ResolverType ) ); // We should have a valid saved path Q_ASSERT( !path.isEmpty() ); - m_resolver = QWeakPointer< ExternalResolverGui >( qobject_cast< ExternalResolverGui* >( Pipeline::instance()->addScriptResolver( path, enabled() ) ) ); - connect( m_resolver.data(), SIGNAL( changed() ), this, SLOT( resolverChanged() ) ); - - // What resolver do we have here? Should only be types that are 'real' resolvers - Q_ASSERT ( !m_resolver.isNull() ); - - setAccountFriendlyName( m_resolver.data()->name() ); - setTypes( AccountType( ResolverType ) ); + init( path ); } @@ -92,16 +88,8 @@ ResolverAccount::ResolverAccount( const QString& accountId, const QString& path QVariantHash configuration; configuration[ "path" ] = path; setConfiguration( configuration ); - setEnabled( true ); - m_resolver = QWeakPointer< ExternalResolverGui >( qobject_cast< ExternalResolverGui* >( Pipeline::instance()->addScriptResolver( path, true ) ) ); - connect( m_resolver.data(), SIGNAL( changed() ), this, SLOT( resolverChanged() ) ); - - // What resolver do we have here? Should only be types that are 'real' resolvers - Q_ASSERT ( m_resolver.data() ); - - setAccountFriendlyName( m_resolver.data()->name() ); - setTypes( AccountType( ResolverType ) ); + init( path ); sync(); } @@ -117,12 +105,44 @@ ResolverAccount::~ResolverAccount() } +void +ResolverAccount::init( const QString& path ) +{ + setTypes( AccountType( ResolverType ) ); + + if ( !QFile::exists( path ) ) + { + AccountManager::instance()->disableAccount( this ); + } + else + { + hookupResolver(); + } +} + + +void +ResolverAccount::hookupResolver() +{ + tDebug() << "Hooking up resolver:" << configuration().value( "path" ).toString() << enabled(); + + m_resolver = QWeakPointer< ExternalResolverGui >( qobject_cast< ExternalResolverGui* >( Pipeline::instance()->addScriptResolver( configuration().value( "path" ).toString() ) ) ); + connect( m_resolver.data(), SIGNAL( changed() ), this, SLOT( resolverChanged() ) ); + + // What resolver do we have here? Should only be types that are 'real' resolvers + Q_ASSERT ( m_resolver.data() ); + + setAccountFriendlyName( m_resolver.data()->name() ); +} + void ResolverAccount::authenticate() { - Q_ASSERT( !m_resolver.isNull() ); - qDebug() << Q_FUNC_INFO << "Authenticating/starting resolver, exists?" << m_resolver; + if ( m_resolver.isNull() ) + return; + + tDebug() << Q_FUNC_INFO << "Authenticating/starting resolver, exists?" << m_resolver.data()->name(); if ( !m_resolver.data()->running() ) m_resolver.data()->start(); @@ -134,14 +154,14 @@ ResolverAccount::authenticate() bool ResolverAccount::isAuthenticated() const { - return m_resolver.data()->running(); + return !m_resolver.isNull() && m_resolver.data()->running(); } void ResolverAccount::deauthenticate() { - if ( m_resolver.data()->running() ) + if ( !m_resolver.isNull() && m_resolver.data()->running() ) m_resolver.data()->stop(); emit connectionStateChanged( connectionState() ); @@ -152,7 +172,7 @@ ResolverAccount::deauthenticate() Account::ConnectionState ResolverAccount::connectionState() const { - if ( m_resolver.data()->running() ) + if ( !m_resolver.isNull() && m_resolver.data()->running() ) return Connected; else return Disconnected; @@ -162,6 +182,9 @@ ResolverAccount::connectionState() const QWidget* ResolverAccount::configurationWidget() { + if ( m_resolver.isNull() ) + return 0; + return m_resolver.data()->configUI(); } @@ -186,13 +209,17 @@ ResolverAccount::removeFromConfig() void ResolverAccount::saveConfig() { Account::saveConfig(); - m_resolver.data()->saveConfig(); + if ( !m_resolver.isNull() ) + m_resolver.data()->saveConfig(); } QString ResolverAccount::path() const { + if ( m_resolver.isNull() ) + return QString(); + return m_resolver.data()->filePath(); } @@ -233,19 +260,34 @@ AtticaResolverAccount::AtticaResolverAccount( const QString& accountId, const QS AtticaResolverAccount::~AtticaResolverAccount() { - } + void AtticaResolverAccount::loadIcon() { + if ( m_resolver.isNull() ) + return; + const QFileInfo fi( m_resolver.data()->filePath() ); QDir codeDir = fi.absoluteDir(); codeDir.cd( "../images" ); if ( codeDir.exists() && codeDir.exists( "icon.png" ) ) m_icon.load( codeDir.absoluteFilePath( "icon.png" ) ); +} + +void +AtticaResolverAccount::setPath( const QString& path ) +{ + QVariantHash config = configuration(); + config[ "path" ] = path; + setConfiguration( config ); + + hookupResolver(); + + sync(); } diff --git a/src/libtomahawk/accounts/ResolverAccount.h b/src/libtomahawk/accounts/ResolverAccount.h index 03bd46fc5..fb3f5487e 100644 --- a/src/libtomahawk/accounts/ResolverAccount.h +++ b/src/libtomahawk/accounts/ResolverAccount.h @@ -89,8 +89,14 @@ private slots: protected: // Created by factory, when user installs a new resolver ResolverAccount( const QString& accountId, const QString& path ); + + void hookupResolver(); + QWeakPointer m_resolver; +private: + void init( const QString& path ); + friend class ResolverAccountFactory; }; @@ -99,7 +105,7 @@ protected: * Extends ResolverAccount with what attica additionally provides---e.g. icon * Assumes certain file layout on disk. */ -class AtticaResolverAccount : public ResolverAccount +class DLLEXPORT AtticaResolverAccount : public ResolverAccount { Q_OBJECT public: @@ -110,6 +116,8 @@ public: virtual QPixmap icon() const; QString atticaId() const { return m_atticaId; } + + void setPath( const QString& path ); private: // Created by factory, when user installs a new resolver AtticaResolverAccount( const QString& accountId, const QString& path, const QString& atticaId ); diff --git a/src/accounts/lastfm/LastFmAccount.cpp b/src/libtomahawk/accounts/lastfm/LastFmAccount.cpp similarity index 84% rename from src/accounts/lastfm/LastFmAccount.cpp rename to src/libtomahawk/accounts/lastfm/LastFmAccount.cpp index 938f66503..eb8e08006 100644 --- a/src/accounts/lastfm/LastFmAccount.cpp +++ b/src/libtomahawk/accounts/lastfm/LastFmAccount.cpp @@ -26,6 +26,7 @@ #include "AtticaManager.h" #include "Pipeline.h" #include "accounts/AccountManager.h" +#include "Source.h" using namespace Tomahawk; using namespace InfoSystem; @@ -54,6 +55,8 @@ LastFmAccountFactory::icon() const LastFmAccount::LastFmAccount( const QString& accountId ) : CustomAtticaAccount( accountId ) { + connect( this, SIGNAL( credentialsChanged( QVariantHash ) ), this, SLOT( onCredentialsChanged( QVariantHash ) ) ); + setAccountFriendlyName( "Last.Fm" ); m_icon.load( RESPATH "images/lastfm-icon.png" ); @@ -74,7 +77,6 @@ LastFmAccount::LastFmAccount( const QString& accountId ) { infoPlugin().data()->moveToThread( Tomahawk::InfoSystem::InfoSystem::instance()->workerThread().data() ); Tomahawk::InfoSystem::InfoSystem::instance()->addInfoPlugin( infoPlugin() ); - QMetaObject::invokeMethod( infoPlugin().data(), "init", Qt::QueuedConnection ); } } @@ -83,7 +85,7 @@ LastFmAccount::~LastFmAccount() { if ( m_infoPlugin ) Tomahawk::InfoSystem::InfoSystem::instance()->removeInfoPlugin( infoPlugin() ); - + delete m_resolver.data(); } @@ -152,7 +154,7 @@ LastFmAccount::configurationWidget() Account::ConnectionState LastFmAccount::connectionState() const { - return (!m_resolver.isNull() && m_resolver.data()->running()) ? Account::Connected : Account::Disconnected; + return ( !m_resolver.isNull() && m_resolver.data()->running() ) ? Account::Connected : Account::Disconnected; } @@ -168,7 +170,7 @@ LastFmAccount::infoPlugin() { if ( m_infoPlugin.isNull() ) m_infoPlugin = QWeakPointer< LastFmInfoPlugin >( new LastFmInfoPlugin( this ) ); - + return InfoPluginPtr( m_infoPlugin.data() ); } @@ -189,57 +191,66 @@ LastFmAccount::saveConfig() setScrobble( m_configWidget.data()->scrobble() ); } - sync(); + saveCredentials( m_credentials ); +} - if ( m_infoPlugin ) - QTimer::singleShot( 0, m_infoPlugin.data(), SLOT( settingsChanged() ) ); + +void +LastFmAccount::onCredentialsChanged(const QVariantHash &credentials) +{ + m_credentials = credentials; + if ( !m_infoPlugin.isNull() ) + m_infoPlugin.data()->settingsChanged(); } QString LastFmAccount::password() const { - return credentials().value( "password" ).toString(); + return m_credentials.value( "password" ).toString(); } void -LastFmAccount::setPassword( const QString& password ) +LastFmAccount::setPassword( const QString& password, bool immediate ) { - QVariantHash creds = credentials(); - creds[ "password" ] = password; - setCredentials( creds ); + m_credentials[ "password" ] = password; + + if ( immediate ) + saveCredentials( m_credentials ); } QString LastFmAccount::sessionKey() const { - return credentials().value( "sessionkey" ).toString(); + return m_credentials.value( "sessionkey" ).toString(); } void -LastFmAccount::setSessionKey( const QString& sessionkey ) +LastFmAccount::setSessionKey( const QString& sessionkey, bool immediate ) { - QVariantHash creds = credentials(); - creds[ "sessionkey" ] = sessionkey; - setCredentials( creds ); + m_credentials[ "sessionkey" ] = sessionkey; + + if ( immediate ) + saveCredentials( m_credentials ); } QString LastFmAccount::username() const { - return credentials().value( "username" ).toString(); + return m_credentials.value( "username" ).toString(); } void -LastFmAccount::setUsername( const QString& username ) +LastFmAccount::setUsername( const QString& username, bool immediate ) { - QVariantHash creds = credentials(); - creds[ "username" ] = username; - setCredentials( creds ); + m_credentials[ "username" ] = username; + + if ( immediate ) + saveCredentials( m_credentials ); } @@ -288,7 +299,7 @@ LastFmAccount::hookupResolver() const AtticaManager::Resolver data = AtticaManager::instance()->resolverData( res.id() ); - m_resolver = QWeakPointer< ExternalResolverGui >( qobject_cast< ExternalResolverGui* >( Pipeline::instance()->addScriptResolver( data.scriptPath, enabled() ) ) ); + m_resolver = QWeakPointer< ExternalResolverGui >( qobject_cast< ExternalResolverGui* >( Pipeline::instance()->addScriptResolver( data.scriptPath ) ) ); connect( m_resolver.data(), SIGNAL( changed() ), this, SLOT( resolverChanged() ) ); } diff --git a/src/accounts/lastfm/LastFmAccount.h b/src/libtomahawk/accounts/lastfm/LastFmAccount.h similarity index 84% rename from src/accounts/lastfm/LastFmAccount.h rename to src/libtomahawk/accounts/lastfm/LastFmAccount.h index 90ca37737..478f4fcc7 100644 --- a/src/accounts/lastfm/LastFmAccount.h +++ b/src/libtomahawk/accounts/lastfm/LastFmAccount.h @@ -21,6 +21,7 @@ #include "accounts/Account.h" #include "AtticaManager.h" +#include "DllMacro.h" #include @@ -37,7 +38,7 @@ namespace Accounts { class LastFmConfig; -class LastFmAccountFactory : public AccountFactory +class DLLEXPORT LastFmAccountFactory : public AccountFactory { Q_OBJECT public: @@ -57,11 +58,11 @@ private: }; /** - * 3.Last.Fm account is special. It is both an attica resolver *and* a InfoPlugin. We always want the infoplugin, + * Last.Fm account is special. It is both an attica resolver *and* a InfoPlugin. We always want the infoplugin, * but the user can install the attica resolver on-demand. So we take care of both there. * */ -class LastFmAccount : public CustomAtticaAccount +class DLLEXPORT LastFmAccount : public CustomAtticaAccount { Q_OBJECT public: @@ -83,11 +84,11 @@ public: virtual void saveConfig(); QString username() const; - void setUsername( const QString& ); + void setUsername( const QString&, bool immediate = false ); QString password() const; - void setPassword( const QString& ); + void setPassword( const QString&, bool immediate = false ); QString sessionKey() const; - void setSessionKey( const QString& ); + void setSessionKey( const QString&, bool immediate = false ); bool scrobble() const; void setScrobble( bool scrobble ); @@ -99,12 +100,18 @@ private slots: void resolverInstalled( const QString& resolverId ); void resolverChanged(); + + void onCredentialsChanged( const QVariantHash& credentials ); + private: void hookupResolver(); QWeakPointer m_resolver; QWeakPointer m_infoPlugin; QWeakPointer m_configWidget; + + QVariantHash m_credentials; + QPixmap m_icon; }; diff --git a/src/accounts/lastfm/LastFmConfig.cpp b/src/libtomahawk/accounts/lastfm/LastFmConfig.cpp similarity index 57% rename from src/accounts/lastfm/LastFmConfig.cpp rename to src/libtomahawk/accounts/lastfm/LastFmConfig.cpp index b344e34c2..ab8420466 100644 --- a/src/accounts/lastfm/LastFmConfig.cpp +++ b/src/libtomahawk/accounts/lastfm/LastFmConfig.cpp @@ -17,18 +17,25 @@ */ #include "LastFmConfig.h" +#include "ui_LastFmConfig.h" #include "LastFmAccount.h" -#include -#include "ui_LastFmConfig.h" +#include "database/Database.h" +#include "database/DatabaseCommand_LogPlayback.h" +#include "utils/TomahawkUtils.h" +#include "utils/Logger.h" #include "lastfm/ws.h" +#include "lastfm/User" #include "lastfm/XmlQuery" using namespace Tomahawk::Accounts; + LastFmConfig::LastFmConfig( LastFmAccount* account ) : QWidget( 0 ) , m_account( account ) + , m_page( 1 ) + , m_lastTimeStamp( 0 ) { m_ui = new Ui_LastFmConfig; m_ui->setupUi( this ); @@ -38,9 +45,8 @@ LastFmConfig::LastFmConfig( LastFmAccount* account ) connect( m_ui->username, SIGNAL( textChanged( QString ) ), this, SLOT( enableButton() ) ); connect( m_ui->password, SIGNAL( textChanged( QString ) ), this, SLOT( enableButton() ) ); -// #ifdef Q_WS_MAC // FIXME -// m_ui->testLogin->setVisible( false ); -// #endif + connect( m_ui->username, SIGNAL( textChanged( QString ) ), SLOT( enableButton() ) ); + connect( m_ui->password, SIGNAL( textChanged( QString ) ), SLOT( enableButton() ) ); } @@ -75,10 +81,10 @@ LastFmConfig::username() const void -LastFmConfig::testLogin(bool ) +LastFmConfig::testLogin() { m_ui->testLogin->setEnabled( false ); - m_ui->testLogin->setText( "Testing..." ); + m_ui->testLogin->setText( tr( "Testing..." ) ); QString authToken = TomahawkUtils::md5( ( m_ui->username->text().toLower() + TomahawkUtils::md5( m_ui->password->text().toUtf8() ) ).toUtf8() ); @@ -105,6 +111,89 @@ LastFmConfig::enableButton() } +void +LastFmConfig::loadHistory() +{ + if ( m_lastTimeStamp ) + { + m_ui->importHistory->setText( tr( "Importing %1", "e.g. Importing 2012/01/01" ).arg( QDateTime::fromTime_t( m_lastTimeStamp ).toString( "MMMM d yyyy" ) ) ); + } + else + m_ui->importHistory->setText( tr( "Importing History..." ) ); + + m_ui->importHistory->setEnabled( false ); + m_ui->progressBar->show(); + + emit sizeHintChanged(); + + QNetworkReply* reply = lastfm::User( m_ui->username->text().toLower() ).getRecentTracks( 200, m_page ); + connect( reply, SIGNAL( finished() ), SLOT( onHistoryLoaded() ) ); +} + + +void +LastFmConfig::onHistoryLoaded() +{ + int total = 0; + bool finished = false; + QNetworkReply* reply = qobject_cast< QNetworkReply* >( sender() ); + + try + { + lastfm::XmlQuery lfm = reply->readAll(); + + foreach ( lastfm::XmlQuery e, lfm.children( "track" ) ) + { +// tDebug() << "Found:" << e["artist"].text() << e["name"].text() << e["date"].attribute( "uts" ).toUInt(); + Tomahawk::query_ptr query = Query::get( e["artist"].text(), e["name"].text(), QString(), QString(), false ); + m_lastTimeStamp = e["date"].attribute( "uts" ).toUInt(); + + DatabaseCommand_LogPlayback* cmd = new DatabaseCommand_LogPlayback( query, DatabaseCommand_LogPlayback::Finished, m_lastTimeStamp ); + Database::instance()->enqueue( QSharedPointer(cmd) ); + } + + if ( !lfm.children( "recenttracks" ).isEmpty() ) + { + lastfm::XmlQuery stats = lfm.children( "recenttracks" ).first(); + + int page = stats.attribute( "page" ).toInt(); + total = stats.attribute( "totalPages" ).toInt(); + + m_ui->progressBar->setMaximum( total ); + m_ui->progressBar->setValue( page ); + + if ( page < total ) + { + m_page = page + 1; + loadHistory(); + } + else + finished = true; + } + else + finished = true; + } + catch( lastfm::ws::ParseError e ) + { + tDebug() << "XmlQuery error:" << e.what(); + finished = true; + } + + if ( finished ) + { + if ( m_page != total ) + { + m_ui->importHistory->setText( tr( "History Incomplete. Resume" ) ); + m_ui->importHistory->setEnabled( true ); + } + else + { + m_ui->importHistory->setText( tr( "Playback History Imported" ) ); + } + } +} + + void LastFmConfig::onLastFmFinished() { diff --git a/src/accounts/lastfm/LastFmConfig.h b/src/libtomahawk/accounts/lastfm/LastFmConfig.h similarity index 88% rename from src/accounts/lastfm/LastFmConfig.h rename to src/libtomahawk/accounts/lastfm/LastFmConfig.h index 7ae0ec9af..4173374ae 100644 --- a/src/accounts/lastfm/LastFmConfig.h +++ b/src/libtomahawk/accounts/lastfm/LastFmConfig.h @@ -24,6 +24,7 @@ class Ui_LastFmConfig; namespace Tomahawk { + namespace Accounts { class LastFmAccount; @@ -41,7 +42,7 @@ public: void loadFromConfig(); public slots: - void testLogin( bool ); + void testLogin(); void onLastFmFinished(); protected: @@ -49,13 +50,23 @@ protected: private slots: void enableButton(); + + void loadHistory(); + void onHistoryLoaded(); + +signals: + void sizeHintChanged(); private: LastFmAccount* m_account; Ui_LastFmConfig* m_ui; + + unsigned int m_page; + unsigned int m_lastTimeStamp; }; } + } #endif // LASTFMCONFIG_H diff --git a/src/accounts/lastfm/LastFmConfig.ui b/src/libtomahawk/accounts/lastfm/LastFmConfig.ui similarity index 85% rename from src/accounts/lastfm/LastFmConfig.ui rename to src/libtomahawk/accounts/lastfm/LastFmConfig.ui index 5cdf0e5e8..1a75ae970 100644 --- a/src/accounts/lastfm/LastFmConfig.ui +++ b/src/libtomahawk/accounts/lastfm/LastFmConfig.ui @@ -7,7 +7,7 @@ 0 0 400 - 220 + 253 @@ -77,6 +77,20 @@
+ + + + Import Playback History + + + + + + + 0 + + + diff --git a/src/accounts/lastfm/LastFmInfoPlugin.cpp b/src/libtomahawk/accounts/lastfm/LastFmInfoPlugin.cpp similarity index 98% rename from src/accounts/lastfm/LastFmInfoPlugin.cpp rename to src/libtomahawk/accounts/lastfm/LastFmInfoPlugin.cpp index f016cf312..198129bb0 100644 --- a/src/accounts/lastfm/LastFmInfoPlugin.cpp +++ b/src/libtomahawk/accounts/lastfm/LastFmInfoPlugin.cpp @@ -30,6 +30,8 @@ #include "utils/TomahawkUtils.h" #include "utils/Logger.h" #include "accounts/lastfm/LastFmAccount.h" +#include "Source.h" +#include "TomahawkSettings.h" #include #include @@ -128,7 +130,7 @@ LastFmInfoPlugin::getInfo( Tomahawk::InfoSystem::InfoRequestData requestData ) case InfoChartCapabilities: fetchChartCapabilities( requestData ); break; - + case InfoTrackSimilars: fetchSimilarTracks( requestData ); break; @@ -210,6 +212,11 @@ LastFmInfoPlugin::scrobble() return; tLog() << Q_FUNC_INFO << "Scrobbling now:" << m_track.toString(); + + // FIXME: workaround for the duration-less dilandau (and others) tracks + if ( m_track.duration() == 0 ) + m_track.setDuration( 31 ); + m_scrobbler->cache( m_track ); m_scrobbler->submit(); } @@ -846,7 +853,7 @@ LastFmInfoPlugin::settingsChanged() m_scrobbler = 0; } - m_account.data()->setSessionKey( QString() ); + m_account.data()->setSessionKey( QString(), true ); createScrobbler(); } } @@ -869,13 +876,13 @@ LastFmInfoPlugin::onAuthenticated() if ( lfm.children( "error" ).size() > 0 ) { tLog() << "Error from authenticating with Last.fm service:" << lfm.text(); - m_account.data()->setSessionKey( QByteArray() ); + m_account.data()->setSessionKey( QString(), true ); } else { lastfm::ws::SessionKey = lfm[ "session" ][ "key" ].text(); - m_account.data()->setSessionKey( lastfm::ws::SessionKey.toLatin1() ); + m_account.data()->setSessionKey( lastfm::ws::SessionKey, true ); // qDebug() << "Got session key from last.fm"; if ( m_account.data()->scrobble() ) diff --git a/src/accounts/lastfm/LastFmInfoPlugin.h b/src/libtomahawk/accounts/lastfm/LastFmInfoPlugin.h similarity index 96% rename from src/accounts/lastfm/LastFmInfoPlugin.h rename to src/libtomahawk/accounts/lastfm/LastFmInfoPlugin.h index 443192b03..f744c740e 100644 --- a/src/accounts/lastfm/LastFmInfoPlugin.h +++ b/src/libtomahawk/accounts/lastfm/LastFmInfoPlugin.h @@ -22,6 +22,7 @@ #include "infosystem/InfoSystem.h" #include "infosystem/InfoSystemWorker.h" +#include "DllMacro.h" #include #include @@ -42,7 +43,7 @@ namespace Accounts namespace InfoSystem { -class LastFmInfoPlugin : public InfoPlugin +class DLLEXPORT LastFmInfoPlugin : public InfoPlugin { Q_OBJECT @@ -51,7 +52,6 @@ public: virtual ~LastFmInfoPlugin(); public slots: - void init(); void settingsChanged(); void onAuthenticated(); @@ -63,6 +63,7 @@ public slots: void similarTracksReturned(); protected slots: + virtual void init(); virtual void getInfo( Tomahawk::InfoSystem::InfoRequestData requestData ); virtual void notInCacheSlot( Tomahawk::InfoSystem::InfoStringHash criteria, Tomahawk::InfoSystem::InfoRequestData requestData ); diff --git a/src/accounts/spotify/SpotifyAccount.cpp b/src/libtomahawk/accounts/spotify/SpotifyAccount.cpp similarity index 68% rename from src/accounts/spotify/SpotifyAccount.cpp rename to src/libtomahawk/accounts/spotify/SpotifyAccount.cpp index a15047d44..d661fd09f 100644 --- a/src/accounts/spotify/SpotifyAccount.cpp +++ b/src/libtomahawk/accounts/spotify/SpotifyAccount.cpp @@ -26,6 +26,9 @@ #include "resolvers/ScriptResolver.h" #include "utils/TomahawkUtils.h" #include "ActionCollection.h" +#include "Pipeline.h" +#include "accounts/AccountManager.h" +#include "utils/Closure.h" #ifndef ENABLE_HEADLESS #include "jobview/JobStatusView.h" @@ -37,6 +40,7 @@ #include #include #include +#include using namespace Tomahawk; using namespace Accounts; @@ -44,6 +48,18 @@ using namespace Accounts; static QPixmap* s_icon = 0; +#ifdef Q_OS_MAC +static QString s_resolverId = "spotify-osx"; +#elif defined(Q_OS_WIN) +static QString s_resolverId = "spotify-win"; +#elif defined(Q_OS_LINUX) && defined(__GNUC__) && defined(__x86_64__) +static QString s_resolverId = "spotify-linux-x64"; +#elif defined(Q_OS_LINUX) +static QString s_resolverId = "spotify-linux-x86"; +#else +static QString s_resolverId = "spotify-unknown"; +#endif + Account* SpotifyAccountFactory::createAccount( const QString& accountId ) { @@ -51,21 +67,6 @@ SpotifyAccountFactory::createAccount( const QString& accountId ) } -bool -SpotifyAccountFactory::acceptsPath( const QString& path ) const -{ - QFileInfo info( path ); - return info.baseName().startsWith( "spotify_" ); -} - - -Account* -SpotifyAccountFactory::createFromPath( const QString& path ) -{ - return new SpotifyAccount( generateId( factoryId() ), path ); -} - - QPixmap SpotifyAccountFactory::icon() const { @@ -77,14 +78,8 @@ SpotifyAccountFactory::icon() const SpotifyAccount::SpotifyAccount( const QString& accountId ) - : ResolverAccount( accountId ) -{ - init(); -} - - -SpotifyAccount::SpotifyAccount( const QString& accountId, const QString& path ) - : ResolverAccount( accountId, path ) + : CustomAtticaAccount( accountId ) + , m_preventEnabling( false ) { init(); } @@ -93,16 +88,88 @@ SpotifyAccount::SpotifyAccount( const QString& accountId, const QString& path ) SpotifyAccount::~SpotifyAccount() { clearUser(); + + if ( m_spotifyResolver.isNull() ) + return; + + Pipeline::instance()->removeScriptResolver( m_spotifyResolver.data()->filePath() ); + delete m_spotifyResolver.data(); } void SpotifyAccount::init() { + connect( this, SIGNAL( credentialsChanged( QVariantHash ) ), this, SLOT( onCredentialsChanged( QVariantHash ) ) ); + + setAccountFriendlyName( "Spotify" ); + setAccountServiceName( "spotify" ); + + AtticaManager::instance()->registerCustomAccount( s_resolverId, this ); qRegisterMetaType< Tomahawk::Accounts::SpotifyPlaylistInfo* >( "Tomahawk::Accounts::SpotifyPlaylist*" ); - m_spotifyResolver = dynamic_cast< ScriptResolver* >( m_resolver.data() ); + if ( !AtticaManager::instance()->resolversLoaded() ) + { + // If we're still waiting to load, wait for the attica resolvers to come down the pipe + connect( AtticaManager::instance(), SIGNAL( resolversLoaded( Attica::Content::List ) ), this, SLOT( delayedInit() ), Qt::UniqueConnection ); + } + else + { + delayedInit(); + } +} + +void +SpotifyAccount::delayedInit() +{ + + connect( AtticaManager::instance(), SIGNAL( resolverInstalled( QString ) ), this, SLOT( resolverInstalled( QString ) ) ); + + const Attica::Content res = AtticaManager::instance()->resolverForId( s_resolverId ); + const AtticaManager::ResolverState state = AtticaManager::instance()->resolverState( res ); + + const QString path = configuration().value( "path" ).toString(); // Manual path override + if ( !checkForResolver() && state != AtticaManager::Uninstalled ) + { + // If the user manually deleted the resolver, mark it as uninstalled, so we re-fetch for the user + AtticaManager::instance()->uninstallResolver( res ); + } + else if ( state == AtticaManager::Installed || !path.isEmpty() ) + { + if ( !path.isEmpty() ) + { + QFileInfo info( path ); + // Resolver was deleted, so abort. + if ( !info.exists() ) + return; + } + hookupResolver(); + } +} + +void +SpotifyAccount::hookupResolver() +{ + // initialize the resolver itself. this is called if the account actually has an installed spotify resolver, + // as it might not. + // If there is a spotify resolver from attica installed, create the corresponding ExternalResolver* and hook up to it + QString path = configuration().value( "path" ).toString(); + if ( path.isEmpty() ) + { + const Attica::Content res = AtticaManager::instance()->resolverForId( s_resolverId ); + const AtticaManager::ResolverState state = AtticaManager::instance()->resolverState( res ); + Q_ASSERT( state == AtticaManager::Installed ); + Q_UNUSED( state ); + + const AtticaManager::Resolver data = AtticaManager::instance()->resolverData( res.id() ); + path = data.scriptPath; + } + + qDebug() << "Starting spotify resolver with path:" << path; + m_spotifyResolver = QWeakPointer< ScriptResolver >( qobject_cast< ScriptResolver* >( Pipeline::instance()->addScriptResolver( path ) ) ); + + connect( m_spotifyResolver.data(), SIGNAL( changed() ), this, SLOT( resolverChanged() ) ); connect( m_spotifyResolver.data(), SIGNAL( customMessage( QString,QVariantMap ) ), this, SLOT( resolverMessage( QString, QVariantMap ) ) ); const bool hasMigrated = configuration().value( "hasMigrated" ).toBool(); @@ -113,6 +180,174 @@ SpotifyAccount::init() msg[ "_msgtype" ] = "getCredentials"; m_spotifyResolver.data()->sendMessage( msg ); } + +} + + +void +SpotifyAccount::onCredentialsChanged(const QVariantHash &credentials) +{ + m_credentials = credentials; + + +} + + +bool +SpotifyAccount::checkForResolver() +{ +#if defined(Q_OS_MAC) + const QDir path = QCoreApplication::applicationDirPath(); + QFile file( path.absoluteFilePath( "spotify_tomahawkresolver" ) ); + return file.exists(); +#elif defined(Q_OS_WIN) + QDir appDataDir = TomahawkUtils::appDataDir(); + return appDataDir.exists( QString( "atticaresolvers/%1/spotify_tomahawkresolver.exe" ).arg( s_resolverId ) ); +#elif defined(Q_OS_LINUX) + QDir appDataDir = TomahawkUtils::appDataDir(); + return appDataDir.exists( QString( "atticaresolvers/%1/spotify_tomahawkresolver" ).arg( s_resolverId ) ); +#endif + + return false; +} + +void +SpotifyAccount::resolverChanged() +{ + emit connectionStateChanged( connectionState() ); +} + + +Attica::Content +SpotifyAccount::atticaContent() const +{ + return AtticaManager::instance()->resolverForId( s_resolverId ); +} + + +void +SpotifyAccount::authenticate() +{ + if ( !AtticaManager::instance()->resolversLoaded() ) + { + // If we're still waiting to load, wait for the attica resolvers to come down the pipe + connect( AtticaManager::instance(), SIGNAL( resolversLoaded( Attica::Content::List ) ), this, SLOT( atticaLoaded( Attica::Content::List ) ), Qt::UniqueConnection ); + return; + } + + const Attica::Content res = AtticaManager::instance()->resolverForId( s_resolverId ); + const AtticaManager::ResolverState state = AtticaManager::instance()->resolverState( res ); + + qDebug() << "Spotify account authenticating..."; + if ( m_spotifyResolver.isNull() && state == AtticaManager::Installed ) + { + // We don;t have the resolver but it has been installed via attica already, so lets just turn it on + qDebug() << "No valid spotify resolver running, but attica reports it is installed, so start it up"; + hookupResolver(); + } + else if ( m_spotifyResolver.isNull() ) + { + qDebug() << "Got null resolver but asked to authenticate, so installing if we have one from attica:" << res.isValid() << res.id(); + if ( res.isValid() && !res.id().isEmpty() ) + AtticaManager::instance()->installResolver( res, false ); + else + { +#ifdef Q_OS_LINUX + m_preventEnabling = true; +#endif + } + } + else if ( !m_spotifyResolver.data()->running() ) + { + qDebug() << "Spotify resolver exists but stopped, starting"; + m_spotifyResolver.data()->start(); + } + else + { + qDebug() << "Spotify resolver exists and is running, ignore authentication attempt"; + } + + emit connectionStateChanged( connectionState() ); +} + + +void +SpotifyAccount::deauthenticate() +{ + if ( !m_spotifyResolver.isNull() && m_spotifyResolver.data()->running() ) + m_spotifyResolver.data()->stop(); + + emit connectionStateChanged( connectionState() ); +} + + +bool +SpotifyAccount::isAuthenticated() const +{ + return !m_spotifyResolver.isNull() && m_spotifyResolver.data()->running(); +} + + +Account::ConnectionState +SpotifyAccount::connectionState() const +{ + return (!m_spotifyResolver.isNull() && m_spotifyResolver.data()->running()) ? Account::Connected : Account::Disconnected; +} + + +void +SpotifyAccount::resolverInstalled(const QString& resolverId) +{ + if ( resolverId == s_resolverId ) + { + // We requested this install, so we want to launch it + hookupResolver(); + AccountManager::instance()->enableAccount( this ); + } +} + + +void +SpotifyAccount::atticaLoaded( Attica::Content::List ) +{ + disconnect( AtticaManager::instance(), SIGNAL( resolversLoaded( Attica::Content::List ) ), this, SLOT( atticaLoaded( Attica::Content::List ) ) ); + authenticate(); +} + + +void +SpotifyAccount::setManualResolverPath( const QString &resolverPath ) +{ + Q_ASSERT( !resolverPath.isEmpty() ); + + QVariantHash conf = configuration(); + conf[ "path" ] = resolverPath; + setConfiguration( conf ); + sync(); + + m_preventEnabling = false; + + if ( !m_spotifyResolver.isNull() ) + { + // replace + AccountManager::instance()->disableAccount( this ); + NewClosure( m_spotifyResolver.data(), SIGNAL( destroyed() ), this, SLOT( hookupAfterDeletion( bool ) ), true ); + m_spotifyResolver.data()->deleteLater(); + } + else + { + hookupResolver(); + AccountManager::instance()->enableAccount( this ); + } +} + + +void +SpotifyAccount::hookupAfterDeletion( bool autoEnable ) +{ + hookupResolver(); + if ( autoEnable ) + AccountManager::instance()->enableAccount( this ); } @@ -124,13 +359,16 @@ SpotifyAccount::aboutToShow( QAction* action, const playlist_ptr& playlist ) // If it's not being synced, allow the option to sync bool found = false; + bool manuallyDisabled = false; QList updaters = playlist->updaters(); foreach ( PlaylistUpdaterInterface* updater, updaters ) { if ( SpotifyPlaylistUpdater* spotifyUpdater = qobject_cast< SpotifyPlaylistUpdater* >( updater ) ) { - if ( spotifyUpdater->sync() ) - found = true; + found = true; + if ( !spotifyUpdater->sync() ) + manuallyDisabled = true; + } } @@ -138,6 +376,10 @@ SpotifyAccount::aboutToShow( QAction* action, const playlist_ptr& playlist ) { action->setText( tr( "Sync with Spotify" ) ); } + else if ( manuallyDisabled ) + { + action->setText( tr( "Re-enable syncing with Spotify" ) ); + } else { action->setText( tr( "Stop syncing with Spotify" ) ); @@ -224,13 +466,12 @@ SpotifyAccount::resolverMessage( const QString &msgType, const QVariantMap &msg { if ( msgType == "credentials" ) { - QVariantHash creds = credentials(); - creds[ "username" ] = msg.value( "username" ); - creds[ "password" ] = msg.value( "password" ); - creds[ "highQuality" ] = msg.value( "highQuality" ); - setCredentials( creds ); + m_credentials[ "username" ] = msg.value( "username" ); + m_credentials[ "password" ] = msg.value( "password" ); + m_credentials[ "highQuality" ] = msg.value( "highQuality" ); + saveCredentials( m_credentials ); - qDebug() << "Set creds:" << creds.value( "username" ) << creds.value( "password" ) << msg.value( "username" ) << msg.value( "password" ); + qDebug() << "Set creds:" << m_credentials.value( "username" ) << m_credentials.value( "password" ) << msg.value( "username" ) << msg.value( "password" ); QVariantHash config = configuration(); config[ "hasMigrated" ] = true; @@ -381,12 +622,10 @@ SpotifyAccount::resolverMessage( const QString &msgType, const QVariantMap &msg } else if ( msgType == "loginResponse" ) { - QVariantHash creds = credentials(); - creds[ "username" ] = msg.value( "username" ).toString(); - creds[ "password" ] = msg.value( "password" ).toString(); - creds[ "highQuality" ] = msg.value( "highQuality" ).toString(); - setCredentials( creds ); - sync(); + m_credentials[ "username" ] = msg.value( "username" ).toString(); + m_credentials[ "password" ] = msg.value( "password" ).toString(); + m_credentials[ "highQuality" ] = msg.value( "highQuality" ).toString(); + saveCredentials( m_credentials ); const bool success = msg.value( "success" ).toBool(); @@ -449,6 +688,9 @@ SpotifyAccount::icon() const QWidget* SpotifyAccount::configurationWidget() { + if ( m_spotifyResolver.isNull() ) + return 0; + if ( m_configWidget.isNull() ) { m_configWidget = QWeakPointer< SpotifyAccountConfig >( new SpotifyAccountConfig( this ) ); @@ -490,15 +732,14 @@ SpotifyAccount::saveConfig() if ( m_configWidget.isNull() ) return; - QVariantHash creds = credentials(); - if ( creds.value( "username" ).toString() != m_configWidget.data()->username() || - creds.value( "password" ).toString() != m_configWidget.data()->password() || - creds.value( "highQuality" ).toBool() != m_configWidget.data()->highQuality() ) + if ( m_credentials.value( "username" ).toString() != m_configWidget.data()->username() || + m_credentials.value( "password" ).toString() != m_configWidget.data()->password() || + m_credentials.value( "highQuality" ).toBool() != m_configWidget.data()->highQuality() ) { - creds[ "username" ] = m_configWidget.data()->username(); - creds[ "password" ] = m_configWidget.data()->password(); - creds[ "highQuality" ] = m_configWidget.data()->highQuality(); - setCredentials( creds ); + m_credentials[ "username" ] = m_configWidget.data()->username(); + m_credentials[ "password" ] = m_configWidget.data()->password(); + m_credentials[ "highQuality" ] = m_configWidget.data()->highQuality(); + saveCredentials( m_credentials ); } @@ -587,7 +828,8 @@ SpotifyAccount::startPlaylistSyncWithPlaylist( const QString& msgType, const QVa */ if ( m_updaters.contains( id ) ) { - Q_ASSERT( m_updaters[ id ]->sync() == false ); /// Should have been unchecked/off before + //Q_ASSERT( m_updaters[ id ]->sync() == false ); /// Should have been unchecked/off before, but might not be if the user + // changed spotify resolver meanwhile, so allow it for now m_updaters[ id ]->setSync( true ); // m_updaters[ id ]-> // TODO @@ -678,7 +920,7 @@ SpotifyAccount::unregisterUpdater( const QString& plid ) void SpotifyAccount::fetchFullPlaylist( SpotifyPlaylistInfo* playlist ) { - + Q_UNUSED( playlist ); } diff --git a/src/accounts/spotify/SpotifyAccount.h b/src/libtomahawk/accounts/spotify/SpotifyAccount.h similarity index 81% rename from src/accounts/spotify/SpotifyAccount.h rename to src/libtomahawk/accounts/spotify/SpotifyAccount.h index c3ee789d8..cc1a490ce 100644 --- a/src/accounts/spotify/SpotifyAccount.h +++ b/src/libtomahawk/accounts/spotify/SpotifyAccount.h @@ -22,9 +22,11 @@ #include "accounts/ResolverAccount.h" #include "SourceList.h" +#include "AtticaManager.h" #include "Playlist.h" #include "utils/TomahawkUtils.h" #include "utils/SmartPointerList.h" +#include "DllMacro.h" class QAction; class SpotifyPlaylistUpdater; @@ -50,7 +52,7 @@ struct SpotifyPlaylistInfo { }; -class SpotifyAccountFactory : public AccountFactory +class DLLEXPORT SpotifyAccountFactory : public AccountFactory { Q_OBJECT public: @@ -61,9 +63,6 @@ public: virtual QString factoryId() const { return "spotifyaccount"; } virtual QString prettyName() const { return "Spotify"; } - virtual bool acceptsPath( const QString& path ) const; - virtual Account* createFromPath( const QString& path ); - virtual AccountTypes types() const { return AccountTypes( ResolverType ); } virtual bool allowUserCreation() const { return false; } virtual QPixmap icon() const; @@ -71,7 +70,7 @@ public: }; -class SpotifyAccount : public ResolverAccount +class DLLEXPORT SpotifyAccount : public CustomAtticaAccount { Q_OBJECT public: @@ -83,10 +82,16 @@ public: virtual QWidget* configurationWidget(); virtual QWidget* aboutWidget(); virtual void saveConfig(); + virtual Attica::Content atticaContent() const; + virtual void authenticate(); + virtual ConnectionState connectionState() const; + virtual bool isAuthenticated() const; + virtual void deauthenticate(); virtual QWidget* aclWidget() { return 0; } virtual Tomahawk::InfoSystem::InfoPluginPtr infoPlugin() { return Tomahawk::InfoSystem::InfoPluginPtr(); } virtual SipPlugin* sipPlugin() { return 0; } + virtual bool preventEnabling() const { return m_preventEnabling; } QString sendMessage( const QVariantMap& msg, QObject* receiver = 0, const QString& slot = QString() ); @@ -95,11 +100,21 @@ public: bool deleteOnUnsync() const; + void setManualResolverPath( const QString& resolverPath ); + + QVariantHash credentials() const { return m_credentials; } + public slots: void aboutToShow( QAction* action, const Tomahawk::playlist_ptr& playlist ); void syncActionTriggered( bool ); + void atticaLoaded(Attica::Content::List); private slots: + void onCredentialsChanged( const QVariantHash& credentials ); + + void resolverChanged(); + void resolverInstalled( const QString& resolverId ); + void resolverMessage( const QString& msgType, const QVariantMap& msg ); void login( const QString& username, const QString& password ); @@ -108,8 +123,14 @@ private slots: void startPlaylistSyncWithPlaylist( const QString& msgType, const QVariantMap& msg ); void playlistCreated( const QString& msgType, const QVariantMap& msg ); + void delayedInit(); + void hookupAfterDeletion( bool autoEnable ); + private: void init(); + bool checkForResolver(); + void hookupResolver(); + void loadPlaylists(); void clearUser( bool permanentlyDelete = false ); @@ -126,6 +147,8 @@ private: QWeakPointer m_aboutWidget; QWeakPointer m_spotifyResolver; + QVariantHash m_credentials; + QMap > m_qidToSlotMap; // List of synced spotify playlists in config UI @@ -134,6 +157,8 @@ private: QHash< QString, playlist_ptr > m_waitingForCreateReply; + bool m_preventEnabling; + SmartPointerList< QAction > m_customActions; friend class ::SpotifyPlaylistUpdater; }; @@ -141,6 +166,6 @@ private: } } -Q_DECLARE_METATYPE( Tomahawk::Accounts::SpotifyPlaylistInfo* ); +Q_DECLARE_METATYPE( Tomahawk::Accounts::SpotifyPlaylistInfo* ) #endif // SpotifyAccount_H diff --git a/src/accounts/spotify/SpotifyAccountConfig.cpp b/src/libtomahawk/accounts/spotify/SpotifyAccountConfig.cpp similarity index 99% rename from src/accounts/spotify/SpotifyAccountConfig.cpp rename to src/libtomahawk/accounts/spotify/SpotifyAccountConfig.cpp index b3fa5f629..5e5676f24 100644 --- a/src/accounts/spotify/SpotifyAccountConfig.cpp +++ b/src/libtomahawk/accounts/spotify/SpotifyAccountConfig.cpp @@ -51,6 +51,8 @@ SpotifyAccountConfig::SpotifyAccountConfig( SpotifyAccount *account ) void SpotifyAccountConfig::showEvent( QShowEvent *event ) { + Q_UNUSED( event ); + loadFromConfig(); m_loggedInManually = false; } diff --git a/src/accounts/spotify/SpotifyAccountConfig.h b/src/libtomahawk/accounts/spotify/SpotifyAccountConfig.h similarity index 100% rename from src/accounts/spotify/SpotifyAccountConfig.h rename to src/libtomahawk/accounts/spotify/SpotifyAccountConfig.h diff --git a/src/accounts/spotify/SpotifyAccountConfig.ui b/src/libtomahawk/accounts/spotify/SpotifyAccountConfig.ui similarity index 100% rename from src/accounts/spotify/SpotifyAccountConfig.ui rename to src/libtomahawk/accounts/spotify/SpotifyAccountConfig.ui diff --git a/src/accounts/spotify/SpotifyPlaylistUpdater.cpp b/src/libtomahawk/accounts/spotify/SpotifyPlaylistUpdater.cpp similarity index 100% rename from src/accounts/spotify/SpotifyPlaylistUpdater.cpp rename to src/libtomahawk/accounts/spotify/SpotifyPlaylistUpdater.cpp diff --git a/src/accounts/spotify/SpotifyPlaylistUpdater.h b/src/libtomahawk/accounts/spotify/SpotifyPlaylistUpdater.h similarity index 95% rename from src/accounts/spotify/SpotifyPlaylistUpdater.h rename to src/libtomahawk/accounts/spotify/SpotifyPlaylistUpdater.h index d07fc172a..896e507d3 100644 --- a/src/accounts/spotify/SpotifyPlaylistUpdater.h +++ b/src/libtomahawk/accounts/spotify/SpotifyPlaylistUpdater.h @@ -21,6 +21,7 @@ #include "playlist/PlaylistUpdaterInterface.h" #include "utils/Closure.h" +#include "DllMacro.h" #include #include @@ -31,7 +32,7 @@ namespace Accounts { } } -class SpotifyPlaylistUpdater : public Tomahawk::PlaylistUpdaterInterface +class DLLEXPORT SpotifyPlaylistUpdater : public Tomahawk::PlaylistUpdaterInterface { Q_OBJECT @@ -106,7 +107,7 @@ private: }; -class SpotifyUpdaterFactory : public Tomahawk::PlaylistUpdaterFactory +class DLLEXPORT SpotifyUpdaterFactory : public Tomahawk::PlaylistUpdaterFactory { public: SpotifyUpdaterFactory() {} diff --git a/src/libtomahawk/audio/AudioEngine.cpp b/src/libtomahawk/audio/AudioEngine.cpp index 26be43e3b..755875614 100644 --- a/src/libtomahawk/audio/AudioEngine.cpp +++ b/src/libtomahawk/audio/AudioEngine.cpp @@ -1,6 +1,6 @@ /* === This file is part of Tomahawk Player - === * - * Copyright 2010-2011, Christian Muehlhaeuser + * Copyright 2010-2012, Christian Muehlhaeuser * Copyright 2010-2012, Jeff Mitchell * * Tomahawk is free software: you can redistribute it and/or modify @@ -36,16 +36,18 @@ #include "HeadlessCheck.h" #include "infosystem/InfoSystem.h" #include "Album.h" +#include "Pipeline.h" #include "utils/Logger.h" - using namespace Tomahawk; -AudioEngine* AudioEngine::s_instance = 0; +#define AUDIO_VOLUME_STEP 5 static QString s_aeInfoIdentifier = QString( "AUDIOENGINE" ); +AudioEngine* AudioEngine::s_instance = 0; + AudioEngine* AudioEngine::instance() @@ -79,21 +81,22 @@ AudioEngine::AudioEngine() connect( m_audioOutput, SIGNAL( volumeChanged( qreal ) ), SLOT( onVolumeChanged( qreal ) ) ); + m_stateQueueTimer.setInterval( 5000 ); + m_stateQueueTimer.setSingleShot( true ); + connect( &m_stateQueueTimer, SIGNAL( timeout() ), SLOT( queueStateSafety() ) ); + onVolumeChanged( m_audioOutput->volume() ); -#ifndef Q_WS_X11 - // On mac & win, phonon volume is independent from system volume, so the onVolumeChanged call above just sets our volume to 100%. - // Since it's indendent, we'll set it to 75% since that's nicer. - setVolume( 75 ); -#endif + setVolume( TomahawkSettings::instance()->volume() ); } AudioEngine::~AudioEngine() { tDebug() << Q_FUNC_INFO; + m_mediaObject->stop(); -// stop(); + TomahawkSettings::instance()->setVolume( volume() ); delete m_audioOutput; delete m_mediaObject; @@ -132,9 +135,7 @@ AudioEngine::play() if ( isPaused() ) { - setVolume( m_volume ); - m_mediaObject->play(); - setVolume( m_volume ); + queueState( Playing ); emit resumed(); sendNowPlayingNotification( Tomahawk::InfoSystem::InfoNowResumed ); @@ -149,8 +150,7 @@ AudioEngine::pause() { tDebug( LOGEXTRA ) << Q_FUNC_INFO; - m_volume = volume(); - m_mediaObject->pause(); + queueState( Paused ); emit paused(); Tomahawk::InfoSystem::InfoSystem::instance()->pushInfo( Tomahawk::InfoSystem::InfoPushData( s_aeInfoIdentifier, Tomahawk::InfoSystem::InfoNowPaused, QVariant(), Tomahawk::InfoSystem::PushNoFlag ) ); @@ -158,16 +158,20 @@ AudioEngine::pause() void -AudioEngine::stop() +AudioEngine::stop( AudioErrorCode errorCode ) { tDebug( LOGEXTRA ) << Q_FUNC_INFO; - emit stopped(); if ( isStopped() ) return; - setState( Stopped ); + if( errorCode == NoError ) + setState( Stopped ); + else + setState( Error ); + m_mediaObject->stop(); + emit stopped(); if ( !m_playlist.isNull() ) m_playlist.data()->reset(); @@ -180,7 +184,6 @@ AudioEngine::stop() sendWaitingNotification(); Tomahawk::InfoSystem::InfoPushData pushData( s_aeInfoIdentifier, Tomahawk::InfoSystem::InfoNowStopped, QVariant(), Tomahawk::InfoSystem::PushNoFlag ); - Tomahawk::InfoSystem::InfoSystem::instance()->pushInfo( pushData ); } @@ -216,8 +219,8 @@ AudioEngine::canGoNext() if ( m_playlist.isNull() ) return false; - if ( m_playlist.data()->skipRestrictions() == PlaylistInterface::NoSkip || - m_playlist.data()->skipRestrictions() == PlaylistInterface::NoSkipForwards ) + if ( m_playlist.data()->skipRestrictions() == PlaylistModes::NoSkip || + m_playlist.data()->skipRestrictions() == PlaylistModes::NoSkipForwards ) return false; if ( !m_currentTrack.isNull() && !m_playlist->hasNextItem() && @@ -239,8 +242,8 @@ AudioEngine::canGoPrevious() if ( m_playlist.isNull() ) return false; - if ( m_playlist.data()->skipRestrictions() == PlaylistInterface::NoSkip || - m_playlist.data()->skipRestrictions() == PlaylistInterface::NoSkipBackwards ) + if ( m_playlist.data()->skipRestrictions() == PlaylistModes::NoSkip || + m_playlist.data()->skipRestrictions() == PlaylistModes::NoSkipBackwards ) return false; return true; @@ -255,7 +258,10 @@ AudioEngine::canSeek() if ( m_mediaObject && m_mediaObject->isValid() ) phononCanSeek = m_mediaObject->isSeekable(); */ - return !m_playlist.isNull() && ( m_playlist.data()->seekRestrictions() != PlaylistInterface::NoSeek ) && phononCanSeek; + if ( m_playlist.isNull() ) + return phononCanSeek; + + return !m_playlist.isNull() && ( m_playlist.data()->seekRestrictions() != PlaylistModes::NoSeek ) && phononCanSeek; } @@ -295,6 +301,20 @@ AudioEngine::setVolume( int percentage ) } +void +AudioEngine::lowerVolume() +{ + setVolume( volume() - AUDIO_VOLUME_STEP ); +} + + +void +AudioEngine::raiseVolume() +{ + setVolume( volume() + AUDIO_VOLUME_STEP ); +} + + void AudioEngine::mute() { @@ -323,12 +343,14 @@ void AudioEngine::sendNowPlayingNotification( const Tomahawk::InfoSystem::InfoType type ) { #ifndef ENABLE_HEADLESS - if ( m_currentTrack->album().isNull() || m_currentTrack->album()->infoLoaded() ) + if ( m_currentTrack->toQuery()->coverLoaded() ) + { onNowPlayingInfoReady( type ); + } else { - NewClosure( m_currentTrack->album().data(), SIGNAL( updated() ), const_cast< AudioEngine* >( this ), SLOT( onNowPlayingInfoReady( const Tomahawk::InfoSystem::InfoType ) ), type ); - m_currentTrack->album()->cover( QSize( 0, 0 ), true ); + NewClosure( m_currentTrack->toQuery().data(), SIGNAL( coverChanged() ), const_cast< AudioEngine* >( this ), SLOT( sendNowPlayingNotification( const Tomahawk::InfoSystem::InfoType ) ), type ); + m_currentTrack->toQuery()->cover( QSize( 0, 0 ), true ); } #endif } @@ -337,44 +359,42 @@ AudioEngine::sendNowPlayingNotification( const Tomahawk::InfoSystem::InfoType ty void AudioEngine::onNowPlayingInfoReady( const Tomahawk::InfoSystem::InfoType type ) { - tDebug( LOGVERBOSE ) << Q_FUNC_INFO << type; if ( m_currentTrack.isNull() || m_currentTrack->track().isNull() || m_currentTrack->artist().isNull() ) return; - + QVariantMap playInfo; - if ( !m_currentTrack->album().isNull() ) - { #ifndef ENABLE_HEADLESS - QImage cover; - cover = m_currentTrack->album()->cover( QSize( 0, 0 ) ).toImage(); - if ( !cover.isNull() ) - { - playInfo["cover"] = cover; + QImage cover; + cover = m_currentTrack->toQuery()->cover( QSize( 0, 0 ) ).toImage(); + if ( !cover.isNull() ) + { + playInfo["cover"] = cover; - QTemporaryFile* coverTempFile = new QTemporaryFile( QDir::toNativeSeparators( QDir::tempPath() + "/" + m_currentTrack->artist()->name() + "_" + m_currentTrack->album()->name() + "_tomahawk_cover.png" ) ); - if ( !coverTempFile->open() ) - tDebug() << Q_FUNC_INFO << "WARNING: could not write temporary file for cover art!"; - else - { - // Finally, save the image to the new temp file - coverTempFile->setAutoRemove( false ); - if ( cover.save( coverTempFile, "PNG" ) ) - { - tDebug( LOGVERBOSE ) << Q_FUNC_INFO << "Saving cover image to:" << QFileInfo( *coverTempFile ).absoluteFilePath(); - playInfo["coveruri"] = QFileInfo( *coverTempFile ).absoluteFilePath(); - } - else - tDebug() << Q_FUNC_INFO << "failed to save cover image!"; - } - delete coverTempFile; + QTemporaryFile* coverTempFile = new QTemporaryFile( QDir::toNativeSeparators( QDir::tempPath() + "/" + m_currentTrack->artist()->name() + "_" + m_currentTrack->album()->name() + "_tomahawk_cover.png" ) ); + if ( !coverTempFile->open() ) + { + tDebug() << Q_FUNC_INFO << "WARNING: could not write temporary file for cover art!"; } else - tDebug() << Q_FUNC_INFO << "Cover from album is null!"; -#endif + { + // Finally, save the image to the new temp file + coverTempFile->setAutoRemove( false ); + if ( cover.save( coverTempFile, "PNG" ) ) + { + tDebug() << Q_FUNC_INFO << "Saving cover image to:" << QFileInfo( *coverTempFile ).absoluteFilePath(); + playInfo["coveruri"] = QFileInfo( *coverTempFile ).absoluteFilePath(); + } + else + tDebug() << Q_FUNC_INFO << "failed to save cover image!"; + } + delete coverTempFile; } + else + tDebug() << Q_FUNC_INFO << "Cover from query is null!"; +#endif Tomahawk::InfoSystem::InfoStringHash trackInfo; trackInfo["title"] = m_currentTrack->track(); @@ -385,10 +405,10 @@ AudioEngine::onNowPlayingInfoReady( const Tomahawk::InfoSystem::InfoType type ) playInfo["trackinfo"] = QVariant::fromValue< Tomahawk::InfoSystem::InfoStringHash >( trackInfo ); playInfo["private"] = TomahawkSettings::instance()->privateListeningMode(); - + Tomahawk::InfoSystem::InfoPushData pushData ( s_aeInfoIdentifier, type, playInfo, Tomahawk::InfoSystem::PushShortUrlFlag ); - tDebug( LOGVERBOSE ) << Q_FUNC_INFO << "pushing data with type " << type; + tDebug( LOGVERBOSE ) << Q_FUNC_INFO << "pushing data with type" << type; Tomahawk::InfoSystem::InfoSystem::instance()->pushInfo( pushData ); } @@ -463,7 +483,7 @@ AudioEngine::loadTrack( const Tomahawk::result_ptr& result ) m_input.clear(); } m_input = io; - m_mediaObject->play(); + queueState( Playing ); emit started( m_currentTrack ); if ( TomahawkSettings::instance()->privateListeningMode() != TomahawkSettings::FullyPrivate ) @@ -471,7 +491,7 @@ AudioEngine::loadTrack( const Tomahawk::result_ptr& result ) DatabaseCommand_LogPlayback* cmd = new DatabaseCommand_LogPlayback( m_currentTrack, DatabaseCommand_LogPlayback::Started ); Database::instance()->enqueue( QSharedPointer(cmd) ); } - + sendNowPlayingNotification( Tomahawk::InfoSystem::InfoNowPlaying ); } } @@ -542,7 +562,7 @@ AudioEngine::loadNextTrack() } else { - if ( !m_playlist.isNull() && m_playlist.data()->retryMode() == Tomahawk::PlaylistInterface::Retry ) + if ( !m_playlist.isNull() && m_playlist.data()->retryMode() == Tomahawk::PlaylistModes::Retry ) m_waitingOnNewTrack = true; stop(); @@ -565,7 +585,7 @@ AudioEngine::playItem( Tomahawk::playlistinterface_ptr playlist, const Tomahawk: { loadTrack( result ); } - else if ( !m_playlist.isNull() && m_playlist.data()->retryMode() == PlaylistInterface::Retry ) + else if ( !m_playlist.isNull() && m_playlist.data()->retryMode() == PlaylistModes::Retry ) { m_waitingOnNewTrack = true; if ( isStopped() ) @@ -576,11 +596,63 @@ AudioEngine::playItem( Tomahawk::playlistinterface_ptr playlist, const Tomahawk: } +void +AudioEngine::playItem( Tomahawk::playlistinterface_ptr playlist, const Tomahawk::query_ptr& query ) +{ + if ( query->resolvingFinished() ) + { + if ( query->numResults() ) + playItem( playlist, query->results().first() ); + } + else + { + Pipeline::instance()->resolve( query ); + + NewClosure( query.data(), SIGNAL( resolvingFinished( bool ) ), + const_cast(this), SLOT( playItem( Tomahawk::playlistinterface_ptr, Tomahawk::query_ptr ) ), playlist, query ); + } +} + + +void +AudioEngine::playItem( const Tomahawk::artist_ptr& artist ) +{ + playlistinterface_ptr pli = artist->playlistInterface( Mixed ); + if ( pli->trackCount() ) + { + playItem( pli, pli->tracks().first() ); + } + else + { + NewClosure( artist.data(), SIGNAL( tracksAdded( QList, Tomahawk::ModelMode, Tomahawk::collection_ptr ) ), + const_cast(this), SLOT( playItem( Tomahawk::artist_ptr ) ), artist ); + pli->tracks(); + } +} + + +void +AudioEngine::playItem( const Tomahawk::album_ptr& album ) +{ + playlistinterface_ptr pli = album->playlistInterface( Mixed ); + if ( pli->trackCount() ) + { + playItem( pli, pli->tracks().first() ); + } + else + { + NewClosure( album.data(), SIGNAL( tracksAdded( QList, Tomahawk::ModelMode, Tomahawk::collection_ptr ) ), + const_cast(this), SLOT( playItem( Tomahawk::album_ptr ) ), album ); + pli->tracks(); + } +} + + void AudioEngine::onPlaylistNextTrackReady() { // If in real-time and you have a few seconds left, you're probably lagging -- finish it up - if ( m_playlist && m_playlist->latchMode() == PlaylistInterface::RealTime && ( m_waitingOnNewTrack || m_currentTrack.isNull() || m_currentTrack->id() == 0 || ( currentTrackTotalTime() - currentTime() > 6000 ) ) ) + if ( m_playlist && m_playlist->latchMode() == PlaylistModes::RealTime && ( m_waitingOnNewTrack || m_currentTrack.isNull() || m_currentTrack->id() == 0 || ( currentTrackTotalTime() - currentTime() > 6000 ) ) ) { m_waitingOnNewTrack = false; loadNextTrack(); @@ -610,14 +682,17 @@ AudioEngine::onStateChanged( Phonon::State newState, Phonon::State oldState ) if ( newState == Phonon::ErrorState ) { - stop(); + stop( UnknownError ); + + tDebug() << "Phonon Error:" << m_mediaObject->errorString() << m_mediaObject->errorType(); - tLog() << "Phonon Error:" << m_mediaObject->errorString() << m_mediaObject->errorType(); emit error( UnknownError ); - return; + setState( Error ); } if ( newState == Phonon::PlayingState ) + { setState( Playing ); + } if ( oldState == Phonon::PlayingState ) { @@ -650,12 +725,22 @@ AudioEngine::onStateChanged( Phonon::State newState, Phonon::State oldState ) loadNextTrack(); else { - if ( !m_playlist.isNull() && m_playlist.data()->retryMode() == Tomahawk::PlaylistInterface::Retry ) + if ( !m_playlist.isNull() && m_playlist.data()->retryMode() == Tomahawk::PlaylistModes::Retry ) m_waitingOnNewTrack = true; stop(); } } } + + if ( newState == Phonon::PausedState || newState == Phonon::PlayingState || newState == Phonon::ErrorState ) + { + tDebug() << "Phonon state now:" << newState; + if ( m_stateQueue.count() ) + { + AudioState qState = m_stateQueue.dequeue(); + checkStateQueue(); + } + } } @@ -692,7 +777,7 @@ AudioEngine::setPlaylist( Tomahawk::playlistinterface_ptr playlist ) if ( !m_playlist.isNull() ) { - if ( m_playlist.data() && m_playlist.data()->retryMode() == PlaylistInterface::Retry ) + if ( m_playlist.data() && m_playlist.data()->retryMode() == PlaylistModes::Retry ) disconnect( m_playlist.data(), SIGNAL( nextTrackReady() ) ); m_playlist.data()->reset(); } @@ -703,11 +788,11 @@ AudioEngine::setPlaylist( Tomahawk::playlistinterface_ptr playlist ) emit playlistChanged( playlist ); return; } - + m_playlist = playlist; m_stopAfterTrack.clear(); - if ( !m_playlist.isNull() && m_playlist.data() && m_playlist.data()->retryMode() == PlaylistInterface::Retry ) + if ( !m_playlist.isNull() && m_playlist.data() && m_playlist.data()->retryMode() == PlaylistModes::Retry ) connect( m_playlist.data(), SIGNAL( nextTrackReady() ), SLOT( onPlaylistNextTrackReady() ) ); emit playlistChanged( playlist ); @@ -720,24 +805,23 @@ AudioEngine::setStopAfterTrack( const query_ptr& query ) if ( m_stopAfterTrack != query ) { m_stopAfterTrack = query; - emit stopAfterTrack_changed(); - } + emit stopAfterTrackChanged(); + } } void AudioEngine::setCurrentTrack( const Tomahawk::result_ptr& result ) { - Tomahawk::result_ptr lastTrack = m_currentTrack; - if ( !lastTrack.isNull() ) + if ( !m_currentTrack.isNull() ) { - if ( TomahawkSettings::instance()->privateListeningMode() == TomahawkSettings::PublicListening ) + if ( m_state != Error && TomahawkSettings::instance()->privateListeningMode() == TomahawkSettings::PublicListening ) { - DatabaseCommand_LogPlayback* cmd = new DatabaseCommand_LogPlayback( lastTrack, DatabaseCommand_LogPlayback::Finished, m_timeElapsed ); + DatabaseCommand_LogPlayback* cmd = new DatabaseCommand_LogPlayback( m_currentTrack, DatabaseCommand_LogPlayback::Finished, m_timeElapsed ); Database::instance()->enqueue( QSharedPointer(cmd) ); } - emit finished( lastTrack ); + emit finished( m_currentTrack ); } m_currentTrack = result; @@ -758,6 +842,61 @@ AudioEngine::isLocalResult( const QString& url ) const } +void +AudioEngine::checkStateQueue() +{ + if ( m_stateQueue.count() ) + { + AudioState state = m_stateQueue.head(); + tDebug() << "Applying state command:" << state; + switch ( state ) + { + case Playing: + { + bool paused = isPaused(); + m_mediaObject->play(); + if ( paused ) + setVolume( m_volume ); + } + + case Paused: + { + m_volume = volume(); + m_mediaObject->pause(); + } + } + } + else + tDebug() << "Queue is empty"; +} + + +void +AudioEngine::queueStateSafety() +{ + tDebug() << Q_FUNC_INFO; + m_stateQueue.clear(); +} + + +void +AudioEngine::queueState( AudioState state ) +{ + if ( m_stateQueueTimer.isActive() ) + m_stateQueueTimer.stop(); + + tDebug() << "Enqueuing state command:" << state << m_stateQueue.count(); + m_stateQueue.enqueue( state ); + + if ( m_stateQueue.count() == 1 ) + { + checkStateQueue(); + } + + m_stateQueueTimer.start(); +} + + void AudioEngine::setState( AudioState state ) { diff --git a/src/libtomahawk/audio/AudioEngine.h b/src/libtomahawk/audio/AudioEngine.h index ab65fb522..d8a56aed8 100644 --- a/src/libtomahawk/audio/AudioEngine.h +++ b/src/libtomahawk/audio/AudioEngine.h @@ -1,6 +1,6 @@ /* === This file is part of Tomahawk Player - === * - * Copyright 2010-2011, Christian Muehlhaeuser + * Copyright 2010-2012, Christian Muehlhaeuser * Copyright 2010-2012, Jeff Mitchell * * Tomahawk is free software: you can redistribute it and/or modify @@ -22,6 +22,7 @@ #include #include +#include #include #include @@ -29,22 +30,19 @@ #include "libtomahawk/infosystem/InfoSystem.h" -#include "Result.h" #include "Typedefs.h" +#include "Result.h" #include "PlaylistInterface.h" #include "DllMacro.h" -#define AUDIO_VOLUME_STEP 5 - - class DLLEXPORT AudioEngine : public QObject { Q_OBJECT public: - enum AudioErrorCode { StreamReadError, AudioDeviceError, DecodeError, UnknownError }; - enum AudioState { Stopped, Playing, Paused }; + enum AudioErrorCode { StreamReadError, AudioDeviceError, DecodeError, UnknownError, NoError }; + enum AudioState { Stopped, Playing, Paused, Error }; static AudioEngine* instance(); @@ -66,7 +64,7 @@ public: Tomahawk::playlistinterface_ptr playlist() const { return m_playlist; } Tomahawk::result_ptr currentTrack() const { return m_currentTrack; } - + Tomahawk::query_ptr stopAfterTrack() const { return m_stopAfterTrack; } qint64 currentTime() const { return m_mediaObject->currentTime(); } @@ -76,7 +74,7 @@ public slots: void playPause(); void play(); void pause(); - void stop(); + void stop( AudioErrorCode errorCode = NoError ); void previous(); void next(); @@ -88,14 +86,17 @@ public slots: void seek( qint64 ms ); void seek( int ms ); // for compatibility with seekbar in audiocontrols void setVolume( int percentage ); - void lowerVolume() { setVolume( volume() - AUDIO_VOLUME_STEP ); } - void raiseVolume() { setVolume( volume() + AUDIO_VOLUME_STEP ); } + void lowerVolume(); + void raiseVolume(); void mute(); void playItem( Tomahawk::playlistinterface_ptr playlist, const Tomahawk::result_ptr& result ); + void playItem( Tomahawk::playlistinterface_ptr playlist, const Tomahawk::query_ptr& query ); + void playItem( const Tomahawk::artist_ptr& artist ); + void playItem( const Tomahawk::album_ptr& album ); void setPlaylist( Tomahawk::playlistinterface_ptr playlist ); void setQueue( Tomahawk::playlistinterface_ptr queue ) { m_queue = queue; } - + void setStopAfterTrack( const Tomahawk::query_ptr& query ); signals: @@ -105,8 +106,8 @@ signals: void stopped(); void paused(); void resumed(); - - void stopAfterTrack_changed(); + + void stopAfterTrackChanged(); void seeked( qint64 ms ); @@ -135,16 +136,20 @@ private slots: void onNowPlayingInfoReady( const Tomahawk::InfoSystem::InfoType type ); void onPlaylistNextTrackReady(); + void sendNowPlayingNotification( const Tomahawk::InfoSystem::InfoType type ); void sendWaitingNotification() const; + + void queueStateSafety(); private: + void checkStateQueue(); + void queueState( AudioState state ); + void setState( AudioState state ); bool isHttpResult( const QString& ) const; bool isLocalResult( const QString& ) const; - void sendNowPlayingNotification( const Tomahawk::InfoSystem::InfoType type ); - QSharedPointer m_input; Tomahawk::query_ptr m_stopAfterTrack; @@ -161,9 +166,12 @@ private: bool m_waitingOnNewTrack; mutable QStringList m_supportedMimeTypes; - AudioState m_state; unsigned int m_volume; + AudioState m_state; + QQueue< AudioState > m_stateQueue; + QTimer m_stateQueueTimer; + static AudioEngine* s_instance; }; diff --git a/src/libtomahawk/context/ContextWidget.cpp b/src/libtomahawk/context/ContextWidget.cpp index 14f70504a..bbf0e8334 100644 --- a/src/libtomahawk/context/ContextWidget.cpp +++ b/src/libtomahawk/context/ContextWidget.cpp @@ -29,8 +29,7 @@ #include "context/pages/TopTracksContext.h" #include "context/pages/WikipediaContext.h" -#include "playlist/ArtistView.h" -#include "playlist/TreeModel.h" +#include "Source.h" #include "utils/StyleHelper.h" #include "utils/TomahawkUtilsGui.h" diff --git a/src/libtomahawk/context/pages/RelatedArtistsContext.cpp b/src/libtomahawk/context/pages/RelatedArtistsContext.cpp index 628d47eb0..f3a7edd4a 100644 --- a/src/libtomahawk/context/pages/RelatedArtistsContext.cpp +++ b/src/libtomahawk/context/pages/RelatedArtistsContext.cpp @@ -21,21 +21,21 @@ #include -#include "playlist/ArtistView.h" +#include "playlist/TreeView.h" #include "playlist/TreeModel.h" +#include "Source.h" using namespace Tomahawk; RelatedArtistsContext::RelatedArtistsContext() : ContextPage() - , m_infoId( uuid() ) { - m_relatedView = new ArtistView(); + m_relatedView = new TreeView(); m_relatedView->setGuid( "RelatedArtistsContext" ); m_relatedView->setUpdatesContextView( false ); m_relatedModel = new TreeModel( m_relatedView ); - m_relatedModel->setColumnStyle( TreeModel::TrackOnly ); + m_relatedModel->setStyle( TreeModel::Large ); m_relatedView->setTreeModel( m_relatedModel ); m_relatedView->setVerticalScrollBarPolicy( Qt::ScrollBarAlwaysOff ); m_relatedView->setSortingEnabled( false ); @@ -47,12 +47,6 @@ RelatedArtistsContext::RelatedArtistsContext() m_proxy = new QGraphicsProxyWidget(); m_proxy->setWidget( m_relatedView ); - - connect( Tomahawk::InfoSystem::InfoSystem::instance(), - SIGNAL( info( Tomahawk::InfoSystem::InfoRequestData, QVariant ) ), - SLOT( infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData, QVariant ) ) ); - - connect( Tomahawk::InfoSystem::InfoSystem::instance(), SIGNAL( finished( QString ) ), SLOT( infoSystemFinished( QString ) ) ); } @@ -69,18 +63,17 @@ RelatedArtistsContext::setArtist( const Tomahawk::artist_ptr& artist ) if ( !m_artist.isNull() && m_artist->name() == artist->name() ) return; + if ( !m_artist.isNull() ) + { + disconnect( m_artist.data(), SIGNAL( similarArtistsLoaded() ), this, SLOT( onSimilarArtistsLoaded() ) ); + } + m_artist = artist; - Tomahawk::InfoSystem::InfoStringHash artistInfo; - artistInfo["artist"] = artist->name(); + connect( m_artist.data(), SIGNAL( similarArtistsLoaded() ), SLOT( onSimilarArtistsLoaded() ) ); - Tomahawk::InfoSystem::InfoRequestData requestData; - requestData.caller = m_infoId; - requestData.customData = QVariantMap(); - requestData.input = QVariant::fromValue< Tomahawk::InfoSystem::InfoStringHash >( artistInfo ); - - requestData.type = Tomahawk::InfoSystem::InfoArtistSimilars; - Tomahawk::InfoSystem::InfoSystem::instance()->getInfo( requestData ); + m_relatedModel->clear(); + onSimilarArtistsLoaded(); } @@ -105,45 +98,10 @@ RelatedArtistsContext::setAlbum( const Tomahawk::album_ptr& album ) void -RelatedArtistsContext::infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData requestData, QVariant output ) +RelatedArtistsContext::onSimilarArtistsLoaded() { - if ( requestData.caller != m_infoId ) - return; - - InfoSystem::InfoStringHash trackInfo; - trackInfo = requestData.input.value< InfoSystem::InfoStringHash >(); - - if ( output.canConvert< QVariantMap >() ) + foreach ( const artist_ptr& artist, m_artist->similarArtists() ) { - if ( trackInfo["artist"] != m_artist->name() ) - { - qDebug() << "Returned info was for:" << trackInfo["artist"] << "- was looking for:" << m_artist->name(); - return; - } - } - - QVariantMap returnedData = output.value< QVariantMap >(); - switch ( requestData.type ) - { - case InfoSystem::InfoArtistSimilars: - { - m_relatedModel->clear(); - const QStringList artists = returnedData["artists"].toStringList(); - foreach ( const QString& artist, artists ) - { - m_relatedModel->addArtists( Artist::get( artist ) ); - } - break; - } - - default: - return; + m_relatedModel->addArtists( artist ); } } - - -void -RelatedArtistsContext::infoSystemFinished( QString target ) -{ - Q_UNUSED( target ); -} diff --git a/src/libtomahawk/context/pages/RelatedArtistsContext.h b/src/libtomahawk/context/pages/RelatedArtistsContext.h index 53d3c9a5e..3e45cebce 100644 --- a/src/libtomahawk/context/pages/RelatedArtistsContext.h +++ b/src/libtomahawk/context/pages/RelatedArtistsContext.h @@ -28,10 +28,9 @@ #include "Album.h" #include "Query.h" #include "context/ContextPage.h" -#include "infosystem/InfoSystem.h" class TreeModel; -class ArtistView; +class TreeView; class DLLEXPORT RelatedArtistsContext : public Tomahawk::ContextPage { @@ -56,16 +55,14 @@ public slots: virtual void setQuery( const Tomahawk::query_ptr& query ); private slots: - void infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData requestData, QVariant output ); - void infoSystemFinished( QString target ); + void onSimilarArtistsLoaded(); private: - ArtistView* m_relatedView; + TreeView* m_relatedView; TreeModel* m_relatedModel; QGraphicsProxyWidget* m_proxy; - QString m_infoId; Tomahawk::artist_ptr m_artist; }; diff --git a/src/libtomahawk/context/pages/TopTracksContext.cpp b/src/libtomahawk/context/pages/TopTracksContext.cpp index 12b8366a8..a8df51fe3 100644 --- a/src/libtomahawk/context/pages/TopTracksContext.cpp +++ b/src/libtomahawk/context/pages/TopTracksContext.cpp @@ -21,20 +21,19 @@ #include "playlist/PlaylistModel.h" #include "playlist/PlaylistView.h" -#include "playlist/TrackHeader.h" +#include "Source.h" using namespace Tomahawk; TopTracksContext::TopTracksContext() : ContextPage() - , m_infoId( uuid() ) { m_topHitsView = new PlaylistView(); m_topHitsView->setGuid( "TopTracksContext" ); m_topHitsView->setUpdatesContextView( false ); m_topHitsModel = new PlaylistModel( m_topHitsView ); - m_topHitsModel->setStyle( TrackModel::Short ); + m_topHitsModel->setStyle( PlayableModel::Short ); m_topHitsView->setPlaylistModel( m_topHitsModel ); m_topHitsView->setVerticalScrollBarPolicy( Qt::ScrollBarAlwaysOff ); @@ -44,12 +43,6 @@ TopTracksContext::TopTracksContext() m_proxy = new QGraphicsProxyWidget(); m_proxy->setWidget( m_topHitsView ); - - connect( Tomahawk::InfoSystem::InfoSystem::instance(), - SIGNAL( info( Tomahawk::InfoSystem::InfoRequestData, QVariant ) ), - SLOT( infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData, QVariant ) ) ); - - connect( Tomahawk::InfoSystem::InfoSystem::instance(), SIGNAL( finished( QString ) ), SLOT( infoSystemFinished( QString ) ) ); } @@ -66,18 +59,19 @@ TopTracksContext::setArtist( const Tomahawk::artist_ptr& artist ) if ( !m_artist.isNull() && m_artist->name() == artist->name() ) return; + if ( !m_artist.isNull() ) + { + disconnect( m_artist.data(), SIGNAL( tracksAdded( QList, Tomahawk::ModelMode, Tomahawk::collection_ptr ) ), + this, SLOT( onTracksFound( QList, Tomahawk::ModelMode ) ) ); + } + m_artist = artist; - Tomahawk::InfoSystem::InfoStringHash artistInfo; - artistInfo["artist"] = artist->name(); + connect( m_artist.data(), SIGNAL( tracksAdded( QList, Tomahawk::ModelMode, Tomahawk::collection_ptr ) ), + SLOT( onTracksFound( QList, Tomahawk::ModelMode ) ) ); - Tomahawk::InfoSystem::InfoRequestData requestData; - requestData.caller = m_infoId; - requestData.customData = QVariantMap(); - requestData.input = QVariant::fromValue< Tomahawk::InfoSystem::InfoStringHash >( artistInfo ); - - requestData.type = Tomahawk::InfoSystem::InfoArtistSongs; - Tomahawk::InfoSystem::InfoSystem::instance()->getInfo( requestData ); + m_topHitsModel->clear(); + onTracksFound( m_artist->tracks(), Mixed ); } @@ -102,51 +96,11 @@ TopTracksContext::setQuery( const Tomahawk::query_ptr& query ) void -TopTracksContext::infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData requestData, QVariant output ) +TopTracksContext::onTracksFound( const QList& queries, ModelMode mode ) { - if ( requestData.caller != m_infoId ) - return; + Q_UNUSED( mode ); - InfoSystem::InfoStringHash trackInfo; - trackInfo = requestData.input.value< InfoSystem::InfoStringHash >(); - - if ( output.canConvert< QVariantMap >() ) - { - if ( trackInfo["artist"] != m_artist->name() ) - { - qDebug() << "Returned info was for:" << trackInfo["artist"] << "- was looking for:" << m_artist->name(); - return; - } - } - - QVariantMap returnedData = output.value< QVariantMap >(); - switch ( requestData.type ) - { - case InfoSystem::InfoArtistSongs: - { - m_topHitsModel->clear(); - const QStringList tracks = returnedData["tracks"].toStringList(); - - int i = 0; - foreach ( const QString& track, tracks ) - { - query_ptr query = Query::get( m_artist->name(), track, QString(), uuid() ); - m_topHitsModel->append( query ); - - if ( ++i == 15 ) - break; - } - break; - } - - default: - return; - } + m_topHitsModel->append( queries ); } -void -TopTracksContext::infoSystemFinished( QString target ) -{ - Q_UNUSED( target ); -} diff --git a/src/libtomahawk/context/pages/TopTracksContext.h b/src/libtomahawk/context/pages/TopTracksContext.h index 662f00c02..bc8219b62 100644 --- a/src/libtomahawk/context/pages/TopTracksContext.h +++ b/src/libtomahawk/context/pages/TopTracksContext.h @@ -28,7 +28,6 @@ #include "Album.h" #include "Query.h" #include "context/ContextPage.h" -#include "infosystem/InfoSystem.h" class PlaylistModel; class PlaylistView; @@ -56,8 +55,7 @@ public slots: virtual void setQuery( const Tomahawk::query_ptr& query ); private slots: - void infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData requestData, QVariant output ); - void infoSystemFinished( QString target ); + void onTracksFound( const QList& queries, Tomahawk::ModelMode mode ); private: PlaylistView* m_topHitsView; @@ -65,7 +63,6 @@ private: QGraphicsProxyWidget* m_proxy; - QString m_infoId; Tomahawk::artist_ptr m_artist; }; diff --git a/src/libtomahawk/context/pages/WebContext.cpp b/src/libtomahawk/context/pages/WebContext.cpp index 807bd5775..e9a93a22d 100644 --- a/src/libtomahawk/context/pages/WebContext.cpp +++ b/src/libtomahawk/context/pages/WebContext.cpp @@ -17,6 +17,7 @@ */ #include "WebContext.h" +#include "Source.h" using namespace Tomahawk; diff --git a/src/libtomahawk/context/pages/WikipediaContext.cpp b/src/libtomahawk/context/pages/WikipediaContext.cpp index 3e89fe5b5..4da16410c 100644 --- a/src/libtomahawk/context/pages/WikipediaContext.cpp +++ b/src/libtomahawk/context/pages/WikipediaContext.cpp @@ -17,6 +17,7 @@ */ #include "WikipediaContext.h" +#include "Source.h" using namespace Tomahawk; diff --git a/src/libtomahawk/database/Database.cpp b/src/libtomahawk/database/Database.cpp index 2dd9e0ac0..eea51aca0 100644 --- a/src/libtomahawk/database/Database.cpp +++ b/src/libtomahawk/database/Database.cpp @@ -23,6 +23,7 @@ #include "DatabaseImpl.h" #include "DatabaseWorker.h" #include "utils/Logger.h" +#include "Source.h" #define DEFAULT_WORKER_THREADS 4 #define MAX_WORKER_THREADS 16 @@ -41,12 +42,16 @@ Database::Database( const QString& dbname, QObject* parent ) : QObject( parent ) , m_ready( false ) , m_impl( new DatabaseImpl( dbname, this ) ) - , m_workerRW( new DatabaseWorker( m_impl, this, true ) ) + , m_workerRW( new DatabaseWorker( this, true ) ) { s_instance = this; - m_maxConcurrentThreads = qBound( DEFAULT_WORKER_THREADS, QThread::idealThreadCount(), MAX_WORKER_THREADS ); - qDebug() << Q_FUNC_INFO << "Using" << m_maxConcurrentThreads << "threads"; + if ( MAX_WORKER_THREADS < DEFAULT_WORKER_THREADS ) + m_maxConcurrentThreads = MAX_WORKER_THREADS; + else + m_maxConcurrentThreads = qBound( DEFAULT_WORKER_THREADS, QThread::idealThreadCount(), MAX_WORKER_THREADS ); + + tDebug() << Q_FUNC_INFO << "Using" << m_maxConcurrentThreads << "database worker threads"; connect( m_impl, SIGNAL( indexReady() ), SIGNAL( indexReady() ) ); connect( m_impl, SIGNAL( indexReady() ), SIGNAL( ready() ) ); @@ -95,7 +100,7 @@ Database::enqueue( const QSharedPointer& lc ) // create new thread if < WORKER_THREADS if ( m_workers.count() < m_maxConcurrentThreads ) { - DatabaseWorker* worker = new DatabaseWorker( m_impl, this, false ); + DatabaseWorker* worker = new DatabaseWorker( this, false ); worker->start(); m_workers << worker; diff --git a/src/libtomahawk/database/Database.h b/src/libtomahawk/database/Database.h index b265bd39c..7bf05a09e 100644 --- a/src/libtomahawk/database/Database.h +++ b/src/libtomahawk/database/Database.h @@ -53,12 +53,13 @@ public: ~Database(); QString dbid() const; - bool indexReady() const { return m_indexReady; } void loadIndex(); - + bool indexReady() const { return m_indexReady; } bool isReady() const { return m_ready; } + DatabaseImpl* impl() const { return m_impl; } + signals: void indexReady(); // search index void ready(); @@ -74,8 +75,6 @@ private slots: void setIsReadyTrue() { m_ready = true; } private: - DatabaseImpl* impl() const { return m_impl; } - bool m_ready; DatabaseImpl* m_impl; DatabaseWorker* m_workerRW; diff --git a/src/libtomahawk/database/DatabaseCollection.cpp b/src/libtomahawk/database/DatabaseCollection.cpp index cb864cc66..2dca1980d 100644 --- a/src/libtomahawk/database/DatabaseCollection.cpp +++ b/src/libtomahawk/database/DatabaseCollection.cpp @@ -1,6 +1,7 @@ /* === This file is part of Tomahawk Player - === * * Copyright 2010-2011, Christian Muehlhaeuser + * Copyright 2010-2011, Leo Franchi * * Tomahawk is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/libtomahawk/database/DatabaseCollection.h b/src/libtomahawk/database/DatabaseCollection.h index a9e9fae65..de94bbd6c 100644 --- a/src/libtomahawk/database/DatabaseCollection.h +++ b/src/libtomahawk/database/DatabaseCollection.h @@ -1,6 +1,7 @@ /* === This file is part of Tomahawk Player - === * * Copyright 2010-2011, Christian Muehlhaeuser + * Copyright 2010-2011, Leo Franchi * * Tomahawk is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/libtomahawk/database/DatabaseCommand_AddClientAuth.cpp b/src/libtomahawk/database/DatabaseCommand_AddClientAuth.cpp index 43c4c5e8f..31b6200e7 100644 --- a/src/libtomahawk/database/DatabaseCommand_AddClientAuth.cpp +++ b/src/libtomahawk/database/DatabaseCommand_AddClientAuth.cpp @@ -1,6 +1,6 @@ /* === This file is part of Tomahawk Player - === * - * Copyright 2010-2011, Christian Muehlhaeuser + * Copyright 2010-2011, Leo Franchi * * Tomahawk is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -21,6 +21,7 @@ #include "DatabaseImpl.h" #include "TomahawkSqlQuery.h" #include "utils/Logger.h" +#include "Source.h" DatabaseCommand_AddClientAuth::DatabaseCommand_AddClientAuth( const QString& clientToken, diff --git a/src/libtomahawk/database/DatabaseCommand_AddSource.cpp b/src/libtomahawk/database/DatabaseCommand_AddSource.cpp index 36a7e9ea7..75056f859 100644 --- a/src/libtomahawk/database/DatabaseCommand_AddSource.cpp +++ b/src/libtomahawk/database/DatabaseCommand_AddSource.cpp @@ -22,6 +22,7 @@ #include "DatabaseImpl.h" #include "utils/Logger.h" +#include "Source.h" DatabaseCommand_addSource::DatabaseCommand_addSource( const QString& username, const QString& fname, QObject* parent ) diff --git a/src/libtomahawk/database/DatabaseCommand_AllAlbums.cpp b/src/libtomahawk/database/DatabaseCommand_AllAlbums.cpp index 5f7e8a396..cfc7c9165 100644 --- a/src/libtomahawk/database/DatabaseCommand_AllAlbums.cpp +++ b/src/libtomahawk/database/DatabaseCommand_AllAlbums.cpp @@ -73,11 +73,11 @@ DatabaseCommand_AllAlbums::execForArtist( DatabaseImpl* dbi ) QStringList sl = m_filter.split( " ", QString::SkipEmptyParts ); foreach( QString s, sl ) { - filtersql += QString( " AND ( artist.name LIKE '%%1%' OR album.name LIKE '%%1%' OR track.name LIKE '%%1%' )" ).arg( TomahawkUtils::sqlEscape( s ) ); + filtersql += QString( " AND ( artist.name LIKE '%%1%' OR album.name LIKE '%%1%' OR track.name LIKE '%%1%' )" ).arg( TomahawkSqlQuery::escape( s ) ); } filterToken = QString( "AND artist.id = file_join.artist AND file_join.track = track.id %1" ).arg( filtersql ); - tables = "artist, track, file, file_join"; + tables = "file, file_join, artist, track"; } else tables = "file, file_join"; @@ -139,7 +139,7 @@ DatabaseCommand_AllAlbums::execForCollection( DatabaseImpl* dbi ) QString sql = QString( "SELECT DISTINCT album.id, album.name, album.artist, artist.name " - "FROM album, file, file_join " + "FROM file_join, file, album " "LEFT OUTER JOIN artist ON album.artist = artist.id " "WHERE file.id = file_join.file " "AND file_join.album = album.id " diff --git a/src/libtomahawk/database/DatabaseCommand_AllArtists.cpp b/src/libtomahawk/database/DatabaseCommand_AllArtists.cpp index c9479a345..5d030d1ac 100644 --- a/src/libtomahawk/database/DatabaseCommand_AllArtists.cpp +++ b/src/libtomahawk/database/DatabaseCommand_AllArtists.cpp @@ -63,7 +63,7 @@ DatabaseCommand_AllArtists::exec( DatabaseImpl* dbi ) QStringList sl = m_filter.split( " ", QString::SkipEmptyParts ); foreach( QString s, sl ) { - filtersql += QString( " AND ( artist.name LIKE '%%1%' OR album.name LIKE '%%1%' OR track.name LIKE '%%1%' )" ).arg( TomahawkUtils::sqlEscape( s ) ); + filtersql += QString( " AND ( artist.name LIKE '%%1%' OR album.name LIKE '%%1%' OR track.name LIKE '%%1%' )" ).arg( TomahawkSqlQuery::escape( s ) ); } filterToken = QString( "AND file_join.track = track.id %1" ).arg( filtersql ); diff --git a/src/libtomahawk/database/DatabaseCommand_AllTracks.cpp b/src/libtomahawk/database/DatabaseCommand_AllTracks.cpp index ffc64a2cb..47d2c4aba 100644 --- a/src/libtomahawk/database/DatabaseCommand_AllTracks.cpp +++ b/src/libtomahawk/database/DatabaseCommand_AllTracks.cpp @@ -163,8 +163,6 @@ DatabaseCommand_AllTracks::exec( DatabaseImpl* dbi ) ql << qry; } - qDebug() << Q_FUNC_INFO << ql.length(); - emit tracks( ql, data() ); emit done( m_collection ); } diff --git a/src/libtomahawk/database/DatabaseCommand_ClientAuthValid.cpp b/src/libtomahawk/database/DatabaseCommand_ClientAuthValid.cpp index 7571b1fd5..1a83a9369 100644 --- a/src/libtomahawk/database/DatabaseCommand_ClientAuthValid.cpp +++ b/src/libtomahawk/database/DatabaseCommand_ClientAuthValid.cpp @@ -1,6 +1,6 @@ /* === This file is part of Tomahawk Player - === * - * Copyright 2010-2011, Christian Muehlhaeuser + * Copyright 2010-2011, Leo Franchi * * Tomahawk is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -21,6 +21,7 @@ #include "DatabaseImpl.h" #include "TomahawkSqlQuery.h" #include "utils/Logger.h" +#include "Source.h" DatabaseCommand_ClientAuthValid::DatabaseCommand_ClientAuthValid( const QString& clientToken, QObject* parent ) diff --git a/src/libtomahawk/database/DatabaseCommand_DeleteFiles.cpp b/src/libtomahawk/database/DatabaseCommand_DeleteFiles.cpp index 0fd08a9bd..38e968bbc 100644 --- a/src/libtomahawk/database/DatabaseCommand_DeleteFiles.cpp +++ b/src/libtomahawk/database/DatabaseCommand_DeleteFiles.cpp @@ -81,7 +81,7 @@ DatabaseCommand_DeleteFiles::exec( DatabaseImpl* dbi ) tDebug() << "Deleting" << m_dir.path() << "from db for localsource" << srcid; TomahawkSqlQuery dirquery = dbi->newquery(); QString path( "file://" + m_dir.canonicalPath() + "/%" ); - dirquery.prepare( QString( "SELECT id FROM file WHERE source IS NULL AND url LIKE '%1'" ).arg( TomahawkUtils::sqlEscape( path ) ) ); + dirquery.prepare( QString( "SELECT id FROM file WHERE source IS NULL AND url LIKE '%1'" ).arg( TomahawkSqlQuery::escape( path ) ) ); dirquery.exec(); while ( dirquery.next() ) diff --git a/src/libtomahawk/database/DatabaseCommand_DirMtimes.cpp b/src/libtomahawk/database/DatabaseCommand_DirMtimes.cpp index ae9ba13a4..6829acc6b 100644 --- a/src/libtomahawk/database/DatabaseCommand_DirMtimes.cpp +++ b/src/libtomahawk/database/DatabaseCommand_DirMtimes.cpp @@ -23,6 +23,7 @@ #include "DatabaseImpl.h" #include "utils/Logger.h" +#include "Source.h" void diff --git a/src/libtomahawk/database/DatabaseCommand_FileMTimes.cpp b/src/libtomahawk/database/DatabaseCommand_FileMTimes.cpp index b314723c7..706fbb65d 100644 --- a/src/libtomahawk/database/DatabaseCommand_FileMTimes.cpp +++ b/src/libtomahawk/database/DatabaseCommand_FileMTimes.cpp @@ -23,6 +23,7 @@ #include "DatabaseImpl.h" #include "utils/Logger.h" +#include "Source.h" void diff --git a/src/libtomahawk/database/DatabaseCommand_LoadDynamicPlaylist.cpp b/src/libtomahawk/database/DatabaseCommand_LoadDynamicPlaylist.cpp index 4deb3b93b..f0444ae01 100644 --- a/src/libtomahawk/database/DatabaseCommand_LoadDynamicPlaylist.cpp +++ b/src/libtomahawk/database/DatabaseCommand_LoadDynamicPlaylist.cpp @@ -24,6 +24,7 @@ #include "Source.h" #include "dynamic/DynamicPlaylist.h" #include "utils/Logger.h" +#include "Source.h" using namespace Tomahawk; diff --git a/src/libtomahawk/database/DatabaseCommand_LoadDynamicPlaylistEntries.cpp b/src/libtomahawk/database/DatabaseCommand_LoadDynamicPlaylistEntries.cpp index 29f6eeff7..985b22c67 100644 --- a/src/libtomahawk/database/DatabaseCommand_LoadDynamicPlaylistEntries.cpp +++ b/src/libtomahawk/database/DatabaseCommand_LoadDynamicPlaylistEntries.cpp @@ -28,6 +28,7 @@ #include "dynamic/GeneratorFactory.h" #include "qjson/parser.h" #include "utils/Logger.h" +#include "Source.h" using namespace Tomahawk; diff --git a/src/libtomahawk/database/DatabaseCommand_LoadFiles.cpp b/src/libtomahawk/database/DatabaseCommand_LoadFiles.cpp index 52e8ed547..24a89ff23 100644 --- a/src/libtomahawk/database/DatabaseCommand_LoadFiles.cpp +++ b/src/libtomahawk/database/DatabaseCommand_LoadFiles.cpp @@ -21,6 +21,7 @@ #include "DatabaseImpl.h" #include "Collection.h" #include "utils/Logger.h" +#include "Source.h" DatabaseCommand_LoadFiles::DatabaseCommand_LoadFiles( unsigned int id, QObject* parent ) diff --git a/src/libtomahawk/database/DatabaseCommand_LoadPlaylistEntries.cpp b/src/libtomahawk/database/DatabaseCommand_LoadPlaylistEntries.cpp index 6d2f997a0..89a4941b1 100644 --- a/src/libtomahawk/database/DatabaseCommand_LoadPlaylistEntries.cpp +++ b/src/libtomahawk/database/DatabaseCommand_LoadPlaylistEntries.cpp @@ -24,6 +24,7 @@ #include "Query.h" #include "qjson/parser.h" #include "utils/Logger.h" +#include "Source.h" using namespace Tomahawk; diff --git a/src/libtomahawk/database/DatabaseCommand_LogPlayback.cpp b/src/libtomahawk/database/DatabaseCommand_LogPlayback.cpp index 81ee8fbd6..37e089940 100644 --- a/src/libtomahawk/database/DatabaseCommand_LogPlayback.cpp +++ b/src/libtomahawk/database/DatabaseCommand_LogPlayback.cpp @@ -37,31 +37,33 @@ using namespace Tomahawk; void DatabaseCommand_LogPlayback::postCommitHook() { + if ( !m_query.isNull() ) + return; + connect( this, SIGNAL( trackPlaying( Tomahawk::query_ptr, unsigned int ) ), source().data(), SLOT( onPlaybackStarted( Tomahawk::query_ptr, unsigned int ) ), Qt::QueuedConnection ); connect( this, SIGNAL( trackPlayed( Tomahawk::query_ptr ) ), source().data(), SLOT( onPlaybackFinished( Tomahawk::query_ptr ) ), Qt::QueuedConnection ); - Tomahawk::query_ptr q; - if ( !m_result.isNull() ) + if ( !m_result.isNull() && m_query.isNull() ) { - q = m_result->toQuery(); + m_query = m_result->toQuery(); } else { // do not auto resolve this track - q = Tomahawk::Query::get( m_artist, m_track, QString() ); + m_query = Tomahawk::Query::get( m_artist, m_track, QString() ); } - q->setPlayedBy( source(), m_playtime ); + m_query->setPlayedBy( source(), m_playtime ); if ( m_action == Finished ) { - emit trackPlayed( q ); + emit trackPlayed( m_query ); } // if the play time is more than 10 minutes in the past, ignore else if ( m_action == Started && QDateTime::fromTime_t( playtime() ).secsTo( QDateTime::currentDateTime() ) < STARTED_THRESHOLD ) { - emit trackPlaying( q, m_trackDuration ); + emit trackPlaying( m_query, m_trackDuration ); } if ( source()->isLocal() ) @@ -78,25 +80,36 @@ DatabaseCommand_LogPlayback::exec( DatabaseImpl* dbi ) if ( m_action != Finished ) return; - if ( m_secsPlayed < FINISHED_THRESHOLD ) + if ( m_secsPlayed < FINISHED_THRESHOLD && m_trackDuration > 0 ) + return; + if ( m_artist.isEmpty() || m_track.isEmpty() ) return; - TomahawkSqlQuery query = dbi->newquery(); - query.prepare( "INSERT INTO playback_log(source, track, playtime, secs_played) " - "VALUES (?, ?, ?, ?)" ); - QVariant srcid = source()->isLocal() ? QVariant( QVariant::Int ) : source()->id(); - qDebug() << "Logging playback of" << m_artist << "-" << m_track << "for source" << srcid; + TomahawkSqlQuery query = dbi->newquery(); + + if ( !m_query.isNull() ) + { + query.prepare( QString( "SELECT * FROM playback_log WHERE source %1 AND playtime = %2" ).arg( srcid.isNull() ? "IS NULL" : srcid.toString() ).arg( m_playtime ) ); + query.exec(); + if ( query.next() ) + { + tDebug() << "Ignoring dupe playback log for source" << srcid << "with timestamp" << m_playtime; + return; + } + } + +// tDebug() << "Logging playback of" << m_artist << "-" << m_track << "for source" << srcid << "- timestamp:" << m_playtime; + + query.prepare( "INSERT INTO playback_log(source, track, playtime, secs_played) VALUES (?, ?, ?, ?)" ); query.bindValue( 0, srcid ); - // If there's no artist, becuase it's a resolver result with bad metadata for example, don't save it - bool autoCreate = m_artist.isEmpty(); - int artid = dbi->artistId( m_artist, autoCreate ); + // If there's no artist, because it's a resolver result with bad metadata for example, don't save it + int artid = dbi->artistId( m_artist, true ); if( artid < 1 ) return; - autoCreate = true; // artistId overwrites autoCreate (reference) - int trkid = dbi->trackId( artid, m_track, autoCreate ); + int trkid = dbi->trackId( artid, m_track, true ); if( trkid < 1 ) return; diff --git a/src/libtomahawk/database/DatabaseCommand_LogPlayback.h b/src/libtomahawk/database/DatabaseCommand_LogPlayback.h index 384a07579..14912ca55 100644 --- a/src/libtomahawk/database/DatabaseCommand_LogPlayback.h +++ b/src/libtomahawk/database/DatabaseCommand_LogPlayback.h @@ -51,6 +51,17 @@ public: : DatabaseCommandLoggable( parent ), m_playtime( 0 ), m_secsPlayed( 0 ), m_trackDuration( 0 ) {} + explicit DatabaseCommand_LogPlayback( const Tomahawk::query_ptr& query, Action action, uint timeStamp, QObject* parent = 0 ) + : DatabaseCommandLoggable( parent ), m_query( query ), m_secsPlayed( 0 ), m_action( action ) + { + m_playtime = timeStamp; + m_trackDuration = 0; + setSource( SourceList::instance()->getLocal() ); + + setArtist( query->artist() ); + setTrack( query->track() ); + } + explicit DatabaseCommand_LogPlayback( const Tomahawk::result_ptr& result, Action action, unsigned int secsPlayed = 0, QObject* parent = 0 ) : DatabaseCommandLoggable( parent ), m_result( result ), m_secsPlayed( secsPlayed ), m_action( action ) { @@ -96,6 +107,7 @@ signals: private: Tomahawk::result_ptr m_result; + Tomahawk::query_ptr m_query; QString m_artist; QString m_track; diff --git a/src/libtomahawk/database/DatabaseCommand_ModifyPlaylist.cpp b/src/libtomahawk/database/DatabaseCommand_ModifyPlaylist.cpp index 1a5a34ea3..eb6018f93 100644 --- a/src/libtomahawk/database/DatabaseCommand_ModifyPlaylist.cpp +++ b/src/libtomahawk/database/DatabaseCommand_ModifyPlaylist.cpp @@ -21,6 +21,7 @@ #include "utils/Logger.h" #include "Playlist.h" +#include "Source.h" using namespace Tomahawk; diff --git a/src/libtomahawk/database/DatabaseCommand_PlaybackHistory.cpp b/src/libtomahawk/database/DatabaseCommand_PlaybackHistory.cpp index 1aa3e37c8..a2093dd25 100644 --- a/src/libtomahawk/database/DatabaseCommand_PlaybackHistory.cpp +++ b/src/libtomahawk/database/DatabaseCommand_PlaybackHistory.cpp @@ -80,6 +80,5 @@ DatabaseCommand_PlaybackHistory::exec( DatabaseImpl* dbi ) } } - if ( ql.count() ) - emit tracks( ql ); + emit tracks( ql ); } diff --git a/src/libtomahawk/database/DatabaseCommand_SetPlaylistRevision.cpp b/src/libtomahawk/database/DatabaseCommand_SetPlaylistRevision.cpp index f166d0fa0..00c8cf6bb 100644 --- a/src/libtomahawk/database/DatabaseCommand_SetPlaylistRevision.cpp +++ b/src/libtomahawk/database/DatabaseCommand_SetPlaylistRevision.cpp @@ -167,7 +167,6 @@ DatabaseCommand_SetPlaylistRevision::exec( DatabaseImpl* lib ) foreach( const plentry_ptr& e, m_entries ) { - adde.bindValue( 0, e->query()->track() ); adde.bindValue( 1, e->query()->artist() ); adde.bindValue( 2, e->query()->album() ); diff --git a/src/libtomahawk/database/DatabaseCommand_SetTrackAttributes.cpp b/src/libtomahawk/database/DatabaseCommand_SetTrackAttributes.cpp index c7009b0d3..d952c0372 100644 --- a/src/libtomahawk/database/DatabaseCommand_SetTrackAttributes.cpp +++ b/src/libtomahawk/database/DatabaseCommand_SetTrackAttributes.cpp @@ -19,6 +19,7 @@ #include "DatabaseCommand_SetTrackAttributes.h" #include "TomahawkSqlQuery.h" #include "DatabaseImpl.h" +#include "Source.h" using namespace Tomahawk; diff --git a/src/libtomahawk/database/DatabaseCommand_SourceOffline.cpp b/src/libtomahawk/database/DatabaseCommand_SourceOffline.cpp index f8eb6e902..ed2edf7b0 100644 --- a/src/libtomahawk/database/DatabaseCommand_SourceOffline.cpp +++ b/src/libtomahawk/database/DatabaseCommand_SourceOffline.cpp @@ -21,6 +21,7 @@ #include "DatabaseImpl.h" #include "TomahawkSqlQuery.h" #include "utils/Logger.h" +#include "Source.h" DatabaseCommand_SourceOffline::DatabaseCommand_SourceOffline( int id ) diff --git a/src/libtomahawk/database/DatabaseCommand_TrackAttributes.cpp b/src/libtomahawk/database/DatabaseCommand_TrackAttributes.cpp index 82719a42b..48a313148 100644 --- a/src/libtomahawk/database/DatabaseCommand_TrackAttributes.cpp +++ b/src/libtomahawk/database/DatabaseCommand_TrackAttributes.cpp @@ -18,6 +18,7 @@ #include "DatabaseCommand_TrackAttributes.h" #include "DatabaseImpl.h" +#include "Source.h" using namespace Tomahawk; DatabaseCommand_TrackAttributes::DatabaseCommand_TrackAttributes( DatabaseCommand_SetTrackAttributes::AttributeType type, const QList< Tomahawk::QID > ids ) @@ -48,7 +49,6 @@ void DatabaseCommand_TrackAttributes::exec( DatabaseImpl* lib ) PairList results; if ( !m_ids.isEmpty() ) { - foreach ( const QID id, m_ids ) { query.prepare( "SELECT v FROM track_attributes WHERE id = ? AND k = ?" ); @@ -57,11 +57,13 @@ void DatabaseCommand_TrackAttributes::exec( DatabaseImpl* lib ) if ( query.exec() ) results.append( QPair< QID, QString >( id, query.value( 0 ).toString() ) ); } - } else { + } + else + { query.prepare( "SELECT id, v FROM track_attributes WHERE k = ?" ); query.bindValue( 0, k ); query.exec(); - while ( !query.next() ) + while ( query.next() ) { results.append( QPair< QID, QString >( query.value( 0 ).toString(), query.value( 1 ).toString() ) ); } diff --git a/src/libtomahawk/database/DatabaseCommand_TrackStats.cpp b/src/libtomahawk/database/DatabaseCommand_TrackStats.cpp index a85397dee..b918a4852 100644 --- a/src/libtomahawk/database/DatabaseCommand_TrackStats.cpp +++ b/src/libtomahawk/database/DatabaseCommand_TrackStats.cpp @@ -56,7 +56,7 @@ DatabaseCommand_TrackStats::exec( DatabaseImpl* dbi ) query.prepare( "SELECT * " "FROM playback_log " - "WHERE track = ?" ); + "WHERE track = ? ORDER BY playtime ASC" ); query.addBindValue( trkid ); query.exec(); } diff --git a/src/libtomahawk/database/DatabaseCommand_UpdateSearchIndex.cpp b/src/libtomahawk/database/DatabaseCommand_UpdateSearchIndex.cpp index 1c11d1c1b..e0bc3558a 100644 --- a/src/libtomahawk/database/DatabaseCommand_UpdateSearchIndex.cpp +++ b/src/libtomahawk/database/DatabaseCommand_UpdateSearchIndex.cpp @@ -1,6 +1,7 @@ /* === This file is part of Tomahawk Player - === * * Copyright 2010-2011, Christian Muehlhaeuser + * Copyright 2012 Leo Franchi * * Tomahawk is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -24,6 +25,7 @@ #include "jobview/IndexingJobItem.h" #include "jobview/JobStatusView.h" #include "jobview/JobStatusModel.h" +#include "Source.h" #include diff --git a/src/libtomahawk/database/DatabaseImpl.cpp b/src/libtomahawk/database/DatabaseImpl.cpp index d51ecea8a..4648a988a 100644 --- a/src/libtomahawk/database/DatabaseImpl.cpp +++ b/src/libtomahawk/database/DatabaseImpl.cpp @@ -27,7 +27,6 @@ #include #include "database/Database.h" -#include "DatabaseCommand_UpdateSearchIndex.h" #include "SourceList.h" #include "Result.h" #include "Artist.h" @@ -46,9 +45,7 @@ DatabaseImpl::DatabaseImpl( const QString& dbname, Database* parent ) : QObject( (QObject*) parent ) - , m_lastartid( 0 ) - , m_lastalbid( 0 ) - , m_lasttrkid( 0 ) + , m_parent( parent ) { QTime t; t.start(); @@ -67,24 +64,18 @@ DatabaseImpl::DatabaseImpl( const QString& dbname, Database* parent ) m_dbid = uuid(); query.exec( QString( "INSERT INTO settings(k,v) VALUES('dbid','%1')" ).arg( m_dbid ) ); } - tLog() << "Database ID:" << m_dbid; - // make sqlite behave how we want: - query.exec( "PRAGMA auto_vacuum = FULL" ); - query.exec( "PRAGMA synchronous = ON" ); - query.exec( "PRAGMA foreign_keys = ON" ); - //query.exec( "PRAGMA temp_store = MEMORY" ); + tLog() << "Database ID:" << m_dbid; + init(); + tDebug( LOGVERBOSE ) << "Tweaked db pragmas:" << t.elapsed(); // in case of unclean shutdown last time: query.exec( "UPDATE source SET isonline = 'false'" ); - m_fuzzyIndex = new FuzzyIndex( *this, schemaUpdated ); - if ( schemaUpdated ) - QTimer::singleShot( 0, this, SLOT( updateIndex() ) ); + m_fuzzyIndex = new FuzzyIndex( this, schemaUpdated ); tDebug( LOGVERBOSE ) << "Loaded index:" << t.elapsed(); - if ( qApp->arguments().contains( "--dumpdb" ) ) { dumpDatabase(); @@ -93,9 +84,59 @@ DatabaseImpl::DatabaseImpl( const QString& dbname, Database* parent ) } +DatabaseImpl::DatabaseImpl( Database* parent, const QString& dbname ) + : QObject( (QObject*) QThread::currentThread() ) + , m_parent( parent ) +{ + openDatabase( dbname, false ); + init(); +} + + +void +DatabaseImpl::init() +{ + m_lastartid = m_lastalbid = m_lasttrkid = 0; + + TomahawkSqlQuery query = newquery(); + + // make sqlite behave how we want: + query.exec( "PRAGMA auto_vacuum = FULL" ); + query.exec( "PRAGMA synchronous = ON" ); + query.exec( "PRAGMA foreign_keys = ON" ); + //query.exec( "PRAGMA temp_store = MEMORY" ); +} + + DatabaseImpl::~DatabaseImpl() { - delete m_fuzzyIndex; + tDebug() << "Shutting down database connection."; + +/* +#ifdef TOMAHAWK_QUERY_ANALYZE + TomahawkSqlQuery q = newquery(); + + q.exec( "ANALYZE" ); + q.exec( "SELECT * FROM sqlite_stat1" ); + while ( q.next() ) + { + tLog( LOGSQL ) << q.value( 0 ).toString() << q.value( 1 ).toString() << q.value( 2 ).toString(); + } + +#endif +*/ +} + + +DatabaseImpl* +DatabaseImpl::clone() const +{ + QMutexLocker lock( &m_mutex ); + + DatabaseImpl* impl = new DatabaseImpl( m_parent, m_db.databaseName() ); + impl->setDatabaseID( m_dbid ); + impl->setFuzzyIndex( m_fuzzyIndex ); + return impl; } @@ -665,34 +706,43 @@ DatabaseImpl::resultFromHint( const Tomahawk::query_ptr& origquery ) bool -DatabaseImpl::openDatabase( const QString& dbname ) +DatabaseImpl::openDatabase( const QString& dbname, bool checkSchema ) { + const QStringList conns = QSqlDatabase::connectionNames(); + const QString connName = QString( "tomahawk%1" ).arg( conns.count() ? QString::number( conns.count() ) : "" ); + bool schemaUpdated = false; int version = -1; { - QSqlDatabase db = QSqlDatabase::addDatabase( "QSQLITE", "tomahawk" ); + QSqlDatabase db = QSqlDatabase::addDatabase( "QSQLITE", connName ); db.setDatabaseName( dbname ); + db.setConnectOptions( "QSQLITE_ENABLE_SHARED_CACHE=1" ); if ( !db.open() ) { tLog() << "Failed to open database" << dbname; throw "failed to open db"; // TODO } - - QSqlQuery qry = QSqlQuery( db ); - qry.exec( "SELECT v FROM settings WHERE k='schema_version'" ); - if ( qry.next() ) + + if ( checkSchema ) { - version = qry.value( 0 ).toInt(); - tLog() << "Database schema of" << dbname << "is" << version; + QSqlQuery qry = QSqlQuery( db ); + qry.exec( "SELECT v FROM settings WHERE k='schema_version'" ); + if ( qry.next() ) + { + version = qry.value( 0 ).toInt(); + tLog() << "Database schema of" << dbname << "is" << version; + } } - + else + version = CURRENT_SCHEMA_VERSION; + if ( version < 0 || version == CURRENT_SCHEMA_VERSION ) m_db = db; } if ( version > 0 && version != CURRENT_SCHEMA_VERSION ) { - QSqlDatabase::removeDatabase( "tomahawk" ); + QSqlDatabase::removeDatabase( connName ); QString newname = QString( "%1.v%2" ).arg( dbname ).arg( version ); tLog() << endl << "****************************" << endl; @@ -703,7 +753,7 @@ DatabaseImpl::openDatabase( const QString& dbname ) QFile::copy( dbname, newname ); { - m_db = QSqlDatabase::addDatabase( "QSQLITE", "tomahawk" ); + m_db = QSqlDatabase::addDatabase( "QSQLITE", connName ); m_db.setDatabaseName( dbname ); if ( !m_db.open() ) throw "db moving failed"; @@ -723,11 +773,3 @@ DatabaseImpl::openDatabase( const QString& dbname ) return schemaUpdated; } - - -void -DatabaseImpl::updateIndex() -{ - DatabaseCommand* cmd = new DatabaseCommand_UpdateSearchIndex(); - Database::instance()->enqueue( QSharedPointer( cmd ) ); -} diff --git a/src/libtomahawk/database/DatabaseImpl.h b/src/libtomahawk/database/DatabaseImpl.h index f734f171f..47b921d33 100644 --- a/src/libtomahawk/database/DatabaseImpl.h +++ b/src/libtomahawk/database/DatabaseImpl.h @@ -48,7 +48,7 @@ public: DatabaseImpl( const QString& dbname, Database* parent = 0 ); ~DatabaseImpl(); - bool openDatabase( const QString& dbname ); + DatabaseImpl* clone() const; TomahawkSqlQuery newquery() { return TomahawkSqlQuery( m_db ); } QSqlDatabase& database() { return m_db; } @@ -81,14 +81,18 @@ public: signals: void indexReady(); -private slots: - void updateIndex(); - private: - QString cleanSql( const QString& sql ); + DatabaseImpl( Database* parent, const QString& dbname ); + void setFuzzyIndex( FuzzyIndex* fi ) { m_fuzzyIndex = fi; } + void setDatabaseID( const QString& dbid ) { m_dbid = dbid; } + + void init(); + bool openDatabase( const QString& dbname, bool checkSchema = true ); bool updateSchema( int oldVersion ); void dumpDatabase(); + QString cleanSql( const QString& sql ); + Database* m_parent; bool m_ready; QSqlDatabase m_db; @@ -97,6 +101,7 @@ private: QString m_dbid; FuzzyIndex* m_fuzzyIndex; + mutable QMutex m_mutex; }; #endif // DATABASEIMPL_H diff --git a/src/libtomahawk/database/DatabaseResolver.cpp b/src/libtomahawk/database/DatabaseResolver.cpp index 2825c4851..f7791c616 100644 --- a/src/libtomahawk/database/DatabaseResolver.cpp +++ b/src/libtomahawk/database/DatabaseResolver.cpp @@ -22,6 +22,7 @@ #include "network/Servent.h" #include "database/Database.h" #include "database/DatabaseCommand_Resolve.h" +#include "Source.h" #include "utils/Logger.h" @@ -53,7 +54,10 @@ DatabaseResolver::resolve( const Tomahawk::query_ptr& query ) void DatabaseResolver::gotResults( const Tomahawk::QID qid, QList< Tomahawk::result_ptr> results ) { - qDebug() << Q_FUNC_INFO << qid << results.length(); + tDebug( LOGVERBOSE ) << Q_FUNC_INFO << qid << results.length(); + + foreach ( const Tomahawk::result_ptr& r, results ) + r->setResolvedBy( this ); Tomahawk::Pipeline::instance()->reportResults( qid, results ); } diff --git a/src/libtomahawk/database/DatabaseWorker.cpp b/src/libtomahawk/database/DatabaseWorker.cpp index bf4972fb0..186012437 100644 --- a/src/libtomahawk/database/DatabaseWorker.cpp +++ b/src/libtomahawk/database/DatabaseWorker.cpp @@ -34,9 +34,9 @@ //#define DEBUG_TIMING TRUE #endif -DatabaseWorker::DatabaseWorker( DatabaseImpl* lib, Database* db, bool mutates ) +DatabaseWorker::DatabaseWorker( Database* db, bool mutates ) : QThread() - , m_dbimpl( lib ) + , m_db( db ) , m_outstanding( 0 ) { Q_UNUSED( db ); @@ -68,6 +68,8 @@ DatabaseWorker::~DatabaseWorker() void DatabaseWorker::run() { + m_dbimpl = m_db->impl()->clone(); + tDebug() << "New db connection with name:" << m_dbimpl->database().connectionName(); exec(); qDebug() << Q_FUNC_INFO << "DatabaseWorker finishing..."; } @@ -191,7 +193,7 @@ DatabaseWorker::doWork() if ( cmd->doesMutates() ) { qDebug() << "Committing" << cmd->commandname() << cmd->guid(); - if ( !m_dbimpl->database().commit() ) + if ( !m_dbimpl->newquery().commitTransaction() ) { tDebug() << "FAILED TO COMMIT TRANSACTION*"; throw "commit failed"; diff --git a/src/libtomahawk/database/DatabaseWorker.h b/src/libtomahawk/database/DatabaseWorker.h index 458d4d427..7da0acc6c 100644 --- a/src/libtomahawk/database/DatabaseWorker.h +++ b/src/libtomahawk/database/DatabaseWorker.h @@ -39,7 +39,7 @@ class DatabaseWorker : public QThread Q_OBJECT public: - DatabaseWorker( DatabaseImpl*, Database*, bool mutates ); + DatabaseWorker( Database*, bool mutates ); ~DatabaseWorker(); bool busy() const { return m_outstanding > 0; } @@ -59,6 +59,7 @@ private: void logOp( DatabaseCommandLoggable* command ); QMutex m_mut; + Database* m_db; DatabaseImpl* m_dbimpl; QList< QSharedPointer > m_commands; int m_outstanding; diff --git a/src/libtomahawk/database/FuzzyIndex.cpp b/src/libtomahawk/database/FuzzyIndex.cpp index a77ac8801..017e1bb4b 100644 --- a/src/libtomahawk/database/FuzzyIndex.cpp +++ b/src/libtomahawk/database/FuzzyIndex.cpp @@ -24,9 +24,12 @@ #include #include +#include "DatabaseCommand_UpdateSearchIndex.h" #include "DatabaseImpl.h" +#include "Database.h" #include "utils/TomahawkUtils.h" #include "utils/Logger.h" +#include "Source.h" using namespace lucene::analysis; using namespace lucene::analysis::standard; @@ -37,22 +40,29 @@ using namespace lucene::queryParser; using namespace lucene::search; -FuzzyIndex::FuzzyIndex( DatabaseImpl& db, bool wipeIndex ) - : QObject() - , m_db( db ) +FuzzyIndex::FuzzyIndex( QObject* parent, bool wipe ) + : QObject( parent ) , m_luceneReader( 0 ) , m_luceneSearcher( 0 ) { QString m_lucenePath = TomahawkUtils::appDataDir().absoluteFilePath( "tomahawk.lucene" ); - m_luceneDir = FSDirectory::getDirectory( m_lucenePath.toStdString().c_str() ); - m_analyzer = _CLNEW SimpleAnalyzer(); + QByteArray path = m_lucenePath.toUtf8(); + const char* cPath = path.constData(); - if ( wipeIndex ) + tDebug() << "Opening Lucene directory:" << path; + try { - tLog( LOGVERBOSE ) << "Wiping fuzzy index..."; - beginIndexing(); - endIndexing(); + m_luceneDir = FSDirectory::getDirectory( cPath ); + m_analyzer = _CLNEW SimpleAnalyzer(); } + catch ( CLuceneError& error ) + { + tDebug() << "Caught CLucene error:" << error.what(); + wipe = true; + } + + if ( wipe ) + wipeIndex(); } @@ -65,6 +75,27 @@ FuzzyIndex::~FuzzyIndex() } +bool +FuzzyIndex::wipeIndex() +{ + tLog( LOGVERBOSE ) << "Wiping fuzzy index..."; + beginIndexing(); + endIndexing(); + + QTimer::singleShot( 0, this, SLOT( updateIndex() ) ); + + return true; // FIXME +} + + +void +FuzzyIndex::updateIndex() +{ + DatabaseCommand* cmd = new DatabaseCommand_UpdateSearchIndex(); + Database::instance()->enqueue( QSharedPointer( cmd ) ); +} + + void FuzzyIndex::beginIndexing() { @@ -89,7 +120,7 @@ FuzzyIndex::beginIndexing() } catch( CLuceneError& error ) { - qDebug() << "Caught CLucene error:" << error.what(); + tDebug() << "Caught CLucene error:" << error.what(); Q_ASSERT( false ); } } @@ -157,8 +188,9 @@ FuzzyIndex::appendFields( const QMap< unsigned int, QMap< QString, QString > >& } catch( CLuceneError& error ) { - qDebug() << "Caught CLucene error:" << error.what(); - Q_ASSERT( false ); + tDebug() << "Caught CLucene error:" << error.what(); + + wipeIndex(); } } @@ -198,7 +230,7 @@ FuzzyIndex::search( const Tomahawk::query_ptr& query ) if ( query->isFullTextQuery() ) { QString escapedQuery = QString::fromWCharArray( parser.escape( DatabaseImpl::sortname( query->fullTextQuery() ).toStdWString().c_str() ) ); - + Term* term = _CLNEW Term( _T( "track" ), escapedQuery.toStdWString().c_str() ); Query* fqry = _CLNEW FuzzyQuery( term ); qry->add( fqry, true, BooleanClause::SHOULD ); @@ -251,7 +283,8 @@ FuzzyIndex::search( const Tomahawk::query_ptr& query ) catch( CLuceneError& error ) { tDebug() << "Caught CLucene error:" << error.what(); - Q_ASSERT( false ); + + wipeIndex(); } return resultsmap; @@ -305,7 +338,8 @@ FuzzyIndex::searchAlbum( const Tomahawk::query_ptr& query ) catch( CLuceneError& error ) { tDebug() << "Caught CLucene error:" << error.what(); - Q_ASSERT( false ); + + wipeIndex(); } return resultsmap; diff --git a/src/libtomahawk/database/FuzzyIndex.h b/src/libtomahawk/database/FuzzyIndex.h index 47fe1bf5d..e1f0be693 100644 --- a/src/libtomahawk/database/FuzzyIndex.h +++ b/src/libtomahawk/database/FuzzyIndex.h @@ -55,7 +55,7 @@ class FuzzyIndex : public QObject Q_OBJECT public: - explicit FuzzyIndex( DatabaseImpl& db, bool wipeIndex = false ); + explicit FuzzyIndex( QObject* parent, bool wipe = false ); ~FuzzyIndex(); void beginIndexing(); @@ -71,8 +71,12 @@ public slots: QMap< int, float > search( const Tomahawk::query_ptr& query ); QMap< int, float > searchAlbum( const Tomahawk::query_ptr& query ); +private slots: + void updateIndex(); + private: - DatabaseImpl& m_db; + bool wipeIndex(); + QMutex m_mutex; QString m_lucenePath; diff --git a/src/libtomahawk/database/TomahawkSqlQuery.cpp b/src/libtomahawk/database/TomahawkSqlQuery.cpp new file mode 100644 index 000000000..aa75fc9e5 --- /dev/null +++ b/src/libtomahawk/database/TomahawkSqlQuery.cpp @@ -0,0 +1,131 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2010-2011, Christian Muehlhaeuser + * + * Tomahawk is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Tomahawk is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Tomahawk. If not, see . + */ + +#include "database/TomahawkSqlQuery.h" + +#include "database/Database.h" +#include "database/DatabaseImpl.h" +#include "utils/TomahawkUtils.h" +#include "utils/Logger.h" + +#include +#include +#include +#include + +#define QUERY_THRESHOLD 60 + + +TomahawkSqlQuery::TomahawkSqlQuery() + : QSqlQuery() +{ +} + + +TomahawkSqlQuery::TomahawkSqlQuery( const QSqlDatabase& db ) + : QSqlQuery( db ) + , m_db( db ) +{ +} + + +QString +TomahawkSqlQuery::escape( const QString& identifier, QSqlDriver::IdentifierType type ) +{ + return Database::instance()->impl()->database().driver()->escapeIdentifier( identifier, type ); +} + + +bool +TomahawkSqlQuery::exec( const QString& query ) +{ + prepare( query ); + return exec(); +} + + +bool +TomahawkSqlQuery::exec() +{ + QTime t; + t.start(); + + unsigned int retries = 0; + while ( !QSqlQuery::exec() && ++retries < 10 ) + { + if ( isBusyError( lastError() ) ) + retries = 0; + + tDebug() << "INFO: Retrying failed query:" << lastQuery() << lastError().text(); + TomahawkUtils::msleep( 10 ); + } + + bool ret = ( retries < 10 ); + if ( !ret ) + showError(); + + int e = t.elapsed(); + bool log = ( e >= QUERY_THRESHOLD ); + +#ifdef TOMAHAWK_QUERY_ANALYZE + log = true; +#endif + + if ( log ) + tLog( LOGSQL ) << "TomahawkSqlQuery (" << t.elapsed() << "ms ):" << lastQuery(); + + return ret; +} + + +bool +TomahawkSqlQuery::commitTransaction() +{ + unsigned int retries = 0; + while ( !m_db.commit() && ++retries < 10 ) + { + if ( isBusyError( lastError() ) ) + retries = 0; + + tDebug() << "INFO: Retrying failed commit:" << retries << lastQuery() << lastError().text(); + TomahawkUtils::msleep( 10 ); + } + + return ( retries < 10 ); +} + + +void +TomahawkSqlQuery::showError() +{ + tLog() << endl << "*** DATABASE ERROR ***" << endl + << lastQuery() << endl + << "boundValues:" << boundValues() << endl + << lastError().text() << endl; + + Q_ASSERT( false ); +} + + +bool +TomahawkSqlQuery::isBusyError( const QSqlError& error ) const +{ + const QString text = error.text().trimmed().toLower(); + + return ( text.contains( "locked" ) || text.contains( "busy" ) || text.isEmpty() ); +} diff --git a/src/libtomahawk/database/TomahawkSqlQuery.h b/src/libtomahawk/database/TomahawkSqlQuery.h index b88d56eb8..a54543a35 100644 --- a/src/libtomahawk/database/TomahawkSqlQuery.h +++ b/src/libtomahawk/database/TomahawkSqlQuery.h @@ -18,62 +18,34 @@ #ifndef TOMAHAWKSQLQUERY_H #define TOMAHAWKSQLQUERY_H + // subclass QSqlQuery so that it prints the error msg if a query fails +#include #include -#include -#include -#include "utils/Logger.h" - -#define TOMAHAWK_QUERY_THRESHOLD 60 +#define TOMAHAWK_QUERY_ANALYZE 1 class TomahawkSqlQuery : public QSqlQuery { public: + TomahawkSqlQuery(); + TomahawkSqlQuery( const QSqlDatabase& db ); - TomahawkSqlQuery() - : QSqlQuery() - {} + static QString escape( const QString& identifier, QSqlDriver::IdentifierType type = QSqlDriver::FieldName ); - TomahawkSqlQuery( const QSqlDatabase& db ) - : QSqlQuery( db ) - {} - - bool exec( const QString& query ) - { - prepare( query ); - - return exec(); - } - - bool exec() - { - QTime t; - t.start(); - - bool ret = QSqlQuery::exec(); - if( !ret ) - showError(); - - int e = t.elapsed(); - if ( e >= TOMAHAWK_QUERY_THRESHOLD ) - tLog( LOGVERBOSE ) << "TomahawkSqlQuery (" << lastQuery() << ") finished in" << t.elapsed() << "ms"; - - return ret; - } + bool exec( const QString& query ); + bool exec(); + + bool commitTransaction(); private: - void showError() - { - tLog() << "\n" << "*** DATABASE ERROR ***" << "\n" - << this->lastQuery() << "\n" - << "boundValues:" << this->boundValues() << "\n" - << this->lastError().text() << "\n" - ; - Q_ASSERT( false ); - } + bool isBusyError( const QSqlError& error ) const; + + void showError(); + + QSqlDatabase m_db; }; #endif // TOMAHAWKSQLQUERY_H diff --git a/src/libtomahawk/infobar/InfoBar.cpp b/src/libtomahawk/infobar/InfoBar.cpp index 0f999e5f7..84f334658 100644 --- a/src/libtomahawk/infobar/InfoBar.cpp +++ b/src/libtomahawk/infobar/InfoBar.cpp @@ -22,15 +22,16 @@ #include #include - -#include "ViewManager.h" -#include "thirdparty/Qocoa/qsearchfield.h" -#include "utils/TomahawkUtils.h" -#include "utils/Logger.h" #include #include #include -#include + +#include "ViewManager.h" +#include "thirdparty/Qocoa/qsearchfield.h" +#include "utils/TomahawkUtilsGui.h" +#include "utils/Logger.h" +#include "widgets/QueryLabel.h" +#include "Source.h" #define ANIMATION_TIME 400 #define IMAGE_HEIGHT 64 @@ -54,6 +55,7 @@ InfoBar::InfoBar( QWidget* parent ) ui->captionLabel->setElideMode( Qt::ElideRight ); boldFont.setPixelSize( 12 ); + boldFont.setBold( false ); ui->descriptionLabel->setFont( boldFont ); QFont regFont = ui->longDescriptionLabel->font(); @@ -323,6 +325,8 @@ InfoBar::createTile( int w ) void InfoBar::paintEvent( QPaintEvent* e ) { + Q_UNUSED( e ); + if ( m_bgTile.isNull() || width() > m_bgTile.width() ) createTile( width() ); diff --git a/src/libtomahawk/infosystem/InfoSystem.cpp b/src/libtomahawk/infosystem/InfoSystem.cpp index 925091b56..af5a06b25 100644 --- a/src/libtomahawk/infosystem/InfoSystem.cpp +++ b/src/libtomahawk/infosystem/InfoSystem.cpp @@ -26,6 +26,7 @@ #include "InfoSystemWorker.h" #include "utils/TomahawkUtils.h" #include "utils/Logger.h" +#include "Source.h" namespace Tomahawk { @@ -33,6 +34,35 @@ namespace Tomahawk namespace InfoSystem { +static const int DEFAULT_TIMEOUT_MILLIS = 10000; + +InfoRequestData::InfoRequestData() + : requestId( TomahawkUtils::infosystemRequestId() ) +{ + init( QString() , Tomahawk::InfoSystem::InfoNoInfo, QVariant(), QVariantMap() ); +} + + +InfoRequestData::InfoRequestData( const quint64 rId, const QString& callr, const InfoType typ, const QVariant& inputvar, const QVariantMap& custom ) + : requestId( rId ) +{ + init( callr, typ, inputvar, custom ); +} + + +void +InfoRequestData::init( const QString& callr, const InfoType typ, const QVariant& inputvar, const QVariantMap& custom ) +{ + internalId = TomahawkUtils::infosystemRequestId(); + caller = callr; + type = typ; + input = inputvar; + customData = custom; + timeoutMillis = DEFAULT_TIMEOUT_MILLIS; + allSources = false; +} + + InfoPlugin::InfoPlugin() : QObject() { @@ -164,7 +194,7 @@ InfoSystem::getInfo( const QString &caller, const QVariantMap &customData, const { requestData.type = type; requestData.input = inputMap[ type ]; - requestData.timeoutMillis = timeoutMap.contains( type ) ? timeoutMap[ type ] : 10000; + requestData.timeoutMillis = timeoutMap.contains( type ) ? timeoutMap[ type ] : DEFAULT_TIMEOUT_MILLIS; QMetaObject::invokeMethod( m_infoSystemWorkerThreadController->worker(), "getInfo", Qt::QueuedConnection, Q_ARG( Tomahawk::InfoSystem::InfoRequestData, requestData ) ); } return false; @@ -224,7 +254,7 @@ InfoSystem::addInfoPlugin( Tomahawk::InfoSystem::InfoPluginPtr plugin ) tDebug() << Q_FUNC_INFO << "Given plugin is null!"; return; } - + if ( plugin.data()->thread() != m_infoSystemWorkerThreadController->worker()->thread() ) { Q_ASSERT( false ); diff --git a/src/libtomahawk/infosystem/InfoSystem.h b/src/libtomahawk/infosystem/InfoSystem.h index 1689e2bbb..324c1e352 100644 --- a/src/libtomahawk/infosystem/InfoSystem.h +++ b/src/libtomahawk/infosystem/InfoSystem.h @@ -34,6 +34,7 @@ #include "DllMacro.h" #include "utils/TomahawkUtils.h" +#include "Typedefs.h" class QNetworkAccessManager; @@ -49,102 +50,8 @@ enum PushInfoFlags { // must be powers of 2 PushShortUrlFlag = 2 }; -enum InfoType { // as items are saved in cache, mark them here to not change them - 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, - InfoTrackSimilars = 23, // cached -- do not change - InfoArtistID = 25, - InfoArtistName = 26, - InfoArtistBiography = 27, - InfoArtistImages = 28, //cached -- do not change - InfoArtistBlog = 29, - InfoArtistFamiliarity = 30, - InfoArtistHotttness = 31, - InfoArtistSongs = 32, //cached -- do not change - InfoArtistSimilars = 33, //cached -- do not change - InfoArtistNews = 34, - InfoArtistProfile = 35, - InfoArtistReviews = 36, - InfoArtistTerms = 37, - InfoArtistLinks = 38, - InfoArtistVideos = 39, - InfoArtistReleases = 40, - - InfoAlbumID = 42, - InfoAlbumCoverArt = 43, //cached -- do not change - InfoAlbumName = 44, - InfoAlbumArtist = 45, - InfoAlbumDate = 46, - InfoAlbumGenre = 47, - InfoAlbumComposer = 48, - InfoAlbumSongs = 49, - -/** \var Tomahawk::InfoSystem::InfoType Tomahawk::InfoSystem::InfoType::InfoChartCapabilities - * Documentation for InfoChartCapabilities - * - * Clients of this InfoType expect a QVariant - * - */ - InfoChartCapabilities = 50, - /** - * Documentation for InfoChartArtists - */ - InfoChart = 51, - - InfoNewReleaseCapabilities = 52, - InfoNewRelease = 53, - - InfoMiscTopHotttness = 60, - InfoMiscTopTerms = 61, - - InfoSubmitNowPlaying = 70, - InfoSubmitScrobble = 71, - - InfoNowPlaying = 80, - InfoNowPaused = 81, - InfoNowResumed = 82, - InfoNowStopped = 83, - InfoTrackUnresolved = 84, - - InfoLove = 90, - InfoUnLove = 91, - InfoShareTrack = 92, - - InfoNotifyUser = 100, - - InfoLastInfo = 101 //WARNING: *ALWAYS* keep this last! -}; - - -typedef QMap< InfoType, QVariant > InfoTypeMap; -typedef QMap< InfoType, uint > InfoTimeoutMap; -typedef QHash< QString, QString > InfoStringHash; -typedef QPair< QVariantMap, QVariant > PushInfoPair; - - -struct InfoRequestData { +struct DLLEXPORT InfoRequestData { quint64 requestId; quint64 internalId; //do not assign to this; it may get overwritten by the InfoSystem QString caller; @@ -154,27 +61,12 @@ struct InfoRequestData { 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(); - 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 ) - {} + InfoRequestData( const quint64 rId, const QString &callr, const Tomahawk::InfoSystem::InfoType typ, const QVariant &inputvar, const QVariantMap &custom ); + +private: + void init( const QString& callr, const InfoType typ, const QVariant& inputvar, const QVariantMap& custom); }; @@ -209,6 +101,10 @@ class DLLEXPORT InfoPlugin : public QObject Q_OBJECT public: + /** + * @brief Creates the plugin. Do *not* perform any network-based setup tasks here; defer that to init(), which will be called automatically. + * + **/ InfoPlugin(); virtual ~InfoPlugin(); @@ -223,6 +119,14 @@ signals: void updateCache( Tomahawk::InfoSystem::InfoStringHash criteria, qint64 maxAge, Tomahawk::InfoSystem::InfoType type, QVariant output ); protected slots: + + /** + * @brief Called after the plugin has been moved to the appropriate thread. Do network-based setup tasks here. + * + * @return void + **/ + virtual void init() = 0; + virtual void getInfo( Tomahawk::InfoSystem::InfoRequestData requestData ) = 0; virtual void pushInfo( Tomahawk::InfoSystem::InfoPushData pushData ) = 0; virtual void notInCacheSlot( Tomahawk::InfoSystem::InfoStringHash criteria, Tomahawk::InfoSystem::InfoRequestData requestData ) = 0; @@ -237,8 +141,6 @@ private: }; -typedef QWeakPointer< InfoPlugin > InfoPluginPtr; - class InfoSystemCacheThread : public QThread { Q_OBJECT @@ -336,7 +238,6 @@ inline uint qHash( Tomahawk::InfoSystem::InfoStringHash hash ) return returnval; } - Q_DECLARE_METATYPE( Tomahawk::InfoSystem::InfoRequestData ); Q_DECLARE_METATYPE( Tomahawk::InfoSystem::InfoPushData ); Q_DECLARE_METATYPE( Tomahawk::InfoSystem::InfoStringHash ); @@ -348,4 +249,6 @@ Q_DECLARE_METATYPE( QList< Tomahawk::InfoSystem::InfoStringHash > ); Q_DECLARE_METATYPE( Tomahawk::InfoSystem::InfoPluginPtr ); Q_DECLARE_METATYPE( Tomahawk::InfoSystem::InfoPlugin* ); +Q_DECLARE_INTERFACE( Tomahawk::InfoSystem::InfoPlugin, "tomahawk.InfoPluginy/1.0" ) + #endif // TOMAHAWK_INFOSYSTEM_H diff --git a/src/libtomahawk/infosystem/InfoSystemCache.cpp b/src/libtomahawk/infosystem/InfoSystemCache.cpp index 6fb4837e9..184783cb2 100644 --- a/src/libtomahawk/infosystem/InfoSystemCache.cpp +++ b/src/libtomahawk/infosystem/InfoSystemCache.cpp @@ -19,9 +19,6 @@ #include -#include -#include -#include #ifndef ENABLE_HEADLESS #include @@ -30,7 +27,11 @@ #include "InfoSystemCache.h" #include "TomahawkSettings.h" #include "utils/Logger.h" +#include "Source.h" +#include +#include +#include namespace Tomahawk { diff --git a/src/libtomahawk/infosystem/InfoSystemWorker.cpp b/src/libtomahawk/infosystem/InfoSystemWorker.cpp index 803c607df..f52ba8729 100644 --- a/src/libtomahawk/infosystem/InfoSystemWorker.cpp +++ b/src/libtomahawk/infosystem/InfoSystemWorker.cpp @@ -18,33 +18,23 @@ * along with Tomahawk. If not, see . */ -#include -#include -#include +#include "InfoSystemWorker.h" #include "config.h" -#include "InfoSystemWorker.h" +#include "HeadlessCheck.h" #include "InfoSystemCache.h" -#include "infoplugins/generic/EchonestPlugin.h" -#include "infoplugins/generic/MusixMatchPlugin.h" -#include "infoplugins/generic/ChartsPlugin.h" -#include "infoplugins/generic/NewReleasesPlugin.h" -#include "infoplugins/generic/spotifyPlugin.h" -#include "infoplugins/generic/musicbrainzPlugin.h" -#include "infoplugins/generic/hypemPlugin.h" #include "GlobalActionManager.h" #include "utils/TomahawkUtils.h" #include "utils/Logger.h" +#include "Source.h" -#ifdef Q_WS_MAC -#include "infoplugins/mac/AdiumPlugin.h" -#endif -#ifdef Q_WS_X11 -#include "infoplugins/unix/FdoNotifyPlugin.h" -#include "infoplugins/unix/MprisPlugin.h" -#endif -#include "infoplugins/generic/RoviPlugin.h" +#include +#include +#include +#include +#include +#include namespace Tomahawk { @@ -82,26 +72,8 @@ InfoSystemWorker::init( Tomahawk::InfoSystem::InfoSystemCache* cache ) tDebug() << Q_FUNC_INFO; m_shortLinksWaiting = 0; m_cache = cache; -#ifndef ENABLE_HEADLESS - addInfoPlugin( InfoPluginPtr( new EchoNestPlugin() ) ); - addInfoPlugin( InfoPluginPtr( new MusixMatchPlugin() ) ); - addInfoPlugin( InfoPluginPtr( new MusicBrainzPlugin() ) ); - addInfoPlugin( InfoPluginPtr( new ChartsPlugin() ) ); - addInfoPlugin( InfoPluginPtr( new NewReleasesPlugin() ) ); - addInfoPlugin( InfoPluginPtr( new RoviPlugin() ) ); - addInfoPlugin( InfoPluginPtr( new SpotifyPlugin() ) ); - addInfoPlugin( InfoPluginPtr( new hypemPlugin() ) ); -#endif -#ifdef Q_WS_MAC - addInfoPlugin( InfoPluginPtr( new AdiumPlugin() ) ); -#endif -#ifndef ENABLE_HEADLESS -#ifdef Q_WS_X11 - addInfoPlugin( InfoPluginPtr( new FdoNotifyPlugin() ) ); - addInfoPlugin( InfoPluginPtr( new MprisPlugin() ) ); -#endif -#endif + loadInfoPlugins( findInfoPlugins() ); } @@ -123,7 +95,8 @@ InfoSystemWorker::addInfoPlugin( Tomahawk::InfoSystem::InfoPluginPtr plugin ) tDebug() << Q_FUNC_INFO << "passed-in plugin is null"; return; } - + + plugin.data()->moveToThread( this->thread() ); m_plugins.append( plugin ); registerInfoTypes( plugin, plugin.data()->supportedGetTypes(), plugin.data()->supportedPushTypes() ); @@ -149,6 +122,8 @@ InfoSystemWorker::addInfoPlugin( Tomahawk::InfoSystem::InfoPluginPtr plugin ) SLOT( updateCacheSlot( Tomahawk::InfoSystem::InfoStringHash, qint64, Tomahawk::InfoSystem::InfoType, QVariant ) ), Qt::QueuedConnection ); + + QMetaObject::invokeMethod( plugin.data(), "init", Qt::QueuedConnection ); } @@ -162,12 +137,12 @@ InfoSystemWorker::removeInfoPlugin( Tomahawk::InfoSystem::InfoPluginPtr plugin ) tDebug() << Q_FUNC_INFO << "passed-in plugin is null"; return; } - + foreach ( InfoPluginPtr ptr, m_plugins ) { if ( ptr == plugin ) break; - + tDebug() << Q_FUNC_INFO << "This plugin does not exist in the infosystem."; return; } @@ -178,6 +153,83 @@ InfoSystemWorker::removeInfoPlugin( Tomahawk::InfoSystem::InfoPluginPtr plugin ) } +QStringList +InfoSystemWorker::findInfoPlugins() +{ + QStringList paths; + QList< QDir > pluginDirs; + + QDir appDir( qApp->applicationDirPath() ); +#ifdef Q_WS_MAC + if ( appDir.dirName() == "MacOS" ) + { + // Development convenience-hack + appDir.cdUp(); + appDir.cdUp(); + appDir.cdUp(); + } +#endif + + QDir libDir( CMAKE_INSTALL_PREFIX "/lib" ); + + QDir lib64Dir( appDir ); + lib64Dir.cdUp(); + lib64Dir.cd( "lib64" ); + + pluginDirs << appDir << libDir << lib64Dir << QDir( qApp->applicationDirPath() ); + foreach ( const QDir& pluginDir, pluginDirs ) + { + tDebug() << Q_FUNC_INFO << "Checking directory for plugins:" << pluginDir; + foreach ( QString fileName, pluginDir.entryList( QStringList() << "*tomahawk_infoplugin_*.so" << "*tomahawk_infoplugin_*.dylib" << "*tomahawk_infoplugin_*.dll", QDir::Files ) ) + { + if ( fileName.startsWith( "libtomahawk_infoplugin" ) ) + { + const QString path = pluginDir.absoluteFilePath( fileName ); + if ( !paths.contains( path ) ) + paths << path; + } + } + } + + return paths; +} + + +void +InfoSystemWorker::loadInfoPlugins( const QStringList& pluginPaths ) +{ + tDebug() << Q_FUNC_INFO << "Attempting to load the following plugin paths:" << pluginPaths; + + if ( pluginPaths.isEmpty() ) + return; + + foreach ( const QString fileName, pluginPaths ) + { + if ( !QLibrary::isLibrary( fileName ) ) + continue; + + tDebug() << Q_FUNC_INFO << "Trying to load plugin:" << fileName; + + QPluginLoader loader( fileName ); + QObject* plugin = loader.instance(); + if ( !plugin ) + { + tDebug() << Q_FUNC_INFO << "Error loading plugin:" << loader.errorString(); + continue; + } + + InfoPlugin* infoPlugin = qobject_cast< InfoPlugin* >( plugin ); + if ( infoPlugin ) + { + tDebug() << Q_FUNC_INFO << "Loaded info plugin:" << loader.fileName(); + addInfoPlugin( InfoPluginPtr( infoPlugin ) ); + } + else + tDebug() << Q_FUNC_INFO << "Loaded invalid plugin:" << loader.fileName(); + } +} + + void InfoSystemWorker::registerInfoTypes( const InfoPluginPtr &plugin, const QSet< InfoType >& getTypes, const QSet< InfoType >& pushTypes ) { @@ -288,7 +340,7 @@ InfoSystemWorker::pushInfo( Tomahawk::InfoSystem::InfoPushData pushData ) } tDebug() << Q_FUNC_INFO << "number of matching plugins: " << m_infoPushMap[ pushData.type ].size(); - + Q_FOREACH( InfoPluginPtr ptr, m_infoPushMap[ pushData.type ] ) { if( ptr ) diff --git a/src/libtomahawk/infosystem/InfoSystemWorker.h b/src/libtomahawk/infosystem/InfoSystemWorker.h index f580ffd78..930f0d2e4 100644 --- a/src/libtomahawk/infosystem/InfoSystemWorker.h +++ b/src/libtomahawk/infosystem/InfoSystemWorker.h @@ -64,6 +64,8 @@ public slots: void addInfoPlugin( Tomahawk::InfoSystem::InfoPluginPtr plugin ); void removeInfoPlugin( Tomahawk::InfoSystem::InfoPluginPtr plugin ); + QStringList findInfoPlugins(); + void loadInfoPlugins( const QStringList &pluginPaths ); void getShortUrl( Tomahawk::InfoSystem::InfoPushData data ); void shortLinkReady( QUrl longUrl, QUrl shortUrl, QVariant callbackObj ); diff --git a/src/libtomahawk/jobview/AclJobItem.cpp b/src/libtomahawk/jobview/AclJobItem.cpp index 45793cbf1..3d086c99e 100644 --- a/src/libtomahawk/jobview/AclJobItem.cpp +++ b/src/libtomahawk/jobview/AclJobItem.cpp @@ -21,6 +21,7 @@ #include "JobStatusModel.h" #include "utils/TomahawkUtils.h" +#include "utils/Logger.h" #include #include @@ -61,13 +62,14 @@ AclJobDelegate::paint( QPainter* painter, const QStyleOptionViewItem& option, co else mainText = QString( tr( "Allow %1 to\nconnect and stream from you?" ) ).arg( item->username() ); tDebug( LOGVERBOSE ) << Q_FUNC_INFO << "Displaying text:" << mainText; - + const QString text = QString( tr( "Allow %1 to\nconnect and stream from you?" ) ).arg( item->username() ); const int w = fm.width( text ); + Q_UNUSED( w ); // looks obsolete const QRect rRect( opt.rect.left() + PADDING, ROW_HEIGHT + PADDING, opt.rect.width() - 2*PADDING, opt.rect.height() - 2*PADDING ); painter->drawText( rRect, Qt::AlignCenter, text ); - + /* QStyleOptionViewItemV4 opt = option; initStyleOption( &opt, index ); diff --git a/src/libtomahawk/jobview/JobStatusModel.cpp b/src/libtomahawk/jobview/JobStatusModel.cpp index 8be72d328..e3ec86e9a 100644 --- a/src/libtomahawk/jobview/JobStatusModel.cpp +++ b/src/libtomahawk/jobview/JobStatusModel.cpp @@ -24,10 +24,10 @@ #include + JobStatusModel::JobStatusModel( QObject* parent ) : QAbstractListModel ( parent ) { - } @@ -52,10 +52,9 @@ JobStatusModel::addJob( JobStatusItem* item ) currentJobCount++; m_jobTypeCount[ item->type() ] = currentJobCount; } - - - connect( item, SIGNAL( statusChanged() ), this, SLOT( itemUpdated() ) ); - connect( item, SIGNAL( finished() ), this, SLOT( itemFinished() ) ); + + connect( item, SIGNAL( statusChanged() ), SLOT( itemUpdated() ) ); + connect( item, SIGNAL( finished() ), SLOT( itemFinished() ) ); if ( item->collapseItem() ) { @@ -77,6 +76,7 @@ JobStatusModel::addJob( JobStatusItem* item ) beginInsertRows( QModelIndex(), currentEndRow, currentEndRow ); m_items.append( item ); endInsertRows(); + if ( item->hasCustomDelegate() ) { tDebug( LOGVERBOSE ) << Q_FUNC_INFO << "job has custom delegate"; @@ -228,5 +228,4 @@ JobStatusModel::itemUpdated() const QModelIndex idx = index( m_items.indexOf( item ), 0, QModelIndex() ); emit dataChanged( idx, idx ); - return; } diff --git a/src/libtomahawk/jobview/JobStatusView.cpp b/src/libtomahawk/jobview/JobStatusView.cpp index 204ae9182..59f823bf1 100644 --- a/src/libtomahawk/jobview/JobStatusView.cpp +++ b/src/libtomahawk/jobview/JobStatusView.cpp @@ -27,6 +27,7 @@ #include "TransferStatusItem.h" #include "LatchedStatusItem.h" #include "utils/Logger.h" +#include "Source.h" #include #include diff --git a/src/libtomahawk/jobview/PipelineStatusItem.cpp b/src/libtomahawk/jobview/PipelineStatusItem.cpp index 39c8d04b3..03296b48b 100644 --- a/src/libtomahawk/jobview/PipelineStatusItem.cpp +++ b/src/libtomahawk/jobview/PipelineStatusItem.cpp @@ -25,13 +25,13 @@ #include "TomahawkApp.h" #include "JobStatusModel.h" #include "JobStatusView.h" +#include "Source.h" +QPixmap* PipelineStatusItem::s_pixmap = 0; PipelineStatusItem::PipelineStatusItem() : JobStatusItem() { - m_icon.load( RESPATH"images/search-icon.png" ); - connect( Tomahawk::Pipeline::instance(), SIGNAL( resolving( Tomahawk::query_ptr ) ), this, SLOT( resolving( Tomahawk::query_ptr ) ) ); connect( Tomahawk::Pipeline::instance(), SIGNAL( idle() ), this, SLOT( idle() ) ); } @@ -64,6 +64,18 @@ PipelineStatusItem::idle() } +QPixmap +PipelineStatusItem::icon() const +{ + if ( !s_pixmap ) + { + s_pixmap = new QPixmap( RESPATH"images/search-icon.png" ); + } + + return *s_pixmap; +} + + void PipelineStatusItem::resolving( const Tomahawk::query_ptr& query ) { @@ -72,6 +84,10 @@ PipelineStatusItem::resolving( const Tomahawk::query_ptr& query ) else m_latestQuery = QString( "%1 - %2" ).arg( query->artist() ).arg( query->track() ); + if ( m_latestQuery.isEmpty() ) + qDebug() << "EMPTY STRING IN STATUS ITEM:" << query->fullTextQuery() << query->track() << query->artist() << query->album(); + Q_ASSERT( !m_latestQuery.isEmpty() ); + emit statusChanged(); } diff --git a/src/libtomahawk/jobview/PipelineStatusItem.h b/src/libtomahawk/jobview/PipelineStatusItem.h index a1d7db719..da95e96fd 100644 --- a/src/libtomahawk/jobview/PipelineStatusItem.h +++ b/src/libtomahawk/jobview/PipelineStatusItem.h @@ -33,7 +33,7 @@ public: virtual QString rightColumnText() const; virtual QString mainText() const; - virtual QPixmap icon() const { return m_icon; } + virtual QPixmap icon() const; virtual QString type() const { return "pipeline"; } @@ -44,8 +44,9 @@ private slots: void idle(); private: - QPixmap m_icon; QString m_latestQuery; + + static QPixmap* s_pixmap; }; class PipelineStatusManager : public QObject diff --git a/src/libtomahawk/mac/FileHelpers.h b/src/libtomahawk/mac/FileHelpers.h new file mode 100644 index 000000000..ad5fb49d0 --- /dev/null +++ b/src/libtomahawk/mac/FileHelpers.h @@ -0,0 +1,41 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2012, Leo Franchi . + */ + +#ifndef MAC_FILE_HELPERS_H +#define MAC_FILE_HELPERS_H + +#import + +// Implement this delegate protocol to get notified about the result of your copy attempt +@interface NSObject (SUInstallerDelegateInformalProtocol) +- (void)moveFinished; +- (void)moveFailedWithError:(NSError *)error; +@end + +@interface FileHelpers : NSObject +{} +// Move a file from point A to point B, asking for authentication if necessary +// Will be asynchronous: Implement the delegate protocol know about the completion ++ (void) moveFile:(NSString *)source to:(NSString*)dest withDelegate:delegate; + +// Internal ++ (void)notifyDelegate:(NSDictionary *)info; + +@end + +#endif diff --git a/src/libtomahawk/mac/FileHelpers.mm b/src/libtomahawk/mac/FileHelpers.mm new file mode 100644 index 000000000..3e3c01d27 --- /dev/null +++ b/src/libtomahawk/mac/FileHelpers.mm @@ -0,0 +1,229 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2012, Leo Franchi . + */ + +#import "FileHelpers.h" + +#import +#import +#import +#import +#import +#import +#import + +#include + +static NSString * const TKCopySourceKey = @"TKInstallerSourcePath"; +static NSString * const TKCopyDestinationKey = @"TKInstallerDestinationPath"; +static NSString * const TKInstallerDelegateKey = @"TKInstallerDelegate"; +static NSString * const TKInstallerResultKey = @"TKInstallerResult"; +static NSString * const TKInstallerErrorKey = @"TKInstallerError"; + +class CAutoreleasePool +{ + NSAutoreleasePool *pool; + +public: + CAutoreleasePool() + { + pool = [[NSAutoreleasePool alloc] init]; + } + + ~CAutoreleasePool() + { + [pool drain]; + } +}; + +// Authorization code based on generous contribution from Allan Odgaard. Thanks, Allan! +static BOOL AuthorizationExecuteWithPrivilegesAndWait(AuthorizationRef authorization, const char* executablePath, AuthorizationFlags options, const char* const* arguments) +{ + // *** MUST BE SAFE TO CALL ON NON-MAIN THREAD! + + sig_t oldSigChildHandler = signal(SIGCHLD, SIG_DFL); + BOOL returnValue = YES; + + if (AuthorizationExecuteWithPrivileges(authorization, executablePath, options, (char* const*)arguments, NULL) == errAuthorizationSuccess) + { + int status; + pid_t pid = wait(&status); + if (pid == -1 || !WIFEXITED(status) || WEXITSTATUS(status) != 0) + returnValue = NO; + } + else + returnValue = NO; + + signal(SIGCHLD, oldSigChildHandler); + return returnValue; +} + +@implementation FileHelpers + ++ (void) moveFile:(NSString *)source to:(NSString*)dest withDelegate:delegate +{ + NSLog(@"FileHelpers moving file from %@ to %@", source, dest); + + NSDictionary *info = [NSDictionary dictionaryWithObjectsAndKeys:source, TKCopySourceKey, dest, TKCopyDestinationKey, delegate, TKInstallerDelegateKey, nil]; + [NSThread detachNewThreadSelector:@selector(performMoveWithInfo:) toTarget:self withObject:info]; +} + + ++ (void)performMoveWithInfo:(NSDictionary *)info +{ + // *** GETS CALLED ON NON-MAIN THREAD! + + NSString* fromPath = [info objectForKey: TKCopySourceKey]; + NSString* toPath = [info objectForKey: TKCopyDestinationKey]; + + AuthorizationRef auth = NULL; + OSStatus authStat = errAuthorizationDenied; + + NSLog(@"FileHelpers moving file from %@ to %@", fromPath, toPath); + BOOL haveOld = [[NSFileManager defaultManager] fileExistsAtPath: toPath]; + + if (haveOld == YES) { // delete the old file if it's there + if (0 != access([[toPath stringByDeletingLastPathComponent] fileSystemRepresentation], W_OK) + || 0 != access([[[toPath stringByDeletingLastPathComponent] stringByDeletingLastPathComponent] fileSystemRepresentation], W_OK)) + { + const char* rmParams[] = { [toPath fileSystemRepresentation], NULL }; + // NSLog( @"WOULD DELETE: %@", [toPath fileSystemRepresentation] ); + + while( authStat == errAuthorizationDenied ) + { + authStat = AuthorizationCreate(NULL, + kAuthorizationEmptyEnvironment, + kAuthorizationFlagDefaults, + &auth); + } + if (authStat == errAuthorizationSuccess) + { + BOOL res = AuthorizationExecuteWithPrivilegesAndWait( auth, "/bin/rm", kAuthorizationFlagDefaults, rmParams ); + if (!res) + NSLog(@"Could not delete: %@", toPath); + } else { + qDebug() << "Failed to authenticate to delete file under target to move, aborting"; + return; + } + } else { + // We can delete it ourselves w/out authenticating + NSFileManager *manager = [[[NSFileManager alloc] init] autorelease]; + NSError* error; + BOOL success = [manager removeItemAtPath:toPath error:&error]; + + if (!success) { + NSLog(@"Failed to delete file (w/out perms) underneath copy!: %@", [[error userInfo] objectForKey: NSLocalizedDescriptionKey]); + return; + } + } + } + + FSRef dstRef, dstDirRef; + OSStatus err = FSPathMakeRefWithOptions((UInt8 *)[toPath fileSystemRepresentation], kFSPathMakeRefDoNotFollowLeafSymlink, &dstRef, NULL); + + if (err != noErr && err != fnfErr) { // If the file is not found that's fine, we're moving to there after all + qDebug() << "GOT AN ERROR DOING FSPathMakeRefWithOptions!!!!! aborting move"; + return; + } + + if (0 != access([[toPath stringByDeletingLastPathComponent] fileSystemRepresentation], W_OK) + || 0 != access([[[toPath stringByDeletingLastPathComponent] stringByDeletingLastPathComponent] fileSystemRepresentation], W_OK)) + { + // Not writeable by user, so authenticate + if (!auth) { + while( authStat == errAuthorizationDenied ) + { + authStat = AuthorizationCreate(NULL, + kAuthorizationEmptyEnvironment, + kAuthorizationFlagDefaults, + &auth); + } + } + + if (authStat == errAuthorizationSuccess) + { + // Fix perms before moving so we have them correct when they arrive + struct stat dstSB; + stat([[toPath stringByDeletingLastPathComponent] fileSystemRepresentation], &dstSB); + char uidgid[42]; + snprintf(uidgid, sizeof(uidgid), "%d:%d", + dstSB.st_uid, dstSB.st_gid); + + const char* coParams[] = { "-R", uidgid, [fromPath fileSystemRepresentation], NULL }; + BOOL res = AuthorizationExecuteWithPrivilegesAndWait( auth, "/usr/sbin/chown", kAuthorizationFlagDefaults, coParams ); + if( !res ) + qDebug() << "Failed to set permissions before moving"; + + // Do the move + const char* mvParams[] = { "-f", [fromPath fileSystemRepresentation], [toPath fileSystemRepresentation], NULL }; + res = AuthorizationExecuteWithPrivilegesAndWait( auth, "/bin/mv", kAuthorizationFlagDefaults, mvParams ); + if( !res ) + NSLog(@"Failed to move source file from %@ to %@ with error %@", fromPath, toPath, res ); + + AuthorizationFree(auth, 0); + auth = NULL; + } + + return; + } + + if (auth) { + AuthorizationFree(auth, 0); + auth = NULL; + } + + err = FSPathMakeRef((UInt8 *)[[toPath stringByDeletingLastPathComponent] fileSystemRepresentation], &dstDirRef, NULL); + + if (err != noErr) { + qDebug() << "GOT AN ERROR DOING FSPathMakeRef to get dir to copy into!!!!! aborting move"; + return; + } + + NSFileManager *manager = [[[NSFileManager alloc] init] autorelease]; + NSError* error; + BOOL success = [manager moveItemAtPath:fromPath toPath:toPath error:&error]; + if (!success) { + NSLog( @"Failed to do non-authenticated move! Help! %@", [[error userInfo] objectForKey: NSLocalizedDescriptionKey] ); + } + + NSDictionary* dict = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:success], TKInstallerResultKey, [info objectForKey:TKInstallerDelegateKey], TKInstallerDelegateKey, error, TKInstallerErrorKey, nil]; + [self notifyDelegate:dict]; + + [fromPath release]; + [toPath release]; +} + + ++ (void)notifyDelegate:(NSDictionary *)info +{ + // *** GETS CALLED ON NON-MAIN THREAD! + BOOL result = [[info objectForKey:TKInstallerResultKey] boolValue]; + if (result) + { + if ([[info objectForKey:TKInstallerDelegateKey] respondsToSelector:@selector(moveFinished)]) + [[info objectForKey:TKInstallerDelegateKey] performSelectorOnMainThread: @selector(moveFinished) withObject:nil waitUntilDone: NO]; + } + else + { + if ([[info objectForKey:TKInstallerDelegateKey] respondsToSelector:@selector(moveFailedWithError:)]) + { + [[info objectForKey:TKInstallerDelegateKey] performSelectorOnMainThread: @selector(moveFailedWithError) withObject:[NSDictionary dictionaryWithObjectsAndKeys:[info objectForKey:TKInstallerErrorKey], TKInstallerErrorKey, nil] waitUntilDone: NO]; + } + } +} + +@end diff --git a/src/libtomahawk/network/Connection.cpp b/src/libtomahawk/network/Connection.cpp index 78ec3fef2..286965d88 100644 --- a/src/libtomahawk/network/Connection.cpp +++ b/src/libtomahawk/network/Connection.cpp @@ -19,11 +19,12 @@ #include "Connection.h" -#include -#include - #include "network/Servent.h" #include "utils/Logger.h" +#include "Source.h" + +#include +#include #define PROTOVER "4" // must match remote peer, or we can't talk. diff --git a/src/libtomahawk/network/PortFwdThread.cpp b/src/libtomahawk/network/PortFwdThread.cpp index fe02655b6..0da9a7b8e 100644 --- a/src/libtomahawk/network/PortFwdThread.cpp +++ b/src/libtomahawk/network/PortFwdThread.cpp @@ -19,14 +19,15 @@ #include "PortFwdThread.h" #include "HeadlessCheck.h" +#include "portfwd/portfwd.h" +#include "utils/Logger.h" +#include "Source.h" #include #include #include #include -#include "portfwd/portfwd.h" -#include "utils/Logger.h" PortFwdThread::PortFwdThread( unsigned int port ) diff --git a/src/libtomahawk/network/Servent.cpp b/src/libtomahawk/network/Servent.cpp index 8643263de..ec409f041 100644 --- a/src/libtomahawk/network/Servent.cpp +++ b/src/libtomahawk/network/Servent.cpp @@ -452,6 +452,14 @@ Servent::socketConnected() tDebug( LOGVERBOSE ) << Q_FUNC_INFO << thread() << "socket: " << sock << ", hostaddr: " << sock->peerAddress() << ", hostname: " << sock->peerName(); + if ( sock->_conn.isNull() ) + { + sock->close(); + sock->deleteLater(); + tDebug( LOGVERBOSE ) << Q_FUNC_INFO << "Socket's connection was null, could have timed out or been given an invalid address"; + return; + } + Connection* conn = sock->_conn.data(); handoverSocket( conn, sock ); } diff --git a/src/libtomahawk/playlist/AlbumItem.cpp b/src/libtomahawk/playlist/AlbumItem.cpp deleted file mode 100644 index d97b222bf..000000000 --- a/src/libtomahawk/playlist/AlbumItem.cpp +++ /dev/null @@ -1,131 +0,0 @@ -/* === This file is part of Tomahawk Player - === - * - * Copyright 2010-2011, Christian Muehlhaeuser - * - * Tomahawk is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Tomahawk is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Tomahawk. If not, see . - */ - -#include "AlbumItem.h" - -#include "utils/TomahawkUtils.h" -#include "utils/Logger.h" - -using namespace Tomahawk; - - -AlbumItem::~AlbumItem() -{ - // Don't use qDeleteAll here! The children will remove themselves - // from the list when they get deleted and the qDeleteAll iterator - // will fail badly! - for ( int i = children.count() - 1; i >= 0; i-- ) - delete children.at( i ); - - if ( parent && index.isValid() ) - { - parent->children.removeAt( index.row() ); - } -} - - -AlbumItem::AlbumItem( AlbumItem* parent, QAbstractItemModel* model ) -{ - this->parent = parent; - this->model = model; - childCount = 0; - toberemoved = false; - - if ( parent ) - { - parent->children.append( this ); - } -} - - -AlbumItem::AlbumItem( const Tomahawk::album_ptr& album, AlbumItem* parent, int row ) - : QObject( parent ) - , m_album( album ) -{ - this->parent = parent; - if ( parent ) - { - if ( row < 0 ) - { - parent->children.append( this ); - row = parent->children.count() - 1; - } - else - { - parent->children.insert( row, this ); - } - - this->model = parent->model; - } - - toberemoved = false; - - connect( album.data(), SIGNAL( updated() ), SIGNAL( dataChanged() ) ); -} - - -AlbumItem::AlbumItem( const Tomahawk::artist_ptr& artist, AlbumItem* parent, int row ) - : QObject( parent ) - , m_artist( artist ) -{ - this->parent = parent; - if ( parent ) - { - if ( row < 0 ) - { - parent->children.append( this ); - row = parent->children.count() - 1; - } - else - { - parent->children.insert( row, this ); - } - - this->model = parent->model; - } - - toberemoved = false; - - connect( artist.data(), SIGNAL( updated() ), SIGNAL( dataChanged() ) ); -} - - -AlbumItem::AlbumItem( const Tomahawk::query_ptr& query, AlbumItem* parent, int row ) - : QObject( parent ) - , m_query( query ) -{ - this->parent = parent; - if ( parent ) - { - if ( row < 0 ) - { - parent->children.append( this ); - row = parent->children.count() - 1; - } - else - { - parent->children.insert( row, this ); - } - - this->model = parent->model; - } - - toberemoved = false; - - connect( query.data(), SIGNAL( updated() ), SIGNAL( dataChanged() ) ); -} diff --git a/src/libtomahawk/playlist/AlbumItem.h b/src/libtomahawk/playlist/AlbumItem.h deleted file mode 100644 index 7ced56118..000000000 --- a/src/libtomahawk/playlist/AlbumItem.h +++ /dev/null @@ -1,66 +0,0 @@ -/* === This file is part of Tomahawk Player - === - * - * Copyright 2010-2011, Christian Muehlhaeuser - * - * Tomahawk is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Tomahawk is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Tomahawk. If not, see . - */ - -#ifndef ALBUMITEM_H -#define ALBUMITEM_H - -#include -#include -#include -#include - -#include "Artist.h" -#include "Album.h" -#include "Query.h" - -#include "DllMacro.h" - -class DLLEXPORT AlbumItem : public QObject -{ -Q_OBJECT - -public: - ~AlbumItem(); - - explicit AlbumItem( AlbumItem* parent = 0, QAbstractItemModel* model = 0 ); - explicit AlbumItem( const Tomahawk::artist_ptr& artist, AlbumItem* parent = 0, int row = -1 ); - explicit AlbumItem( const Tomahawk::album_ptr& album, AlbumItem* parent = 0, int row = -1 ); - explicit AlbumItem( const Tomahawk::query_ptr& query, AlbumItem* parent = 0, int row = -1 ); - - const Tomahawk::artist_ptr& artist() const { return m_artist; } - const Tomahawk::album_ptr& album() const { return m_album; } - const Tomahawk::query_ptr& query() const { return m_query; } - - AlbumItem* parent; - QList children; - QHash hash; - int childCount; - QPersistentModelIndex index; - QAbstractItemModel* model; - bool toberemoved; - -signals: - void dataChanged(); - -private: - Tomahawk::artist_ptr m_artist; - Tomahawk::album_ptr m_album; - Tomahawk::query_ptr m_query; -}; - -#endif // ALBUMITEM_H diff --git a/src/libtomahawk/playlist/AlbumItemDelegate.cpp b/src/libtomahawk/playlist/AlbumItemDelegate.cpp deleted file mode 100644 index 533ed8fb2..000000000 --- a/src/libtomahawk/playlist/AlbumItemDelegate.cpp +++ /dev/null @@ -1,309 +0,0 @@ -/* === This file is part of Tomahawk Player - === - * - * Copyright 2010-2011, Christian Muehlhaeuser - * Copyright 2011-2012, Leo Franchi - * - * Tomahawk is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Tomahawk is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Tomahawk. If not, see . - */ - -#include "AlbumItemDelegate.h" - -#include -#include -#include - -#include "Artist.h" -#include "Query.h" -#include "Result.h" - -#include "utils/TomahawkUtils.h" -#include "utils/Logger.h" -#include "utils/PixmapDelegateFader.h" -#include - -#include "playlist/AlbumItem.h" -#include "playlist/AlbumProxyModel.h" -#include "AlbumView.h" -#include -#include - - -AlbumItemDelegate::AlbumItemDelegate( QAbstractItemView* parent, AlbumProxyModel* proxy ) - : QStyledItemDelegate( (QObject*)parent ) - , m_view( parent ) - , m_model( proxy ) -{ - if ( m_view && m_view->metaObject()->indexOfSignal( "modelChanged()" ) > -1 ) - connect( m_view, SIGNAL( modelChanged() ), this, SLOT( modelChanged() ) ); -} - - -QSize -AlbumItemDelegate::sizeHint( const QStyleOptionViewItem& option, const QModelIndex& index ) const -{ - QSize size = QStyledItemDelegate::sizeHint( option, index ); - return size; -} - - -void -AlbumItemDelegate::paint( QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index ) const -{ - AlbumItem* item = m_model->sourceModel()->itemFromIndex( m_model->mapToSource( index ) ); - if ( !item ) - return; - - QStyleOptionViewItemV4 opt = option; - initStyleOption( &opt, QModelIndex() ); - qApp->style()->drawControl( QStyle::CE_ItemViewItem, &opt, painter ); - - painter->save(); - painter->setRenderHint( QPainter::Antialiasing ); - - 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 ) ); - } - - QRect r = option.rect.adjusted( 6, 5, -6, -41 ); - - QString top, bottom; - if ( !item->album().isNull() ) - { - top = item->album()->name(); - - if ( !item->album()->artist().isNull() ) - bottom = item->album()->artist()->name(); - } - else if ( !item->artist().isNull() ) - { - top = item->artist()->name(); - } - else - { - top = item->query()->track(); - bottom = item->query()->artist(); - } - - if ( !m_covers.contains( index ) ) - { - if ( !item->album().isNull() ) - { - m_covers.insert( index, QSharedPointer< Tomahawk::PixmapDelegateFader >( new Tomahawk::PixmapDelegateFader( item->album(), r.size(), TomahawkUtils::CoverInCase ) ) ); - } - else if ( !item->artist().isNull() ) - { - m_covers.insert( index, QSharedPointer< Tomahawk::PixmapDelegateFader >( new Tomahawk::PixmapDelegateFader( item->artist(), r.size(), TomahawkUtils::CoverInCase ) ) ); - } - else - { - m_covers.insert( index, QSharedPointer< Tomahawk::PixmapDelegateFader >( new Tomahawk::PixmapDelegateFader( item->query(), r.size(), TomahawkUtils::CoverInCase ) ) ); - } - - _detail::Closure* closure = NewClosure( m_covers[ index ], SIGNAL( repaintRequest() ), const_cast(this), SLOT( doUpdateIndex( QPersistentModelIndex ) ), QPersistentModelIndex( index ) ); - closure->setAutoDelete( false ); - } - - const QPixmap cover = m_covers[ index ]->currentPixmap(); - - if ( option.state & QStyle::State_Selected ) - { -#if defined(Q_WS_MAC) || defined(Q_WS_WIN) - painter->save(); - painter->setRenderHint( QPainter::Antialiasing ); - - QPainterPath border; - border.addRoundedRect( r.adjusted( -2, -2, 2, 2 ), 3, 3 ); - QPen borderPen( QColor( 86, 170, 243 ) ); - borderPen.setWidth( 5 ); - painter->setPen( borderPen ); - painter->drawPath( border ); - - painter->restore(); -#else - opt.palette.setColor( QPalette::Text, opt.palette.color( QPalette::HighlightedText ) ); -#endif - } - - painter->drawPixmap( r, cover ); - - painter->setPen( opt.palette.color( QPalette::Text ) ); - QTextOption to; - to.setWrapMode( QTextOption::NoWrap ); - - QString text; - QFont font = opt.font; - font.setPixelSize( 11 ); - QFont boldFont = font; - boldFont.setBold( true ); - - QRect textRect = option.rect.adjusted( 0, option.rect.height() - 32, 0, -2 ); - - painter->setFont( boldFont ); - bool oneLiner = false; - if ( bottom.isEmpty() ) - oneLiner = true; - else - oneLiner = ( textRect.height() / 2 < painter->fontMetrics().boundingRect( top ).height() || - textRect.height() / 2 < painter->fontMetrics().boundingRect( bottom ).height() ); - - if ( oneLiner ) - { - to.setAlignment( Qt::AlignHCenter | Qt::AlignVCenter ); - text = painter->fontMetrics().elidedText( top, Qt::ElideRight, textRect.width() - 3 ); - painter->drawText( textRect, text, to ); - } - else - { - to.setAlignment( Qt::AlignHCenter | Qt::AlignTop ); - text = painter->fontMetrics().elidedText( top, Qt::ElideRight, textRect.width() - 3 ); - painter->drawText( textRect, text, to ); - - // 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.1 ); - painter->setPen( opt.palette.color( QPalette::HighlightedText ) ); - } - else - { - if ( !( option.state & QStyle::State_Selected ) ) -#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( bottom, Qt::ElideRight, textRect.width() - 10 ); - painter->drawText( textRect.adjusted( 5, -1, -5, -1 ), 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( model ); - 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 ); - } -} - - -void -AlbumItemDelegate::modelChanged() -{ - m_artistNameRects.clear(); - m_hoveringOver = QPersistentModelIndex(); - - if ( AlbumView* view = qobject_cast< AlbumView* >( m_view ) ) - m_model = view->proxyModel(); -} - - -void -AlbumItemDelegate::doUpdateIndex( const QPersistentModelIndex& idx ) -{ - if ( !idx.isValid() ) - return; - emit updateIndex( idx ); -} - diff --git a/src/libtomahawk/playlist/AlbumModel.cpp b/src/libtomahawk/playlist/AlbumModel.cpp index b04a8e203..d7d82f98a 100644 --- a/src/libtomahawk/playlist/AlbumModel.cpp +++ b/src/libtomahawk/playlist/AlbumModel.cpp @@ -24,7 +24,7 @@ #include #include "Artist.h" -#include "AlbumItem.h" +#include "PlayableItem.h" #include "Source.h" #include "SourceList.h" #include "database/Database.h" @@ -35,8 +35,7 @@ using namespace Tomahawk; AlbumModel::AlbumModel( QObject* parent ) - : QAbstractItemModel( parent ) - , m_rootItem( new AlbumItem( 0, this ) ) + : PlayableModel( parent ) , m_overwriteOnAdd( false ) { } @@ -44,200 +43,6 @@ AlbumModel::AlbumModel( QObject* parent ) AlbumModel::~AlbumModel() { - delete m_rootItem; -} - - -QModelIndex -AlbumModel::index( int row, int column, const QModelIndex& parent ) const -{ - if ( !m_rootItem || row < 0 || column < 0 ) - return QModelIndex(); - - AlbumItem* parentItem = itemFromIndex( parent ); - AlbumItem* childItem = parentItem->children.value( row ); - if ( !childItem ) - return QModelIndex(); - - return createIndex( row, column, childItem ); -} - - -int -AlbumModel::rowCount( const QModelIndex& parent ) const -{ - if ( parent.column() > 0 ) - return 0; - - AlbumItem* parentItem = itemFromIndex( parent ); - if ( !parentItem ) - return 0; - - return parentItem->children.count(); -} - - -int -AlbumModel::columnCount( const QModelIndex& parent ) const -{ - Q_UNUSED( parent ); - return 1; -} - - -QModelIndex -AlbumModel::parent( const QModelIndex& child ) const -{ - AlbumItem* entry = itemFromIndex( child ); - if ( !entry ) - return QModelIndex(); - - AlbumItem* parentEntry = entry->parent; - if ( !parentEntry ) - return QModelIndex(); - - AlbumItem* grandparentEntry = parentEntry->parent; - if ( !grandparentEntry ) - return QModelIndex(); - - int row = grandparentEntry->children.indexOf( parentEntry ); - return createIndex( row, 0, parentEntry ); -} - - -QVariant -AlbumModel::data( const QModelIndex& index, int role ) const -{ - if ( role == Qt::SizeHintRole ) - { - return QSize( 116, 150 ); - } - - AlbumItem* entry = itemFromIndex( index ); - if ( !entry ) - return QVariant(); - - if ( role != Qt::DisplayRole ) // && role != Qt::ToolTipRole ) - return QVariant(); - - QString name; - if ( !entry->album().isNull() ) - name = entry->album()->name(); - else if ( !entry->artist().isNull() ) - name = entry->artist()->name(); - - switch( index.column() ) - { - case 0: - return name; - break; - - } - - return QVariant(); -} - - -QVariant -AlbumModel::headerData( int section, Qt::Orientation orientation, int role ) const -{ - QStringList headers; - headers << tr( "Album" ); - if ( orientation == Qt::Horizontal && role == Qt::DisplayRole && section >= 0 ) - { - return headers.at( section ); - } - - return QVariant(); -} - - -Qt::ItemFlags -AlbumModel::flags( const QModelIndex& index ) const -{ - Qt::ItemFlags defaultFlags = QAbstractItemModel::flags( index ); - - if ( index.isValid() && index.column() == 0 ) - return Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled | defaultFlags; - else - return defaultFlags; -} - - -QStringList -AlbumModel::mimeTypes() const -{ - QStringList types; - types << "application/tomahawk.query.list"; - return types; -} - - -QMimeData* -AlbumModel::mimeData( const QModelIndexList &indexes ) const -{ - QByteArray queryData; - QDataStream queryStream( &queryData, QIODevice::WriteOnly ); - - bool isAlbumData = true; - foreach ( const QModelIndex& i, indexes ) - { - if ( i.column() > 0 ) - continue; - - QModelIndex idx = index( i.row(), 0, i.parent() ); - AlbumItem* item = itemFromIndex( idx ); - if ( item && !item->album().isNull() ) - { - const album_ptr& album = item->album(); - queryStream << album->artist()->name(); - queryStream << album->name(); - - isAlbumData = true; - } - else if ( item && !item->artist().isNull() ) - { - const artist_ptr& artist = item->artist(); - queryStream << artist->name(); - - isAlbumData = false; - } - } - - QMimeData* mimeData = new QMimeData; - mimeData->setData( isAlbumData ? "application/tomahawk.metadata.album" : "application/tomahawk.metadata.artist", queryData ); - - return mimeData; -} - - -void -AlbumModel::removeIndex( const QModelIndex& index ) -{ - qDebug() << Q_FUNC_INFO; - - if ( index.column() > 0 ) - return; - - AlbumItem* item = itemFromIndex( index ); - if ( item ) - { - emit beginRemoveRows( index.parent(), index.row(), index.row() ); - delete item; - emit endRemoveRows(); - } - - emit itemCountChanged( rowCount( QModelIndex() ) ); -} - - -void -AlbumModel::removeIndexes( const QList& indexes ) -{ - foreach( const QModelIndex& idx, indexes ) - { - removeIndex( idx ); - } } @@ -257,7 +62,7 @@ AlbumModel::addCollection( const collection_ptr& collection, bool overwrite ) Database::instance()->enqueue( QSharedPointer( cmd ) ); - m_title = tr( "All albums from %1" ).arg( collection->source()->friendlyName() ); + setTitle( tr( "All albums from %1" ).arg( collection->source()->friendlyName() ) ); if ( collection.isNull() ) { @@ -299,9 +104,9 @@ AlbumModel::addFilteredCollection( const collection_ptr& collection, unsigned in Database::instance()->enqueue( QSharedPointer( cmd ) ); if ( !collection.isNull() ) - m_title = tr( "All albums from %1" ).arg( collection->source()->friendlyName() ); + setTitle( tr( "All albums from %1" ).arg( collection->source()->friendlyName() ) ); else - m_title = tr( "All albums" ); + setTitle( tr( "All albums" ) ); emit loadingStarted(); } @@ -339,11 +144,11 @@ AlbumModel::addAlbums( const QList& albums ) emit beginInsertRows( QModelIndex(), crows.first, crows.second ); - AlbumItem* albumitem; + PlayableItem* albumitem; foreach( const album_ptr& album, trimmedAlbums ) { - albumitem = new AlbumItem( album, m_rootItem ); - albumitem->index = createIndex( m_rootItem->children.count() - 1, 0, albumitem ); + albumitem = new PlayableItem( album, rootItem() ); + albumitem->index = createIndex( rootItem()->children.count() - 1, 0, albumitem ); connect( albumitem, SIGNAL( dataChanged() ), SLOT( onDataChanged() ) ); } @@ -385,11 +190,11 @@ AlbumModel::addArtists( const QList& artists ) emit beginInsertRows( QModelIndex(), crows.first, crows.second ); - AlbumItem* albumitem; + PlayableItem* albumitem; foreach ( const artist_ptr& artist, trimmedArtists ) { - albumitem = new AlbumItem( artist, m_rootItem ); - albumitem->index = createIndex( m_rootItem->children.count() - 1, 0, albumitem ); + albumitem = new PlayableItem( artist, rootItem() ); + albumitem->index = createIndex( rootItem()->children.count() - 1, 0, albumitem ); connect( albumitem, SIGNAL( dataChanged() ), SLOT( onDataChanged() ) ); } @@ -414,11 +219,11 @@ AlbumModel::addQueries( const QList& queries ) emit beginInsertRows( QModelIndex(), crows.first, crows.second ); - AlbumItem* albumitem; + PlayableItem* albumitem; foreach ( const query_ptr& query, queries ) { - albumitem = new AlbumItem( query, m_rootItem ); - albumitem->index = createIndex( m_rootItem->children.count() - 1, 0, albumitem ); + albumitem = new PlayableItem( query, rootItem() ); + albumitem->index = createIndex( rootItem()->children.count() - 1, 0, albumitem ); connect( albumitem, SIGNAL( dataChanged() ), SLOT( onDataChanged() ) ); } @@ -442,30 +247,12 @@ AlbumModel::onCollectionChanged() } -void -AlbumModel::clear() -{ - beginResetModel(); - delete m_rootItem; - m_rootItem = new AlbumItem( 0, this ); - endResetModel(); -} - - -void -AlbumModel::onDataChanged() -{ - AlbumItem* p = (AlbumItem*)sender(); - emit dataChanged( p->index, p->index.sibling( p->index.row(), columnCount( QModelIndex() ) - 1 ) ); -} - - -AlbumItem* +PlayableItem* AlbumModel::findItem( const artist_ptr& artist ) const { for ( int i = 0; i < rowCount( QModelIndex() ); i++ ) { - AlbumItem* item = itemFromIndex( index( i, 0, QModelIndex() ) ); + PlayableItem* item = itemFromIndex( index( i, 0, QModelIndex() ) ); if ( !item->artist().isNull() && item->artist() == artist ) { return item; @@ -476,12 +263,12 @@ AlbumModel::findItem( const artist_ptr& artist ) const } -AlbumItem* +PlayableItem* AlbumModel::findItem( const album_ptr& album ) const { for ( int i = 0; i < rowCount( QModelIndex() ); i++ ) { - AlbumItem* item = itemFromIndex( index( i, 0, QModelIndex() ) ); + PlayableItem* item = itemFromIndex( index( i, 0, QModelIndex() ) ); if ( !item->album().isNull() && item->album() == album ) { return item; diff --git a/src/libtomahawk/playlist/AlbumModel.h b/src/libtomahawk/playlist/AlbumModel.h index 71d7743d8..0c6413786 100644 --- a/src/libtomahawk/playlist/AlbumModel.h +++ b/src/libtomahawk/playlist/AlbumModel.h @@ -20,19 +20,19 @@ #ifndef ALBUMMODEL_H #define ALBUMMODEL_H -#include #include #include "Album.h" #include "PlaylistInterface.h" #include "database/DatabaseCommand_AllAlbums.h" +#include "PlayableModel.h" #include "DllMacro.h" -class AlbumItem; +class PlayableItem; class QMetaData; -class DLLEXPORT AlbumModel : public QAbstractItemModel +class DLLEXPORT AlbumModel : public PlayableModel { Q_OBJECT @@ -40,80 +40,26 @@ public: explicit AlbumModel( QObject* parent = 0 ); virtual ~AlbumModel(); - virtual QModelIndex index( int row, int column, const QModelIndex& parent ) const; - virtual QModelIndex parent( const QModelIndex& child ) const; - - virtual bool isReadOnly() const { return true; } - - virtual int trackCount() const { return rowCount( QModelIndex() ); } - virtual int albumCount() const { return rowCount( QModelIndex() ); } - - virtual int rowCount( const QModelIndex& parent ) const; - virtual int columnCount( const QModelIndex& parent ) const; - - virtual QVariant data( const QModelIndex& index, int role = Qt::DisplayRole ) const; - virtual QVariant headerData( int section, Qt::Orientation orientation, int role ) const; - - virtual void removeIndex( const QModelIndex& index ); - virtual void removeIndexes( const QList& indexes ); - - virtual QMimeData* mimeData( const QModelIndexList& indexes ) const; - virtual QStringList mimeTypes() const; - virtual Qt::ItemFlags flags( const QModelIndex& index ) const; - Tomahawk::collection_ptr collection() const { return m_collection; } - void clear(); void addCollection( const Tomahawk::collection_ptr& collection, bool overwrite = false ); void addFilteredCollection( const Tomahawk::collection_ptr& collection, unsigned int amount, DatabaseCommand_AllAlbums::SortOrder order, bool overwrite = false ); - virtual QString title() const { return m_title; } - virtual QString description() const { return m_description; } - virtual void setTitle( const QString& title ) { m_title = title; } - virtual void setDescription( const QString& description ) { m_description = description; } - - AlbumItem* findItem( const Tomahawk::artist_ptr& artist ) const; - AlbumItem* findItem( const Tomahawk::album_ptr& album ) const; - - AlbumItem* itemFromIndex( const QModelIndex& index ) const - { - if ( index.isValid() ) - return static_cast( index.internalPointer() ); - else - { - return m_rootItem; - } - } + PlayableItem* findItem( const Tomahawk::artist_ptr& artist ) const; + PlayableItem* findItem( const Tomahawk::album_ptr& album ) const; public slots: - virtual void setRepeatMode( Tomahawk::PlaylistInterface::RepeatMode /*mode*/ ) {} - virtual void setShuffled( bool /*shuffled*/ ) {} - void addAlbums( const QList& albums ); void addArtists( const QList& artists ); void addQueries( const QList& queries ); signals: - void repeatModeChanged( Tomahawk::PlaylistInterface::RepeatMode mode ); - void shuffleModeChanged( bool enabled ); - - void itemCountChanged( unsigned int items ); - - void loadingStarted(); - void loadingFinished(); private slots: - void onDataChanged(); - void onSourceAdded( const Tomahawk::source_ptr& source ); void onCollectionChanged(); private: - QPersistentModelIndex m_currentIndex; - AlbumItem* m_rootItem; - - QString m_title; - QString m_description; bool m_overwriteOnAdd; Tomahawk::collection_ptr m_collection; diff --git a/src/libtomahawk/playlist/AlbumProxyModel.cpp b/src/libtomahawk/playlist/AlbumProxyModel.cpp deleted file mode 100644 index b7d3d8f4f..000000000 --- a/src/libtomahawk/playlist/AlbumProxyModel.cpp +++ /dev/null @@ -1,153 +0,0 @@ -/* === This file is part of Tomahawk Player - === - * - * Copyright 2010-2011, Christian Muehlhaeuser - * Copyright 2010-2012, Jeff Mitchell - * - * Tomahawk is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Tomahawk is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Tomahawk. If not, see . - */ - -#include "AlbumProxyModel.h" - -#include - -#include "AlbumProxyModelPlaylistInterface.h" -#include "Artist.h" -#include "AlbumItem.h" -#include "Query.h" -#include "utils/Logger.h" - - -AlbumProxyModel::AlbumProxyModel( QObject* parent ) - : QSortFilterProxyModel( parent ) - , m_model( 0 ) -{ - setFilterCaseSensitivity( Qt::CaseInsensitive ); - setSortCaseSensitivity( Qt::CaseInsensitive ); - setDynamicSortFilter( true ); - - setSourceAlbumModel( 0 ); -} - - -void -AlbumProxyModel::setSourceModel( QAbstractItemModel* sourceModel ) -{ - Q_UNUSED( sourceModel ); - qDebug() << "Explicitly use setSourceAlbumModel instead"; - Q_ASSERT( false ); -} - - -void -AlbumProxyModel::setSourceAlbumModel( AlbumModel* sourceModel ) -{ - m_model = sourceModel; - - if ( m_model && m_model->metaObject()->indexOfSignal( "trackCountChanged(uint)" ) > -1 ) - connect( m_model, SIGNAL( trackCountChanged( unsigned int ) ), SIGNAL( sourceTrackCountChanged( unsigned int ) ) ); - - QSortFilterProxyModel::setSourceModel( sourceModel ); -} - - -bool -AlbumProxyModel::filterAcceptsRow( int sourceRow, const QModelIndex& sourceParent ) const -{ - if ( filterRegExp().isEmpty() ) - return true; - - AlbumItem* pi = sourceModel()->itemFromIndex( sourceModel()->index( sourceRow, 0, sourceParent ) ); - if ( !pi ) - return false; - - const Tomahawk::album_ptr& q = pi->album(); - - QStringList sl = filterRegExp().pattern().split( " ", QString::SkipEmptyParts ); - - bool found = true; - foreach( const QString& s, sl ) - { - if ( !q->name().contains( s, Qt::CaseInsensitive ) && !q->artist()->name().contains( s, Qt::CaseInsensitive ) ) - { - found = false; - } - } - - return found; -} - - -bool -AlbumProxyModel::lessThan( const QModelIndex& left, const QModelIndex& right ) const -{ - AlbumItem* p1 = sourceModel()->itemFromIndex( left ); - AlbumItem* p2 = sourceModel()->itemFromIndex( right ); - - if ( !p1 ) - return true; - if ( !p2 ) - return false; - - if ( p1->album().isNull() || p1->album()->artist().isNull() ) - return true; - - if ( p2->album().isNull() || p2->album()->artist().isNull() ) - return false; - - if ( p1->album()->artist()->name() == p2->album()->artist()->name() ) - { - return QString::localeAwareCompare( p1->album()->name(), p2->album()->name() ) < 0; - } - - return QString::localeAwareCompare( p1->album()->artist()->name(), p2->album()->artist()->name() ) < 0; -} - - -void -AlbumProxyModel::removeIndex( const QModelIndex& index ) -{ - qDebug() << Q_FUNC_INFO; - - if ( !sourceModel() ) - return; - if ( index.column() > 0 ) - return; - - sourceModel()->removeIndex( mapToSource( index ) ); -} - - -void -AlbumProxyModel::removeIndexes( const QList& indexes ) -{ - if ( !sourceModel() ) - return; - - foreach( const QModelIndex& idx, indexes ) - { - removeIndex( idx ); - } -} - - -Tomahawk::playlistinterface_ptr -AlbumProxyModel::playlistInterface() -{ - if ( m_playlistInterface.isNull() ) - { - m_playlistInterface = Tomahawk::playlistinterface_ptr( new Tomahawk::AlbumProxyModelPlaylistInterface( this ) ); - } - - return m_playlistInterface; -} diff --git a/src/libtomahawk/playlist/AlbumProxyModel.h b/src/libtomahawk/playlist/AlbumProxyModel.h deleted file mode 100644 index c0973caaa..000000000 --- a/src/libtomahawk/playlist/AlbumProxyModel.h +++ /dev/null @@ -1,64 +0,0 @@ -/* === This file is part of Tomahawk Player - === - * - * Copyright 2010-2011, Christian Muehlhaeuser - * Copyright 2010-2012, Jeff Mitchell - * - * Tomahawk is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Tomahawk is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Tomahawk. If not, see . - */ - -#ifndef ALBUMPROXYMODEL_H -#define ALBUMPROXYMODEL_H - -#include - -#include "PlaylistInterface.h" -#include "playlist/AlbumModel.h" - -#include "DllMacro.h" - -class DLLEXPORT AlbumProxyModel : public QSortFilterProxyModel -{ -Q_OBJECT - -public: - explicit AlbumProxyModel( QObject* parent = 0 ); - virtual ~AlbumProxyModel() {} - - virtual AlbumModel* sourceModel() const { return m_model; } - virtual void setSourceAlbumModel( AlbumModel* sourceModel ); - virtual void setSourceModel( QAbstractItemModel* sourceModel ); - - virtual int albumCount() const { return rowCount( QModelIndex() ); } - - virtual void removeIndex( const QModelIndex& index ); - virtual void removeIndexes( const QList& indexes ); - - virtual void emitFilterChanged( const QString &pattern ) { emit filterChanged( pattern ); } - - virtual Tomahawk::playlistinterface_ptr playlistInterface(); - -signals: - void filterChanged( const QString& filter ); - -protected: - bool filterAcceptsRow( int sourceRow, const QModelIndex& sourceParent ) const; - bool lessThan( const QModelIndex& left, const QModelIndex& right ) const; - -private: - AlbumModel* m_model; - - Tomahawk::playlistinterface_ptr m_playlistInterface; -}; - -#endif // ALBUMPROXYMODEL_H diff --git a/src/libtomahawk/playlist/AlbumProxyModelPlaylistInterface.cpp b/src/libtomahawk/playlist/AlbumProxyModelPlaylistInterface.cpp deleted file mode 100644 index 141d44021..000000000 --- a/src/libtomahawk/playlist/AlbumProxyModelPlaylistInterface.cpp +++ /dev/null @@ -1,101 +0,0 @@ -/* === This file is part of Tomahawk Player - === - * - * Copyright 2010-2011, Christian Muehlhaeuser - * Copyright 2010-2011, Jeff Mitchell - * - * Tomahawk is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Tomahawk is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Tomahawk. If not, see . - */ - -#include "AlbumProxyModelPlaylistInterface.h" - -#include "AlbumProxyModel.h" -#include "Artist.h" -#include "AlbumItem.h" -#include "Query.h" -#include "utils/Logger.h" - -using namespace Tomahawk; - -AlbumProxyModelPlaylistInterface::AlbumProxyModelPlaylistInterface( AlbumProxyModel *proxyModel ) - : Tomahawk::PlaylistInterface() - , m_proxyModel( proxyModel ) - , m_repeatMode( PlaylistInterface::NoRepeat ) - , m_shuffled( false ) -{ -} - - -AlbumProxyModelPlaylistInterface::~AlbumProxyModelPlaylistInterface() -{ - m_proxyModel.clear(); -} - - -QList< Tomahawk::query_ptr > -AlbumProxyModelPlaylistInterface::tracks() -{ - Q_ASSERT( FALSE ); - QList queries; - return queries; -} - - -int -AlbumProxyModelPlaylistInterface::unfilteredTrackCount() const -{ - return ( m_proxyModel.isNull() ? 0 : m_proxyModel.data()->sourceModel()->rowCount( QModelIndex() ) ); -} - - -int -AlbumProxyModelPlaylistInterface::trackCount() const -{ - return ( m_proxyModel.isNull() ? 0 : m_proxyModel.data()->rowCount( QModelIndex() ) ); -} - - -Tomahawk::result_ptr -AlbumProxyModelPlaylistInterface::currentItem() const -{ - return Tomahawk::result_ptr(); -} - - -QString -AlbumProxyModelPlaylistInterface::filter() const -{ - return ( m_proxyModel.isNull() ? QString() : m_proxyModel.data()->filterRegExp().pattern() ); -} - - -void -AlbumProxyModelPlaylistInterface::setFilter( const QString& pattern ) -{ - qDebug() << Q_FUNC_INFO; - - if ( m_proxyModel.isNull() ) - return; - - m_proxyModel.data()->setFilterRegExp( pattern ); - m_proxyModel.data()->emitFilterChanged( pattern ); -} - - -Tomahawk::result_ptr -AlbumProxyModelPlaylistInterface::siblingItem( int itemsAway ) -{ - Q_UNUSED( itemsAway ); - qDebug() << Q_FUNC_INFO; - return Tomahawk::result_ptr( 0 ); -} diff --git a/src/libtomahawk/playlist/AlbumProxyModelPlaylistInterface.h b/src/libtomahawk/playlist/AlbumProxyModelPlaylistInterface.h deleted file mode 100644 index 9cf5e9179..000000000 --- a/src/libtomahawk/playlist/AlbumProxyModelPlaylistInterface.h +++ /dev/null @@ -1,78 +0,0 @@ -/* === This file is part of Tomahawk Player - === - * - * Copyright 2010-2011, Christian Muehlhaeuser - * Copyright 2010-2012, Jeff Mitchell - * - * Tomahawk is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Tomahawk is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Tomahawk. If not, see . - */ - -#ifndef ALBUMPROXYMODELPLAYLISTINTERFACE_H -#define ALBUMPROXYMODELPLAYLISTINTERFACE_H - -#include "PlaylistInterface.h" -#include "playlist/AlbumModel.h" - -#include "DllMacro.h" - -class AlbumProxyModel; - -namespace Tomahawk -{ - -class DLLEXPORT AlbumProxyModelPlaylistInterface : public Tomahawk::PlaylistInterface -{ -Q_OBJECT - -public: - explicit AlbumProxyModelPlaylistInterface( AlbumProxyModel *proxyModel ); - virtual ~AlbumProxyModelPlaylistInterface(); - - virtual QList tracks(); - - virtual int unfilteredTrackCount() const; - virtual int trackCount() const; - - virtual bool hasNextItem() { return true; } - virtual Tomahawk::result_ptr currentItem() const; - virtual Tomahawk::result_ptr siblingItem( int direction ); - - virtual QString filter() const; - virtual void setFilter( const QString& pattern ); - - virtual Tomahawk::PlaylistInterface::RepeatMode repeatMode() const { return m_repeatMode; } - virtual bool shuffled() const { return m_shuffled; } - virtual Tomahawk::PlaylistInterface::ViewMode viewMode() const { return Tomahawk::PlaylistInterface::Album; } - -signals: - void repeatModeChanged( Tomahawk::PlaylistInterface::RepeatMode mode ); - void shuffleModeChanged( bool enabled ); - - void trackCountChanged( unsigned int tracks ); - void sourceTrackCountChanged( unsigned int tracks ); - - void nextTrackReady(); - -public slots: - virtual void setRepeatMode( Tomahawk::PlaylistInterface::RepeatMode mode ) { m_repeatMode = mode; emit repeatModeChanged( mode ); } - virtual void setShuffled( bool enabled ) { m_shuffled = enabled; emit shuffleModeChanged( enabled ); } - -private: - QWeakPointer< AlbumProxyModel > m_proxyModel; - RepeatMode m_repeatMode; - bool m_shuffled; -}; - -} //ns - -#endif // ALBUMPROXYMODELPLAYLISTINTERFACE_H diff --git a/src/libtomahawk/playlist/CollectionFlatModel.cpp b/src/libtomahawk/playlist/CollectionFlatModel.cpp deleted file mode 100644 index 01d426c3d..000000000 --- a/src/libtomahawk/playlist/CollectionFlatModel.cpp +++ /dev/null @@ -1,140 +0,0 @@ -/* === This file is part of Tomahawk Player - === - * - * Copyright 2010-2011, Christian Muehlhaeuser - * - * Tomahawk is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Tomahawk is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Tomahawk. If not, see . - */ - -#include "CollectionFlatModel.h" - -#include "database/Database.h" -#include "SourceList.h" -#include "utils/Logger.h" - -using namespace Tomahawk; - - -CollectionFlatModel::CollectionFlatModel( QObject* parent ) - : TrackModel( parent ) -{ -} - - -CollectionFlatModel::~CollectionFlatModel() -{ -} - - -void -CollectionFlatModel::addCollections( const QList< collection_ptr >& collections ) -{ - foreach( const collection_ptr& col, collections ) - { - addCollection( col ); - } - - // we are waiting for some to load - if( !m_loadingCollections.isEmpty() ) - emit loadingStarted(); -} - - -void -CollectionFlatModel::addCollection( const collection_ptr& collection, bool sendNotifications ) -{ - qDebug() << Q_FUNC_INFO << collection->name() - << collection->source()->id() - << collection->source()->userName(); - - if ( sendNotifications ) - emit loadingStarted(); - - DatabaseCommand_AllTracks* cmd = new DatabaseCommand_AllTracks( collection ); - connect( cmd, SIGNAL( tracks( QList, QVariant ) ), - SLOT( onTracksAdded( QList ) ), Qt::QueuedConnection ); - - Database::instance()->enqueue( QSharedPointer( cmd ) ); - - m_loadingCollections << collection.data(); - - if ( collection->source()->isLocal() ) - setTitle( tr( "My Collection" ) ); - else - setTitle( tr( "Collection of %1" ).arg( collection->source()->friendlyName() ) ); -} - - -void -CollectionFlatModel::addFilteredCollection( const collection_ptr& collection, unsigned int amount, DatabaseCommand_AllTracks::SortOrder order ) -{ - qDebug() << Q_FUNC_INFO << collection->name() - << collection->source()->id() - << collection->source()->userName() - << amount << order; - - DatabaseCommand_AllTracks* cmd = new DatabaseCommand_AllTracks( collection ); - cmd->setLimit( amount ); - cmd->setSortOrder( order ); - cmd->setSortDescending( true ); - - connect( cmd, SIGNAL( tracks( QList, QVariant ) ), - SLOT( onTracksAdded( QList ) ), Qt::QueuedConnection ); - - Database::instance()->enqueue( QSharedPointer( cmd ) ); -} - - -void -CollectionFlatModel::onTracksAdded( const QList& tracks ) -{ - qDebug() << Q_FUNC_INFO << tracks.count() << rowCount( QModelIndex() ); - - if ( !m_loadingCollections.isEmpty() && sender() && qobject_cast< Collection* >( sender() ) ) - { - // we are keeping track and are called as a slot - m_loadingCollections.removeAll( qobject_cast< Collection* >( sender() ) ); - } - - append( tracks ); - - if ( m_loadingCollections.isEmpty() ) - emit loadingFinished(); -} - - -void -CollectionFlatModel::onTracksRemoved( const QList& tracks ) -{ - QList t = tracks; - for ( int i = rowCount( QModelIndex() ); i >= 0 && t.count(); i-- ) - { - QModelIndex idx = index( i, 0, QModelIndex() ); - TrackModelItem* item = itemFromIndex( idx ); - if ( !item ) - continue; - - int j = 0; - foreach ( const query_ptr& query, t ) - { - if ( item->query().data() == query.data() ) - { - remove( idx ); - t.removeAt( j ); - break; - } - - j++; - } - } -} diff --git a/src/libtomahawk/playlist/CollectionFlatModel.h b/src/libtomahawk/playlist/CollectionFlatModel.h deleted file mode 100644 index a93d2f39d..000000000 --- a/src/libtomahawk/playlist/CollectionFlatModel.h +++ /dev/null @@ -1,72 +0,0 @@ -/* === This file is part of Tomahawk Player - === - * - * Copyright 2010-2011, Christian Muehlhaeuser - * Copyright 2010-2011, Jeff Mitchell - * - * Tomahawk is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Tomahawk is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Tomahawk. If not, see . - */ - -#ifndef COLLECTIONFLATMODEL_H -#define COLLECTIONFLATMODEL_H - -#include -#include -#include - -#include "Typedefs.h" -#include "TrackModel.h" -#include "Query.h" -#include "Source.h" -#include "PlaylistInterface.h" - -#include "database/DatabaseCommand_AllTracks.h" - -#include "DllMacro.h" - -class QMetaData; - -class DLLEXPORT CollectionFlatModel : public TrackModel -{ -Q_OBJECT - -public: - explicit CollectionFlatModel( QObject* parent = 0 ); - ~CollectionFlatModel(); - - virtual int trackCount() const { return rowCount( QModelIndex() ) + m_tracksToAdd.count(); } - - void addCollections( const QList< Tomahawk::collection_ptr >& collections ); - void addCollection( const Tomahawk::collection_ptr& collection, bool sendNotifications = true ); - void addFilteredCollection( const Tomahawk::collection_ptr& collection, unsigned int amount, DatabaseCommand_AllTracks::SortOrder order ); - -signals: - void repeatModeChanged( Tomahawk::PlaylistInterface::RepeatMode mode ); - void shuffleModeChanged( bool enabled ); - - void loadingStarted(); - void loadingFinished(); - void trackCountChanged( unsigned int tracks ); - -private slots: - void onTracksAdded( const QList& tracks ); - void onTracksRemoved( const QList& tracks ); - -private: - QMap< Tomahawk::collection_ptr, QPair< int, int > > m_collectionRows; - QList m_tracksToAdd; - // just to keep track of what we are waiting to be loaded - QList m_loadingCollections; -}; - -#endif // COLLECTIONFLATMODEL_H diff --git a/src/libtomahawk/playlist/CollectionProxyModel.cpp b/src/libtomahawk/playlist/CollectionProxyModel.cpp deleted file mode 100644 index 483b95dcb..000000000 --- a/src/libtomahawk/playlist/CollectionProxyModel.cpp +++ /dev/null @@ -1,45 +0,0 @@ -/* === This file is part of Tomahawk Player - === - * - * Copyright 2010-2011, Christian Muehlhaeuser - * Copyright 2010-2012, Jeff Mitchell - * - * Tomahawk is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Tomahawk is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Tomahawk. If not, see . - */ - -#include "CollectionProxyModel.h" - -#include "CollectionProxyModelPlaylistInterface.h" - -#include - -#include "Album.h" -#include "Query.h" -#include "utils/Logger.h" - - -CollectionProxyModel::CollectionProxyModel( QObject* parent ) - : TrackProxyModel( parent ) -{ -} - -Tomahawk::playlistinterface_ptr -CollectionProxyModel::playlistInterface() -{ - if ( m_playlistInterface.isNull() ) - { - m_playlistInterface = Tomahawk::playlistinterface_ptr( new Tomahawk::CollectionProxyModelPlaylistInterface( this ) ); - } - - return m_playlistInterface; -} diff --git a/src/libtomahawk/playlist/CollectionProxyModelPlaylistInterface.cpp b/src/libtomahawk/playlist/CollectionProxyModelPlaylistInterface.cpp deleted file mode 100644 index b3e0cad67..000000000 --- a/src/libtomahawk/playlist/CollectionProxyModelPlaylistInterface.cpp +++ /dev/null @@ -1,37 +0,0 @@ -/* === This file is part of Tomahawk Player - === - * - * Copyright 2010-2011, Christian Muehlhaeuser - * Copyright 2010-2011, Jeff Mitchell - * - * Tomahawk is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Tomahawk is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Tomahawk. If not, see . - */ - -#include "CollectionProxyModelPlaylistInterface.h" - -#include "CollectionProxyModel.h" -#include "Album.h" -#include "Query.h" -#include "utils/Logger.h" - -using namespace Tomahawk; - -CollectionProxyModelPlaylistInterface::CollectionProxyModelPlaylistInterface( CollectionProxyModel *proxyModel ) - : TrackProxyModelPlaylistInterface( proxyModel ) -{ -} - -CollectionProxyModelPlaylistInterface::~CollectionProxyModelPlaylistInterface() -{ - m_proxyModel.clear(); -} diff --git a/src/libtomahawk/playlist/CollectionProxyModelPlaylistInterface.h b/src/libtomahawk/playlist/CollectionProxyModelPlaylistInterface.h deleted file mode 100644 index 1cf5ac727..000000000 --- a/src/libtomahawk/playlist/CollectionProxyModelPlaylistInterface.h +++ /dev/null @@ -1,46 +0,0 @@ -/* === This file is part of Tomahawk Player - === - * - * Copyright 2010-2011, Christian Muehlhaeuser - * Copyright 2010-2011, Jeff Mitchell - * - * Tomahawk is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Tomahawk is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Tomahawk. If not, see . - */ - -#ifndef COLLECTIONPROXYMODELPLAYLISTINTERFACE_H -#define COLLECTIONPROXYMODELPLAYLISTINTERFACE_H - -#include "TrackProxyModel.h" -#include "TrackProxyModelPlaylistInterface.h" - -#include "DllMacro.h" - -class CollectionProxyModel; - -namespace Tomahawk -{ - -class DLLEXPORT CollectionProxyModelPlaylistInterface : public TrackProxyModelPlaylistInterface -{ -Q_OBJECT - -public: - explicit CollectionProxyModelPlaylistInterface( CollectionProxyModel* proxyModel ); - virtual ~CollectionProxyModelPlaylistInterface(); - - virtual PlaylistInterface::ViewMode viewMode() const { return PlaylistInterface::Flat; } -}; - -} //ns - -#endif // COLLECTIONPROXYMODELPLAYLISTINTERFACE_H diff --git a/src/libtomahawk/playlist/CollectionView.cpp b/src/libtomahawk/playlist/CollectionView.cpp deleted file mode 100644 index a9572682d..000000000 --- a/src/libtomahawk/playlist/CollectionView.cpp +++ /dev/null @@ -1,98 +0,0 @@ -/* === This file is part of Tomahawk Player - === - * - * Copyright 2010-2011, Christian Muehlhaeuser - * Copyright 2010-2011, Jeff Mitchell - * - * Tomahawk is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Tomahawk is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Tomahawk. If not, see . - */ - -#include "CollectionView.h" - -#include -#include - -#include "CollectionProxyModel.h" -#include "TrackModel.h" -#include "widgets/OverlayWidget.h" -#include "utils/Logger.h" - -using namespace Tomahawk; - - -CollectionView::CollectionView( QWidget* parent ) - : TrackView( parent ) -{ - setProxyModel( new CollectionProxyModel( this ) ); - - setDragDropMode( QAbstractItemView::DragOnly ); -} - - -CollectionView::~CollectionView() -{ - qDebug() << Q_FUNC_INFO; -} - - -void -CollectionView::setModel( QAbstractItemModel* model ) -{ - Q_UNUSED( model ); - qDebug() << "Explicitly use setTrackModel instead"; - Q_ASSERT( false ); -} - - -void -CollectionView::setTrackModel( TrackModel* model ) -{ - TrackView::setTrackModel( model ); - - setColumnHidden( TrackModel::Score, true ); // Hide score column per default - setColumnHidden( TrackModel::Origin, true ); // Hide origin column per default - setColumnHidden( TrackModel::Composer, true ); //Hide composer column per default - - setGuid( QString( "collectionview/%1" ).arg( model->columnCount() ) ); - sortByColumn( TrackModel::Artist, Qt::AscendingOrder ); - - connect( model, SIGNAL( trackCountChanged( unsigned int ) ), SLOT( onTrackCountChanged( unsigned int ) ) ); -} - - -void -CollectionView::dragEnterEvent( QDragEnterEvent* event ) -{ - event->ignore(); -} - - -void -CollectionView::onTrackCountChanged( unsigned int tracks ) -{ - if ( tracks == 0 ) - { - overlay()->setText( tr( "This collection is empty." ) ); - overlay()->show(); - } - else - overlay()->hide(); -} - - -bool -CollectionView::jumpToCurrentTrack() -{ - scrollTo( proxyModel()->currentIndex(), QAbstractItemView::PositionAtCenter ); - return true; -} diff --git a/src/libtomahawk/playlist/CollectionView.h b/src/libtomahawk/playlist/CollectionView.h deleted file mode 100644 index 6f80df54a..000000000 --- a/src/libtomahawk/playlist/CollectionView.h +++ /dev/null @@ -1,61 +0,0 @@ -/* === This file is part of Tomahawk Player - === - * - * Copyright 2010-2011, Christian Muehlhaeuser - * Copyright 2010-2012, Jeff Mitchell - * - * Tomahawk is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Tomahawk is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Tomahawk. If not, see . - */ - -#ifndef COLLECTIONVIEW_H -#define COLLECTIONVIEW_H - -#include "TrackProxyModel.h" -#include "TrackView.h" -#include "ViewPage.h" - -#include "DllMacro.h" - -class TrackModel; - -class DLLEXPORT CollectionView : public TrackView, public Tomahawk::ViewPage -{ -Q_OBJECT - -public: - explicit CollectionView( QWidget* parent = 0 ); - ~CollectionView(); - - virtual void setTrackModel( TrackModel* model ); - virtual void setModel( QAbstractItemModel* model ); - - virtual QWidget* widget() { return this; } - virtual Tomahawk::playlistinterface_ptr playlistInterface() const { return proxyModel()->playlistInterface(); } - - virtual QString title() const { return model()->title(); } - virtual QString description() const { return model()->description(); } - virtual QPixmap pixmap() const { return QPixmap( RESPATH "images/music-icon.png" ); } - - virtual bool showModes() const { return true; } - virtual bool showFilter() const { return true; } - - virtual bool jumpToCurrentTrack(); - -private slots: - void onTrackCountChanged( unsigned int tracks ); - -protected: - virtual void dragEnterEvent( QDragEnterEvent* event ); -}; - -#endif // COLLECTIONVIEW_H diff --git a/src/libtomahawk/playlist/CustomPlaylistView.cpp b/src/libtomahawk/playlist/CustomPlaylistView.cpp index 886c7d998..40f81ffdc 100644 --- a/src/libtomahawk/playlist/CustomPlaylistView.cpp +++ b/src/libtomahawk/playlist/CustomPlaylistView.cpp @@ -34,10 +34,7 @@ CustomPlaylistView::CustomPlaylistView( CustomPlaylistView::PlaylistType type, c , m_model( new PlaylistModel( this ) ) { // Generate the tracks, add them to the playlist - setFrameShape( QFrame::NoFrame ); - setAttribute( Qt::WA_MacShowFocusRect, 0 ); - - m_model->setStyle( TrackModel::Large ); + m_model->setStyle( PlayableModel::Large ); setPlaylistModel( m_model ); generateTracks(); @@ -76,6 +73,8 @@ CustomPlaylistView::jumpToCurrentTrack() void CustomPlaylistView::generateTracks() { + m_model->startLoading(); + QString sql; switch ( m_type ) { @@ -106,8 +105,9 @@ void CustomPlaylistView::tracksGenerated( QList< query_ptr > tracks ) { bool changed = false; - QList< query_ptr > newTracks = TomahawkUtils::mergePlaylistChanges( m_model->queries(), tracks, changed); + QList< query_ptr > newTracks = TomahawkUtils::mergePlaylistChanges( m_model->queries(), tracks, changed ); + m_model->finishLoading(); if ( !changed ) return; diff --git a/src/libtomahawk/playlist/GridItemDelegate.cpp b/src/libtomahawk/playlist/GridItemDelegate.cpp new file mode 100644 index 000000000..4568c1705 --- /dev/null +++ b/src/libtomahawk/playlist/GridItemDelegate.cpp @@ -0,0 +1,567 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2010-2011, Christian Muehlhaeuser + * Copyright 2011-2012, Leo Franchi + * + * Tomahawk is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Tomahawk is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Tomahawk. If not, see . + */ + +#include "GridItemDelegate.h" + +#include +#include +#include +#include +#include + +#include "Artist.h" +#include "Query.h" +#include "Result.h" +#include "Source.h" +#include "audio/AudioEngine.h" + +#include "utils/TomahawkUtilsGui.h" +#include "utils/PixmapDelegateFader.h" +#include + +#include "playlist/PlayableItem.h" +#include "playlist/PlayableProxyModel.h" +#include "GridView.h" +#include "ViewManager.h" +#include "utils/AnimatedSpinner.h" +#include "widgets/ImageButton.h" +#include "utils/Logger.h" + +namespace { + static const int FADE_DURATION = 200; +}; + + +GridItemDelegate::GridItemDelegate( QAbstractItemView* parent, PlayableProxyModel* proxy ) + : QStyledItemDelegate( (QObject*)parent ) + , m_view( parent ) + , m_model( proxy ) +{ + if ( m_view && m_view->metaObject()->indexOfSignal( "modelChanged()" ) > -1 ) + connect( m_view, SIGNAL( modelChanged() ), this, SLOT( modelChanged() ) ); + + connect( m_view, SIGNAL( scrolledContents( int, int ) ), SLOT( onScrolled( int, int ) ) ); +} + + +QSize +GridItemDelegate::sizeHint( const QStyleOptionViewItem& option, const QModelIndex& index ) const +{ + QSize size = QStyledItemDelegate::sizeHint( option, index ); + return size; +} + + +void +GridItemDelegate::paint( QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index ) const +{ + PlayableItem* item = m_model->sourceModel()->itemFromIndex( m_model->mapToSource( index ) ); + if ( !item ) + return; + + QStyleOptionViewItemV4 opt = option; + initStyleOption( &opt, QModelIndex() ); + qApp->style()->drawControl( QStyle::CE_ItemViewItem, &opt, painter ); + + painter->save(); + painter->setRenderHint( QPainter::Antialiasing ); + + QRect r = option.rect; + QString top, bottom; + if ( !item->album().isNull() ) + { + top = item->album()->name(); + + if ( !item->album()->artist().isNull() ) + bottom = item->album()->artist()->name(); + } + else if ( !item->artist().isNull() ) + { + top = item->artist()->name(); + } + else + { + top = item->query()->track(); + bottom = item->query()->artist(); + } + + if ( !m_covers.contains( index ) ) + { + if ( !item->album().isNull() ) + { + m_covers.insert( index, QSharedPointer< Tomahawk::PixmapDelegateFader >( new Tomahawk::PixmapDelegateFader( item->album(), r.size(), TomahawkUtils::Grid ) ) ); + } + else if ( !item->artist().isNull() ) + { + m_covers.insert( index, QSharedPointer< Tomahawk::PixmapDelegateFader >( new Tomahawk::PixmapDelegateFader( item->artist(), r.size(), TomahawkUtils::Grid ) ) ); + } + else + { + m_covers.insert( index, QSharedPointer< Tomahawk::PixmapDelegateFader >( new Tomahawk::PixmapDelegateFader( item->query(), r.size(), TomahawkUtils::Grid ) ) ); + } + + _detail::Closure* closure = NewClosure( m_covers[ index ], SIGNAL( repaintRequest() ), const_cast(this), SLOT( doUpdateIndex( QPersistentModelIndex ) ), QPersistentModelIndex( index ) ); + closure->setAutoDelete( false ); + } + + QSharedPointer< Tomahawk::PixmapDelegateFader > fader = m_covers[ index ]; + if ( fader->size() != r.size() ) + fader->setSize( r.size() ); + + const QPixmap cover = fader->currentPixmap(); + painter->drawPixmap( r, cover ); + + qreal opacity = -1.; + if ( m_hoverFaders.contains( index ) ) + { + const qreal pct = ( m_hoverFaders[ index ]->currentFrame() / 100. ); + opacity = 0.35 - pct * 0.35; + } + else if ( m_hoverIndex == index ) + { + opacity = 0.35; + } + + if ( opacity > -1. ) + { + painter->save(); + + painter->setPen( QColor( 33, 33, 33 ) ); + painter->setBrush( QColor( 33, 33, 33 ) ); + painter->setOpacity( opacity ); + painter->drawRect( r ); + + painter->restore(); + } + + painter->save(); + + painter->setPen( Qt::black ); + painter->setBrush( Qt::black ); + painter->setOpacity( 0.5 ); + painter->drawRoundedRect( r.adjusted( 4, +r.height() - 36, -4, -4 ), 3, 3 ); + + painter->restore(); + + painter->setPen( opt.palette.color( QPalette::HighlightedText ) ); + QTextOption to; + to.setWrapMode( QTextOption::NoWrap ); + + QString text; + QFont font = opt.font; + font.setPixelSize( 10 ); + QFont boldFont = font; + boldFont.setBold( true ); + boldFont.setPixelSize( 14 ); + + QRect textRect = option.rect.adjusted( 6, option.rect.height() - 36, -4, -6 ); + painter->setFont( font ); + int bottomHeight = painter->fontMetrics().boundingRect( bottom ).height(); + painter->setFont( boldFont ); + int topHeight = painter->fontMetrics().boundingRect( top ).height(); + + bool oneLiner = false; + if ( bottom.isEmpty() ) + oneLiner = true; + else + oneLiner = ( textRect.height() < topHeight + bottomHeight ); + + if ( oneLiner ) + { + to.setAlignment( Qt::AlignHCenter | Qt::AlignVCenter ); + text = painter->fontMetrics().elidedText( top, Qt::ElideRight, textRect.width() - 3 ); + painter->drawText( textRect, text, to ); + } + else + { + to.setAlignment( Qt::AlignHCenter | Qt::AlignTop ); + text = painter->fontMetrics().elidedText( top, Qt::ElideRight, textRect.width() - 3 ); + painter->drawText( textRect, text, to ); + + painter->setFont( font ); + // 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.1 ); + painter->setPen( opt.palette.color( QPalette::HighlightedText ) ); + } + + to.setAlignment( Qt::AlignHCenter | Qt::AlignBottom ); + text = painter->fontMetrics().elidedText( bottom, Qt::ElideRight, textRect.width() - 10 ); + painter->drawText( textRect.adjusted( 5, -1, -5, -1 ), text, to ); + + // Calculate rect of artist on-hover button click area + m_artistNameRects[ index ] = r; + } + + painter->restore(); +} + + +void +GridItemDelegate::onPlayClicked( const QPersistentModelIndex& index ) +{ + QPoint pos = m_playButton[ index ]->pos(); + foreach ( ImageButton* button, m_playButton ) + button->deleteLater(); + m_playButton.clear(); + + AnimatedSpinner* spinner = new AnimatedSpinner( m_view ); + spinner->setAutoCenter( false ); + spinner->fadeIn(); + spinner->move( pos ); + spinner->setFocusPolicy( Qt::NoFocus ); + spinner->installEventFilter( this ); + + m_spinner[ index ] = spinner; + + PlayableItem* item = m_model->sourceModel()->itemFromIndex( m_model->mapToSource( index ) ); + if ( item ) + { + _detail::Closure* closure; + + closure = NewClosure( AudioEngine::instance(), SIGNAL( loading( Tomahawk::result_ptr ) ), + const_cast(this), SLOT( onPlaybackStarted( QPersistentModelIndex ) ), QPersistentModelIndex( index ) ); + + closure = NewClosure( AudioEngine::instance(), SIGNAL( started( Tomahawk::result_ptr ) ), + const_cast(this), SLOT( onPlaylistChanged( QPersistentModelIndex ) ), QPersistentModelIndex( index ) ); + closure->setAutoDelete( false ); + + connect( AudioEngine::instance(), SIGNAL( stopped() ), SLOT( onPlaybackFinished() ) ); + + if ( !item->query().isNull() ) + AudioEngine::instance()->playItem( Tomahawk::playlistinterface_ptr(), item->query() ); + else if ( !item->album().isNull() ) + AudioEngine::instance()->playItem( item->album() ); + else if ( !item->artist().isNull() ) + AudioEngine::instance()->playItem( item->artist() ); + } +} + + +bool +GridItemDelegate::editorEvent( QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option, const QModelIndex& index ) +{ + Q_UNUSED( model ); + Q_UNUSED( option ); + + if ( event->type() != QEvent::MouseButtonRelease && + event->type() != QEvent::MouseMove && + event->type() != QEvent::MouseButtonPress && + event->type() != QEvent::Leave ) + return false; + + bool hoveringArtist = false; + if ( m_artistNameRects.contains( index ) ) + { + const QRect artistNameRect = m_artistNameRects[ index ]; + const QMouseEvent* ev = static_cast< QMouseEvent* >( event ); + hoveringArtist = artistNameRect.contains( ev->pos() ); + } + + if ( event->type() == QEvent::MouseMove ) + { + if ( hoveringArtist ) + m_view->setCursor( Qt::PointingHandCursor ); + else + m_view->setCursor( Qt::ArrowCursor ); + + foreach ( const QModelIndex& idx, m_playButton.keys() ) + { + if ( index != idx ) + m_playButton.take( idx )->deleteLater(); + } + + if ( !m_playButton.contains( index ) && !m_spinner.contains( index ) && !m_pauseButton.contains( index ) ) + { + foreach ( ImageButton* button, m_playButton ) + button->deleteLater(); + m_playButton.clear(); + + ImageButton* button = new ImageButton( m_view ); + button->setPixmap( RESPATH "images/play-rest.png" ); + button->setPixmap( RESPATH "images/play-pressed.png", QIcon::Off, QIcon::Active ); + button->setFixedSize( 48, 48 ); + button->move( option.rect.center() - QPoint( 23, 23 ) ); + button->setContentsMargins( 0, 0, 0, 0 ); + button->setFocusPolicy( Qt::NoFocus ); + button->installEventFilter( this ); + button->show(); + + NewClosure( button, SIGNAL( clicked( bool ) ), + const_cast(this), SLOT( onPlayClicked( QPersistentModelIndex ) ), QPersistentModelIndex( index ) ); + + m_playButton[ index ] = button; + } + + if ( m_hoveringOver != index || ( !hoveringArtist && m_hoveringOver.isValid() ) ) + { + emit updateIndex( m_hoveringOver ); + + if ( hoveringArtist ) + m_hoveringOver = index; + else + m_hoveringOver = QPersistentModelIndex(); + + emit updateIndex( index ); + } + + if ( m_hoverIndex != index ) + { + if ( m_hoverIndex.isValid() ) + { + QTimeLine* fadeOut = createTimeline( QTimeLine::Forward ); + _detail::Closure* c = NewClosure( fadeOut, SIGNAL( frameChanged( int ) ), this, SLOT( fadingFrameChanged( QPersistentModelIndex ) ), QPersistentModelIndex( m_hoverIndex ) ); + c->setAutoDelete( false ); + c = NewClosure( fadeOut, SIGNAL( finished() ), this, SLOT( fadingFrameFinished( QPersistentModelIndex ) ), QPersistentModelIndex( m_hoverIndex ) ); + c->setAutoDelete( false ); + m_hoverFaders[ m_hoverIndex ] = fadeOut; + fadeOut->start(); + } + + emit updateIndex( m_hoverIndex ); + m_hoverIndex = index; + + QTimeLine* fadeIn = createTimeline( QTimeLine::Backward ); + _detail::Closure* c = NewClosure( fadeIn, SIGNAL( frameChanged( int ) ), this, SLOT( fadingFrameChanged( QPersistentModelIndex ) ), QPersistentModelIndex( index ) ); + c->setAutoDelete( false ); + c = NewClosure( fadeIn, SIGNAL( finished() ), this, SLOT( fadingFrameFinished( QPersistentModelIndex ) ), QPersistentModelIndex( index ) ); + c->setAutoDelete( false ); + + m_hoverFaders[ index ] = fadeIn; + fadeIn->start(); + + emit updateIndex( index ); + } + + event->accept(); + return true; + } + + // reset mouse cursor. we switch to a pointing hand cursor when hovering an artist name + m_view->setCursor( Qt::ArrowCursor ); + + if ( hoveringArtist ) + { + + if ( event->type() == QEvent::MouseButtonRelease ) + { + PlayableItem* item = m_model->sourceModel()->itemFromIndex( m_model->mapToSource( index ) ); + if ( !item ) + return false; + + if ( !item->query().isNull() ) + ViewManager::instance()->show( Tomahawk::Artist::get( item->query()->artist() ) ); + else if ( !item->album().isNull() && !item->album()->artist().isNull() ) + 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; + } + } + + return false; +} + + +void +GridItemDelegate::modelChanged() +{ + m_artistNameRects.clear(); + m_hoveringOver = QPersistentModelIndex(); + m_hoverIndex = QPersistentModelIndex(); + + foreach ( ImageButton* button, m_playButton ) + button->deleteLater(); + m_playButton.clear(); + foreach ( ImageButton* button, m_pauseButton ) + button->deleteLater(); + m_pauseButton.clear(); + foreach ( QWidget* widget, m_spinner ) + widget->deleteLater(); + m_spinner.clear(); + + if ( GridView* view = qobject_cast< GridView* >( m_view ) ) + m_model = view->proxyModel(); +} + + +void +GridItemDelegate::doUpdateIndex( const QPersistentModelIndex& idx ) +{ + if ( !idx.isValid() ) + return; + emit updateIndex( idx ); +} + + +void +GridItemDelegate::onScrolled( int dx, int dy ) +{ + foreach ( QWidget* widget, m_spinner.values() ) + { + widget->move( widget->pos() + QPoint( dx, dy ) ); + } + foreach ( ImageButton* button, m_playButton.values() ) + { + button->move( button->pos() + QPoint( dx, dy ) ); + } + foreach ( ImageButton* button, m_pauseButton.values() ) + { + button->move( button->pos() + QPoint( dx, dy ) ); + } +} + + +void +GridItemDelegate::onPlaybackFinished() +{ + foreach ( ImageButton* button, m_pauseButton ) + button->deleteLater(); + m_pauseButton.clear(); +} + + +void +GridItemDelegate::onPlaylistChanged( const QPersistentModelIndex& index ) +{ + PlayableItem* item = m_model->sourceModel()->itemFromIndex( m_model->mapToSource( index ) ); + if ( item ) + { + bool finished = false; + + if ( !item->query().isNull() ) + { + if ( !item->query()->numResults() || AudioEngine::instance()->currentTrack() != item->query()->results().first() ) + finished = true; + } + else if ( !item->album().isNull() ) + { + if ( AudioEngine::instance()->currentTrackPlaylist() != item->album()->playlistInterface( Tomahawk::Mixed ) ) + finished = true; + } + else if ( !item->artist().isNull() ) + { + if ( AudioEngine::instance()->currentTrackPlaylist() != item->artist()->playlistInterface( Tomahawk::Mixed ) ) + finished = true; + } + + if ( finished ) + { + if ( m_pauseButton.contains( index ) ) + { + m_pauseButton[ index ]->deleteLater(); + m_pauseButton.remove( index ); + } + } + } +} + + +void +GridItemDelegate::onPlaybackStarted( const QPersistentModelIndex& index ) +{ + if ( !m_spinner.contains( index ) ) + return; + + QPoint pos = m_spinner[ index ]->pos(); + foreach ( QWidget* widget, m_spinner.values() ) + { + delete widget; + } + m_spinner.clear(); + + ImageButton* button = new ImageButton( m_view ); + button->setPixmap( RESPATH "images/pause-rest.png" ); + button->setPixmap( RESPATH "images/pause-pressed.png", QIcon::Off, QIcon::Active ); + button->setFixedSize( 48, 48 ); + button->move( pos ); + button->setContentsMargins( 0, 0, 0, 0 ); + button->setFocusPolicy( Qt::NoFocus ); + button->installEventFilter( this ); + button->show(); + + connect( button, SIGNAL( clicked( bool ) ), AudioEngine::instance(), SLOT( playPause() ) ); + + m_pauseButton[ index ] = button; +} + + +void +GridItemDelegate::fadingFrameChanged( const QPersistentModelIndex& idx ) +{ + emit updateIndex( idx ); +} + + +void +GridItemDelegate::fadingFrameFinished( const QPersistentModelIndex& idx ) +{ + if ( m_hoverFaders.contains( idx ) ) + { + m_hoverFaders.take( idx )->deleteLater(); + emit updateIndex( idx ); + } +} + + +QTimeLine* +GridItemDelegate::createTimeline( QTimeLine::Direction direction ) +{ + QTimeLine* timeline = new QTimeLine( FADE_DURATION, this ); + timeline->setDirection( direction ); + timeline->setCurveShape( QTimeLine::LinearCurve ); + timeline->setUpdateInterval( 30 ); + timeline->setStartFrame( 0 ); + timeline->setEndFrame( 100 ); + + return timeline; +} + + +bool +GridItemDelegate::eventFilter( QObject* obj, QEvent* event ) +{ + if ( event->type() == QEvent::Wheel ) + { + QWheelEvent* we = static_cast( event ); + QWheelEvent* wheelEvent = new QWheelEvent( + we->pos(), + we->globalPos(), + we->delta(), + we->buttons(), + we->modifiers(), + we->orientation() ); + + qApp->postEvent( m_view->viewport(), wheelEvent ); + event->accept(); + return true; + } + else + return QObject::eventFilter( obj, event ); +} diff --git a/src/libtomahawk/playlist/AlbumItemDelegate.h b/src/libtomahawk/playlist/GridItemDelegate.h similarity index 60% rename from src/libtomahawk/playlist/AlbumItemDelegate.h rename to src/libtomahawk/playlist/GridItemDelegate.h index cb25c7889..d2caa9999 100644 --- a/src/libtomahawk/playlist/AlbumItemDelegate.h +++ b/src/libtomahawk/playlist/GridItemDelegate.h @@ -17,10 +17,11 @@ * along with Tomahawk. If not, see . */ -#ifndef ALBUMITEMDELEGATE_H -#define ALBUMITEMDELEGATE_H +#ifndef GRIDITEMDELEGATE_H +#define GRIDITEMDELEGATE_H #include +#include #include "DllMacro.h" @@ -29,22 +30,23 @@ namespace Tomahawk { } class QEvent; -class AlbumProxyModel; +class QTimeLine; +class PlayableProxyModel; +class ImageButton; -class DLLEXPORT AlbumItemDelegate : public QStyledItemDelegate +class DLLEXPORT GridItemDelegate : public QStyledItemDelegate { Q_OBJECT public: - AlbumItemDelegate( QAbstractItemView* parent = 0, AlbumProxyModel* proxy = 0 ); - - void whitespaceMouseEvent(); + GridItemDelegate( QAbstractItemView* parent = 0, PlayableProxyModel* proxy = 0 ); 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 ); + bool eventFilter( QObject* obj, QEvent* event ); signals: void updateIndex( const QModelIndex& idx ); @@ -53,16 +55,34 @@ private slots: void modelChanged(); void doUpdateIndex( const QPersistentModelIndex& idx ); + void onScrolled( int dx, int dy ); + void onPlaybackStarted( const QPersistentModelIndex& index ); + void onPlaybackFinished(); + + void onPlayClicked( const QPersistentModelIndex& index ); + void onPlaylistChanged( const QPersistentModelIndex& index ); + + void fadingFrameChanged( const QPersistentModelIndex& ); + void fadingFrameFinished( const QPersistentModelIndex& ); private: + QTimeLine* createTimeline( QTimeLine::Direction direction ); + QAbstractItemView* m_view; - AlbumProxyModel* m_model; + PlayableProxyModel* m_model; mutable QHash< QPersistentModelIndex, QRect > m_artistNameRects; mutable QHash< QPersistentModelIndex, QSharedPointer< Tomahawk::PixmapDelegateFader > > m_covers; + QPersistentModelIndex m_hoverIndex; QPersistentModelIndex m_hoveringOver; + mutable QRect m_playButtonRect; QPixmap m_shadowPixmap; + mutable QHash< QPersistentModelIndex, QWidget* > m_spinner; + mutable QHash< QPersistentModelIndex, ImageButton* > m_playButton; + mutable QHash< QPersistentModelIndex, ImageButton* > m_pauseButton; + + mutable QHash< QPersistentModelIndex, QTimeLine* > m_hoverFaders; }; -#endif // ALBUMITEMDELEGATE_H +#endif // GRIDITEMDELEGATE_H diff --git a/src/libtomahawk/playlist/AlbumView.cpp b/src/libtomahawk/playlist/GridView.cpp similarity index 51% rename from src/libtomahawk/playlist/AlbumView.cpp rename to src/libtomahawk/playlist/GridView.cpp index b9af8d5ff..62209e3d5 100644 --- a/src/libtomahawk/playlist/AlbumView.cpp +++ b/src/libtomahawk/playlist/GridView.cpp @@ -17,7 +17,7 @@ * along with Tomahawk. If not, see . */ -#include "AlbumView.h" +#include "GridView.h" #include #include @@ -29,57 +29,75 @@ #include "context/ContextWidget.h" #include "TomahawkSettings.h" #include "Artist.h" -#include "AlbumItem.h" -#include "AlbumItemDelegate.h" +#include "Source.h" +#include "PlayableItem.h" +#include "GridItemDelegate.h" #include "AlbumModel.h" +#include "PlayableModel.h" +#include "ContextMenu.h" #include "ViewManager.h" #include "utils/Logger.h" #include "utils/AnimatedSpinner.h" +#include "utils/TomahawkUtilsGui.h" #define SCROLL_TIMEOUT 280 using namespace Tomahawk; -AlbumView::AlbumView( QWidget* parent ) +GridView::GridView( QWidget* parent ) : QListView( parent ) , m_model( 0 ) , m_proxyModel( 0 ) , m_delegate( 0 ) - , m_loadingSpinner( new AnimatedSpinner( this ) ) + , m_loadingSpinner( new LoadingSpinner( this ) ) , m_overlay( new OverlayWidget( this ) ) + , m_contextMenu( new ContextMenu( this ) ) , m_inited( false ) { + setFrameShape( QFrame::NoFrame ); + setAttribute( Qt::WA_MacShowFocusRect, 0 ); + setDragEnabled( true ); setDropIndicatorShown( false ); setDragDropOverwriteMode( false ); setUniformItemSizes( true ); - setSpacing( 16 ); + setSpacing( 0 ); setContentsMargins( 0, 0, 0, 0 ); setMouseTracking( true ); - + setContextMenuPolicy( Qt::CustomContextMenu ); setResizeMode( Adjust ); setViewMode( IconMode ); setVerticalScrollMode( QAbstractItemView::ScrollPerPixel ); + setVerticalScrollBarPolicy( Qt::ScrollBarAlwaysOn ); + + setStyleSheet( "QListView { background-color: #323435; }" ); setAutoFitItems( true ); - setProxyModel( new AlbumProxyModel( this ) ); + setProxyModel( new PlayableProxyModel( this ) ); + +/* m_overlay->setText( tr( "After you have scanned your music collection you will find your latest album additions right here." ) ); + m_overlay->setText( tr( "This collection doesn't have any recent albums." ) );*/ connect( this, SIGNAL( doubleClicked( QModelIndex ) ), SLOT( onItemActivated( QModelIndex ) ) ); + connect( this, SIGNAL( customContextMenuRequested( QPoint ) ), SLOT( onCustomContextMenu( QPoint ) ) ); + connect( this, SIGNAL( customContextMenuRequested( QPoint ) ), SLOT( onCustomContextMenu( QPoint ) ) ); + connect( proxyModel(), SIGNAL( modelReset() ), SLOT( layoutItems() ) ); +// connect( m_contextMenu, SIGNAL( triggered( int ) ), SLOT( onMenuTriggered( int ) ) ); } -AlbumView::~AlbumView() +GridView::~GridView() { qDebug() << Q_FUNC_INFO; } void -AlbumView::setProxyModel( AlbumProxyModel* model ) +GridView::setProxyModel( PlayableProxyModel* model ) { m_proxyModel = model; - m_delegate = new AlbumItemDelegate( this, m_proxyModel ); + m_delegate = new GridItemDelegate( this, m_proxyModel ); connect( m_delegate, SIGNAL( updateIndex( QModelIndex ) ), this, SLOT( update( QModelIndex ) ) ); setItemDelegate( m_delegate ); @@ -88,7 +106,7 @@ AlbumView::setProxyModel( AlbumProxyModel* model ) void -AlbumView::setModel( QAbstractItemModel* model ) +GridView::setModel( QAbstractItemModel* model ) { Q_UNUSED( model ); qDebug() << "Explicitly use setAlbumModel instead"; @@ -97,32 +115,37 @@ AlbumView::setModel( QAbstractItemModel* model ) void -AlbumView::setAlbumModel( AlbumModel* model ) +GridView::setPlayableModel( PlayableModel* model ) { + m_inited = false; m_model = model; if ( m_proxyModel ) { - m_proxyModel->setSourceAlbumModel( m_model ); + m_proxyModel->setSourcePlayableModel( m_model ); m_proxyModel->sort( 0 ); } connect( m_proxyModel, SIGNAL( filterChanged( QString ) ), SLOT( onFilterChanged( QString ) ) ); - connect( m_model, SIGNAL( itemCountChanged( unsigned int ) ), SLOT( onItemCountChanged( unsigned int ) ) ); - connect( m_model, SIGNAL( loadingStarted() ), m_loadingSpinner, SLOT( fadeIn() ) ); - connect( m_model, SIGNAL( loadingFinished() ), m_loadingSpinner, SLOT( fadeOut() ) ); - emit modelChanged(); } void -AlbumView::currentChanged( const QModelIndex& current, const QModelIndex& previous ) +GridView::setEmptyTip( const QString& tip ) +{ + m_emptyTip = tip; + m_overlay->setText( tip ); +} + + +void +GridView::currentChanged( const QModelIndex& current, const QModelIndex& previous ) { QListView::currentChanged( current, previous ); - AlbumItem* item = m_model->itemFromIndex( m_proxyModel->mapToSource( current ) ); + PlayableItem* item = m_model->itemFromIndex( m_proxyModel->mapToSource( current ) ); if ( item ) { if ( !item->album().isNull() ) @@ -132,9 +155,9 @@ AlbumView::currentChanged( const QModelIndex& current, const QModelIndex& previo void -AlbumView::onItemActivated( const QModelIndex& index ) +GridView::onItemActivated( const QModelIndex& index ) { - AlbumItem* item = m_model->itemFromIndex( m_proxyModel->mapToSource( index ) ); + PlayableItem* item = m_model->itemFromIndex( m_proxyModel->mapToSource( index ) ); if ( item ) { // qDebug() << "Result activated:" << item->album()->tracks().first()->toString() << item->album()->tracks().first()->results().first()->url(); @@ -144,31 +167,22 @@ AlbumView::onItemActivated( const QModelIndex& index ) ViewManager::instance()->show( item->album() ); else if ( !item->artist().isNull() ) ViewManager::instance()->show( item->artist() ); - else if ( item->query()->numResults() ) - AudioEngine::instance()->playItem( playlistinterface_ptr(), item->query()->results().first() ); + else if ( !item->query().isNull() ) + AudioEngine::instance()->playItem( playlistinterface_ptr(), item->query() ); } } void -AlbumView::onItemCountChanged( unsigned int items ) +GridView::scrollContentsBy( int dx, int dy ) { - if ( items == 0 ) - { - if ( m_model->collection().isNull() || ( !m_model->collection().isNull() && m_model->collection()->source()->isLocal() ) ) - m_overlay->setText( tr( "After you have scanned your music collection you will find your latest album additions right here." ) ); - else - m_overlay->setText( tr( "This collection doesn't have any recent albums." ) ); - - m_overlay->show(); - } - else - m_overlay->hide(); + QListView::scrollContentsBy( dx, dy ); + emit scrolledContents( dx, dy ); } void -AlbumView::paintEvent( QPaintEvent* event ) +GridView::paintEvent( QPaintEvent* event ) { if ( !autoFitItems() || m_inited || !m_proxyModel->rowCount() ) QListView::paintEvent( event ); @@ -176,26 +190,37 @@ AlbumView::paintEvent( QPaintEvent* event ) void -AlbumView::resizeEvent( QResizeEvent* event ) +GridView::resizeEvent( QResizeEvent* event ) { - if ( autoFitItems() ) + QListView::resizeEvent( event ); + layoutItems(); +} + + +void +GridView::layoutItems() +{ + if ( autoFitItems() && m_model ) { #ifdef Q_WS_X11 - int scrollbar = verticalScrollBar()->isVisible() ? verticalScrollBar()->width() : 0; +// int scrollbar = verticalScrollBar()->isVisible() ? verticalScrollBar()->width() + 16 : 0; + int scrollbar = 0; verticalScrollBar()->rect().width(); #else int scrollbar = verticalScrollBar()->rect().width(); #endif - int rectWidth = contentsRect().width() - scrollbar - 16 - 3; - QSize itemSize = m_proxyModel->data( QModelIndex(), Qt::SizeHintRole ).toSize(); + int rectWidth = contentsRect().width() - scrollbar - 3; + int itemWidth = 160; +// 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 ) ); + int itemsPerRow = qMax( 1, qFloor( rectWidth / itemWidth ) ); +// int rightSpacing = rectWidth - ( itemsPerRow * ( itemSize.width() + 16 ) ); +// int newSpacing = 16 + floor( rightSpacing / ( itemsPerRow + 1 ) ); - if ( itemsPerRow < 1 ) - setSpacing( 16 ); - else - setSpacing( newSpacing ); + int remSpace = rectWidth - ( itemsPerRow * itemWidth ); + int extraSpace = remSpace / itemsPerRow; + int newItemWidth = itemWidth + extraSpace; + + m_model->setItemSize( QSize( newItemWidth, newItemWidth ) ); if ( !m_inited ) { @@ -203,13 +228,11 @@ AlbumView::resizeEvent( QResizeEvent* event ) repaint(); } } - - QListView::resizeEvent( event ); } void -AlbumView::onFilterChanged( const QString& ) +GridView::onFilterChanged( const QString& ) { if ( selectedIndexes().count() ) scrollTo( selectedIndexes().at( 0 ), QAbstractItemView::PositionAtCenter ); @@ -217,7 +240,7 @@ AlbumView::onFilterChanged( const QString& ) void -AlbumView::startDrag( Qt::DropActions supportedActions ) +GridView::startDrag( Qt::DropActions supportedActions ) { QList pindexes; QModelIndexList indexes; @@ -246,3 +269,42 @@ AlbumView::startDrag( Qt::DropActions supportedActions ) /* Qt::DropAction action = */ drag->exec( supportedActions, Qt::CopyAction ); } + + +void +GridView::onCustomContextMenu( const QPoint& pos ) +{ + m_contextMenu->clear(); + + QModelIndex idx = indexAt( pos ); + idx = idx.sibling( idx.row(), 0 ); + m_contextMenuIndex = idx; + + if ( !idx.isValid() ) + return; + + QList queries; + QList artists; + QList albums; + + foreach ( const QModelIndex& index, selectedIndexes() ) + { + if ( index.column() || selectedIndexes().contains( index.parent() ) ) + continue; + + PlayableItem* item = m_model->itemFromIndex( m_proxyModel->mapToSource( index ) ); + + if ( item && !item->query().isNull() ) + queries << item->query(); + else if ( item && !item->artist().isNull() ) + artists << item->artist(); + else if ( item && !item->album().isNull() ) + albums << item->album(); + } + + m_contextMenu->setQueries( queries ); + m_contextMenu->setArtists( artists ); + m_contextMenu->setAlbums( albums ); + + m_contextMenu->exec( viewport()->mapToGlobal( pos ) ); +} diff --git a/src/libtomahawk/playlist/AlbumView.h b/src/libtomahawk/playlist/GridView.h similarity index 69% rename from src/libtomahawk/playlist/AlbumView.h rename to src/libtomahawk/playlist/GridView.h index c7d3918b5..a69120969 100644 --- a/src/libtomahawk/playlist/AlbumView.h +++ b/src/libtomahawk/playlist/GridView.h @@ -17,42 +17,48 @@ * along with Tomahawk. If not, see . */ -#ifndef ALBUMVIEW_H -#define ALBUMVIEW_H +#ifndef GRIDVIEW_H +#define GRIDVIEW_H #include #include #include #include "ViewPage.h" -#include "AlbumProxyModel.h" +#include "PlayableProxyModel.h" #include "widgets/OverlayWidget.h" #include "DllMacro.h" -class AlbumModel; -class AnimatedSpinner; -class AlbumItemDelegate; +namespace Tomahawk +{ + class ContextMenu; +}; -class DLLEXPORT AlbumView : public QListView, public Tomahawk::ViewPage +class AnimatedSpinner; +class GridItemDelegate; +class PlayableModel; + +class DLLEXPORT GridView : public QListView, public Tomahawk::ViewPage { Q_OBJECT public: - explicit AlbumView( QWidget* parent = 0 ); - ~AlbumView(); + explicit GridView( QWidget* parent = 0 ); + ~GridView(); - void setProxyModel( AlbumProxyModel* model ); + void setProxyModel( PlayableProxyModel* model ); - AlbumModel* model() const { return m_model; } - AlbumProxyModel* proxyModel() const { return m_proxyModel; } -// PlaylistItemDelegate* delegate() { return m_delegate; } + PlayableModel* model() const { return m_model; } + PlayableProxyModel* proxyModel() const { return m_proxyModel; } bool autoFitItems() const { return m_autoFitItems; } void setAutoFitItems( bool b ) { m_autoFitItems = b; } - void setAlbumModel( AlbumModel* model ); + void setPlayableModel( PlayableModel* model ); void setModel( QAbstractItemModel* model ); + void setEmptyTip( const QString& tip ); + virtual QWidget* widget() { return this; } virtual Tomahawk::playlistinterface_ptr playlistInterface() const { return proxyModel()->playlistInterface(); } @@ -68,9 +74,11 @@ public slots: signals: void modelChanged(); + void scrolledContents( int dx, int dy ); protected: virtual void startDrag( Qt::DropActions supportedActions ); + virtual void scrollContentsBy( int dx, int dy ); void paintEvent( QPaintEvent* event ); void resizeEvent( QResizeEvent* event ); @@ -79,19 +87,26 @@ protected slots: virtual void currentChanged( const QModelIndex& current, const QModelIndex& previous ); private slots: - void onItemCountChanged( unsigned int items ); - void onFilterChanged( const QString& filter ); + void onCustomContextMenu( const QPoint& pos ); + + void layoutItems(); private: - AlbumModel* m_model; - AlbumProxyModel* m_proxyModel; - AlbumItemDelegate* m_delegate; + PlayableModel* m_model; + PlayableProxyModel* m_proxyModel; + GridItemDelegate* m_delegate; AnimatedSpinner* m_loadingSpinner; OverlayWidget* m_overlay; + QModelIndex m_contextMenuIndex; + Tomahawk::ContextMenu* m_contextMenu; + + QString m_emptyTip; bool m_inited; bool m_autoFitItems; + + QRect m_paintRect; }; -#endif // ALBUMVIEW_H +#endif // GRIDVIEW_H diff --git a/src/libtomahawk/playlist/TreeModelItem.cpp b/src/libtomahawk/playlist/PlayableItem.cpp similarity index 58% rename from src/libtomahawk/playlist/TreeModelItem.cpp rename to src/libtomahawk/playlist/PlayableItem.cpp index 0752719f8..0bf7237b6 100644 --- a/src/libtomahawk/playlist/TreeModelItem.cpp +++ b/src/libtomahawk/playlist/PlayableItem.cpp @@ -1,6 +1,6 @@ /* === This file is part of Tomahawk Player - === * - * Copyright 2010-2011, Christian Muehlhaeuser + * Copyright 2010-2012, Christian Muehlhaeuser * * Tomahawk is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -16,18 +16,19 @@ * along with Tomahawk. If not, see . */ -#include "TreeModelItem.h" +#include "PlayableItem.h" #include "utils/TomahawkUtils.h" -#include "utils/Logger.h" #include "Artist.h" #include "Album.h" #include "Query.h" +#include "Source.h" +#include "utils/Logger.h" using namespace Tomahawk; -TreeModelItem::~TreeModelItem() +PlayableItem::~PlayableItem() { // Don't use qDeleteAll here! The children will remove themselves // from the list when they get deleted and the qDeleteAll iterator @@ -35,138 +36,58 @@ TreeModelItem::~TreeModelItem() for ( int i = children.count() - 1; i >= 0; i-- ) delete children.at( i ); - if ( parent && index.isValid() ) + if ( m_parent && index.isValid() ) { - parent->children.removeAt( index.row() ); + m_parent->children.removeAt( index.row() ); } } -TreeModelItem::TreeModelItem( TreeModelItem* parent, QAbstractItemModel* model ) +PlayableItem::PlayableItem( PlayableItem* parent, QAbstractItemModel* model ) { - this->parent = parent; - this->model = model; - childCount = 0; - toberemoved = false; - fetchingMore = false; - m_isPlaying = false; - - if ( parent ) - { - parent->children.append( this ); - } + init( parent ); } -TreeModelItem::TreeModelItem( const Tomahawk::album_ptr& album, TreeModelItem* parent, int row ) +PlayableItem::PlayableItem( const Tomahawk::album_ptr& album, PlayableItem* parent, int row ) : QObject( parent ) , m_album( album ) { - this->parent = parent; - fetchingMore = false; - m_isPlaying = false; - - if ( parent ) - { - if ( row < 0 ) - { - parent->children.append( this ); - row = parent->children.count() - 1; - } - else - { - parent->children.insert( row, this ); - } - - this->model = parent->model; - } - - toberemoved = false; + init( parent, row ); connect( album.data(), SIGNAL( updated() ), SIGNAL( dataChanged() ) ); } -TreeModelItem::TreeModelItem( const Tomahawk::artist_ptr& artist, TreeModelItem* parent, int row ) +PlayableItem::PlayableItem( const Tomahawk::artist_ptr& artist, PlayableItem* parent, int row ) : QObject( parent ) , m_artist( artist ) { - this->parent = parent; - fetchingMore = false; - m_isPlaying = false; - - if ( parent ) - { - if ( row < 0 ) - { - parent->children.append( this ); - row = parent->children.count() - 1; - } - else - { - parent->children.insert( row, this ); - } - - this->model = parent->model; - } - - toberemoved = false; + init( parent, row ); connect( artist.data(), SIGNAL( updated() ), SIGNAL( dataChanged() ) ); } -TreeModelItem::TreeModelItem( const Tomahawk::result_ptr& result, TreeModelItem* parent, int row ) +PlayableItem::PlayableItem( const Tomahawk::result_ptr& result, PlayableItem* parent, int row ) : QObject( parent ) , m_result( result ) { - this->parent = parent; - fetchingMore = false; - m_isPlaying = false; - - if ( parent ) - { - if ( row < 0 ) - { - parent->children.append( this ); - row = parent->children.count() - 1; - } - else - { - parent->children.insert( row, this ); - } - - this->model = parent->model; - } - - toberemoved = false; + init( parent, row ); } -TreeModelItem::TreeModelItem( const Tomahawk::query_ptr& query, TreeModelItem* parent, int row ) +PlayableItem::PlayableItem( const Tomahawk::query_ptr& query, PlayableItem* parent, int row ) : QObject( parent ) , m_query( query ) { - this->parent = parent; - fetchingMore = false; - m_isPlaying = false; + init( parent, row ); - if ( parent ) - { - if ( row < 0 ) - { - parent->children.append( this ); - row = parent->children.count() - 1; - } - else - { - parent->children.insert( row, this ); - } + connect( query.data(), SIGNAL( socialActionsLoaded() ), + SIGNAL( dataChanged() ) ); - this->model = parent->model; - } - - toberemoved = false; + connect( query.data(), SIGNAL( updated() ), + SIGNAL( dataChanged() ) ); connect( query.data(), SIGNAL( resultsAdded( QList ) ), SLOT( onResultsChanged() ) ); @@ -179,10 +100,63 @@ TreeModelItem::TreeModelItem( const Tomahawk::query_ptr& query, TreeModelItem* p } -void -TreeModelItem::onResultsChanged() +PlayableItem::PlayableItem( const Tomahawk::plentry_ptr& entry, PlayableItem* parent, int row ) + : QObject( parent ) + , m_entry( entry ) { - if ( m_query->numResults() ) + m_query = entry->query(); + init( parent, row ); + + connect( m_query.data(), SIGNAL( socialActionsLoaded() ), + SIGNAL( dataChanged() ) ); + + connect( m_query.data(), SIGNAL( updated() ), + SIGNAL( dataChanged() ) ); + + connect( m_query.data(), SIGNAL( resultsAdded( QList ) ), + SLOT( onResultsChanged() ) ); + + connect( m_query.data(), SIGNAL( resultsRemoved( Tomahawk::result_ptr ) ), + SLOT( onResultsChanged() ) ); + + connect( m_query.data(), SIGNAL( resultsChanged() ), + SLOT( onResultsChanged() ) ); +} + + +void +PlayableItem::init( PlayableItem* parent, int row ) +{ + m_fetchingMore = false; + m_isPlaying = false; + m_parent = parent; + + if ( parent ) + { + if ( row < 0 ) + { + parent->children.append( this ); + row = parent->children.count() - 1; + } + else + { + parent->children.insert( row, this ); + } + + this->model = parent->model; + } + + if ( !m_query.isNull() ) + { + onResultsChanged(); + } +} + + +void +PlayableItem::onResultsChanged() +{ + if ( !m_query->results().isEmpty() ) m_result = m_query->results().first(); else m_result = result_ptr(); @@ -192,7 +166,7 @@ TreeModelItem::onResultsChanged() QString -TreeModelItem::name() const +PlayableItem::name() const { if ( !m_artist.isNull() ) { @@ -217,7 +191,7 @@ TreeModelItem::name() const QString -TreeModelItem::artistName() const +PlayableItem::artistName() const { if ( !m_result.isNull() ) { @@ -233,7 +207,7 @@ TreeModelItem::artistName() const QString -TreeModelItem::albumName() const +PlayableItem::albumName() const { if ( !m_result.isNull() && !m_result->album().isNull() ) { @@ -249,11 +223,11 @@ TreeModelItem::albumName() const const Tomahawk::result_ptr& -TreeModelItem::result() const +PlayableItem::result() const { if ( m_result.isNull() && !m_query.isNull() ) { - if ( m_query->results().count() ) + if ( m_query->numResults() ) return m_query->results().first(); } diff --git a/src/libtomahawk/playlist/TreeModelItem.h b/src/libtomahawk/playlist/PlayableItem.h similarity index 56% rename from src/libtomahawk/playlist/TreeModelItem.h rename to src/libtomahawk/playlist/PlayableItem.h index eec43e07d..2c37f8cc5 100644 --- a/src/libtomahawk/playlist/TreeModelItem.h +++ b/src/libtomahawk/playlist/PlayableItem.h @@ -1,6 +1,6 @@ /* === This file is part of Tomahawk Player - === * - * Copyright 2010-2011, Christian Muehlhaeuser + * Copyright 2010-2012, Christian Muehlhaeuser * * Tomahawk is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -16,8 +16,8 @@ * along with Tomahawk. If not, see . */ -#ifndef TREEMODELITEM_H -#define TREEMODELITEM_H +#ifndef PLAYABLEITEM_H +#define PLAYABLEITEM_H #include #include @@ -27,41 +27,42 @@ #include "Typedefs.h" #include "DllMacro.h" -class DLLEXPORT TreeModelItem : public QObject +class DLLEXPORT PlayableItem : public QObject { Q_OBJECT public: - ~TreeModelItem(); + ~PlayableItem(); - explicit TreeModelItem( TreeModelItem* parent = 0, QAbstractItemModel* model = 0 ); - explicit TreeModelItem( const Tomahawk::artist_ptr& artist, TreeModelItem* parent = 0, int row = -1 ); - explicit TreeModelItem( const Tomahawk::album_ptr& album, TreeModelItem* parent = 0, int row = -1 ); - explicit TreeModelItem( const Tomahawk::result_ptr& result, TreeModelItem* parent = 0, int row = -1 ); - explicit TreeModelItem( const Tomahawk::query_ptr& query, TreeModelItem* parent = 0, int row = -1 ); + explicit PlayableItem( PlayableItem* parent = 0, QAbstractItemModel* model = 0 ); + explicit PlayableItem( const Tomahawk::artist_ptr& artist, PlayableItem* parent = 0, int row = -1 ); + explicit PlayableItem( const Tomahawk::album_ptr& album, PlayableItem* parent = 0, int row = -1 ); + explicit PlayableItem( const Tomahawk::result_ptr& result, PlayableItem* parent = 0, int row = -1 ); + explicit PlayableItem( const Tomahawk::query_ptr& query, PlayableItem* parent = 0, int row = -1 ); + explicit PlayableItem( const Tomahawk::plentry_ptr& entry, PlayableItem* parent = 0, int row = -1 ); - const Tomahawk::artist_ptr& artist() const { return m_artist; }; - const Tomahawk::album_ptr& album() const { return m_album; }; - const Tomahawk::query_ptr& query() const { return m_query; }; + const Tomahawk::artist_ptr& artist() const { return m_artist; } + const Tomahawk::album_ptr& album() const { return m_album; } + const Tomahawk::query_ptr& query() const { return m_query; } + const Tomahawk::plentry_ptr& entry() const { return m_entry; } const Tomahawk::result_ptr& result() const; - bool isPlaying() { return m_isPlaying; } + PlayableItem* parent() const { return m_parent; } + + bool isPlaying() const { return m_isPlaying; } void setIsPlaying( bool b ) { m_isPlaying = b; emit dataChanged(); } + bool fetchingMore() const { return m_fetchingMore; } + void setFetchingMore( bool b ) { m_fetchingMore = b; } QString name() const; QString artistName() const; QString albumName() const; - TreeModelItem* parent; - QList children; - QHash hash; - int childCount; + QList children; + QPersistentModelIndex index; QAbstractItemModel* model; - bool toberemoved; - bool fetchingMore; - signals: void dataChanged(); @@ -69,12 +70,17 @@ private slots: void onResultsChanged(); private: + void init( PlayableItem* parent, int row = -1 ); + Tomahawk::artist_ptr m_artist; Tomahawk::album_ptr m_album; Tomahawk::result_ptr m_result; Tomahawk::query_ptr m_query; + Tomahawk::plentry_ptr m_entry; + PlayableItem* m_parent; + bool m_fetchingMore; bool m_isPlaying; }; -#endif // TREEMODELITEM_H +#endif // PLAYABLEITEM_H diff --git a/src/libtomahawk/playlist/PlayableModel.cpp b/src/libtomahawk/playlist/PlayableModel.cpp new file mode 100644 index 000000000..d068fde54 --- /dev/null +++ b/src/libtomahawk/playlist/PlayableModel.cpp @@ -0,0 +1,911 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2010-2011, Christian Muehlhaeuser + * Copyright 2011 Leo Franchi + * Copyright 2010-2011, Jeff Mitchell + * + * Tomahawk is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Tomahawk is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Tomahawk. If not, see . + */ + +#include "PlayableModel.h" + +#include +#include +#include + +#include "audio/AudioEngine.h" +#include "utils/TomahawkUtils.h" +#include "Source.h" + +#include "Artist.h" +#include "Album.h" +#include "Pipeline.h" +#include "PlayableItem.h" +#include "utils/Logger.h" + +using namespace Tomahawk; + + +PlayableModel::PlayableModel( QObject* parent, bool loading ) + : QAbstractItemModel( parent ) + , m_rootItem( new PlayableItem( 0, this ) ) + , m_readOnly( true ) + , m_style( Detailed ) + , m_loading( loading ) +{ + connect( AudioEngine::instance(), SIGNAL( started( Tomahawk::result_ptr ) ), SLOT( onPlaybackStarted( Tomahawk::result_ptr ) ), Qt::DirectConnection ); + connect( AudioEngine::instance(), SIGNAL( stopped() ), SLOT( onPlaybackStopped() ), Qt::DirectConnection ); + + m_header << tr( "Artist" ) << tr( "Title" ) << tr( "Composer" ) << tr( "Album" ) << tr( "Track" ) << tr( "Duration" ) + << tr( "Bitrate" ) << tr( "Age" ) << tr( "Year" ) << tr( "Size" ) << tr( "Origin" ) << tr( "Score" ) << tr( "Name" ); + + m_headerStyle[ Detailed ] << Artist << Track << Composer << Album << AlbumPos << Duration << Bitrate << Age << Year << Filesize << Origin << Score; + m_headerStyle[ Collection ] << Name << Composer << Duration << Bitrate << Age << Year << Filesize << Origin; +} + + +PlayableModel::~PlayableModel() +{ +} + + +QModelIndex +PlayableModel::index( int row, int column, const QModelIndex& parent ) const +{ + if ( !m_rootItem || row < 0 || column < 0 ) + return QModelIndex(); + + PlayableItem* parentItem = itemFromIndex( parent ); + PlayableItem* childItem = parentItem->children.value( row ); + if ( !childItem ) + return QModelIndex(); + + return createIndex( row, column, childItem ); +} + + +int +PlayableModel::rowCount( const QModelIndex& parent ) const +{ + if ( parent.column() > 0 ) + return 0; + + PlayableItem* parentItem = itemFromIndex( parent ); + if ( !parentItem ) + return 0; + + return parentItem->children.count(); +} + + +int +PlayableModel::columnCount( const QModelIndex& parent ) const +{ + Q_UNUSED( parent ); + + switch ( m_style ) + { + case Short: + case ShortWithAvatars: + case Large: + return 1; + break; + + case Collection: + return 8; + break; + + case Detailed: + default: + return 12; + break; + } +} + + +bool +PlayableModel::hasChildren( const QModelIndex& parent ) const +{ + PlayableItem* parentItem = itemFromIndex( parent ); + if ( !parentItem ) + return false; + + if ( parentItem == m_rootItem ) + return true; + + return ( !parentItem->artist().isNull() || !parentItem->album().isNull() ); +} + + +QModelIndex +PlayableModel::parent( const QModelIndex& child ) const +{ + PlayableItem* entry = itemFromIndex( child ); + if ( !entry ) + return QModelIndex(); + + PlayableItem* parentEntry = entry->parent(); + if ( !parentEntry ) + return QModelIndex(); + + PlayableItem* grandparentEntry = parentEntry->parent(); + if ( !grandparentEntry ) + return QModelIndex(); + + int row = grandparentEntry->children.indexOf( parentEntry ); + return createIndex( row, 0, parentEntry ); +} + + +QVariant +PlayableModel::artistData( const artist_ptr& artist, int role ) const +{ + if ( role == Qt::SizeHintRole ) + return QSize( 0, 44 ); + + if ( role != Qt::DisplayRole ) // && role != Qt::ToolTipRole ) + return QVariant(); + + return artist->name(); +} + + +QVariant +PlayableModel::albumData( const album_ptr& album, int role ) const +{ + if ( role == Qt::SizeHintRole ) + return QSize( 0, 32 ); + + if ( role != Qt::DisplayRole ) // && role != Qt::ToolTipRole ) + return QVariant(); + + return album->name(); +} + + +QVariant +PlayableModel::queryData( const query_ptr& query, int column, int role ) const +{ + if ( role == Qt::SizeHintRole ) + return QSize( 0, 18 ); + + if ( role != Qt::DisplayRole ) // && role != Qt::ToolTipRole ) + return QVariant(); + + if ( !m_headerStyle.contains( m_style ) ) + return query->track(); + + switch( m_headerStyle[ m_style ].at( column ) ) + { + case Artist: + return query->artist(); + break; + + case Name: + case Track: + return query->track(); + break; + + case Album: + return query->album(); + break; + + case Composer: + return query->composer(); + break; + + case Duration: + return TomahawkUtils::timeToString( query->duration() ); + break; + + case AlbumPos: + { + QString tPos; + if ( query->albumpos() != 0 ) + { + tPos = QString::number( query->albumpos() ); + if( query->discnumber() == 0 ) + return tPos; + else + return QString( "%1.%2" ).arg( QString::number( query->discnumber() ) ) + .arg( tPos ); + } + } + break; + + default: + break; + } + if ( query->numResults() ) + { + switch( m_headerStyle[ m_style ].at( column ) ) + { + case Bitrate: + if ( query->results().first()->bitrate() > 0 ) + return query->results().first()->bitrate(); + break; + + case Age: + return TomahawkUtils::ageToString( QDateTime::fromTime_t( query->results().first()->modificationTime() ) ); + break; + + case Year: + if ( query->results().first()->year() != 0 ) + return query->results().first()->year(); + break; + + case Filesize: + return TomahawkUtils::filesizeToString( query->results().first()->size() ); + break; + + case Origin: + return query->results().first()->friendlySource(); + break; + + case Score: + return query->results().first()->score(); + break; + + default: + break; + } + } + + return QVariant(); +} + + +QVariant +PlayableModel::data( const QModelIndex& index, int role ) const +{ + PlayableItem* entry = itemFromIndex( index ); + if ( !entry ) + return QVariant(); + + if ( role == Qt::DecorationRole ) + { + return QVariant(); + } + + if ( role == Qt::TextAlignmentRole ) + { + return QVariant( columnAlignment( index.column() ) ); + } + + if ( role == StyleRole ) + { + return m_style; + } + + if ( role == Qt::SizeHintRole && !m_itemSize.isEmpty() ) + { + return m_itemSize; + } + + if ( !entry->query().isNull() ) + { + return queryData( entry->query()->displayQuery(), index.column(), role ); + } + else if ( !entry->artist().isNull() ) + { + return artistData( entry->artist(), role ); + } + else if ( !entry->album().isNull() ) + { + return albumData( entry->album(), role ); + } + + return QVariant(); +} + + +QVariant +PlayableModel::headerData( int section, Qt::Orientation orientation, int role ) const +{ + Q_UNUSED( orientation ); + + if ( role == Qt::DisplayRole && section >= 0 ) + { + if ( m_headerStyle.contains( m_style ) ) + { + return m_header.at( m_headerStyle[ m_style ].at( section ) ); + } + else + return tr( "Name" ); + } + + if ( role == Qt::TextAlignmentRole ) + { + return QVariant( columnAlignment( section ) ); + } + + return QVariant(); +} + + +void +PlayableModel::updateDetailedInfo( const QModelIndex& index ) +{ + if ( style() != PlayableModel::Short && style() != PlayableModel::Large ) + return; + + PlayableItem* item = itemFromIndex( index ); + if ( item->query().isNull() ) + return; + + if ( style() == PlayableModel::Short || style() == PlayableModel::Large ) + { + item->query()->cover( QSize( 0, 0 ) ); + } + + if ( style() == PlayableModel::Large ) + { + item->query()->loadSocialActions(); + } +} + + +void +PlayableModel::setCurrentItem( const QModelIndex& index ) +{ + PlayableItem* oldEntry = itemFromIndex( m_currentIndex ); + if ( oldEntry ) + { + oldEntry->setIsPlaying( false ); + } + + PlayableItem* entry = itemFromIndex( index ); + if ( index.isValid() && entry && !entry->query().isNull() ) + { + m_currentIndex = index; + m_currentUuid = entry->query()->id(); + entry->setIsPlaying( true ); + } + else + { + m_currentIndex = QModelIndex(); + m_currentUuid = QString(); + } +} + + +Qt::DropActions +PlayableModel::supportedDropActions() const +{ + return Qt::CopyAction | Qt::MoveAction; +} + + +Qt::ItemFlags +PlayableModel::flags( const QModelIndex& index ) const +{ + Qt::ItemFlags defaultFlags = QAbstractItemModel::flags( index ); + + if ( index.isValid() && index.column() == 0 ) + return Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled | defaultFlags; + else + return Qt::ItemIsDropEnabled | defaultFlags; +} + + +QStringList +PlayableModel::mimeTypes() const +{ + QStringList types; + types << "application/tomahawk.mixed"; + return types; +} + + +QMimeData* +PlayableModel::mimeData( const QModelIndexList &indexes ) const +{ + qDebug() << Q_FUNC_INFO; + + QByteArray resultData; + QDataStream resultStream( &resultData, QIODevice::WriteOnly ); + + // lets try with artist only + bool fail = false; + foreach ( const QModelIndex& i, indexes) + { + if ( i.column() > 0 || indexes.contains( i.parent() ) ) + continue; + + PlayableItem* item = itemFromIndex( i ); + if ( !item ) + continue; + + if ( !item->artist().isNull() ) + { + const artist_ptr& artist = item->artist(); + resultStream << artist->name(); + } + else + { + fail = true; + break; + } + } + if ( !fail ) + { + QMimeData* mimeData = new QMimeData(); + mimeData->setData( "application/tomahawk.metadata.artist", resultData ); + return mimeData; + } + + // lets try with album only + fail = false; + resultData.clear(); + foreach ( const QModelIndex& i, indexes ) + { + if ( i.column() > 0 || indexes.contains( i.parent() ) ) + continue; + + PlayableItem* item = itemFromIndex( i ); + if ( !item ) + continue; + + if ( !item->album().isNull() ) + { + const album_ptr& album = item->album(); + resultStream << album->artist()->name(); + resultStream << album->name(); + } + else + { + fail = true; + break; + } + } + if ( !fail ) + { + QMimeData* mimeData = new QMimeData(); + mimeData->setData( "application/tomahawk.metadata.album", resultData ); + return mimeData; + } + + // lets try with tracks only + fail = false; + resultData.clear(); + foreach ( const QModelIndex& i, indexes ) + { + if ( i.column() > 0 || indexes.contains( i.parent() ) ) + continue; + + PlayableItem* item = itemFromIndex( i ); + if ( !item ) + continue; + + if ( !item->result().isNull() ) + { + const result_ptr& result = item->result(); + resultStream << qlonglong( &result ); + } + else + { + fail = true; + break; + } + } + if ( !fail ) + { + QMimeData* mimeData = new QMimeData(); + mimeData->setData( "application/tomahawk.result.list", resultData ); + return mimeData; + } + + // Ok... we have to use mixed + resultData.clear(); + foreach ( const QModelIndex& i, indexes ) + { + if ( i.column() > 0 || indexes.contains( i.parent() ) ) + continue; + + PlayableItem* item = itemFromIndex( i ); + if ( !item ) + continue; + + if ( !item->artist().isNull() ) + { + const artist_ptr& artist = item->artist(); + resultStream << QString( "application/tomahawk.metadata.artist" ) << artist->name(); + } + else if ( !item->album().isNull() ) + { + const album_ptr& album = item->album(); + resultStream << QString( "application/tomahawk.metadata.album" ) << album->artist()->name() << album->name(); + } + else if ( !item->result().isNull() ) + { + const result_ptr& result = item->result(); + resultStream << QString( "application/tomahawk.result.list" ) << qlonglong( &result ); + } + else if ( !item->result().isNull() ) + { + const query_ptr& query = item->query(); + resultStream << QString( "application/tomahawk.query.list" ) << qlonglong( &query ); + } + } + + QMimeData* mimeData = new QMimeData(); + mimeData->setData( "application/tomahawk.mixed", resultData ); + return mimeData; +} + + +void +PlayableModel::clear() +{ + if ( rowCount( QModelIndex() ) ) + { + finishLoading(); + + emit beginResetModel(); + delete m_rootItem; + m_rootItem = 0; + m_rootItem = new PlayableItem( 0, this ); + emit endResetModel(); + } +} + + +QList< query_ptr > +PlayableModel::queries() const +{ + Q_ASSERT( m_rootItem ); + + QList< query_ptr > tracks; + foreach ( PlayableItem* item, m_rootItem->children ) + { + tracks << item->query(); + } + + return tracks; +} + + +template +void +PlayableModel::insertInternal( const T& item, int row ) +{ + if ( item.isNull() ) + return; + + QList< T > list; + list << item; + + insert( list, row ); +} + + +template +void +PlayableModel::insertInternal( const QList< T >& items, int row ) +{ + if ( !items.count() ) + { + emit trackCountChanged( rowCount( QModelIndex() ) ); + emit itemCountChanged( rowCount( QModelIndex() ) ); + + finishLoading(); + return; + } + + int c = row; + QPair< int, int > crows; + crows.first = c; + crows.second = c + items.count() - 1; + + emit beginInsertRows( QModelIndex(), crows.first, crows.second ); + + int i = 0; + PlayableItem* plitem; + foreach( const T& item, items ) + { + plitem = new PlayableItem( item, m_rootItem, row + i ); + plitem->index = createIndex( row + i, 0, plitem ); + i++; + +/* if ( item->id() == currentItemUuid() ) + setCurrentItem( plitem->index );*/ + + connect( plitem, SIGNAL( dataChanged() ), SLOT( onDataChanged() ) ); + } + + emit endInsertRows(); + emit trackCountChanged( rowCount( QModelIndex() ) ); + finishLoading(); +} + + +void +PlayableModel::remove( int row, bool moreToCome ) +{ + remove( index( row, 0, QModelIndex() ), moreToCome ); +} + + +void +PlayableModel::remove( const QModelIndex& index, bool moreToCome ) +{ + if ( QThread::currentThread() != thread() ) + { + QMetaObject::invokeMethod( this, "remove", + Qt::QueuedConnection, + Q_ARG(const QModelIndex, index), + Q_ARG(bool, moreToCome) ); + return; + } + + if ( index.column() > 0 ) + return; + + PlayableItem* item = itemFromIndex( index ); + if ( item ) + { + emit beginRemoveRows( index.parent(), index.row(), index.row() ); + delete item; + emit endRemoveRows(); + } + + if ( !moreToCome ) + emit trackCountChanged( rowCount( QModelIndex() ) ); +} + + +void +PlayableModel::remove( const QList& indexes ) +{ + QList pil; + foreach ( const QModelIndex& idx, indexes ) + { + pil << idx; + } + + remove( pil ); +} + + +void +PlayableModel::remove( const QList& indexes ) +{ + QList finalIndexes; + foreach ( const QPersistentModelIndex index, indexes ) + { + if ( index.column() > 0 ) + continue; + finalIndexes << index; + } + + for ( int i = 0; i < finalIndexes.count(); i++ ) + { + remove( finalIndexes.at( i ), i + 1 != finalIndexes.count() ); + } +} + + +void +PlayableModel::onPlaybackStarted( const Tomahawk::result_ptr& result ) +{ + PlayableItem* oldEntry = itemFromIndex( m_currentIndex ); + if ( oldEntry && ( oldEntry->query().isNull() || !oldEntry->query()->numResults() || oldEntry->query()->results().first().data() != result.data() ) ) + { + oldEntry->setIsPlaying( false ); + } +} + + +void +PlayableModel::onPlaybackStopped() +{ + PlayableItem* oldEntry = itemFromIndex( m_currentIndex ); + if ( oldEntry ) + { + oldEntry->setIsPlaying( false ); + } +} + + +void +PlayableModel::ensureResolved() +{ + for( int i = 0; i < rowCount( QModelIndex() ); i++ ) + { + query_ptr query = itemFromIndex( index( i, 0, QModelIndex() ) )->query(); + + if ( !query->resolvingFinished() ) + Pipeline::instance()->resolve( query ); + } +} + + +void +PlayableModel::setStyle( PlayableModel::PlayableItemStyle style ) +{ + m_style = style; +} + + +QList< double > +PlayableModel::columnWeights() const +{ + QList< double > w; + + switch ( m_style ) + { + case Short: + case ShortWithAvatars: + case Large: + w << 1.0; + break; + + case Collection: + w << 0.42 << 0.12 << 0.07 << 0.07 << 0.07 << 0.07 << 0.07; // << 0.11; + break; + + case Detailed: + default: + w << 0.16 << 0.16 << 0.14 << 0.12 << 0.05 << 0.05 << 0.05 << 0.05 << 0.05 << 0.05 << 0.09; // << 0.03; + break; + } + + return w; +} + + +Qt::Alignment +PlayableModel::columnAlignment( int column ) const +{ + if ( !m_headerStyle.contains( m_style ) ) + return Qt::AlignLeft; + + switch( m_headerStyle[ m_style ].at( column ) ) + { + case Age: + case AlbumPos: + case Bitrate: + case Duration: + case Filesize: + case Year: + return Qt::AlignHCenter; + break; + + default: + return Qt::AlignLeft; + } +} + + +void +PlayableModel::onDataChanged() +{ + PlayableItem* p = (PlayableItem*)sender(); + if ( p && p->index.isValid() ) + emit dataChanged( p->index, p->index.sibling( p->index.row(), columnCount() - 1 ) ); +} + + +void +PlayableModel::startLoading() +{ + m_loading = true; + emit loadingStarted(); +} + + +void +PlayableModel::finishLoading() +{ + m_loading = false; + emit loadingFinished(); +} + + +PlayableItem* +PlayableModel::itemFromIndex( const QModelIndex& index ) const +{ + if ( index.isValid() ) + { + return static_cast( index.internalPointer() ); + } + else + { + return m_rootItem; + } +} + + +void +PlayableModel::append( const Tomahawk::artist_ptr& artist ) +{ + insert( artist, rowCount( QModelIndex() ) ); +} + + +void +PlayableModel::append( const Tomahawk::album_ptr& album ) +{ + insert( album, rowCount( QModelIndex() ) ); +} + + +void +PlayableModel::append( const Tomahawk::query_ptr& query ) +{ + insert( query, rowCount( QModelIndex() ) ); +} + + +void +PlayableModel::append( const QList< Tomahawk::artist_ptr >& artists ) +{ + insert( artists, rowCount( QModelIndex() ) ); +} + + +void +PlayableModel::append( const QList< Tomahawk::album_ptr >& albums ) +{ + insert( albums, rowCount( QModelIndex() ) ); +} + + +void +PlayableModel::append( const QList< Tomahawk::query_ptr >& queries ) +{ + insert( queries, rowCount( QModelIndex() ) ); +} + + +void +PlayableModel::insert( const Tomahawk::artist_ptr& artist, int row ) +{ + insertInternal( artist, row ); +} + + +void +PlayableModel::insert( const Tomahawk::album_ptr& album, int row ) +{ + insertInternal( album, row ); +} + + +void +PlayableModel::insert( const Tomahawk::query_ptr& query, int row ) +{ + insertInternal( query, row ); +} + + +void +PlayableModel::insert( const QList< Tomahawk::artist_ptr >& artists, int row ) +{ + insertInternal( artists, row ); +} + + +void +PlayableModel::insert( const QList< Tomahawk::album_ptr >& albums, int row ) +{ + insertInternal( albums, row ); +} + + +void +PlayableModel::insert( const QList< Tomahawk::query_ptr >& queries, int row ) +{ + insertInternal( queries, row ); +} diff --git a/src/libtomahawk/playlist/TrackModel.h b/src/libtomahawk/playlist/PlayableModel.h similarity index 59% rename from src/libtomahawk/playlist/TrackModel.h rename to src/libtomahawk/playlist/PlayableModel.h index cf85f9d50..2d436c5db 100644 --- a/src/libtomahawk/playlist/TrackModel.h +++ b/src/libtomahawk/playlist/PlayableModel.h @@ -1,6 +1,6 @@ /* === This file is part of Tomahawk Player - === * - * Copyright 2010-2011, Christian Muehlhaeuser + * Copyright 2010-2012, Christian Muehlhaeuser * Copyright 2011 Leo Franchi * Copyright 2010-2011, Jeff Mitchell * @@ -18,28 +18,30 @@ * along with Tomahawk. If not, see . */ -#ifndef TRACKMODEL_H -#define TRACKMODEL_H +#ifndef PLAYABLEMODEL_H +#define PLAYABLEMODEL_H #include +#include #include "PlaylistInterface.h" -#include "TrackModelItem.h" #include "Typedefs.h" #include "DllMacro.h" class QMetaData; -class DLLEXPORT TrackModel : public QAbstractItemModel +class PlayableItem; + +class DLLEXPORT PlayableModel : public QAbstractItemModel { Q_OBJECT public: - enum TrackItemStyle - { Detailed = 0, Short = 1, ShortWithAvatars = 2, Large = 3 }; + enum PlayableItemStyle + { Detailed = 0, Short = 1, ShortWithAvatars = 2, Large = 3, Collection = 4 }; - enum TrackModelRole + enum PlayableModelRole { StyleRole = Qt::UserRole + 1 }; enum Columns { @@ -54,33 +56,45 @@ public: Year = 8, Filesize = 9, Origin = 10, - Score = 11 + Score = 11, + Name = 12 }; - explicit TrackModel( QObject* parent = 0 ); - virtual ~TrackModel(); + explicit PlayableModel( QObject* parent = 0, bool loading = true ); + virtual ~PlayableModel(); - TrackModel::TrackItemStyle style() const { return m_style; } - void setStyle( TrackModel::TrackItemStyle style ); + PlayableModel::PlayableItemStyle style() const { return m_style; } + void setStyle( PlayableModel::PlayableItemStyle style ); virtual QModelIndex index( int row, int column, const QModelIndex& parent ) const; virtual QModelIndex parent( const QModelIndex& child ) const; virtual bool isReadOnly() const { return m_readOnly; } virtual void setReadOnly( bool b ) { m_readOnly = b; } + virtual bool isLoading() const { return m_loading; } virtual QString title() const { return m_title; } virtual void setTitle( const QString& title ) { m_title = title; } virtual QString description() const { return m_description; } virtual void setDescription( const QString& description ) { m_description = description; } + virtual QPixmap icon() const { return m_icon; } + virtual void setIcon( const QPixmap& pixmap ) { m_icon = pixmap; } virtual int trackCount() const { return rowCount( QModelIndex() ); } + virtual int itemCount() const { return rowCount( QModelIndex() ); } virtual int rowCount( const QModelIndex& parent ) const; virtual int columnCount( const QModelIndex& parent = QModelIndex() ) const; + virtual bool hasChildren( const QModelIndex& parent ) const; + + QList< double > columnWeights() const; virtual QVariant data( const QModelIndex& index, int role = Qt::DisplayRole ) const; virtual QVariant headerData( int section, Qt::Orientation orientation, int role ) const; + + virtual QVariant artistData( const Tomahawk::artist_ptr& artist, int role = Qt::DisplayRole ) const; + virtual QVariant albumData( const Tomahawk::album_ptr& album, int role = Qt::DisplayRole ) const; + virtual QVariant queryData( const Tomahawk::query_ptr&, int column, int role = Qt::DisplayRole ) const; virtual QMimeData* mimeData( const QModelIndexList& indexes ) const; virtual QStringList mimeTypes() const; @@ -90,22 +104,28 @@ public: virtual QPersistentModelIndex currentItem() { return m_currentIndex; } virtual Tomahawk::QID currentItemUuid() { return m_currentUuid; } - virtual Tomahawk::PlaylistInterface::RepeatMode repeatMode() const { return Tomahawk::PlaylistInterface::NoRepeat; } + virtual Tomahawk::PlaylistModes::RepeatMode repeatMode() const { return Tomahawk::PlaylistModes::NoRepeat; } virtual bool shuffled() const { return false; } virtual void ensureResolved(); - TrackModelItem* itemFromIndex( const QModelIndex& index ) const; + PlayableItem* itemFromIndex( const QModelIndex& index ) const; /// Returns a flat list of all tracks in this model QList< Tomahawk::query_ptr > queries() const; void updateDetailedInfo( const QModelIndex& index ); + + void setItemSize( const QSize& size ) { m_itemSize = size; } + + void startLoading(); + void finishLoading(); signals: - void repeatModeChanged( Tomahawk::PlaylistInterface::RepeatMode mode ); + void repeatModeChanged( Tomahawk::PlaylistModes::RepeatMode mode ); void shuffleModeChanged( bool enabled ); void trackCountChanged( unsigned int tracks ); + void itemCountChanged( unsigned int items ); void loadingStarted(); void loadingFinished(); @@ -116,23 +136,29 @@ public slots: virtual void clear(); virtual void append( const QList< Tomahawk::query_ptr >& queries ); + virtual void append( const QList< Tomahawk::artist_ptr >& artists ); + virtual void append( const QList< Tomahawk::album_ptr >& albums ); virtual void append( const Tomahawk::query_ptr& query ); - virtual void append( const Tomahawk::artist_ptr& artist ) { Q_UNUSED( artist ); } - virtual void append( const Tomahawk::album_ptr& album ) { Q_UNUSED( album ); } + virtual void append( const Tomahawk::artist_ptr& artist ); + virtual void append( const Tomahawk::album_ptr& album ); virtual void insert( const QList< Tomahawk::query_ptr >& queries, int row = 0 ); + virtual void insert( const QList< Tomahawk::artist_ptr >& artists, int row = 0 ); + virtual void insert( const QList< Tomahawk::album_ptr >& albums, int row = 0 ); virtual void insert( const Tomahawk::query_ptr& query, int row = 0 ); + virtual void insert( const Tomahawk::artist_ptr& artist, int row = 0 ); + virtual void insert( const Tomahawk::album_ptr& album, int row = 0 ); virtual void remove( int row, bool moreToCome = false ); virtual void remove( const QModelIndex& index, bool moreToCome = false ); virtual void remove( const QList& indexes ); virtual void remove( const QList& indexes ); - virtual void setRepeatMode( Tomahawk::PlaylistInterface::RepeatMode /*mode*/ ) {} + virtual void setRepeatMode( Tomahawk::PlaylistModes::RepeatMode /*mode*/ ) {} virtual void setShuffled( bool /*shuffled*/ ) {} protected: - TrackModelItem* rootItem() const { return m_rootItem; } + PlayableItem* rootItem() const { return m_rootItem; } private slots: void onDataChanged(); @@ -141,18 +167,29 @@ private slots: void onPlaybackStopped(); private: + template + void insertInternal( const QList< T >& items, int row ); + template + void insertInternal( const T& item, int row ); + Qt::Alignment columnAlignment( int column ) const; - TrackModelItem* m_rootItem; + PlayableItem* m_rootItem; QPersistentModelIndex m_currentIndex; Tomahawk::QID m_currentUuid; + QSize m_itemSize; bool m_readOnly; QString m_title; QString m_description; + QPixmap m_icon; - TrackItemStyle m_style; + QHash< PlayableItemStyle, QList > m_headerStyle; + QStringList m_header; + + PlayableItemStyle m_style; + bool m_loading; }; -#endif // TRACKMODEL_H +#endif // PLAYABLEMODEL_H diff --git a/src/libtomahawk/playlist/PlayableProxyModel.cpp b/src/libtomahawk/playlist/PlayableProxyModel.cpp new file mode 100644 index 000000000..783250f6c --- /dev/null +++ b/src/libtomahawk/playlist/PlayableProxyModel.cpp @@ -0,0 +1,372 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2010-2011, Christian Muehlhaeuser + * Copyright 2010-2012, Jeff Mitchell + * + * Tomahawk is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Tomahawk is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Tomahawk. If not, see . + */ + +#include "PlayableProxyModel.h" + +#include + +#include "PlayableProxyModelPlaylistInterface.h" +#include "Artist.h" +#include "Album.h" +#include "Query.h" +#include "Source.h" +#include "PlayableItem.h" +#include "utils/Logger.h" + + +PlayableProxyModel::PlayableProxyModel( QObject* parent ) + : QSortFilterProxyModel( parent ) + , m_model( 0 ) + , m_showOfflineResults( true ) +{ + setFilterCaseSensitivity( Qt::CaseInsensitive ); + setSortCaseSensitivity( Qt::CaseInsensitive ); + setDynamicSortFilter( true ); + + setSourcePlayableModel( 0 ); +} + + +bool +PlayableProxyModel::isLoading() const +{ + if ( m_model ) + { + return m_model->isLoading(); + } + + return false; +} + + +void +PlayableProxyModel::setSourceModel( QAbstractItemModel* model ) +{ + Q_UNUSED( model ); + qDebug() << "Explicitly use setSourcePlayableModel instead"; + Q_ASSERT( false ); +} + + +void +PlayableProxyModel::setSourcePlayableModel( PlayableModel* sourceModel ) +{ + if ( m_model ) + { + if ( m_model->metaObject()->indexOfSignal( "trackCountChanged(uint)" ) > -1 ) + disconnect( m_model, SIGNAL( trackCountChanged( unsigned int ) ), this, SIGNAL( sourceTrackCountChanged( unsigned int ) ) ); + + disconnect( m_model, SIGNAL( loadingStarted() ), this, SIGNAL( loadingStarted() ) ); + disconnect( m_model, SIGNAL( loadingFinished() ), this, SIGNAL( loadingFinished() ) ); + } + + m_model = sourceModel; + + if ( m_model ) + { + if ( m_model->metaObject()->indexOfSignal( "trackCountChanged(uint)" ) > -1 ) + connect( m_model, SIGNAL( trackCountChanged( unsigned int ) ), playlistInterface().data(), SIGNAL( sourceTrackCountChanged( unsigned int ) ) ); + + connect( m_model, SIGNAL( loadingStarted() ), SIGNAL( loadingStarted() ) ); + connect( m_model, SIGNAL( loadingFinished() ), SIGNAL( loadingFinished() ) ); + } + + QSortFilterProxyModel::setSourceModel( m_model ); +} + + +bool +PlayableProxyModel::filterAcceptsRow( int sourceRow, const QModelIndex& sourceParent ) const +{ + PlayableItem* pi = itemFromIndex( sourceModel()->index( sourceRow, 0, sourceParent ) ); + if ( !pi ) + return false; + + if ( pi->query() ) + { + const Tomahawk::query_ptr& q = pi->query()->displayQuery(); + if ( q.isNull() ) // uh oh? filter out invalid queries i guess + return false; + + Tomahawk::result_ptr r; + if ( q->numResults() ) + r = q->results().first(); + + if ( !m_showOfflineResults && !r.isNull() && !r->isOnline() ) + return false; + + if ( filterRegExp().isEmpty() ) + return true; + + QStringList sl = filterRegExp().pattern().split( " ", QString::SkipEmptyParts ); + foreach( QString s, sl ) + { + s = s.toLower(); + if ( !q->artist().toLower().contains( s ) && + !q->album().toLower().contains( s ) && + !q->track().toLower().contains( s ) ) + { + return false; + } + } + } + + const Tomahawk::album_ptr& al = pi->album(); + if ( al ) + { + QStringList sl = filterRegExp().pattern().split( " ", QString::SkipEmptyParts ); + + bool found = true; + foreach( const QString& s, sl ) + { + if ( !al->name().contains( s, Qt::CaseInsensitive ) && !al->artist()->name().contains( s, Qt::CaseInsensitive ) ) + { + found = false; + } + } + + return found; + } + + const Tomahawk::album_ptr& ar = pi->album(); + if ( ar ) + { + QStringList sl = filterRegExp().pattern().split( " ", QString::SkipEmptyParts ); + + bool found = true; + foreach( const QString& s, sl ) + { + if ( !ar->name().contains( s, Qt::CaseInsensitive ) && !ar->artist()->name().contains( s, Qt::CaseInsensitive ) ) + { + found = false; + } + } + + return found; + } + + return true; +} + + +void +PlayableProxyModel::remove( const QModelIndex& index ) +{ + if ( !sourceModel() ) + return; + if ( !index.isValid() ) + return; + + sourceModel()->remove( mapToSource( index ) ); +} + + +void +PlayableProxyModel::remove( const QModelIndexList& indexes ) +{ + if ( !sourceModel() ) + return; + + QList pil; + foreach ( const QModelIndex& idx, indexes ) + { + if ( idx.isValid() ) + pil << mapToSource( idx ); + } + + sourceModel()->remove( pil ); +} + + +void +PlayableProxyModel::remove( const QList< QPersistentModelIndex >& indexes ) +{ + if ( !sourceModel() ) + return; + + QList pil; + foreach ( const QPersistentModelIndex& idx, indexes ) + { + if ( idx.isValid() ) + pil << mapToSource( idx ); + } + + sourceModel()->remove( pil ); +} + + +bool +PlayableProxyModel::lessThan( int column, const Tomahawk::query_ptr& q1, const Tomahawk::query_ptr& q2 ) const +{ + const QString artist1 = q1->artistSortname(); + const QString artist2 = q2->artistSortname(); + const QString album1 = q1->albumSortname(); + const QString album2 = q2->albumSortname(); + const QString track1 = q1->trackSortname(); + const QString track2 = q2->trackSortname(); + const unsigned int albumpos1 = q1->albumpos(); + const unsigned int albumpos2 = q2->albumpos(); + const unsigned int discnumber1 = q1->discnumber(); + const unsigned int discnumber2 = q2->discnumber(); + unsigned int bitrate1 = 0, bitrate2 = 0; + unsigned int mtime1 = 0, mtime2 = 0; + unsigned int size1 = 0, size2 = 0; + float score1 = 0, score2 = 0; + qint64 id1 = 0, id2 = 0; + + if ( q1->numResults() ) + { + const Tomahawk::result_ptr& r = q1->results().at( 0 ); + bitrate1 = r->bitrate(); + mtime1 = r->modificationTime(); + size1 = r->size(); + score1 = r->score(); + id1 = (qint64)&r; + } + if ( q2->numResults() ) + { + const Tomahawk::result_ptr& r = q2->results().at( 0 ); + bitrate2 = r->bitrate(); + mtime2 = r->modificationTime(); + size2 = r->size(); + score2 = r->score(); + id2 = (qint64)&r; + } + + // This makes it a stable sorter and prevents items from randomly jumping about. + if ( id1 == id2 ) + { + id1 = (qint64)&q1; + id2 = (qint64)&q2; + } + + if ( column == PlayableModel::Artist ) // sort by artist + { + if ( artist1 == artist2 ) + { + if ( album1 == album2 ) + { + if ( discnumber1 == discnumber2 ) + { + if ( albumpos1 == albumpos2 ) + return id1 < id2; + + return albumpos1 < albumpos2; + } + + return discnumber1 < discnumber2; + } + + return QString::localeAwareCompare( album1, album2 ) < 0; + } + + return QString::localeAwareCompare( artist1, artist2 ) < 0; + } + else if ( column == PlayableModel::Album ) // sort by album + { + if ( album1 == album2 ) + { + if ( discnumber1 == discnumber2 ) + { + if ( albumpos1 == albumpos2 ) + return id1 < id2; + + return albumpos1 < albumpos2; + } + + return discnumber1 < discnumber2; + } + + return QString::localeAwareCompare( album1, album2 ) < 0; + } + else if ( column == PlayableModel::Bitrate ) // sort by bitrate + { + if ( bitrate1 == bitrate2 ) + return id1 < id2; + + return bitrate1 < bitrate2; + } + else if ( column == PlayableModel::Age ) // sort by mtime + { + if ( mtime1 == mtime2 ) + return id1 < id2; + + return mtime1 < mtime2; + } + else if ( column == PlayableModel::Filesize ) // sort by file size + { + if ( size1 == size2 ) + return id1 < id2; + + return size1 < size2; + } + else if ( column == PlayableModel::AlbumPos ) // sort by album pos + { + if ( discnumber1 != discnumber2 ) + { + return discnumber1 < discnumber2; + } + else + { + if ( albumpos1 != albumpos2 ) + return albumpos1 < albumpos2; + } + } + + const QString& lefts = q1->track(); + const QString& rights = q2->track(); + if ( lefts == rights ) + return id1 < id2; + + return QString::localeAwareCompare( lefts, rights ) < 0; +} + + +bool +PlayableProxyModel::lessThan( const QModelIndex& left, const QModelIndex& right ) const +{ + PlayableItem* p1 = itemFromIndex( left ); + PlayableItem* p2 = itemFromIndex( right ); + + if ( !p1 ) + return true; + if ( !p2 ) + return false; + + if ( p1->query() && p2->query() ) + { + const Tomahawk::query_ptr& q1 = p1->query()->displayQuery(); + const Tomahawk::query_ptr& q2 = p2->query()->displayQuery(); + return lessThan( left.column(), q1, q2 ); + } + + return QString::localeAwareCompare( sourceModel()->data( left ).toString(), sourceModel()->data( right ).toString() ) < 0; +} + + +Tomahawk::playlistinterface_ptr +PlayableProxyModel::playlistInterface() +{ + if ( m_playlistInterface.isNull() ) + { + m_playlistInterface = Tomahawk::playlistinterface_ptr( new Tomahawk::PlayableProxyModelPlaylistInterface( this ) ); + } + + return m_playlistInterface; +} diff --git a/src/libtomahawk/playlist/TrackProxyModel.h b/src/libtomahawk/playlist/PlayableProxyModel.h similarity index 74% rename from src/libtomahawk/playlist/TrackProxyModel.h rename to src/libtomahawk/playlist/PlayableProxyModel.h index 64ec60db8..2e2f2b662 100644 --- a/src/libtomahawk/playlist/TrackProxyModel.h +++ b/src/libtomahawk/playlist/PlayableProxyModel.h @@ -23,22 +23,24 @@ #include #include "PlaylistInterface.h" -#include "playlist/TrackModel.h" +#include "playlist/PlayableModel.h" #include "DllMacro.h" -class DLLEXPORT TrackProxyModel : public QSortFilterProxyModel +class DLLEXPORT PlayableProxyModel : public QSortFilterProxyModel { Q_OBJECT public: - explicit TrackProxyModel ( QObject* parent = 0 ); - virtual ~TrackProxyModel() {} + explicit PlayableProxyModel ( QObject* parent = 0 ); + virtual ~PlayableProxyModel() {} - virtual TrackModel* sourceModel() const { return m_model; } - virtual void setSourceTrackModel( TrackModel* sourceModel ); + virtual PlayableModel* sourceModel() const { return m_model; } + virtual void setSourcePlayableModel( PlayableModel* sourceModel ); virtual void setSourceModel( QAbstractItemModel* model ); + virtual bool isLoading() const; + virtual QPersistentModelIndex currentIndex() const { return mapFromSource( m_model->currentItem() ); } virtual void setCurrentIndex( const QModelIndex& index ) { m_model->setCurrentItem( mapToSource( index ) ); } @@ -51,20 +53,30 @@ public: virtual void emitFilterChanged( const QString &pattern ) { emit filterChanged( pattern ); } - virtual TrackModelItem* itemFromIndex( const QModelIndex& index ) const { return sourceModel()->itemFromIndex( index ); } + virtual PlayableItem* itemFromIndex( const QModelIndex& index ) const { return sourceModel()->itemFromIndex( index ); } virtual Tomahawk::playlistinterface_ptr playlistInterface(); signals: void filterChanged( const QString& filter ); + void filteringStarted(); + void filteringFinished(); + + void loadingStarted(); + void loadingFinished(); + protected: virtual bool filterAcceptsRow( int sourceRow, const QModelIndex& sourceParent ) const; virtual bool lessThan( const QModelIndex& left, const QModelIndex& right ) const; - TrackModel* m_model; - bool m_showOfflineResults; Tomahawk::playlistinterface_ptr m_playlistInterface; + +private: + virtual bool lessThan( int column, const Tomahawk::query_ptr& left, const Tomahawk::query_ptr& right ) const; + + PlayableModel* m_model; + bool m_showOfflineResults; }; #endif // TRACKPROXYMODEL_H diff --git a/src/libtomahawk/playlist/TrackProxyModelPlaylistInterface.cpp b/src/libtomahawk/playlist/PlayableProxyModelPlaylistInterface.cpp similarity index 72% rename from src/libtomahawk/playlist/TrackProxyModelPlaylistInterface.cpp rename to src/libtomahawk/playlist/PlayableProxyModelPlaylistInterface.cpp index 492adfb1b..96df71e03 100644 --- a/src/libtomahawk/playlist/TrackProxyModelPlaylistInterface.cpp +++ b/src/libtomahawk/playlist/PlayableProxyModelPlaylistInterface.cpp @@ -17,54 +17,57 @@ * along with Tomahawk. If not, see . */ -#include "TrackProxyModelPlaylistInterface.h" +#include "PlayableProxyModelPlaylistInterface.h" -#include "TrackProxyModel.h" +#include "PlayableProxyModel.h" #include "Artist.h" #include "Album.h" #include "Query.h" +#include "PlayableItem.h" +#include "Source.h" #include "utils/Logger.h" using namespace Tomahawk; -TrackProxyModelPlaylistInterface::TrackProxyModelPlaylistInterface( TrackProxyModel* proxyModel ) + +PlayableProxyModelPlaylistInterface::PlayableProxyModelPlaylistInterface( PlayableProxyModel* proxyModel ) : PlaylistInterface() , m_proxyModel( proxyModel ) - , m_repeatMode( PlaylistInterface::NoRepeat ) + , m_repeatMode( PlaylistModes::NoRepeat ) , m_shuffled( false ) { } -TrackProxyModelPlaylistInterface::~TrackProxyModelPlaylistInterface() +PlayableProxyModelPlaylistInterface::~PlayableProxyModelPlaylistInterface() { m_proxyModel.clear(); } int -TrackProxyModelPlaylistInterface::unfilteredTrackCount() const +PlayableProxyModelPlaylistInterface::unfilteredTrackCount() const { return ( m_proxyModel.isNull() ? 0 : m_proxyModel.data()->sourceModel()->trackCount() ); } int -TrackProxyModelPlaylistInterface::trackCount() const +PlayableProxyModelPlaylistInterface::trackCount() const { return ( m_proxyModel.isNull() ? 0 : m_proxyModel.data()->rowCount( QModelIndex() ) ); } QString -TrackProxyModelPlaylistInterface::filter() const +PlayableProxyModelPlaylistInterface::filter() const { return ( m_proxyModel.isNull() ? QString() : m_proxyModel.data()->filterRegExp().pattern() ); } void -TrackProxyModelPlaylistInterface::setFilter( const QString& pattern ) +PlayableProxyModelPlaylistInterface::setFilter( const QString& pattern ) { if ( m_proxyModel.isNull() ) return; @@ -77,17 +80,17 @@ TrackProxyModelPlaylistInterface::setFilter( const QString& pattern ) QList< Tomahawk::query_ptr > -TrackProxyModelPlaylistInterface::tracks() +PlayableProxyModelPlaylistInterface::tracks() { if ( m_proxyModel.isNull() ) return QList< Tomahawk::query_ptr >(); - TrackProxyModel* proxyModel = m_proxyModel.data(); + PlayableProxyModel* proxyModel = m_proxyModel.data(); QList queries; for ( int i = 0; i < proxyModel->rowCount( QModelIndex() ); i++ ) { - TrackModelItem* item = proxyModel->itemFromIndex( proxyModel->mapToSource( proxyModel->index( i, 0 ) ) ); + PlayableItem* item = proxyModel->itemFromIndex( proxyModel->mapToSource( proxyModel->index( i, 0 ) ) ); if ( item ) queries << item->query(); } @@ -97,28 +100,28 @@ TrackProxyModelPlaylistInterface::tracks() Tomahawk::result_ptr -TrackProxyModelPlaylistInterface::siblingItem( int itemsAway ) +PlayableProxyModelPlaylistInterface::siblingItem( int itemsAway ) { return siblingItem( itemsAway, false ); } bool -TrackProxyModelPlaylistInterface::hasNextItem() +PlayableProxyModelPlaylistInterface::hasNextItem() { return !( siblingItem( 1, true ).isNull() ); } Tomahawk::result_ptr -TrackProxyModelPlaylistInterface::siblingItem( int itemsAway, bool readOnly ) +PlayableProxyModelPlaylistInterface::siblingItem( int itemsAway, bool readOnly ) { qDebug() << Q_FUNC_INFO; if ( m_proxyModel.isNull() ) return Tomahawk::result_ptr(); - TrackProxyModel* proxyModel = m_proxyModel.data(); + PlayableProxyModel* proxyModel = m_proxyModel.data(); QModelIndex idx = proxyModel->index( 0, 0 ); if ( proxyModel->rowCount() ) @@ -134,7 +137,7 @@ TrackProxyModelPlaylistInterface::siblingItem( int itemsAway, bool readOnly ) idx = proxyModel->currentIndex(); // random mode is disabled - if ( m_repeatMode != PlaylistInterface::RepeatOne ) + if ( m_repeatMode != PlaylistModes::RepeatOne ) { // keep progressing through the playlist normally idx = proxyModel->index( idx.row() + itemsAway, 0 ); @@ -142,7 +145,7 @@ TrackProxyModelPlaylistInterface::siblingItem( int itemsAway, bool readOnly ) } } - if ( !idx.isValid() && m_repeatMode == PlaylistInterface::RepeatAll ) + if ( !idx.isValid() && m_repeatMode == PlaylistModes::RepeatAll ) { // repeat all tracks if ( itemsAway > 0 ) @@ -160,7 +163,7 @@ TrackProxyModelPlaylistInterface::siblingItem( int itemsAway, bool readOnly ) // Try to find the next available PlaylistItem (with results) while ( idx.isValid() ) { - TrackModelItem* item = proxyModel->itemFromIndex( proxyModel->mapToSource( idx ) ); + PlayableItem* item = proxyModel->itemFromIndex( proxyModel->mapToSource( idx ) ); if ( item && item->query()->playable() ) { qDebug() << "Next PlaylistItem found:" << item->query()->toString() << item->query()->results().at( 0 )->url(); @@ -179,14 +182,14 @@ TrackProxyModelPlaylistInterface::siblingItem( int itemsAway, bool readOnly ) Tomahawk::result_ptr -TrackProxyModelPlaylistInterface::currentItem() const +PlayableProxyModelPlaylistInterface::currentItem() const { if ( m_proxyModel.isNull() ) return Tomahawk::result_ptr(); - TrackProxyModel* proxyModel = m_proxyModel.data(); + PlayableProxyModel* proxyModel = m_proxyModel.data(); - TrackModelItem* item = proxyModel->itemFromIndex( proxyModel->mapToSource( proxyModel->currentIndex() ) ); + PlayableItem* item = proxyModel->itemFromIndex( proxyModel->mapToSource( proxyModel->currentIndex() ) ); if ( item && !item->query().isNull() && item->query()->playable() ) return item->query()->results().at( 0 ); return Tomahawk::result_ptr(); diff --git a/src/libtomahawk/playlist/TrackProxyModelPlaylistInterface.h b/src/libtomahawk/playlist/PlayableProxyModelPlaylistInterface.h similarity index 75% rename from src/libtomahawk/playlist/TrackProxyModelPlaylistInterface.h rename to src/libtomahawk/playlist/PlayableProxyModelPlaylistInterface.h index b899ea983..bc785aa58 100644 --- a/src/libtomahawk/playlist/TrackProxyModelPlaylistInterface.h +++ b/src/libtomahawk/playlist/PlayableProxyModelPlaylistInterface.h @@ -23,21 +23,21 @@ #include #include "PlaylistInterface.h" -#include "playlist/TrackModel.h" +#include "playlist/PlayableModel.h" #include "DllMacro.h" -class TrackProxyModel; +class PlayableProxyModel; namespace Tomahawk { -class DLLEXPORT TrackProxyModelPlaylistInterface : public Tomahawk::PlaylistInterface +class DLLEXPORT PlayableProxyModelPlaylistInterface : public Tomahawk::PlaylistInterface { Q_OBJECT public: - explicit TrackProxyModelPlaylistInterface( TrackProxyModel* proxyModel ); - virtual ~TrackProxyModelPlaylistInterface(); + explicit PlayableProxyModelPlaylistInterface( PlayableProxyModel* proxyModel ); + virtual ~PlayableProxyModelPlaylistInterface(); virtual QList tracks(); @@ -52,16 +52,16 @@ public: virtual QString filter() const; virtual void setFilter( const QString& pattern ); - virtual PlaylistInterface::RepeatMode repeatMode() const { return m_repeatMode; } + virtual PlaylistModes::RepeatMode repeatMode() const { return m_repeatMode; } virtual bool shuffled() const { return m_shuffled; } public slots: - virtual void setRepeatMode( Tomahawk::PlaylistInterface::RepeatMode mode ) { m_repeatMode = mode; emit repeatModeChanged( mode ); } + virtual void setRepeatMode( Tomahawk::PlaylistModes::RepeatMode mode ) { m_repeatMode = mode; emit repeatModeChanged( mode ); } virtual void setShuffled( bool enabled ) { m_shuffled = enabled; emit shuffleModeChanged( enabled ); } protected: - QWeakPointer< TrackProxyModel > m_proxyModel; - RepeatMode m_repeatMode; + QWeakPointer< PlayableProxyModel > m_proxyModel; + PlaylistModes::RepeatMode m_repeatMode; bool m_shuffled; }; diff --git a/src/libtomahawk/playlist/PlaylistChartItemDelegate.cpp b/src/libtomahawk/playlist/PlaylistChartItemDelegate.cpp index c01dab9e0..b36058e94 100644 --- a/src/libtomahawk/playlist/PlaylistChartItemDelegate.cpp +++ b/src/libtomahawk/playlist/PlaylistChartItemDelegate.cpp @@ -28,11 +28,11 @@ #include "SourceList.h" #include "PlaylistView.h" -#include "TrackModel.h" -#include "TrackModelItem.h" -#include "TrackProxyModel.h" +#include "PlayableModel.h" +#include "PlayableItem.h" +#include "PlayableProxyModel.h" #include "TrackView.h" -#include "TrackHeader.h" +#include "ViewHeader.h" #include "utils/TomahawkUtilsGui.h" #include "utils/Logger.h" @@ -42,7 +42,7 @@ using namespace Tomahawk; -PlaylistChartItemDelegate::PlaylistChartItemDelegate( TrackView* parent, TrackProxyModel* proxy ) +PlaylistChartItemDelegate::PlaylistChartItemDelegate( TrackView* parent, PlayableProxyModel* proxy ) : QStyledItemDelegate( (QObject*)parent ) , m_view( parent ) , m_model( proxy ) @@ -59,7 +59,7 @@ PlaylistChartItemDelegate::PlaylistChartItemDelegate( TrackView* parent, TrackPr m_bottomOption = QTextOption( Qt::AlignBottom ); m_bottomOption.setWrapMode( QTextOption::NoWrap ); - connect( m_model->sourceModel(), SIGNAL( modelReset() ), this, SLOT( modelChanged() ) ); + connect( m_model, SIGNAL( modelReset() ), this, SLOT( modelChanged() ) ); if ( PlaylistView* plView = qobject_cast< PlaylistView* >( parent ) ) connect( plView, SIGNAL( modelChanged() ), this, SLOT( modelChanged() ) ); @@ -112,7 +112,7 @@ PlaylistChartItemDelegate::createEditor( QWidget* parent, const QStyleOptionView void -PlaylistChartItemDelegate::prepareStyleOption( QStyleOptionViewItemV4* option, const QModelIndex& index, TrackModelItem* item ) const +PlaylistChartItemDelegate::prepareStyleOption( QStyleOptionViewItemV4* option, const QModelIndex& index, PlayableItem* item ) const { initStyleOption( option, index ); @@ -123,7 +123,7 @@ PlaylistChartItemDelegate::prepareStyleOption( QStyleOptionViewItemV4* option, c void PlaylistChartItemDelegate::paint( QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index ) const { - TrackModelItem* item = m_model->itemFromIndex( m_model->mapToSource( index ) ); + PlayableItem* item = m_model->itemFromIndex( m_model->mapToSource( index ) ); Q_ASSERT( item ); QStyleOptionViewItemV4 opt = option; @@ -135,21 +135,12 @@ PlaylistChartItemDelegate::paint( QPainter* painter, const QStyleOptionViewItem& if ( m_view->header()->visualIndex( index.column() ) > 0 ) return; + const query_ptr q = item->query()->displayQuery(); + unsigned int duration = q->duration(); + QString artist = q->artist(); + QString track = q->track(); QPixmap avatar; - QString artist, track, upperText, lowerText; - unsigned int duration = 0; - - if ( item->query()->results().count() ) - { - artist = item->query()->results().first()->artist()->name(); - track = item->query()->results().first()->track(); - duration = item->query()->results().first()->duration(); - } - else - { - artist = item->query()->artist(); - track = item->query()->track(); - } + QString upperText, lowerText; painter->save(); { diff --git a/src/libtomahawk/playlist/PlaylistChartItemDelegate.h b/src/libtomahawk/playlist/PlaylistChartItemDelegate.h index d5119aa0d..87017d453 100644 --- a/src/libtomahawk/playlist/PlaylistChartItemDelegate.h +++ b/src/libtomahawk/playlist/PlaylistChartItemDelegate.h @@ -31,8 +31,8 @@ class PixmapDelegateFader; } class TrackModel; -class TrackModelItem; -class TrackProxyModel; +class PlayableItem; +class PlayableProxyModel; class TrackView; class DLLEXPORT PlaylistChartItemDelegate : public QStyledItemDelegate @@ -40,7 +40,7 @@ class DLLEXPORT PlaylistChartItemDelegate : public QStyledItemDelegate Q_OBJECT public: - PlaylistChartItemDelegate( TrackView* parent = 0, TrackProxyModel* proxy = 0 ); + PlaylistChartItemDelegate( TrackView* parent = 0, PlayableProxyModel* proxy = 0 ); signals: void updateRequest( const QModelIndex& idx ); @@ -55,7 +55,7 @@ private slots: void doUpdateIndex( const QPersistentModelIndex& idx ); private: - void prepareStyleOption( QStyleOptionViewItemV4* option, const QModelIndex& index, TrackModelItem* item ) const; + void prepareStyleOption( QStyleOptionViewItemV4* option, const QModelIndex& index, PlayableItem* item ) const; QTextOption m_topOption; QTextOption m_centerOption; @@ -63,7 +63,7 @@ private: QTextOption m_bottomOption; TrackView* m_view; - TrackProxyModel* m_model; + PlayableProxyModel* m_model; mutable QHash< QPersistentModelIndex, QSharedPointer< Tomahawk::PixmapDelegateFader > > m_pixmaps; }; diff --git a/src/libtomahawk/playlist/PlaylistItemDelegate.cpp b/src/libtomahawk/playlist/PlaylistItemDelegate.cpp index d324e1dab..494b683f7 100644 --- a/src/libtomahawk/playlist/PlaylistItemDelegate.cpp +++ b/src/libtomahawk/playlist/PlaylistItemDelegate.cpp @@ -28,11 +28,11 @@ #include "Source.h" #include "SourceList.h" -#include "TrackModel.h" -#include "TrackModelItem.h" -#include "TrackProxyModel.h" +#include "PlayableModel.h" +#include "PlayableItem.h" +#include "PlayableProxyModel.h" #include "TrackView.h" -#include "TrackHeader.h" +#include "ViewHeader.h" #include "utils/TomahawkUtilsGui.h" #include "utils/Logger.h" @@ -40,7 +40,7 @@ using namespace Tomahawk; -PlaylistItemDelegate::PlaylistItemDelegate( TrackView* parent, TrackProxyModel* proxy ) +PlaylistItemDelegate::PlaylistItemDelegate( TrackView* parent, PlayableProxyModel* proxy ) : QStyledItemDelegate( (QObject*)parent ) , m_view( parent ) , m_model( proxy ) @@ -67,8 +67,8 @@ PlaylistItemDelegate::sizeHint( const QStyleOptionViewItem& option, const QModel if ( index.isValid() ) { - int style = index.data( TrackModel::StyleRole ).toInt(); - if ( style == TrackModel::Short || style == TrackModel::ShortWithAvatars ) + int style = index.data( PlayableModel::StyleRole ).toInt(); + if ( style == PlayableModel::Short || style == PlayableModel::ShortWithAvatars ) { int rowHeight = option.fontMetrics.height() + 8; size.setHeight( rowHeight * 2 ); @@ -90,7 +90,7 @@ PlaylistItemDelegate::createEditor( QWidget* parent, const QStyleOptionViewItem& void -PlaylistItemDelegate::prepareStyleOption( QStyleOptionViewItemV4* option, const QModelIndex& index, TrackModelItem* item ) const +PlaylistItemDelegate::prepareStyleOption( QStyleOptionViewItemV4* option, const QModelIndex& index, PlayableItem* item ) const { initStyleOption( option, index ); @@ -101,17 +101,17 @@ PlaylistItemDelegate::prepareStyleOption( QStyleOptionViewItemV4* option, const void PlaylistItemDelegate::paint( QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index ) const { - int style = index.data( TrackModel::StyleRole ).toInt(); + int style = index.data( PlayableModel::StyleRole ).toInt(); switch ( style ) { - case TrackModel::Detailed: + case PlayableModel::Detailed: paintDetailed( painter, option, index ); break; - case TrackModel::Short: + case PlayableModel::Short: paintShort( painter, option, index ); break; - case TrackModel::ShortWithAvatars: + case PlayableModel::ShortWithAvatars: paintShort( painter, option, index, true ); break; } @@ -121,7 +121,7 @@ PlaylistItemDelegate::paint( QPainter* painter, const QStyleOptionViewItem& opti void PlaylistItemDelegate::paintShort( QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index, bool useAvatars ) const { - TrackModelItem* item = m_model->itemFromIndex( m_model->mapToSource( index ) ); + PlayableItem* item = m_model->itemFromIndex( m_model->mapToSource( index ) ); Q_ASSERT( item ); QStyleOptionViewItemV4 opt = option; @@ -133,21 +133,13 @@ PlaylistItemDelegate::paintShort( QPainter* painter, const QStyleOptionViewItem& if ( m_view->header()->visualIndex( index.column() ) > 0 ) return; + const query_ptr q = item->query()->displayQuery(); + QString artist = q->artist(); + QString track = q->track(); QPixmap pixmap; - QString artist, track, upperText, lowerText; + QString upperText, lowerText; source_ptr source = item->query()->playedBy().first; - if ( item->query()->results().count() ) - { - artist = item->query()->results().first()->artist()->name(); - track = item->query()->results().first()->track(); - } - else - { - artist = item->query()->artist(); - track = item->query()->track(); - } - if ( source.isNull() ) { upperText = track; @@ -220,7 +212,7 @@ PlaylistItemDelegate::paintShort( QPainter* painter, const QStyleOptionViewItem& void PlaylistItemDelegate::paintDetailed( QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index ) const { - TrackModelItem* item = m_model->itemFromIndex( m_model->mapToSource( index ) ); + PlayableItem* item = m_model->itemFromIndex( m_model->mapToSource( index ) ); Q_ASSERT( item ); QTextOption textOption( Qt::AlignVCenter | (Qt::Alignment)index.data( Qt::TextAlignmentRole ).toUInt() ); @@ -232,7 +224,7 @@ PlaylistItemDelegate::paintDetailed( QPainter* painter, const QStyleOptionViewIt qApp->style()->drawControl( QStyle::CE_ItemViewItem, &opt, painter ); if ( m_view->hoveredIndex().row() == index.row() && m_view->hoveredIndex().column() == index.column() && - ( index.column() == TrackModel::Artist || index.column() == TrackModel::Album || index.column() == TrackModel::Track ) ) + ( index.column() == PlayableModel::Artist || index.column() == PlayableModel::Album || index.column() == PlayableModel::Track ) ) { opt.rect.setWidth( opt.rect.width() - 16 ); QRect arrowRect( opt.rect.x() + opt.rect.width(), opt.rect.y() + 1, opt.rect.height() - 2, opt.rect.height() - 2 ); @@ -243,7 +235,7 @@ PlaylistItemDelegate::paintDetailed( QPainter* painter, const QStyleOptionViewIt painter->save(); - if ( index.column() == TrackModel::Score ) + if ( index.column() == PlayableModel::Score ) { QColor barColor( 167, 183, 211 ); // This matches the sidebar (sourcetreeview.cpp:672) if ( opt.state & QStyle::State_Selected ) diff --git a/src/libtomahawk/playlist/PlaylistItemDelegate.h b/src/libtomahawk/playlist/PlaylistItemDelegate.h index 8c270fa41..244a726d2 100644 --- a/src/libtomahawk/playlist/PlaylistItemDelegate.h +++ b/src/libtomahawk/playlist/PlaylistItemDelegate.h @@ -25,8 +25,8 @@ #include "DllMacro.h" class TrackModel; -class TrackModelItem; -class TrackProxyModel; +class PlayableItem; +class PlayableProxyModel; class TrackView; class DLLEXPORT PlaylistItemDelegate : public QStyledItemDelegate @@ -34,7 +34,7 @@ class DLLEXPORT PlaylistItemDelegate : public QStyledItemDelegate Q_OBJECT public: - PlaylistItemDelegate( TrackView* parent = 0, TrackProxyModel* proxy = 0 ); + PlaylistItemDelegate( TrackView* parent = 0, PlayableProxyModel* proxy = 0 ); void updateRowSize( const QModelIndex& index ); @@ -44,7 +44,7 @@ protected: QWidget* createEditor( QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index ) const; private: - void prepareStyleOption( QStyleOptionViewItemV4* option, const QModelIndex& index, TrackModelItem* item ) const; + void prepareStyleOption( QStyleOptionViewItemV4* option, const QModelIndex& index, PlayableItem* item ) const; void paintDetailed( QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index ) const; void paintShort( QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index, bool useAvatars = false ) const; @@ -53,7 +53,7 @@ private: QTextOption m_bottomOption; TrackView* m_view; - TrackProxyModel* m_model; + PlayableProxyModel* m_model; }; #endif // PLAYLISTITEMDELEGATE_H diff --git a/src/libtomahawk/playlist/PlaylistLargeItemDelegate.cpp b/src/libtomahawk/playlist/PlaylistLargeItemDelegate.cpp index 85fcd62f4..1ca6b793f 100644 --- a/src/libtomahawk/playlist/PlaylistLargeItemDelegate.cpp +++ b/src/libtomahawk/playlist/PlaylistLargeItemDelegate.cpp @@ -29,11 +29,11 @@ #include "SourceList.h" #include "PlaylistView.h" -#include "TrackModel.h" -#include "TrackModelItem.h" -#include "TrackProxyModel.h" +#include "PlayableModel.h" +#include "PlayableItem.h" +#include "PlayableProxyModel.h" #include "TrackView.h" -#include "TrackHeader.h" +#include "ViewHeader.h" #include "utils/TomahawkUtilsGui.h" #include "utils/Logger.h" @@ -43,7 +43,7 @@ using namespace Tomahawk; -PlaylistLargeItemDelegate::PlaylistLargeItemDelegate( DisplayMode mode, TrackView* parent, TrackProxyModel* proxy ) +PlaylistLargeItemDelegate::PlaylistLargeItemDelegate( DisplayMode mode, TrackView* parent, PlayableProxyModel* proxy ) : QStyledItemDelegate( (QObject*)parent ) , m_view( parent ) , m_model( proxy ) @@ -58,7 +58,7 @@ PlaylistLargeItemDelegate::PlaylistLargeItemDelegate( DisplayMode mode, TrackVie m_bottomOption = QTextOption( Qt::AlignBottom ); m_bottomOption.setWrapMode( QTextOption::NoWrap ); - connect( proxy->sourceModel(), SIGNAL( modelReset() ), this, SLOT( modelChanged() ) ); + connect( proxy, SIGNAL( modelReset() ), this, SLOT( modelChanged() ) ); if ( PlaylistView* plView = qobject_cast< PlaylistView* >( parent ) ) connect( plView, SIGNAL( modelChanged() ), this, SLOT( modelChanged() ) ); } @@ -90,7 +90,7 @@ PlaylistLargeItemDelegate::createEditor( QWidget* parent, const QStyleOptionView void -PlaylistLargeItemDelegate::prepareStyleOption( QStyleOptionViewItemV4* option, const QModelIndex& index, TrackModelItem* item ) const +PlaylistLargeItemDelegate::prepareStyleOption( QStyleOptionViewItemV4* option, const QModelIndex& index, PlayableItem* item ) const { initStyleOption( option, index ); @@ -124,7 +124,7 @@ PlaylistLargeItemDelegate::drawRichText( QPainter* painter, const QRect& rect, i void PlaylistLargeItemDelegate::paint( QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index ) const { - TrackModelItem* item = m_model->itemFromIndex( m_model->mapToSource( index ) ); + PlayableItem* item = m_model->itemFromIndex( m_model->mapToSource( index ) ); Q_ASSERT( item ); QStyleOptionViewItemV4 opt = option; @@ -136,21 +136,12 @@ PlaylistLargeItemDelegate::paint( QPainter* painter, const QStyleOptionViewItem& if ( m_view->header()->visualIndex( index.column() ) > 0 ) return; + const query_ptr q = item->query()->displayQuery(); + QString artist = q->artist(); + QString track = q->track(); + unsigned int duration = q->duration(); QPixmap avatar; - QString artist, track, lowerText; - unsigned int duration = 0; - - if ( item->query()->results().count() ) - { - artist = item->query()->results().first()->artist()->name(); - track = item->query()->results().first()->track(); - duration = item->query()->results().first()->duration(); - } - else - { - artist = item->query()->artist(); - track = item->query()->track(); - } + QString lowerText; QSize avatarSize( 32, 32 ); source_ptr source = item->query()->playedBy().first; @@ -248,6 +239,7 @@ PlaylistLargeItemDelegate::paint( QPainter* painter, const QStyleOptionViewItem& if ( duration > 0 ) { + painter->setPen( opt.palette.text().color() ); painter->setFont( smallBoldFont ); text = painter->fontMetrics().elidedText( TomahawkUtils::timeToString( duration ), Qt::ElideRight, rightRect.width() ); painter->drawText( rightRect, text, m_centerRightOption ); diff --git a/src/libtomahawk/playlist/PlaylistLargeItemDelegate.h b/src/libtomahawk/playlist/PlaylistLargeItemDelegate.h index 9ddc4a58a..d6b33f076 100644 --- a/src/libtomahawk/playlist/PlaylistLargeItemDelegate.h +++ b/src/libtomahawk/playlist/PlaylistLargeItemDelegate.h @@ -31,8 +31,8 @@ class PixmapDelegateFader; } class TrackModel; -class TrackModelItem; -class TrackProxyModel; +class PlayableItem; +class PlayableProxyModel; class TrackView; class DLLEXPORT PlaylistLargeItemDelegate : public QStyledItemDelegate @@ -43,7 +43,7 @@ public: enum DisplayMode { LovedTracks, RecentlyPlayed, LatestAdditions }; - PlaylistLargeItemDelegate( DisplayMode mode, TrackView* parent = 0, TrackProxyModel* proxy = 0 ); + PlaylistLargeItemDelegate( DisplayMode mode, TrackView* parent = 0, PlayableProxyModel* proxy = 0 ); protected: void paint( QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index ) const; @@ -58,7 +58,7 @@ private slots: void doUpdateIndex( const QPersistentModelIndex& idx ); private: - void prepareStyleOption( QStyleOptionViewItemV4* option, const QModelIndex& index, TrackModelItem* item ) const; + void prepareStyleOption( QStyleOptionViewItemV4* option, const QModelIndex& index, PlayableItem* item ) const; void drawRichText( QPainter* painter, const QRect& rect, int flags, QTextDocument& text ) const; QTextOption m_topOption; @@ -68,7 +68,7 @@ private: mutable QHash< QPersistentModelIndex, QSharedPointer< Tomahawk::PixmapDelegateFader > > m_pixmaps; TrackView* m_view; - TrackProxyModel* m_model; + PlayableProxyModel* m_model; DisplayMode m_mode; }; diff --git a/src/libtomahawk/playlist/PlaylistModel.cpp b/src/libtomahawk/playlist/PlaylistModel.cpp index 6644ba0dc..7eae940c9 100644 --- a/src/libtomahawk/playlist/PlaylistModel.cpp +++ b/src/libtomahawk/playlist/PlaylistModel.cpp @@ -31,6 +31,7 @@ #include "database/DatabaseCommand_PlaybackHistory.h" #include "dynamic/GeneratorInterface.h" #include "DropJob.h" +#include "PlayableItem.h" #include "utils/TomahawkUtils.h" #include "utils/Logger.h" @@ -38,7 +39,7 @@ using namespace Tomahawk; PlaylistModel::PlaylistModel( QObject* parent ) - : TrackModel( parent ) + : PlayableModel( parent ) , m_isTemporary( false ) , m_changesOngoing( false ) , m_isLoading( false ) @@ -83,6 +84,7 @@ PlaylistModel::loadPlaylist( const Tomahawk::playlist_ptr& playlist, bool loadEn .arg( TomahawkUtils::ageToString( QDateTime::fromTime_t( playlist->createdOn() ), true ) ) ); m_isTemporary = false; + emit playlistChanged(); if ( !loadEntries ) { @@ -91,8 +93,6 @@ PlaylistModel::loadPlaylist( const Tomahawk::playlist_ptr& playlist, bool loadEn } QList entries = playlist->entries(); - - qDebug() << "playlist loading entries:"; foreach( const plentry_ptr& p, entries ) qDebug() << p->guid() << p->query()->track() << p->query()->artist(); @@ -105,7 +105,7 @@ PlaylistModel::loadPlaylist( const Tomahawk::playlist_ptr& playlist, bool loadEn void PlaylistModel::clear() { - TrackModel::clear(); + PlayableModel::clear(); m_waitingForResolved.clear(); } @@ -138,7 +138,7 @@ PlaylistModel::append( const Tomahawk::album_ptr& album ) if ( album.isNull() ) return; - connect( album.data(), SIGNAL( tracksAdded( QList ) ), + connect( album.data(), SIGNAL( tracksAdded( QList, Tomahawk::ModelMode, Tomahawk::collection_ptr ) ), SLOT( append( QList ) ) ); if ( rowCount( QModelIndex() ) == 0 ) @@ -148,7 +148,7 @@ PlaylistModel::append( const Tomahawk::album_ptr& album ) m_isTemporary = true; } - append( album->playlistInterface()->tracks() ); + append( album->playlistInterface( Mixed )->tracks() ); } @@ -158,7 +158,7 @@ PlaylistModel::append( const Tomahawk::artist_ptr& artist ) if ( artist.isNull() ) return; - connect( artist.data(), SIGNAL( tracksAdded( QList ) ), + connect( artist.data(), SIGNAL( tracksAdded( QList, Tomahawk::ModelMode, Tomahawk::collection_ptr ) ), SLOT( append( QList ) ) ); if ( rowCount( QModelIndex() ) == 0 ) @@ -168,14 +168,14 @@ PlaylistModel::append( const Tomahawk::artist_ptr& artist ) m_isTemporary = true; } - append( artist->playlistInterface()->tracks() ); + append( artist->playlistInterface( Mixed )->tracks() ); } void PlaylistModel::insert( const Tomahawk::query_ptr& query, int row ) { - TrackModel::insert( query, row ); + PlayableModel::insert( query, row ); } @@ -187,11 +187,7 @@ PlaylistModel::insert( const QList< Tomahawk::query_ptr >& queries, int row ) { plentry_ptr entry = plentry_ptr( new PlaylistEntry() ); - if ( query->results().count() ) - entry->setDuration( query->results().at( 0 )->duration() ); - else - entry->setDuration( 0 ); - + entry->setDuration( query->displayQuery()->duration() ); entry->setLastmodified( 0 ); QString annotation = ""; if ( !query->property( "annotation" ).toString().isEmpty() ) @@ -214,6 +210,7 @@ PlaylistModel::insert( const QList< Tomahawk::plentry_ptr >& entries, int row ) if ( !entries.count() ) { emit trackCountChanged( rowCount( QModelIndex() ) ); + finishLoading(); return; } @@ -232,10 +229,10 @@ PlaylistModel::insert( const QList< Tomahawk::plentry_ptr >& entries, int row ) QList< Tomahawk::query_ptr > queries; int i = 0; - TrackModelItem* plitem; + PlayableItem* plitem; foreach( const plentry_ptr& entry, entries ) { - plitem = new TrackModelItem( entry, rootItem(), row + i ); + plitem = new PlayableItem( entry, rootItem(), row + i ); plitem->index = createIndex( row + i, 0, plitem ); i++; @@ -260,6 +257,7 @@ PlaylistModel::insert( const QList< Tomahawk::plentry_ptr >& entries, int row ) emit endInsertRows(); emit trackCountChanged( rowCount( QModelIndex() ) ); + finishLoading(); } @@ -300,7 +298,7 @@ QMimeData* PlaylistModel::mimeData( const QModelIndexList& indexes ) const { // Add the playlist id to the mime data so that we can detect dropping on ourselves - QMimeData* d = TrackModel::mimeData( indexes ); + QMimeData* d = PlayableModel::mimeData( indexes ); if ( !m_playlist.isNull() ) d->setData( "application/tomahawk.playlist.id", m_playlist->guid().toLatin1() ); @@ -440,7 +438,7 @@ PlaylistModel::endPlaylistChanges() const QModelIndex idx = index( i, 0, QModelIndex() ); if ( !idx.isValid() ) continue; - const TrackModelItem* item = itemFromIndex( idx ); + const PlayableItem* item = itemFromIndex( idx ); if ( !item || item->entry().isNull() ) continue; @@ -480,7 +478,7 @@ PlaylistModel::playlistEntries() const if ( !idx.isValid() ) continue; - TrackModelItem* item = itemFromIndex( idx ); + PlayableItem* item = itemFromIndex( idx ); if ( item ) l << item->entry(); } @@ -492,14 +490,14 @@ PlaylistModel::playlistEntries() const void PlaylistModel::remove( int row, bool moreToCome ) { - TrackModel::remove( row, moreToCome ); + PlayableModel::remove( row, moreToCome ); } void PlaylistModel::remove( const QModelIndex& index, bool moreToCome ) { - TrackModelItem* item = itemFromIndex( index ); + PlayableItem* item = itemFromIndex( index ); if ( item && m_waitingForResolved.contains( item->query().data() ) ) { @@ -515,7 +513,7 @@ PlaylistModel::remove( const QModelIndex& index, bool moreToCome ) if ( item && !m_isLoading ) m_savedRemoveTracks << item->query(); - TrackModel::remove( index, moreToCome ); + PlayableModel::remove( index, moreToCome ); if ( !moreToCome ) endPlaylistChanges(); @@ -525,14 +523,14 @@ PlaylistModel::remove( const QModelIndex& index, bool moreToCome ) void PlaylistModel::remove( const QList& indexes ) { - TrackModel::remove( indexes ); + PlayableModel::remove( indexes ); } void PlaylistModel::remove( const QList& indexes ) { - TrackModel::remove( indexes ); + PlayableModel::remove( indexes ); } diff --git a/src/libtomahawk/playlist/PlaylistModel.h b/src/libtomahawk/playlist/PlaylistModel.h index 2e5b0a79b..e7a3341d3 100644 --- a/src/libtomahawk/playlist/PlaylistModel.h +++ b/src/libtomahawk/playlist/PlaylistModel.h @@ -23,7 +23,7 @@ #include #include "Typedefs.h" -#include "TrackModel.h" +#include "PlayableModel.h" #include "Playlist.h" #include "Query.h" #include "PlaylistInterface.h" @@ -33,7 +33,7 @@ class QMimeData; class QMetaData; -class DLLEXPORT PlaylistModel : public TrackModel +class DLLEXPORT PlaylistModel : public PlayableModel { Q_OBJECT @@ -74,7 +74,7 @@ public slots: virtual void remove( const QList& indexes ); signals: - void repeatModeChanged( Tomahawk::PlaylistInterface::RepeatMode mode ); + void repeatModeChanged( Tomahawk::PlaylistModes::RepeatMode mode ); void shuffleModeChanged( bool enabled ); void playlistDeleted(); void playlistChanged(); diff --git a/src/libtomahawk/playlist/PlaylistProxyModel.cpp b/src/libtomahawk/playlist/PlaylistProxyModel.cpp deleted file mode 100644 index 4ed1011c8..000000000 --- a/src/libtomahawk/playlist/PlaylistProxyModel.cpp +++ /dev/null @@ -1,40 +0,0 @@ -/* === This file is part of Tomahawk Player - === - * - * Copyright 2010-2011, Christian Muehlhaeuser - * Copyright 2010-2012, Jeff Mitchell - * - * Tomahawk is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Tomahawk is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Tomahawk. If not, see . - */ - -#include "PlaylistProxyModel.h" - -#include "PlaylistProxyModelPlaylistInterface.h" -#include "utils/Logger.h" - - -PlaylistProxyModel::PlaylistProxyModel( QObject* parent ) - : TrackProxyModel( parent ) -{ -} - -Tomahawk::playlistinterface_ptr -PlaylistProxyModel::playlistInterface() -{ - if ( m_playlistInterface.isNull() ) - { - m_playlistInterface = Tomahawk::playlistinterface_ptr( new Tomahawk::PlaylistProxyModelPlaylistInterface( this ) ); - } - - return m_playlistInterface; -} diff --git a/src/libtomahawk/playlist/PlaylistProxyModelPlaylistInterface.cpp b/src/libtomahawk/playlist/PlaylistProxyModelPlaylistInterface.cpp deleted file mode 100644 index 73e443712..000000000 --- a/src/libtomahawk/playlist/PlaylistProxyModelPlaylistInterface.cpp +++ /dev/null @@ -1,35 +0,0 @@ -/* === This file is part of Tomahawk Player - === - * - * Copyright 2010-2011, Christian Muehlhaeuser - * Copyright 2010-2011, Jeff Mitchell - * - * Tomahawk is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Tomahawk is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Tomahawk. If not, see . - */ - -#include "PlaylistProxyModelPlaylistInterface.h" - -#include "PlaylistProxyModel.h" -#include "utils/Logger.h" - -using namespace Tomahawk; - -PlaylistProxyModelPlaylistInterface::PlaylistProxyModelPlaylistInterface( PlaylistProxyModel *proxyModel ) - : TrackProxyModelPlaylistInterface( proxyModel ) -{ -} - -PlaylistProxyModelPlaylistInterface::~PlaylistProxyModelPlaylistInterface() -{ - m_proxyModel.clear(); -} diff --git a/src/libtomahawk/playlist/PlaylistProxyModelPlaylistInterface.h b/src/libtomahawk/playlist/PlaylistProxyModelPlaylistInterface.h deleted file mode 100644 index 133e6f5e1..000000000 --- a/src/libtomahawk/playlist/PlaylistProxyModelPlaylistInterface.h +++ /dev/null @@ -1,43 +0,0 @@ -/* === This file is part of Tomahawk Player - === - * - * Copyright 2010-2011, Christian Muehlhaeuser - * Copyright 2010-2011, Jeff Mitchell - * - * Tomahawk is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Tomahawk is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Tomahawk. If not, see . - */ - -#ifndef PLAYLISTPROXYMODELPLAYLISTINTERFACE_H -#define PLAYLISTPROXYMODELPLAYLISTINTERFACE_H - -#include "TrackProxyModelPlaylistInterface.h" - -#include "DllMacro.h" - -class PlaylistProxyModel; - -namespace Tomahawk -{ - -class DLLEXPORT PlaylistProxyModelPlaylistInterface : public TrackProxyModelPlaylistInterface -{ -Q_OBJECT - -public: - explicit PlaylistProxyModelPlaylistInterface( PlaylistProxyModel *proxyModel ); - virtual ~PlaylistProxyModelPlaylistInterface(); -}; - -} //ns - -#endif // PLAYLISTPROXYMODELPLAYLISTINTERFACE_H diff --git a/src/libtomahawk/playlist/PlaylistUpdaterInterface.cpp b/src/libtomahawk/playlist/PlaylistUpdaterInterface.cpp index 74be0b520..1f8253431 100644 --- a/src/libtomahawk/playlist/PlaylistUpdaterInterface.cpp +++ b/src/libtomahawk/playlist/PlaylistUpdaterInterface.cpp @@ -18,6 +18,7 @@ #include "PlaylistUpdaterInterface.h" #include "TomahawkSettings.h" +#include "Source.h" namespace Tomahawk { diff --git a/src/libtomahawk/playlist/PlaylistUpdaterInterface.h b/src/libtomahawk/playlist/PlaylistUpdaterInterface.h index 22bf0a566..0776451bd 100644 --- a/src/libtomahawk/playlist/PlaylistUpdaterInterface.h +++ b/src/libtomahawk/playlist/PlaylistUpdaterInterface.h @@ -40,16 +40,7 @@ namespace Tomahawk class PlaylistUpdaterFactory; // used when loading/saving from settings -struct SerializedUpdater { - QString type; - QVariantHash customData; - SerializedUpdater( const QString& t, const QVariantHash cd = QVariantHash() ) : type( t ), customData( cd ) {} - SerializedUpdater() {} -}; - -typedef QMultiHash< QString, SerializedUpdater > SerializedUpdaters; -typedef QList< SerializedUpdater > SerializedUpdaterList; class DLLEXPORT PlaylistUpdaterInterface : public QObject { @@ -80,6 +71,8 @@ public: static void registerUpdaterFactory( PlaylistUpdaterFactory* f ); + virtual bool sync() const { return true; } + signals: void changed(); diff --git a/src/libtomahawk/playlist/PlaylistView.cpp b/src/libtomahawk/playlist/PlaylistView.cpp index 92d6ebe41..ef62eca80 100644 --- a/src/libtomahawk/playlist/PlaylistView.cpp +++ b/src/libtomahawk/playlist/PlaylistView.cpp @@ -22,11 +22,10 @@ #include #include -#include "playlist/PlaylistProxyModel.h" -#include "widgets/OverlayWidget.h" #include "ViewManager.h" #include "utils/Logger.h" #include "PlaylistUpdaterInterface.h" +#include "Source.h" using namespace Tomahawk; @@ -35,8 +34,6 @@ PlaylistView::PlaylistView( QWidget* parent ) : TrackView( parent ) , m_model( 0 ) { - setProxyModel( new PlaylistProxyModel( this ) ); - connect( contextMenu(), SIGNAL( triggered( int ) ), SLOT( onMenuTriggered( int ) ) ); } @@ -61,9 +58,9 @@ PlaylistView::setPlaylistModel( PlaylistModel* model ) { m_model = model; - TrackView::setTrackModel( m_model ); - setColumnHidden( TrackModel::Age, true ); // Hide age column per default - setColumnHidden( TrackModel::Composer, true ); // Hide composer column per default + TrackView::setPlayableModel( m_model ); + setColumnHidden( PlayableModel::Age, true ); // Hide age column per default + setColumnHidden( PlayableModel::Composer, true ); // Hide composer column per default if ( guid().isEmpty() ) { @@ -77,7 +74,6 @@ PlaylistView::setPlaylistModel( PlaylistModel* model ) } } - connect( m_model, SIGNAL( trackCountChanged( unsigned int ) ), SLOT( onTrackCountChanged( unsigned int ) ) ); connect( m_model, SIGNAL( playlistDeleted() ), SLOT( onDeleted() ) ); connect( m_model, SIGNAL( playlistChanged() ), SLOT( onChanged() ) ); @@ -88,7 +84,6 @@ PlaylistView::setPlaylistModel( PlaylistModel* model ) void PlaylistView::keyPressEvent( QKeyEvent* event ) { - qDebug() << Q_FUNC_INFO; TrackView::keyPressEvent( event ); if ( !model() ) @@ -102,6 +97,38 @@ PlaylistView::keyPressEvent( QKeyEvent* event ) } +bool +PlaylistView::eventFilter( QObject* obj, QEvent* event ) +{ + if ( event->type() == QEvent::DragEnter ) + { + QDragEnterEvent* e = static_cast(event); + dragEnterEvent( e ); + return true; + } + if ( event->type() == QEvent::DragMove ) + { + QDragMoveEvent* e = static_cast(event); + dragMoveEvent( e ); + return true; + } + if ( event->type() == QEvent::DragLeave ) + { + QDragLeaveEvent* e = static_cast(event); + dragLeaveEvent( e ); + return true; + } + if ( event->type() == QEvent::Drop ) + { + QDropEvent* e = static_cast(event); + dropEvent( e ); + return true; + } + + return QObject::eventFilter( obj, event ); +} + + void PlaylistView::deleteItems() { @@ -119,27 +146,6 @@ PlaylistView::updaters() const } -void -PlaylistView::onTrackCountChanged( unsigned int tracks ) -{ - if ( tracks == 0 ) - { - overlay()->setText( tr( "This playlist is currently empty. Add some tracks to it and enjoy the music!" ) ); - overlay()->show(); - } - else - overlay()->hide(); -} - - -bool -PlaylistView::jumpToCurrentTrack() -{ - scrollTo( proxyModel()->currentIndex(), QAbstractItemView::PositionAtCenter ); - return true; -} - - void PlaylistView::onDeleted() { @@ -151,9 +157,17 @@ PlaylistView::onDeleted() void PlaylistView::onChanged() { - if ( m_model && !m_model->playlist().isNull() && - ViewManager::instance()->currentPage() == this ) - emit nameChanged( m_model->playlist()->title() ); + if ( m_model ) + { + if ( m_model->isReadOnly() ) + setEmptyTip( tr( "This playlist is currently empty." ) ); + else + setEmptyTip( tr( "This playlist is currently empty. Add some tracks to it and enjoy the music!" ) ); + m_model->finishLoading(); + + if ( !m_model->playlist().isNull() && ViewManager::instance()->currentPage() == this ) + emit nameChanged( m_model->playlist()->title() ); + } } @@ -174,7 +188,6 @@ PlaylistView::onMenuTriggered( int action ) break; default: - TrackView::onMenuTriggered( action ); break; } } diff --git a/src/libtomahawk/playlist/PlaylistView.h b/src/libtomahawk/playlist/PlaylistView.h index 978e4e15f..14140f4ad 100644 --- a/src/libtomahawk/playlist/PlaylistView.h +++ b/src/libtomahawk/playlist/PlaylistView.h @@ -20,13 +20,13 @@ #ifndef PLAYLISTVIEW_H #define PLAYLISTVIEW_H -#include "playlist/TrackProxyModel.h" +#include "playlist/PlayableProxyModel.h" #include "playlist/PlaylistModel.h" #include "TrackView.h" #include "ViewPage.h" #include "DllMacro.h" -class DLLEXPORT PlaylistView : public TrackView, public Tomahawk::ViewPage +class DLLEXPORT PlaylistView : public TrackView { Q_OBJECT @@ -38,17 +38,9 @@ public: virtual void setPlaylistModel( PlaylistModel* model ); virtual void setModel( QAbstractItemModel* model ); - virtual QWidget* widget() { return this; } - virtual Tomahawk::playlistinterface_ptr playlistInterface() const { return proxyModel()->playlistInterface(); } - - virtual bool showFilter() const { return true; } - virtual QList updaters() const; - virtual QString title() const { return playlistModel()->title(); } - virtual QString description() const { return m_model->description(); } virtual QPixmap pixmap() const { return QPixmap( RESPATH "images/playlist-icon.png" ); } - virtual bool jumpToCurrentTrack(); virtual bool isTemporaryPage() const; signals: @@ -58,9 +50,9 @@ signals: protected: void keyPressEvent( QKeyEvent* event ); + bool eventFilter( QObject* obj, QEvent* event ); private slots: - void onTrackCountChanged( unsigned int tracks ); void onMenuTriggered( int action ); void deleteItems(); diff --git a/src/libtomahawk/playlist/QueueProxyModel.cpp b/src/libtomahawk/playlist/QueueProxyModel.cpp index b70d4a45a..e544eb645 100644 --- a/src/libtomahawk/playlist/QueueProxyModel.cpp +++ b/src/libtomahawk/playlist/QueueProxyModel.cpp @@ -19,21 +19,22 @@ #include "QueueProxyModel.h" -#include "QueueProxyModelPlaylistInterface.h" +#include "audio/AudioEngine.h" #include "playlist/TrackView.h" +#include "PlayableItem.h" #include "ViewManager.h" +#include "Source.h" #include "utils/Logger.h" using namespace Tomahawk; QueueProxyModel::QueueProxyModel( TrackView* parent ) - : PlaylistProxyModel( parent ) + : PlayableProxyModel( parent ) { - qDebug() << Q_FUNC_INFO; - - connect( parent, SIGNAL( itemActivated( QModelIndex ) ), this, SLOT( onIndexActivated( QModelIndex ) ) ); - connect( playlistInterface().data(), SIGNAL( sourceTrackCountChanged( unsigned int ) ), this, SLOT( onTrackCountChanged( unsigned int ) ) ); + connect( parent, SIGNAL( itemActivated( QModelIndex ) ), SLOT( onIndexActivated( QModelIndex ) ) ); + connect( playlistInterface().data(), SIGNAL( sourceTrackCountChanged( unsigned int ) ), SLOT( onTrackCountChanged( unsigned int ) ) ); + connect( AudioEngine::instance(), SIGNAL( loading( Tomahawk::result_ptr ) ), SLOT( onPlaybackStarted( Tomahawk::result_ptr ) ) ); } @@ -42,6 +43,19 @@ QueueProxyModel::~QueueProxyModel() } +void +QueueProxyModel::onPlaybackStarted( const Tomahawk::result_ptr& result ) +{ + for ( int i = 0; i < rowCount(); i++ ) + { + QModelIndex idx = index( i, 0 ); + PlayableItem* item = itemFromIndex( mapToSource( idx ) ); + if ( item && item->query() && item->query()->equals( result->toQuery() ) ) + remove( idx ); + } +} + + void QueueProxyModel::onIndexActivated( const QModelIndex& index ) { @@ -56,15 +70,3 @@ QueueProxyModel::onTrackCountChanged( unsigned int count ) if ( count == 0 ) ViewManager::instance()->hideQueue(); } - - -Tomahawk::playlistinterface_ptr -QueueProxyModel::playlistInterface() -{ - if ( m_playlistInterface.isNull() ) - { - m_playlistInterface = Tomahawk::playlistinterface_ptr( new Tomahawk::QueueProxyModelPlaylistInterface( this ) ); - } - - return m_playlistInterface; -} diff --git a/src/libtomahawk/playlist/QueueProxyModel.h b/src/libtomahawk/playlist/QueueProxyModel.h index 8156d8df3..3c39a68dc 100644 --- a/src/libtomahawk/playlist/QueueProxyModel.h +++ b/src/libtomahawk/playlist/QueueProxyModel.h @@ -20,14 +20,14 @@ #ifndef QUEUEPROXYMODEL_H #define QUEUEPROXYMODEL_H -#include "PlaylistProxyModel.h" +#include "PlayableProxyModel.h" #include "DllMacro.h" class QMetaData; class TrackView; -class DLLEXPORT QueueProxyModel : public PlaylistProxyModel +class DLLEXPORT QueueProxyModel : public PlayableProxyModel { Q_OBJECT @@ -35,11 +35,10 @@ public: explicit QueueProxyModel( TrackView* parent = 0 ); virtual ~QueueProxyModel(); - virtual Tomahawk::playlistinterface_ptr playlistInterface(); - private slots: void onIndexActivated( const QModelIndex& index ); void onTrackCountChanged( unsigned int count ); + void onPlaybackStarted( const Tomahawk::result_ptr& result ); }; #endif // QUEUEPROXYMODEL_H diff --git a/src/libtomahawk/playlist/QueueProxyModelPlaylistInterface.cpp b/src/libtomahawk/playlist/QueueProxyModelPlaylistInterface.cpp deleted file mode 100644 index 25f94cd78..000000000 --- a/src/libtomahawk/playlist/QueueProxyModelPlaylistInterface.cpp +++ /dev/null @@ -1,51 +0,0 @@ -/* === This file is part of Tomahawk Player - === - * - * Copyright 2010-2011, Christian Muehlhaeuser - * Copyright 2010-2011, Jeff Mitchell - * - * Tomahawk is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Tomahawk is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Tomahawk. If not, see . - */ - -#include "QueueProxyModelPlaylistInterface.h" - -#include "QueueProxyModel.h" -#include "utils/Logger.h" - -using namespace Tomahawk; - -QueueProxyModelPlaylistInterface::QueueProxyModelPlaylistInterface( QueueProxyModel *proxyModel ) - : PlaylistProxyModelPlaylistInterface( proxyModel ) -{ -} - - -QueueProxyModelPlaylistInterface::~QueueProxyModelPlaylistInterface() -{ - m_proxyModel.clear(); -} - - -Tomahawk::result_ptr -QueueProxyModelPlaylistInterface::siblingItem( int itemsAway ) -{ - if ( m_proxyModel.isNull() ) - return Tomahawk::result_ptr(); - - m_proxyModel.data()->setCurrentIndex( QModelIndex() ); - Tomahawk::result_ptr res = PlaylistProxyModelPlaylistInterface::siblingItem( itemsAway ); - - m_proxyModel.data()->remove( m_proxyModel.data()->currentIndex() ); - - return res; -} diff --git a/src/libtomahawk/playlist/QueueProxyModelPlaylistInterface.h b/src/libtomahawk/playlist/QueueProxyModelPlaylistInterface.h deleted file mode 100644 index 12540f2ff..000000000 --- a/src/libtomahawk/playlist/QueueProxyModelPlaylistInterface.h +++ /dev/null @@ -1,46 +0,0 @@ -/* === This file is part of Tomahawk Player - === - * - * Copyright 2010-2011, Christian Muehlhaeuser - * Copyright 2010-2011, Jeff Mitchell - * - * Tomahawk is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Tomahawk is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Tomahawk. If not, see . - */ - -#ifndef QUEUEPROXYMODELPLAYLISTINTERFACE_H -#define QUEUEPROXYMODELPLAYLISTINTERFACE_H - -#include "PlaylistProxyModelPlaylistInterface.h" - -#include "Result.h" -#include "DllMacro.h" - -class QueueProxyModel; - -namespace Tomahawk -{ - -class DLLEXPORT QueueProxyModelPlaylistInterface : public PlaylistProxyModelPlaylistInterface -{ -Q_OBJECT - -public: - explicit QueueProxyModelPlaylistInterface( QueueProxyModel *proxyModel ); - virtual ~QueueProxyModelPlaylistInterface(); - - virtual Tomahawk::result_ptr siblingItem( int itemsAway ); -}; - -} //ns - -#endif // QUEUEPROXYMODELPLAYLISTINTERFACE_H diff --git a/src/libtomahawk/playlist/QueueView.cpp b/src/libtomahawk/playlist/QueueView.cpp index 0625684aa..83c1414ba 100644 --- a/src/libtomahawk/playlist/QueueView.cpp +++ b/src/libtomahawk/playlist/QueueView.cpp @@ -23,10 +23,11 @@ #include "widgets/HeaderLabel.h" #include "playlist/QueueProxyModel.h" -#include "widgets/OverlayWidget.h" #include "utils/Logger.h" #include "PlaylistView.h" +#include "Source.h" #include "utils/TomahawkUtilsGui.h" +#include "widgets/OverlayWidget.h" using namespace Tomahawk; @@ -44,13 +45,21 @@ QueueView::QueueView( AnimatedSplitter* parent ) ui->queue->setProxyModel( new QueueProxyModel( ui->queue ) ); ui->queue->setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Ignored ); - ui->queue->setFrameShape( QFrame::NoFrame ); - ui->queue->setAttribute( Qt::WA_MacShowFocusRect, 0 ); - ui->queue->overlay()->setEnabled( false ); + PlaylistModel* queueModel = new PlaylistModel( this ); + queueModel->setStyle( PlaylistModel::Short ); + queueModel->finishLoading(); + ui->queue->setPlaylistModel( queueModel ); + queueModel->setReadOnly( false ); + + ui->queue->setEmptyTip( tr( "The queue is currently empty. Drop something to enqueue it!" ) ); + + connect( queueModel, SIGNAL( trackCountChanged( unsigned int ) ), SLOT( updateLabel() ) ); connect( ui->toggleButton, SIGNAL( clicked() ), SLOT( show() ) ); + connect( this, SIGNAL( animationFinished() ), SLOT( onAnimationFinished() ) ); ui->toggleButton->installEventFilter( this ); + ui->toggleButton->setCursor( Qt::PointingHandCursor ); } @@ -117,8 +126,11 @@ QueueView::hide() { disconnect( ui->toggleButton, SIGNAL( clicked() ), this, SLOT( hide() ) ); connect( ui->toggleButton, SIGNAL( clicked() ), SLOT( show() ) ); - ui->toggleButton->setText( tr( "Show Queue" ) ); - emit hideWidget(); + disconnect( ui->toggleButton, SIGNAL( resized( QPoint ) ), this, SIGNAL( resizeBy( QPoint ) ) ); + + ui->queue->overlay()->setVisible( false ); + AnimatedWidget::hide(); + updateLabel(); } @@ -127,8 +139,11 @@ QueueView::show() { disconnect( ui->toggleButton, SIGNAL( clicked() ), this, SLOT( show() ) ); connect( ui->toggleButton, SIGNAL( clicked() ), SLOT( hide() ) ); - ui->toggleButton->setText( tr( "Hide Queue" ) ); - emit showWidget(); + connect( ui->toggleButton, SIGNAL( resized( QPoint ) ), SIGNAL( resizeBy( QPoint ) ) ); + + ui->queue->overlay()->setVisible( false ); + AnimatedWidget::show(); + updateLabel(); } @@ -150,3 +165,29 @@ QueueView::onHidden( QWidget* widget, bool animated ) AnimatedWidget::onHidden( widget, animated ); } + + +void +QueueView::onAnimationFinished() +{ + ui->queue->overlay()->setVisible( true ); +} + + +void +QueueView::updateLabel() +{ + if ( isHidden() ) + { + const unsigned int c = queue()->model()->rowCount( QModelIndex() ); + + if ( c ) + ui->toggleButton->setText( tr( "Open Queue - %n item(s)", "", c ) ); + else + ui->toggleButton->setText( tr( "Open Queue" ) ); + } + else + { + ui->toggleButton->setText( tr( "Close Queue" ) ); + } +} diff --git a/src/libtomahawk/playlist/QueueView.h b/src/libtomahawk/playlist/QueueView.h index 49952dc37..ce8ef7eae 100644 --- a/src/libtomahawk/playlist/QueueView.h +++ b/src/libtomahawk/playlist/QueueView.h @@ -57,6 +57,10 @@ public slots: protected: void changeEvent( QEvent* e ); +private slots: + void updateLabel(); + void onAnimationFinished(); + private: Ui::QueueView* ui; QTimer* m_dragTimer; diff --git a/src/libtomahawk/playlist/QueueView.ui b/src/libtomahawk/playlist/QueueView.ui index e9e318b12..fba21c5b1 100644 --- a/src/libtomahawk/playlist/QueueView.ui +++ b/src/libtomahawk/playlist/QueueView.ui @@ -38,7 +38,7 @@ true - Show Queue + Open Queue Qt::AlignCenter diff --git a/src/libtomahawk/playlist/RecentlyAddedModel.cpp b/src/libtomahawk/playlist/RecentlyAddedModel.cpp index 2847fa124..243a43f76 100644 --- a/src/libtomahawk/playlist/RecentlyAddedModel.cpp +++ b/src/libtomahawk/playlist/RecentlyAddedModel.cpp @@ -34,7 +34,7 @@ using namespace Tomahawk; RecentlyAddedModel::RecentlyAddedModel( const source_ptr& source, QObject* parent ) - : TrackModel( parent ) + : PlayableModel( parent ) , m_source( source ) , m_limit( LATEST_TRACK_ITEMS ) { diff --git a/src/libtomahawk/playlist/RecentlyAddedModel.h b/src/libtomahawk/playlist/RecentlyAddedModel.h index d47032039..e4a3a98fa 100644 --- a/src/libtomahawk/playlist/RecentlyAddedModel.h +++ b/src/libtomahawk/playlist/RecentlyAddedModel.h @@ -23,11 +23,11 @@ #include #include "Typedefs.h" -#include "TrackModel.h" +#include "PlayableModel.h" #include "DllMacro.h" -class DLLEXPORT RecentlyAddedModel : public TrackModel +class DLLEXPORT RecentlyAddedModel : public PlayableModel { Q_OBJECT diff --git a/src/libtomahawk/playlist/RecentlyPlayedModel.cpp b/src/libtomahawk/playlist/RecentlyPlayedModel.cpp index 9e071838d..5902ee44a 100644 --- a/src/libtomahawk/playlist/RecentlyPlayedModel.cpp +++ b/src/libtomahawk/playlist/RecentlyPlayedModel.cpp @@ -25,6 +25,7 @@ #include "SourceList.h" #include "database/Database.h" #include "database/DatabaseCommand_PlaybackHistory.h" +#include "PlayableItem.h" #include "utils/TomahawkUtils.h" #include "utils/Logger.h" @@ -40,9 +41,11 @@ RecentlyPlayedModel::RecentlyPlayedModel( const source_ptr& source, QObject* par { if ( source.isNull() ) { - onSourcesReady(); + if ( SourceList::instance()->isReady() ) + onSourcesReady(); + else + connect( SourceList::instance(), SIGNAL( ready() ), SLOT( onSourcesReady() ) ); - connect( SourceList::instance(), SIGNAL( ready() ), SLOT( onSourcesReady() ) ); connect( SourceList::instance(), SIGNAL( sourceAdded( Tomahawk::source_ptr ) ), SLOT( onSourceAdded( Tomahawk::source_ptr ) ) ); } else @@ -65,6 +68,7 @@ RecentlyPlayedModel::loadHistory() { clear(); } + loadingStarted(); DatabaseCommand_PlaybackHistory* cmd = new DatabaseCommand_PlaybackHistory( m_source ); cmd->setLimit( m_limit ); @@ -103,19 +107,19 @@ RecentlyPlayedModel::onPlaybackFinished( const Tomahawk::query_ptr& query ) if ( count ) { - TrackModelItem* oldestItem = itemFromIndex( index( count - 1, 0, QModelIndex() ) ); + PlayableItem* oldestItem = itemFromIndex( index( count - 1, 0, QModelIndex() ) ); if ( oldestItem->query()->playedBy().second >= playtime ) return; - TrackModelItem* youngestItem = itemFromIndex( index( 0, 0, QModelIndex() ) ); + PlayableItem* youngestItem = itemFromIndex( index( 0, 0, QModelIndex() ) ); if ( youngestItem->query()->playedBy().second <= playtime ) insert( query, 0 ); else { for ( int i = 0; i < count - 1; i++ ) { - TrackModelItem* item1 = itemFromIndex( index( i, 0, QModelIndex() ) ); - TrackModelItem* item2 = itemFromIndex( index( i + 1, 0, QModelIndex() ) ); + PlayableItem* item1 = itemFromIndex( index( i, 0, QModelIndex() ) ); + PlayableItem* item2 = itemFromIndex( index( i + 1, 0, QModelIndex() ) ); if ( item1->query()->playedBy().second >= playtime && item2->query()->playedBy().second <= playtime ) { diff --git a/src/libtomahawk/playlist/TrackHeader.cpp b/src/libtomahawk/playlist/TrackHeader.cpp deleted file mode 100644 index 74ccf1ff2..000000000 --- a/src/libtomahawk/playlist/TrackHeader.cpp +++ /dev/null @@ -1,39 +0,0 @@ -/* === This file is part of Tomahawk Player - === - * - * Copyright 2010-2011, Christian Muehlhaeuser - * Copyright 2010-2011, Jeff Mitchell - * - * Tomahawk is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Tomahawk is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Tomahawk. If not, see . - */ - -#include "TrackHeader.h" - -#include "playlist/TrackView.h" -#include "utils/Logger.h" - - -TrackHeader::TrackHeader( TrackView* parent ) - : ViewHeader( parent ) - , m_parent( parent ) -{ - QList< double > columnWeights; - columnWeights << 0.16 << 0.16 << 0.14 << 0.12 << 0.05 << 0.05 << 0.05 << 0.05 << 0.05 << 0.05 << 0.09; // << 0.03; - - setDefaultColumnWeights( columnWeights ); -} - - -TrackHeader::~TrackHeader() -{ -} diff --git a/src/libtomahawk/playlist/TrackModel.cpp b/src/libtomahawk/playlist/TrackModel.cpp deleted file mode 100644 index 9b7c6cc6d..000000000 --- a/src/libtomahawk/playlist/TrackModel.cpp +++ /dev/null @@ -1,607 +0,0 @@ -/* === This file is part of Tomahawk Player - === - * - * Copyright 2010-2011, Christian Muehlhaeuser - * Copyright 2011 Leo Franchi - * Copyright 2010-2011, Jeff Mitchell - * - * Tomahawk is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Tomahawk is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Tomahawk. If not, see . - */ - -#include "TrackModel.h" - -#include -#include -#include - -#include "audio/AudioEngine.h" -#include "utils/TomahawkUtils.h" - -#include "Artist.h" -#include "Album.h" -#include "Pipeline.h" -#include "utils/Logger.h" - -using namespace Tomahawk; - - -TrackModel::TrackModel( QObject* parent ) - : QAbstractItemModel( parent ) - , m_rootItem( new TrackModelItem( 0, this ) ) - , m_readOnly( true ) - , m_style( Detailed ) -{ - connect( AudioEngine::instance(), SIGNAL( started( Tomahawk::result_ptr ) ), SLOT( onPlaybackStarted( Tomahawk::result_ptr ) ), Qt::DirectConnection ); - connect( AudioEngine::instance(), SIGNAL( stopped() ), SLOT( onPlaybackStopped() ), Qt::DirectConnection ); -} - - -TrackModel::~TrackModel() -{ -} - - -QModelIndex -TrackModel::index( int row, int column, const QModelIndex& parent ) const -{ - if ( !m_rootItem || row < 0 || column < 0 ) - return QModelIndex(); - - TrackModelItem* parentItem = itemFromIndex( parent ); - TrackModelItem* childItem = parentItem->children.value( row ); - if ( !childItem ) - return QModelIndex(); - - return createIndex( row, column, childItem ); -} - - -int -TrackModel::rowCount( const QModelIndex& parent ) const -{ - if ( parent.column() > 0 ) - return 0; - - TrackModelItem* parentItem = itemFromIndex( parent ); - if ( !parentItem ) - return 0; - - return parentItem->children.count(); -} - - -int -TrackModel::columnCount( const QModelIndex& parent ) const -{ - Q_UNUSED( parent ); - - switch ( m_style ) - { - case Short: - case ShortWithAvatars: - case Large: - return 1; - break; - - case Detailed: - default: - return 12; - break; - } -} - - -QModelIndex -TrackModel::parent( const QModelIndex& child ) const -{ - TrackModelItem* entry = itemFromIndex( child ); - if ( !entry ) - return QModelIndex(); - - TrackModelItem* parentEntry = entry->parent; - if ( !parentEntry ) - return QModelIndex(); - - TrackModelItem* grandparentEntry = parentEntry->parent; - if ( !grandparentEntry ) - return QModelIndex(); - - int row = grandparentEntry->children.indexOf( parentEntry ); - return createIndex( row, 0, parentEntry ); -} - - -QVariant -TrackModel::data( const QModelIndex& index, int role ) const -{ - TrackModelItem* entry = itemFromIndex( index ); - if ( !entry ) - return QVariant(); - - if ( role == Qt::DecorationRole ) - { - return QVariant(); - } - - if ( role == Qt::SizeHintRole ) - { - return QSize( 0, 18 ); - } - - if ( role == Qt::TextAlignmentRole ) - { - return QVariant( columnAlignment( index.column() ) ); - } - - if ( role == StyleRole ) - { - return m_style; - } - - if ( role != Qt::DisplayRole ) // && role != Qt::ToolTipRole ) - return QVariant(); - - const query_ptr& query = entry->query(); - if ( !query->numResults() ) - { - switch( index.column() ) - { - case Artist: - return query->artist(); - break; - - case Track: - return query->track(); - break; - - case Album: - return query->album(); - break; - } - } - else - { - switch( index.column() ) - { - case Artist: - return query->results().first()->artist()->name(); - break; - - case Track: - return query->results().first()->track(); - break; - - case Album: - return query->results().first()->album()->name(); - break; - - case Composer: - if ( !query->results().first()->composer().isNull() ) - return query->results().first()->composer()->name(); - break; - - case Duration: - return TomahawkUtils::timeToString( query->results().first()->duration() ); - break; - - case Bitrate: - if ( query->results().first()->bitrate() > 0 ) - return query->results().first()->bitrate(); - break; - - case Age: - return TomahawkUtils::ageToString( QDateTime::fromTime_t( query->results().first()->modificationTime() ) ); - break; - - case Year: - if ( query->results().first()->year() != 0 ) - return query->results().first()->year(); - break; - - case Filesize: - return TomahawkUtils::filesizeToString( query->results().first()->size() ); - break; - - case Origin: - return query->results().first()->friendlySource(); - break; - - case Score: - return query->results().first()->score(); - break; - - case AlbumPos: - QString tPos; - if ( query->results().first()->albumpos() != 0 ) - { - tPos = QString::number( query->results().first()->albumpos() ); - if( query->results().first()->discnumber() == 0 ) - return tPos; - else - return QString( "%1.%2" ).arg( QString::number( query->results().first()->discnumber() ) ) - .arg( tPos ); - } - break; - } - } - - return QVariant(); -} - - -QVariant -TrackModel::headerData( int section, Qt::Orientation orientation, int role ) const -{ - Q_UNUSED( orientation ); - - QStringList headers; - headers << tr( "Artist" ) << tr( "Title" ) << tr( "Composer" ) << tr( "Album" ) << tr( "Track" ) << tr( "Duration" ) << tr( "Bitrate" ) << tr( "Age" ) << tr( "Year" ) << tr( "Size" ) << tr( "Origin" ) << tr( "Score" ); - if ( role == Qt::DisplayRole && section >= 0 ) - { - return headers.at( section ); - } - - if ( role == Qt::TextAlignmentRole ) - { - return QVariant( columnAlignment( section ) ); - } - - return QVariant(); -} - - -void -TrackModel::updateDetailedInfo( const QModelIndex& index ) -{ - if ( style() != TrackModel::Short && style() != TrackModel::Large ) - return; - - TrackModelItem* item = itemFromIndex( index ); - if ( item->query().isNull() ) - return; - - if ( style() == TrackModel::Short || style() == TrackModel::Large ) - { - item->query()->cover( QSize( 0, 0 ) ); - } - - if ( style() == TrackModel::Large ) - { - item->query()->loadSocialActions(); - } -} - - -void -TrackModel::setCurrentItem( const QModelIndex& index ) -{ - TrackModelItem* oldEntry = itemFromIndex( m_currentIndex ); - if ( oldEntry ) - { - oldEntry->setIsPlaying( false ); - } - - TrackModelItem* entry = itemFromIndex( index ); - if ( index.isValid() && entry && !entry->query().isNull() ) - { - m_currentIndex = index; - m_currentUuid = entry->query()->id(); - entry->setIsPlaying( true ); - } - else - { - m_currentIndex = QModelIndex(); - m_currentUuid = QString(); - } -} - - -Qt::DropActions -TrackModel::supportedDropActions() const -{ - return Qt::CopyAction | Qt::MoveAction; -} - - -Qt::ItemFlags -TrackModel::flags( const QModelIndex& index ) const -{ - Qt::ItemFlags defaultFlags = QAbstractItemModel::flags( index ); - - if ( index.isValid() && index.column() == 0 ) - return Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled | defaultFlags; - else - return Qt::ItemIsDropEnabled | defaultFlags; -} - - -QStringList -TrackModel::mimeTypes() const -{ - QStringList types; - types << "application/tomahawk.query.list"; - return types; -} - - -QMimeData* -TrackModel::mimeData( const QModelIndexList &indexes ) const -{ - qDebug() << Q_FUNC_INFO; - - QByteArray queryData; - QDataStream queryStream( &queryData, QIODevice::WriteOnly ); - - foreach ( const QModelIndex& i, indexes ) - { - if ( i.column() > 0 ) - continue; - - QModelIndex idx = index( i.row(), 0, i.parent() ); - TrackModelItem* item = itemFromIndex( idx ); - if ( item ) - { - const query_ptr& query = item->query(); - queryStream << qlonglong( &query ); - } - } - - QMimeData* mimeData = new QMimeData(); - mimeData->setData( "application/tomahawk.query.list", queryData ); - - return mimeData; -} - - -void -TrackModel::clear() -{ - if ( rowCount( QModelIndex() ) ) - { - emit loadingFinished(); - - emit beginResetModel(); - delete m_rootItem; - m_rootItem = 0; - m_rootItem = new TrackModelItem( 0, this ); - emit endResetModel(); - } -} - - -QList< query_ptr > -TrackModel::queries() const -{ - Q_ASSERT( m_rootItem ); - - QList< query_ptr > tracks; - foreach ( TrackModelItem* item, m_rootItem->children ) - { - tracks << item->query(); - } - - return tracks; -} - - -void -TrackModel::append( const Tomahawk::query_ptr& query ) -{ - insert( query, rowCount( QModelIndex() ) ); -} - - -void -TrackModel::append( const QList< Tomahawk::query_ptr >& queries ) -{ - insert( queries, rowCount( QModelIndex() ) ); -} - - -void -TrackModel::insert( const Tomahawk::query_ptr& query, int row ) -{ - if ( query.isNull() ) - return; - - QList< Tomahawk::query_ptr > ql; - ql << query; - - insert( ql, row ); -} - - -void -TrackModel::insert( const QList< Tomahawk::query_ptr >& queries, int row ) -{ - if ( !queries.count() ) - { - emit trackCountChanged( rowCount( QModelIndex() ) ); - return; - } - - int c = row; - QPair< int, int > crows; - crows.first = c; - crows.second = c + queries.count() - 1; - - emit beginInsertRows( QModelIndex(), crows.first, crows.second ); - - int i = 0; - TrackModelItem* plitem; - foreach( const query_ptr& query, queries ) - { - plitem = new TrackModelItem( query, m_rootItem, row + i ); - plitem->index = createIndex( row + i, 0, plitem ); - i++; - - if ( query->id() == currentItemUuid() ) - setCurrentItem( plitem->index ); - - connect( plitem, SIGNAL( dataChanged() ), SLOT( onDataChanged() ) ); - } - - emit endInsertRows(); - emit trackCountChanged( rowCount( QModelIndex() ) ); -} - - -void -TrackModel::remove( int row, bool moreToCome ) -{ - remove( index( row, 0, QModelIndex() ), moreToCome ); -} - - -void -TrackModel::remove( const QModelIndex& index, bool moreToCome ) -{ - if ( QThread::currentThread() != thread() ) - { - QMetaObject::invokeMethod( this, "remove", - Qt::QueuedConnection, - Q_ARG(const QModelIndex, index), - Q_ARG(bool, moreToCome) ); - return; - } - - if ( index.column() > 0 ) - return; - - TrackModelItem* item = itemFromIndex( index ); - if ( item ) - { - emit beginRemoveRows( index.parent(), index.row(), index.row() ); - delete item; - emit endRemoveRows(); - } - - if ( !moreToCome ) - emit trackCountChanged( rowCount( QModelIndex() ) ); -} - - -void -TrackModel::remove( const QList& indexes ) -{ - QList pil; - foreach ( const QModelIndex& idx, indexes ) - { - pil << idx; - } - - remove( pil ); -} - - -void -TrackModel::remove( const QList& indexes ) -{ - QList finalIndexes; - foreach ( const QPersistentModelIndex index, indexes ) - { - if ( index.column() > 0 ) - continue; - finalIndexes << index; - } - - for ( int i = 0; i < finalIndexes.count(); i++ ) - { - remove( finalIndexes.at( i ), i + 1 != finalIndexes.count() ); - } -} - - -TrackModelItem* -TrackModel::itemFromIndex( const QModelIndex& index ) const -{ - if ( index.isValid() ) - { - return static_cast( index.internalPointer() ); - } - else - { - return m_rootItem; - } -} - - -void -TrackModel::onPlaybackStarted( const Tomahawk::result_ptr& result ) -{ - TrackModelItem* oldEntry = itemFromIndex( m_currentIndex ); - if ( oldEntry && ( oldEntry->query().isNull() || !oldEntry->query()->numResults() || oldEntry->query()->results().first().data() != result.data() ) ) - { - oldEntry->setIsPlaying( false ); - } -} - - -void -TrackModel::onPlaybackStopped() -{ - TrackModelItem* oldEntry = itemFromIndex( m_currentIndex ); - if ( oldEntry ) - { - oldEntry->setIsPlaying( false ); - } -} - - -void -TrackModel::ensureResolved() -{ - for( int i = 0; i < rowCount( QModelIndex() ); i++ ) - { - query_ptr query = itemFromIndex( index( i, 0, QModelIndex() ) )->query(); - - if ( !query->resolvingFinished() ) - Pipeline::instance()->resolve( query ); - } -} - - -void -TrackModel::setStyle( TrackModel::TrackItemStyle style ) -{ - m_style = style; -} - - -Qt::Alignment -TrackModel::columnAlignment( int column ) const -{ - switch( column ) - { - case Age: - case AlbumPos: - case Bitrate: - case Duration: - case Filesize: - case Year: - return Qt::AlignHCenter; - break; - - default: - return Qt::AlignLeft; - } -} - - -void -TrackModel::onDataChanged() -{ - TrackModelItem* p = (TrackModelItem*)sender(); - if ( p && p->index.isValid() ) - emit dataChanged( p->index, p->index.sibling( p->index.row(), columnCount() - 1 ) ); -} diff --git a/src/libtomahawk/playlist/TrackModelItem.cpp b/src/libtomahawk/playlist/TrackModelItem.cpp deleted file mode 100644 index 3e707d664..000000000 --- a/src/libtomahawk/playlist/TrackModelItem.cpp +++ /dev/null @@ -1,117 +0,0 @@ - -/* === This file is part of Tomahawk Player - === - * - * Copyright 2010-2011, Christian Muehlhaeuser - * - * Tomahawk is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Tomahawk is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Tomahawk. If not, see . - */ - -#include "TrackModelItem.h" - -#include "Playlist.h" -#include "Query.h" -#include "utils/TomahawkUtils.h" -#include "utils/Logger.h" - -using namespace Tomahawk; - - -TrackModelItem::~TrackModelItem() -{ - // Don't use qDeleteAll here! The children will remove themselves - // from the list when they get deleted and the qDeleteAll iterator - // will fail badly! - if ( parent && index.isValid() ) - { - parent->children.remove( index.row() ); - } - - for ( int i = children.count() - 1; i >= 0; i-- ) - delete children.at( i ); -} - - -TrackModelItem::TrackModelItem( TrackModelItem* parent, QAbstractItemModel* model ) -{ - this->parent = parent; - this->model = model; - childCount = 0; - toberemoved = false; - m_isPlaying = false; - - if ( parent ) - { - parent->children.append( this ); - } -} - - -TrackModelItem::TrackModelItem( const Tomahawk::query_ptr& query, TrackModelItem* parent, int row ) - : QObject( parent ) -{ - setupItem( query, parent, row ); -} - - -TrackModelItem::TrackModelItem( const Tomahawk::plentry_ptr& entry, TrackModelItem* parent, int row ) - : QObject( parent ) - , m_entry( entry ) -{ - setupItem( entry->query(), parent, row ); -} - - -const Tomahawk::plentry_ptr& -TrackModelItem::entry() const -{ - return m_entry; -} - - -const Tomahawk::query_ptr& -TrackModelItem::query() const -{ - return m_query; -} - - -void -TrackModelItem::setupItem( const Tomahawk::query_ptr& query, TrackModelItem* parent, int row ) -{ - this->parent = parent; - if ( parent ) - { - if ( row < 0 ) - { - parent->children.append( this ); - row = parent->children.count() - 1; - } - else - { - parent->children.insert( row, this ); - } - - this->model = parent->model; - } - - m_isPlaying = false; - toberemoved = false; - m_query = query; - - connect( query.data(), SIGNAL( resultsAdded( QList ) ), SIGNAL( dataChanged() ) ); - connect( query.data(), SIGNAL( resultsRemoved( Tomahawk::result_ptr ) ), SIGNAL( dataChanged() ) ); - connect( query.data(), SIGNAL( resultsChanged() ), SIGNAL( dataChanged() ) ); - connect( query.data(), SIGNAL( updated() ), SIGNAL( dataChanged() ) ); - connect( query.data(), SIGNAL( socialActionsLoaded() ), SIGNAL( dataChanged() ) ); -} diff --git a/src/libtomahawk/playlist/TrackModelItem.h b/src/libtomahawk/playlist/TrackModelItem.h deleted file mode 100644 index 1e9e06bee..000000000 --- a/src/libtomahawk/playlist/TrackModelItem.h +++ /dev/null @@ -1,66 +0,0 @@ -/* === This file is part of Tomahawk Player - === - * - * Copyright 2010-2011, Christian Muehlhaeuser - * - * Tomahawk is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Tomahawk is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Tomahawk. If not, see . - */ - -#ifndef PLITEM_H -#define PLITEM_H - -#include -#include -#include -#include - -#include "Typedefs.h" - -#include "DllMacro.h" - -class DLLEXPORT TrackModelItem : public QObject -{ -Q_OBJECT - -public: - virtual ~TrackModelItem(); - - explicit TrackModelItem( TrackModelItem* parent = 0, QAbstractItemModel* model = 0 ); - explicit TrackModelItem( const Tomahawk::query_ptr& query, TrackModelItem* parent = 0, int row = -1 ); - explicit TrackModelItem( const Tomahawk::plentry_ptr& entry, TrackModelItem* parent = 0, int row = -1 ); - - const Tomahawk::plentry_ptr& entry() const; - const Tomahawk::query_ptr& query() const; - - bool isPlaying() { return m_isPlaying; } - void setIsPlaying( bool b ) { m_isPlaying = b; emit dataChanged(); } - - TrackModelItem* parent; - QVector children; - int childCount; - QPersistentModelIndex index; - QAbstractItemModel* model; - bool toberemoved; - -signals: - void dataChanged(); - -private: - void setupItem( const Tomahawk::query_ptr& query, TrackModelItem* parent, int row = -1 ); - - Tomahawk::plentry_ptr m_entry; - Tomahawk::query_ptr m_query; - bool m_isPlaying; -}; - -#endif // PLITEM_H diff --git a/src/libtomahawk/playlist/TrackProxyModel.cpp b/src/libtomahawk/playlist/TrackProxyModel.cpp deleted file mode 100644 index d28358162..000000000 --- a/src/libtomahawk/playlist/TrackProxyModel.cpp +++ /dev/null @@ -1,312 +0,0 @@ -/* === This file is part of Tomahawk Player - === - * - * Copyright 2010-2011, Christian Muehlhaeuser - * Copyright 2010-2012, Jeff Mitchell - * - * Tomahawk is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Tomahawk is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Tomahawk. If not, see . - */ - -#include "TrackProxyModel.h" - -#include - -#include "TrackProxyModelPlaylistInterface.h" -#include "Artist.h" -#include "Album.h" -#include "Query.h" -#include "utils/Logger.h" - - -TrackProxyModel::TrackProxyModel( QObject* parent ) - : QSortFilterProxyModel( parent ) - , m_model( 0 ) - , m_showOfflineResults( true ) -{ - setFilterCaseSensitivity( Qt::CaseInsensitive ); - setSortCaseSensitivity( Qt::CaseInsensitive ); - setDynamicSortFilter( true ); - - setSourceTrackModel( 0 ); -} - - -void -TrackProxyModel::setSourceModel( QAbstractItemModel* model ) -{ - Q_UNUSED( model ); - qDebug() << "Explicitly use setSourceTrackModel instead"; - Q_ASSERT( false ); -} - - -void -TrackProxyModel::setSourceTrackModel( TrackModel* sourceModel ) -{ - m_model = sourceModel; - - if ( m_model && m_model->metaObject()->indexOfSignal( "trackCountChanged(uint)" ) > -1 ) - connect( m_model, SIGNAL( trackCountChanged( unsigned int ) ), playlistInterface().data(), SIGNAL( sourceTrackCountChanged( unsigned int ) ) ); - - QSortFilterProxyModel::setSourceModel( m_model ); -} - - -bool -TrackProxyModel::filterAcceptsRow( int sourceRow, const QModelIndex& sourceParent ) const -{ - TrackModelItem* pi = itemFromIndex( sourceModel()->index( sourceRow, 0, sourceParent ) ); - if ( !pi ) - return false; - - const Tomahawk::query_ptr& q = pi->query(); - if ( q.isNull() ) // uh oh? filter out invalid queries i guess - return false; - - Tomahawk::result_ptr r; - if ( q->numResults() ) - r = q->results().first(); - - if ( !m_showOfflineResults && !r.isNull() && !r->isOnline() ) - return false; - - if ( filterRegExp().isEmpty() ) - return true; - - QStringList sl = filterRegExp().pattern().split( " ", QString::SkipEmptyParts ); - foreach( QString s, sl ) - { - s = s.toLower(); - if ( !r.isNull() ) - { - if ( !r->artist()->name().toLower().contains( s ) && - !r->album()->name().toLower().contains( s ) && - !r->track().toLower().contains( s ) ) - { - return false; - } - } - else - { - if ( !q->artist().toLower().contains( s ) && - !q->album().toLower().contains( s ) && - !q->track().toLower().contains( s ) ) - { - return false; - } - } - } - - return true; -} - - -void -TrackProxyModel::remove( const QModelIndex& index ) -{ - if ( !sourceModel() ) - return; - if ( !index.isValid() ) - return; - - sourceModel()->remove( mapToSource( index ) ); -} - - -void -TrackProxyModel::remove( const QModelIndexList& indexes ) -{ - if ( !sourceModel() ) - return; - - QList pil; - foreach ( const QModelIndex& idx, indexes ) - { - if ( idx.isValid() ) - pil << mapToSource( idx ); - } - - sourceModel()->remove( pil ); -} - - -void -TrackProxyModel::remove( const QList< QPersistentModelIndex >& indexes ) -{ - if ( !sourceModel() ) - return; - - QList pil; - foreach ( const QPersistentModelIndex& idx, indexes ) - { - if ( idx.isValid() ) - pil << mapToSource( idx ); - } - - sourceModel()->remove( pil ); -} - - -bool -TrackProxyModel::lessThan( const QModelIndex& left, const QModelIndex& right ) const -{ - TrackModelItem* p1 = itemFromIndex( left ); - TrackModelItem* p2 = itemFromIndex( right ); - - if ( !p1 ) - return true; - if ( !p2 ) - return false; - - const Tomahawk::query_ptr& q1 = p1->query(); - const Tomahawk::query_ptr& q2 = p2->query(); - - QString artist1 = q1->artistSortname(); - QString artist2 = q2->artistSortname(); - QString album1 = q1->album(); - QString album2 = q2->album(); - QString track1 = q1->track(); - QString track2 = q2->track(); - unsigned int albumpos1 = 0, albumpos2 = 0; - unsigned int discnumber1 = 0, discnumber2 = 0; - unsigned int bitrate1 = 0, bitrate2 = 0; - unsigned int mtime1 = 0, mtime2 = 0; - unsigned int size1 = 0, size2 = 0; - qint64 id1 = 0, id2 = 0; - - if ( q1->numResults() ) - { - const Tomahawk::result_ptr& r = q1->results().at( 0 ); - artist1 = r->artist()->sortname(); - album1 = r->album()->name(); - track1 = r->track(); - albumpos1 = r->albumpos(); - discnumber1 = qMax( 1, (int)r->discnumber() ); - bitrate1 = r->bitrate(); - mtime1 = r->modificationTime(); - id1 = r->trackId(); - size1 = r->size(); - } - if ( q2->numResults() ) - { - const Tomahawk::result_ptr& r = q2->results().at( 0 ); - artist2 = r->artist()->sortname(); - album2 = r->album()->name(); - track2 = r->track(); - albumpos2 = r->albumpos(); - discnumber2 = qMax( 1, (int)r->discnumber() ); - bitrate2 = r->bitrate(); - mtime2 = r->modificationTime(); - id2 = r->trackId(); - size2 = r->size(); - } - - // This makes it a stable sorter and prevents items from randomly jumping about. - if ( id1 == id2 ) - { - id1 = (qint64)&q1; - id2 = (qint64)&q2; - } - - if ( left.column() == TrackModel::Artist ) // sort by artist - { - if ( artist1 == artist2 ) - { - if ( album1 == album2 ) - { - if ( discnumber1 == discnumber2 ) - { - if ( albumpos1 == albumpos2 ) - return id1 < id2; - - return albumpos1 < albumpos2; - } - - return discnumber1 < discnumber2; - } - - return QString::localeAwareCompare( album1, album2 ) < 0; - } - - return QString::localeAwareCompare( artist1, artist2 ) < 0; - } - else if ( left.column() == TrackModel::Album ) // sort by album - { - if ( album1 == album2 ) - { - if ( discnumber1 == discnumber2 ) - { - if ( albumpos1 == albumpos2 ) - return id1 < id2; - - return albumpos1 < albumpos2; - } - - return discnumber1 < discnumber2; - } - - return QString::localeAwareCompare( album1, album2 ) < 0; - } - else if ( left.column() == TrackModel::Bitrate ) // sort by bitrate - { - if ( bitrate1 == bitrate2 ) - return id1 < id2; - - return bitrate1 < bitrate2; - } - else if ( left.column() == TrackModel::Age ) // sort by mtime - { - if ( mtime1 == mtime2 ) - return id1 < id2; - - return mtime1 < mtime2; - } - else if ( left.column() == TrackModel::Filesize ) // sort by file size - { - if ( size1 == size2 ) - return id1 < id2; - - return size1 < size2; - } - else if ( left.column() == TrackModel::AlbumPos ) // sort by album pos - { - if ( discnumber1 != discnumber2 ) - { - return discnumber1 < discnumber2; - } - else - { - if ( albumpos1 != albumpos2 ) - return albumpos1 < albumpos2; - } - } - - const QString& lefts = sourceModel()->data( left ).toString(); - const QString& rights = sourceModel()->data( right ).toString(); - if ( lefts == rights ) - return id1 < id2; - - return QString::localeAwareCompare( lefts, rights ) < 0; -} - - -Tomahawk::playlistinterface_ptr -TrackProxyModel::playlistInterface() -{ - if ( m_playlistInterface.isNull() ) - { - m_playlistInterface = Tomahawk::playlistinterface_ptr( new Tomahawk::TrackProxyModelPlaylistInterface( this ) ); - } - - return m_playlistInterface; -} diff --git a/src/libtomahawk/playlist/TrackView.cpp b/src/libtomahawk/playlist/TrackView.cpp index a49f5cc4c..418d92827 100644 --- a/src/libtomahawk/playlist/TrackView.cpp +++ b/src/libtomahawk/playlist/TrackView.cpp @@ -23,19 +23,21 @@ #include #include -#include "TrackHeader.h" +#include "ViewHeader.h" #include "ViewManager.h" -#include "TrackModel.h" -#include "TrackProxyModel.h" +#include "PlayableModel.h" +#include "PlayableProxyModel.h" +#include "PlayableItem.h" #include "audio/AudioEngine.h" #include "context/ContextWidget.h" #include "widgets/OverlayWidget.h" -#include "utils/TomahawkUtils.h" +#include "utils/TomahawkUtilsGui.h" #include "utils/Logger.h" #include "utils/Closure.h" #include "DropJob.h" #include "Artist.h" #include "Album.h" +#include "Source.h" #include "utils/AnimatedSpinner.h" #define SCROLL_TIMEOUT 280 @@ -48,14 +50,18 @@ TrackView::TrackView( QWidget* parent ) , m_model( 0 ) , m_proxyModel( 0 ) , m_delegate( 0 ) - , m_header( new TrackHeader( this ) ) + , m_header( new ViewHeader( this ) ) , m_overlay( new OverlayWidget( this ) ) - , m_loadingSpinner( new AnimatedSpinner( this ) ) + , m_loadingSpinner( new LoadingSpinner( this ) ) , m_resizing( false ) , m_dragging( false ) , m_updateContextView( true ) , m_contextMenu( new ContextMenu( this ) ) { + setFrameShape( QFrame::NoFrame ); + setAttribute( Qt::WA_MacShowFocusRect, 0 ); + + setContentsMargins( 0, 0, 0, 0 ); setMouseTracking( true ); setAlternatingRowColors( true ); setSelectionMode( QAbstractItemView::ExtendedSelection ); @@ -93,6 +99,8 @@ TrackView::TrackView( QWidget* parent ) connect( this, SIGNAL( doubleClicked( QModelIndex ) ), SLOT( onItemActivated( QModelIndex ) ) ); connect( this, SIGNAL( customContextMenuRequested( const QPoint& ) ), SLOT( onCustomContextMenu( const QPoint& ) ) ); connect( m_contextMenu, SIGNAL( triggered( int ) ), SLOT( onMenuTriggered( int ) ) ); + + setProxyModel( new PlayableProxyModel( this ) ); } @@ -111,7 +119,7 @@ TrackView::setGuid( const QString& guid ) void -TrackView::setProxyModel( TrackProxyModel* model ) +TrackView::setProxyModel( PlayableProxyModel* model ) { m_proxyModel = model; @@ -126,34 +134,32 @@ void TrackView::setModel( QAbstractItemModel* model ) { Q_UNUSED( model ); - tDebug() << "Explicitly use setTrackModel instead"; + tDebug() << "Explicitly use setPlayableModel instead"; Q_ASSERT( false ); } void -TrackView::setTrackModel( TrackModel* model ) +TrackView::setPlayableModel( PlayableModel* model ) { m_model = model; if ( m_proxyModel ) { - m_proxyModel->setSourceTrackModel( m_model ); + m_proxyModel->setSourcePlayableModel( m_model ); } - connect( m_model, SIGNAL( loadingStarted() ), m_loadingSpinner, SLOT( fadeIn() ) ); - connect( m_model, SIGNAL( loadingFinished() ), m_loadingSpinner, SLOT( fadeOut() ) ); - connect( m_proxyModel, SIGNAL( filterChanged( QString ) ), SLOT( onFilterChanged( QString ) ) ); connect( m_proxyModel, SIGNAL( rowsInserted( QModelIndex, int, int ) ), SLOT( onViewChanged() ) ); setAcceptDrops( true ); + m_header->setDefaultColumnWeights( model->columnWeights() ); switch( model->style() ) { - case TrackModel::Short: - case TrackModel::ShortWithAvatars: - case TrackModel::Large: + case PlayableModel::Short: + case PlayableModel::ShortWithAvatars: + case PlayableModel::Large: setHeaderHidden( true ); setHorizontalScrollBarPolicy( Qt::ScrollBarAlwaysOff ); break; @@ -162,13 +168,23 @@ TrackView::setTrackModel( TrackModel* model ) setHeaderHidden( false ); setHorizontalScrollBarPolicy( Qt::ScrollBarAsNeeded ); } + + emit modelChanged(); +} + + +void +TrackView::setEmptyTip( const QString& tip ) +{ + m_emptyTip = tip; + m_overlay->setText( tip ); } void TrackView::onViewChanged() { - if ( m_model->style() != TrackModel::Short && m_model->style() != TrackModel::Large ) // eventual FIXME? + if ( m_model->style() != PlayableModel::Short && m_model->style() != PlayableModel::Large ) // eventual FIXME? return; if ( m_timer.isActive() ) @@ -249,7 +265,7 @@ TrackView::currentChanged( const QModelIndex& current, const QModelIndex& previo if ( !m_updateContextView ) return; - TrackModelItem* item = m_model->itemFromIndex( m_proxyModel->mapToSource( current ) ); + PlayableItem* item = m_model->itemFromIndex( m_proxyModel->mapToSource( current ) ); if ( item ) { ViewManager::instance()->context()->setQuery( item->query() ); @@ -275,7 +291,7 @@ TrackView::startAutoPlay( const QModelIndex& index ) return; // item isn't playable but still resolving - TrackModelItem* item = m_model->itemFromIndex( m_proxyModel->mapToSource( index ) ); + PlayableItem* item = m_model->itemFromIndex( m_proxyModel->mapToSource( index ) ); if ( item && !item->query().isNull() && !item->query()->resolvingFinished() ) { m_autoPlaying = item->query(); // So we can kill it if user starts autoplaying this playlist again @@ -294,12 +310,11 @@ TrackView::startAutoPlay( const QModelIndex& index ) bool TrackView::tryToPlayItem( const QModelIndex& index ) { - TrackModelItem* item = m_model->itemFromIndex( m_proxyModel->mapToSource( index ) ); - if ( item && !item->query().isNull() && item->query()->numResults() ) + PlayableItem* item = m_model->itemFromIndex( m_proxyModel->mapToSource( index ) ); + if ( item && !item->query().isNull() ) { - tDebug() << "Result activated:" << item->query()->toString() << item->query()->results().first()->url(); m_proxyModel->setCurrentIndex( index ); - AudioEngine::instance()->playItem( m_proxyModel->playlistInterface(), item->query()->results().first() ); + AudioEngine::instance()->playItem( m_proxyModel->playlistInterface(), item->query() ); return true; } @@ -494,8 +509,17 @@ TrackView::onFilterChanged( const QString& ) m_overlay->show(); } else + { if ( model()->trackCount() ) + { m_overlay->hide(); + } + else + { + m_overlay->setText( m_emptyTip ); + m_overlay->show(); + } + } } @@ -556,7 +580,7 @@ TrackView::onCustomContextMenu( const QPoint& pos ) if ( index.column() ) continue; - TrackModelItem* item = proxyModel()->itemFromIndex( proxyModel()->mapToSource( index ) ); + PlayableItem* item = proxyModel()->itemFromIndex( proxyModel()->mapToSource( index ) ); if ( item && !item->query().isNull() ) { if ( item->query()->numResults() > 0 ) @@ -597,10 +621,10 @@ TrackView::updateHoverIndex( const QPoint& pos ) repaint(); } - if ( !m_model || m_model->style() != TrackModel::Detailed ) + if ( !m_model || m_model->style() != PlayableModel::Detailed ) return; - if ( idx.column() == TrackModel::Artist || idx.column() == TrackModel::Album || idx.column() == TrackModel::Track ) + if ( idx.column() == PlayableModel::Artist || idx.column() == PlayableModel::Album || idx.column() == PlayableModel::Track ) { if ( pos.x() > header()->sectionViewportPosition( idx.column() ) + header()->sectionSize( idx.column() ) - 16 && pos.x() < header()->sectionViewportPosition( idx.column() ) + header()->sectionSize( idx.column() ) ) @@ -649,19 +673,19 @@ TrackView::mousePressEvent( QMouseEvent* event ) { QTreeView::mousePressEvent( event ); - if ( !m_model || m_model->style() != TrackModel::Detailed ) + if ( !m_model || m_model->style() != PlayableModel::Detailed ) return; QModelIndex idx = indexAt( event->pos() ); if ( event->pos().x() > header()->sectionViewportPosition( idx.column() ) + header()->sectionSize( idx.column() ) - 16 && event->pos().x() < header()->sectionViewportPosition( idx.column() ) + header()->sectionSize( idx.column() ) ) { - TrackModelItem* item = proxyModel()->itemFromIndex( proxyModel()->mapToSource( idx ) ); + PlayableItem* item = proxyModel()->itemFromIndex( proxyModel()->mapToSource( idx ) ); switch ( idx.column() ) { - case TrackModel::Artist: + case PlayableModel::Artist: { - if ( item->query()->results().count() ) + if ( item->query()->numResults() ) { ViewManager::instance()->show( item->query()->results().first()->artist() ); } @@ -672,9 +696,9 @@ TrackView::mousePressEvent( QMouseEvent* event ) break; } - case TrackModel::Album: + case PlayableModel::Album: { - if ( item->query()->results().count() ) + if ( item->query()->numResults() ) { ViewManager::instance()->show( item->query()->results().first()->album() ); } @@ -686,7 +710,7 @@ TrackView::mousePressEvent( QMouseEvent* event ) break; } - case TrackModel::Track: + case PlayableModel::Track: { ViewManager::instance()->show( item->query() ); break; @@ -697,3 +721,39 @@ TrackView::mousePressEvent( QMouseEvent* event ) } } } + + +Tomahawk::playlistinterface_ptr +TrackView::playlistInterface() const +{ + return proxyModel()->playlistInterface(); +} + + +QString +TrackView::title() const +{ + return model()->title(); +} + + +QString +TrackView::description() const +{ + return model()->description(); +} + + +QPixmap +TrackView::pixmap() const +{ + return QPixmap( RESPATH "images/music-icon.png" ); +} + + +bool +TrackView::jumpToCurrentTrack() +{ + scrollTo( proxyModel()->currentIndex(), QAbstractItemView::PositionAtCenter ); + return true; +} diff --git a/src/libtomahawk/playlist/TrackView.h b/src/libtomahawk/playlist/TrackView.h index 1a27ccfed..24f119a7e 100644 --- a/src/libtomahawk/playlist/TrackView.h +++ b/src/libtomahawk/playlist/TrackView.h @@ -26,17 +26,18 @@ #include "ContextMenu.h" #include "PlaylistItemDelegate.h" +#include "ViewPage.h" #include "DllMacro.h" class QAction; class AnimatedSpinner; -class TrackHeader; -class TrackModel; -class TrackProxyModel; +class ViewHeader; +class PlayableModel; +class PlayableProxyModel; class OverlayWidget; -class DLLEXPORT TrackView : public QTreeView +class DLLEXPORT TrackView : public QTreeView, public Tomahawk::ViewPage { Q_OBJECT @@ -47,18 +48,32 @@ public: virtual QString guid() const { return m_guid; } virtual void setGuid( const QString& guid ); - virtual void setTrackModel( TrackModel* model ); + virtual void setPlayableModel( PlayableModel* model ); virtual void setModel( QAbstractItemModel* model ); - void setProxyModel( TrackProxyModel* model ); + void setProxyModel( PlayableProxyModel* model ); - virtual TrackModel* model() const { return m_model; } - TrackProxyModel* proxyModel() const { return m_proxyModel; } + virtual PlayableModel* model() const { return m_model; } + PlayableProxyModel* proxyModel() const { return m_proxyModel; } PlaylistItemDelegate* delegate() const { return m_delegate; } - TrackHeader* header() const { return m_header; } + ViewHeader* header() const { return m_header; } OverlayWidget* overlay() const { return m_overlay; } Tomahawk::ContextMenu* contextMenu() const { return m_contextMenu; } AnimatedSpinner* loadingSpinner() const { return m_loadingSpinner; } + void setEmptyTip( const QString& tip ); + + virtual QWidget* widget() { return this; } + virtual Tomahawk::playlistinterface_ptr playlistInterface() const; + + virtual QString title() const; + virtual QString description() const; + virtual QPixmap pixmap() const; + + virtual bool showModes() const { return true; } + virtual bool showFilter() const { return true; } + + virtual bool jumpToCurrentTrack(); + QModelIndex hoveredIndex() const { return m_hoveredIndex; } QModelIndex contextMenuIndex() const { return m_contextMenuIndex; } void setContextMenuIndex( const QModelIndex& idx ) { m_contextMenuIndex = idx; } @@ -80,6 +95,7 @@ public slots: signals: void itemActivated( const QModelIndex& index ); + void modelChanged(); protected: virtual void resizeEvent( QResizeEvent* event ); @@ -114,13 +130,14 @@ private: void updateHoverIndex( const QPoint& pos ); QString m_guid; - TrackModel* m_model; - TrackProxyModel* m_proxyModel; + PlayableModel* m_model; + PlayableProxyModel* m_proxyModel; PlaylistItemDelegate* m_delegate; - TrackHeader* m_header; + ViewHeader* m_header; OverlayWidget* m_overlay; AnimatedSpinner* m_loadingSpinner; + QString m_emptyTip; bool m_resizing; bool m_dragging; QRect m_dropRect; @@ -131,7 +148,6 @@ private: QModelIndex m_contextMenuIndex; Tomahawk::query_ptr m_autoPlaying; - Tomahawk::ContextMenu* m_contextMenu; QTimer m_timer; diff --git a/src/libtomahawk/playlist/TreeHeader.cpp b/src/libtomahawk/playlist/TreeHeader.cpp deleted file mode 100644 index 105268ade..000000000 --- a/src/libtomahawk/playlist/TreeHeader.cpp +++ /dev/null @@ -1,38 +0,0 @@ -/* === This file is part of Tomahawk Player - === - * - * Copyright 2010-2011, Christian Muehlhaeuser - * - * Tomahawk is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Tomahawk is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Tomahawk. If not, see . - */ - -#include "TreeHeader.h" - -#include "playlist/ArtistView.h" -#include "utils/Logger.h" - - -TreeHeader::TreeHeader( ArtistView* parent ) - : ViewHeader( parent ) - , m_parent( parent ) -{ - QList< double > columnWeights; - columnWeights << 0.42 << 0.12 << 0.07 << 0.07 << 0.07 << 0.07 << 0.07; // << 0.11; - - setDefaultColumnWeights( columnWeights ); -} - - -TreeHeader::~TreeHeader() -{ -} diff --git a/src/libtomahawk/playlist/TreeItemDelegate.cpp b/src/libtomahawk/playlist/TreeItemDelegate.cpp index 667de0351..c22847709 100644 --- a/src/libtomahawk/playlist/TreeItemDelegate.cpp +++ b/src/libtomahawk/playlist/TreeItemDelegate.cpp @@ -32,12 +32,13 @@ #include "utils/Closure.h" #include "utils/PixmapDelegateFader.h" -#include "TreeModelItem.h" +#include "PlayableItem.h" #include "TreeProxyModel.h" -#include "ArtistView.h" +#include "Source.h" +#include "TreeView.h" -TreeItemDelegate::TreeItemDelegate( ArtistView* parent, TreeProxyModel* proxy ) +TreeItemDelegate::TreeItemDelegate( TreeView* parent, TreeProxyModel* proxy ) : QStyledItemDelegate( (QObject*)parent ) , m_view( parent ) , m_model( proxy ) @@ -56,7 +57,7 @@ TreeItemDelegate::sizeHint( const QStyleOptionViewItem& option, const QModelInde void TreeItemDelegate::paint( QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index ) const { - TreeModelItem* item = m_model->sourceModel()->itemFromIndex( m_model->mapToSource( index ) ); + PlayableItem* item = m_model->sourceModel()->itemFromIndex( m_model->mapToSource( index ) ); if ( !item ) return; diff --git a/src/libtomahawk/playlist/TreeItemDelegate.h b/src/libtomahawk/playlist/TreeItemDelegate.h index 419bc6790..2c4e90f12 100644 --- a/src/libtomahawk/playlist/TreeItemDelegate.h +++ b/src/libtomahawk/playlist/TreeItemDelegate.h @@ -28,7 +28,7 @@ namespace Tomahawk { class PixmapDelegateFader; } -class ArtistView; +class TreeView; class TreeProxyModel; class DLLEXPORT TreeItemDelegate : public QStyledItemDelegate @@ -36,7 +36,7 @@ class DLLEXPORT TreeItemDelegate : public QStyledItemDelegate Q_OBJECT public: - TreeItemDelegate( ArtistView* parent = 0, TreeProxyModel* proxy = 0 ); + TreeItemDelegate( TreeView* parent = 0, TreeProxyModel* proxy = 0 ); protected: void paint( QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index ) const; @@ -51,7 +51,7 @@ private slots: void doUpdateIndex( const QPersistentModelIndex& index ); private: - ArtistView* m_view; + TreeView* m_view; TreeProxyModel* m_model; mutable QHash< QPersistentModelIndex, QSharedPointer< Tomahawk::PixmapDelegateFader > > m_pixmaps; diff --git a/src/libtomahawk/playlist/TreeModel.cpp b/src/libtomahawk/playlist/TreeModel.cpp index e4bb13fb2..9e9e57349 100644 --- a/src/libtomahawk/playlist/TreeModel.cpp +++ b/src/libtomahawk/playlist/TreeModel.cpp @@ -29,6 +29,8 @@ #include "database/DatabaseCommand_AllAlbums.h" #include "database/DatabaseCommand_AllTracks.h" #include "database/Database.h" +#include "AlbumPlaylistInterface.h" +#include "PlayableItem.h" #include "utils/TomahawkUtils.h" #include "utils/Logger.h" @@ -36,41 +38,20 @@ using namespace Tomahawk; TreeModel::TreeModel( QObject* parent ) - : QAbstractItemModel( parent ) - , m_rootItem( new TreeModelItem( 0, this ) ) - , m_infoId( uuid() ) - , m_columnStyle( AllColumns ) + : PlayableModel( parent ) , m_mode( DatabaseMode ) { + setStyle( Collection ); setIcon( QPixmap( RESPATH "images/music-icon.png" ) ); connect( AudioEngine::instance(), SIGNAL( started( Tomahawk::result_ptr ) ), SLOT( onPlaybackStarted( Tomahawk::result_ptr ) ), Qt::DirectConnection ); connect( AudioEngine::instance(), SIGNAL( stopped() ), SLOT( onPlaybackStopped() ), Qt::DirectConnection ); - - connect( Tomahawk::InfoSystem::InfoSystem::instance(), - SIGNAL( info( Tomahawk::InfoSystem::InfoRequestData, QVariant ) ), - SLOT( infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData, QVariant ) ) ); } TreeModel::~TreeModel() { -} - - -void -TreeModel::clear() -{ - if ( rowCount( QModelIndex() ) ) - { - emit loadingFinished(); - - emit beginResetModel(); - delete m_rootItem; - m_rootItem = 0; - m_rootItem = new TreeModelItem( 0, this ); - emit endResetModel(); - } + tDebug() << Q_FUNC_INFO; } @@ -93,60 +74,21 @@ TreeModel::collection() const void TreeModel::getCover( const QModelIndex& index ) { - TreeModelItem* item = itemFromIndex( index ); + PlayableItem* item = itemFromIndex( index ); - if ( !item->artist().isNull() && !item->artist()->infoLoaded() ) + if ( !item->artist().isNull() && !item->artist()->coverLoaded() ) item->artist()->cover( QSize( 0, 0 ) ); - else if ( !item->album().isNull() && !item->album()->infoLoaded() ) + else if ( !item->album().isNull() && !item->album()->coverLoaded() ) item->album()->cover( QSize( 0, 0 ) ); } -void -TreeModel::setCurrentItem( const QModelIndex& index ) -{ - qDebug() << Q_FUNC_INFO; - - TreeModelItem* oldEntry = itemFromIndex( m_currentIndex ); - if ( oldEntry ) - { - oldEntry->setIsPlaying( false ); - } - - TreeModelItem* entry = itemFromIndex( index ); - if ( entry ) - { - m_currentIndex = index; - entry->setIsPlaying( true ); - } - else - { - m_currentIndex = QModelIndex(); - } -} - - -QModelIndex -TreeModel::index( int row, int column, const QModelIndex& parent ) const -{ - if ( !m_rootItem || row < 0 || column < 0 ) - return QModelIndex(); - - TreeModelItem* parentItem = itemFromIndex( parent ); - TreeModelItem* childItem = parentItem->children.value( row ); - if ( !childItem ) - return QModelIndex(); - - return createIndex( row, column, childItem ); -} - - bool TreeModel::canFetchMore( const QModelIndex& parent ) const { - TreeModelItem* parentItem = itemFromIndex( parent ); + PlayableItem* parentItem = itemFromIndex( parent ); - if ( parentItem->fetchingMore ) + if ( parentItem->fetchingMore() ) return false; if ( !parentItem->artist().isNull() ) @@ -165,11 +107,11 @@ TreeModel::canFetchMore( const QModelIndex& parent ) const void TreeModel::fetchMore( const QModelIndex& parent ) { - TreeModelItem* parentItem = itemFromIndex( parent ); - if ( !parentItem || parentItem->fetchingMore ) + PlayableItem* parentItem = itemFromIndex( parent ); + if ( !parentItem || parentItem->fetchingMore() ) return; - parentItem->fetchingMore = true; + parentItem->setFetchingMore( true ); if ( !parentItem->artist().isNull() ) { tDebug() << Q_FUNC_INFO << "Loading Artist:" << parentItem->artist()->name(); @@ -185,388 +127,11 @@ TreeModel::fetchMore( const QModelIndex& parent ) } -bool -TreeModel::hasChildren( const QModelIndex& parent ) const -{ - TreeModelItem* parentItem = itemFromIndex( parent ); - if ( !parentItem ) - return false; - - if ( parentItem == m_rootItem ) - return true; - - return ( !parentItem->artist().isNull() || !parentItem->album().isNull() ); -} - - -int -TreeModel::rowCount( const QModelIndex& parent ) const -{ - if ( parent.column() > 0 ) - return 0; - - TreeModelItem* parentItem = itemFromIndex( parent ); - if ( !parentItem ) - return 0; - - return parentItem->children.count(); -} - - -int -TreeModel::columnCount( const QModelIndex& parent ) const -{ - Q_UNUSED( parent ); - - if ( m_columnStyle == AllColumns ) - return 8; - else if ( m_columnStyle == TrackOnly ) - return 1; - - // UH.. - return 0; -} - - -QModelIndex -TreeModel::parent( const QModelIndex& child ) const -{ - TreeModelItem* entry = itemFromIndex( child ); - if ( !entry ) - return QModelIndex(); - - TreeModelItem* parentEntry = entry->parent; - if ( !parentEntry ) - return QModelIndex(); - - TreeModelItem* grandparentEntry = parentEntry->parent; - if ( !grandparentEntry ) - return QModelIndex(); - - int row = grandparentEntry->children.indexOf( parentEntry ); - return createIndex( row, 0, parentEntry ); -} - - -QVariant -TreeModel::data( const QModelIndex& index, int role ) const -{ - TreeModelItem* entry = itemFromIndex( index ); - if ( !entry ) - return QVariant(); - - if ( role == Qt::SizeHintRole ) - { - if ( !entry->result().isNull() || !entry->query().isNull() ) - { - return QSize( 128, 20 ); - } - else if ( !entry->album().isNull() ) - { - return QSize( 128, 32 ); - } - else if ( !entry->artist().isNull() ) - { - return QSize( 128, 44 ); - } - - return QSize( 128, 0 ); - } - - if ( role != Qt::DisplayRole ) // && role != Qt::ToolTipRole ) - return QVariant(); - - if ( !entry->artist().isNull() && index.column() == Name ) - { - return entry->artist()->name(); - } - else if ( !entry->album().isNull() && index.column() == Name ) - { - return entry->album()->name(); - } - else if ( !entry->result().isNull() ) - { - const result_ptr& result = entry->result(); - unsigned int discnumber = 0; - if ( !entry->query().isNull() ) - discnumber = entry->query()->discnumber(); - if ( discnumber == 0 ) - discnumber = result->discnumber(); - - unsigned int albumpos = 0; - if ( !entry->query().isNull() ) - albumpos = entry->query()->albumpos(); - if ( albumpos == 0 ) - albumpos = result->albumpos(); - - switch( index.column() ) - { - case Name: - return QString( "%1%2%3" ).arg( discnumber > 0 ? QString( "%1." ).arg( discnumber ) : QString() ) - .arg( albumpos > 0 ? QString( "%1. ").arg( albumpos ) : QString() ) - .arg( result->track() ); - - case Duration: - return TomahawkUtils::timeToString( result->duration() ); - - case Bitrate: - if ( result->bitrate() > 0 ) - return result->bitrate(); - break; - - case Age: - return TomahawkUtils::ageToString( QDateTime::fromTime_t( result->modificationTime() ) ); - - case Year: - if ( result->year() != 0 ) - return result->year(); - break; - - case Filesize: - return TomahawkUtils::filesizeToString( result->size() ); - - case Origin: - return result->friendlySource(); - - case AlbumPosition: - return result->albumpos(); - - case Composer: - if ( !result->composer().isNull() ) - return result->composer()->name(); - break; - - default: - return QVariant(); - } - } - else if ( !entry->query().isNull() ) - { - const query_ptr& query = entry->query(); - switch( index.column() ) - { - case Name: - return QString( "%1%2%3" ).arg( query->discnumber() > 0 ? QString( "%1." ).arg( query->discnumber() ) : QString() ) - .arg( query->albumpos() > 0 ? QString( "%1. ").arg( query->albumpos() ) : QString() ) - .arg( query->track() ); - - case AlbumPosition: - return entry->query()->albumpos(); - - default: - return QVariant(); - } - } - - return QVariant(); -} - - -QVariant -TreeModel::headerData( int section, Qt::Orientation orientation, int role ) const -{ - QStringList headers; - headers << tr( "Name" ) << tr( "Composer" ) << tr( "Duration" ) << tr( "Bitrate" ) << tr( "Age" ) << tr( "Year" ) << tr( "Size" ) << tr( "Origin" ); - if ( orientation == Qt::Horizontal && role == Qt::DisplayRole && section >= 0 ) - { - return headers.at( section ); - } - - return QVariant(); -} - - -Qt::ItemFlags -TreeModel::flags( const QModelIndex& index ) const -{ - Qt::ItemFlags defaultFlags = QAbstractItemModel::flags( index ); - - if ( index.isValid() && index.column() == 0 ) - { - TreeModelItem* item = itemFromIndex( index ); - if ( item && !item->result().isNull() ) - return Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled | defaultFlags; - if ( item && ( !item->album().isNull() || !item->artist().isNull() ) ) - return Qt::ItemIsDragEnabled | defaultFlags; - } - - return defaultFlags; -} - - -QStringList -TreeModel::mimeTypes() const -{ - QStringList types; - types << "application/tomahawk.mixed"; - return types; -} - - -QMimeData* -TreeModel::mimeData( const QModelIndexList &indexes ) const -{ - qDebug() << Q_FUNC_INFO; - - QByteArray resultData; - QDataStream resultStream( &resultData, QIODevice::WriteOnly ); - - // lets try with artist only - bool fail = false; - foreach ( const QModelIndex& i, indexes) - { - if ( i.column() > 0 || indexes.contains( i.parent() ) ) - continue; - - TreeModelItem* item = itemFromIndex( i ); - if ( !item ) - continue; - - if ( !item->artist().isNull() ) - { - const artist_ptr& artist = item->artist(); - resultStream << artist->name(); - } - else - { - fail = true; - break; - } - } - if ( !fail ) - { - QMimeData* mimeData = new QMimeData(); - mimeData->setData( "application/tomahawk.metadata.artist", resultData ); - return mimeData; - } - - // lets try with album only - fail = false; - resultData.clear(); - foreach ( const QModelIndex& i, indexes ) - { - if ( i.column() > 0 || indexes.contains( i.parent() ) ) - continue; - - TreeModelItem* item = itemFromIndex( i ); - if ( !item ) - continue; - - if ( !item->album().isNull() ) - { - const album_ptr& album = item->album(); - resultStream << album->artist()->name(); - resultStream << album->name(); - } - else - { - fail = true; - break; - } - } - if ( !fail ) - { - QMimeData* mimeData = new QMimeData(); - mimeData->setData( "application/tomahawk.metadata.album", resultData ); - return mimeData; - } - - // lets try with tracks only - fail = false; - resultData.clear(); - foreach ( const QModelIndex& i, indexes ) - { - if ( i.column() > 0 || indexes.contains( i.parent() ) ) - continue; - - TreeModelItem* item = itemFromIndex( i ); - if ( !item ) - continue; - - if ( !item->result().isNull() ) - { - const result_ptr& result = item->result(); - resultStream << qlonglong( &result ); - } - else - { - fail = true; - break; - } - } - if ( !fail ) - { - QMimeData* mimeData = new QMimeData(); - mimeData->setData( "application/tomahawk.result.list", resultData ); - return mimeData; - } - - // Ok... we have to use mixed - resultData.clear(); - foreach ( const QModelIndex& i, indexes ) - { - if ( i.column() > 0 || indexes.contains( i.parent() ) ) - continue; - - TreeModelItem* item = itemFromIndex( i ); - if ( !item ) - continue; - - if ( !item->artist().isNull() ) - { - const artist_ptr& artist = item->artist(); - resultStream << QString( "application/tomahawk.metadata.artist" ) << artist->name(); - } - else if ( !item->album().isNull() ) - { - const album_ptr& album = item->album(); - resultStream << QString( "application/tomahawk.metadata.album" ) << album->artist()->name() << album->name(); - } - else if ( !item->result().isNull() ) - { - const result_ptr& result = item->result(); - resultStream << QString( "application/tomahawk.result.list" ) << qlonglong( &result ); - } - } - - QMimeData* mimeData = new QMimeData(); - mimeData->setData( "application/tomahawk.mixed", resultData ); - return mimeData; -} - - -void -TreeModel::removeIndex( const QModelIndex& index ) -{ - qDebug() << Q_FUNC_INFO; - - if ( index.column() > 0 ) - return; - - TreeModelItem* item = itemFromIndex( index ); - if ( item ) - { - emit beginRemoveRows( index.parent(), index.row(), index.row() ); - delete item; - emit endRemoveRows(); - } -} - - -void -TreeModel::removeIndexes( const QList& indexes ) -{ - foreach( const QModelIndex& idx, indexes ) - { - removeIndex( idx ); - } -} - - void TreeModel::addAllCollections() { - emit loadingStarted(); + startLoading(); + DatabaseCommand_AllArtists* cmd = new DatabaseCommand_AllArtists(); connect( cmd, SIGNAL( artists( QList ) ), @@ -582,7 +147,7 @@ TreeModel::addAllCollections() connect( source->collection().data(), SIGNAL( changed() ), SLOT( onCollectionChanged() ), Qt::UniqueConnection ); } - m_title = tr( "All Artists" ); + setTitle( tr( "All Artists" ) ); } @@ -592,7 +157,7 @@ TreeModel::addArtists( const artist_ptr& artist ) if ( artist.isNull() ) return; - emit loadingStarted(); + startLoading(); QList artists; artists << artist; @@ -603,7 +168,7 @@ TreeModel::addArtists( const artist_ptr& artist ) void TreeModel::fetchAlbums( const artist_ptr& artist ) { - emit loadingStarted(); + startLoading(); connect( artist.data(), SIGNAL( albumsAdded( QList, Tomahawk::ModelMode ) ), SLOT( onAlbumsFound( QList, Tomahawk::ModelMode ) ), Qt::UniqueConnection ); @@ -619,11 +184,15 @@ TreeModel::onAlbumsFound( const QList& albums, ModelMode mo if ( m_mode != mode ) return; - const artist_ptr artist = qobject_cast< Artist* >( sender() )->weakRef().toStrongRef(); - disconnect( artist.data(), SIGNAL( albumsAdded( QList, Tomahawk::ModelMode ) ), - this, SLOT( onAlbumsFound( QList, Tomahawk::ModelMode ) ) ); + Tomahawk::Artist* artist = qobject_cast< Tomahawk::Artist* >( sender() ); + if ( !artist ) + return; - const QModelIndex parent = indexFromArtist( artist ); + const artist_ptr artistp = artist->weakRef().toStrongRef(); + disconnect( artist, SIGNAL( albumsAdded( QList, Tomahawk::ModelMode ) ), + this, SLOT( onAlbumsFound( QList, Tomahawk::ModelMode ) ) ); + + const QModelIndex parent = indexFromArtist( artistp ); addAlbums( parent, albums ); } @@ -631,11 +200,11 @@ TreeModel::onAlbumsFound( const QList& albums, ModelMode mo void TreeModel::addAlbums( const QModelIndex& parent, const QList& albums ) { - emit loadingFinished(); + finishLoading(); if ( !albums.count() ) return; - TreeModelItem* parentItem = itemFromIndex( parent ); + PlayableItem* parentItem = itemFromIndex( parent ); QPair< int, int > crows; const int c = rowCount( parent ); @@ -644,10 +213,10 @@ TreeModel::addAlbums( const QModelIndex& parent, const QListindex = createIndex( parentItem->children.count() - 1, 0, albumitem ); connect( albumitem, SIGNAL( dataChanged() ), SLOT( onDataChanged() ) ); @@ -661,42 +230,15 @@ TreeModel::addAlbums( const QModelIndex& parent, const QList rows; - rows << parent.row(); - rows << parent.parent().row(); + startLoading(); - if ( m_mode == DatabaseMode ) - { - DatabaseCommand_AllTracks* cmd = new DatabaseCommand_AllTracks( m_collection ); - cmd->setAlbum( album ); - cmd->setData( QVariant( rows ) ); + connect( album.data(), SIGNAL( tracksAdded( QList, Tomahawk::ModelMode, Tomahawk::collection_ptr ) ), + SLOT( onTracksFound( QList, Tomahawk::ModelMode, Tomahawk::collection_ptr ) ) ); - connect( cmd, SIGNAL( tracks( QList, QVariant ) ), - SLOT( onTracksFound( QList, QVariant ) ) ); - - Database::instance()->enqueue( QSharedPointer( cmd ) ); - } - else if ( m_mode == InfoSystemMode ) - { - Tomahawk::InfoSystem::InfoStringHash artistInfo; - artistInfo["artist"] = album->artist()->name(); - artistInfo["album"] = album->name(); - - m_receivedInfoData.removeAll( artistInfo ); - Tomahawk::InfoSystem::InfoRequestData requestData; - requestData.caller = m_infoId; - requestData.customData["rows"] = rows; - requestData.customData["refetch"] = autoRefetch; - 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 - Q_ASSERT( false ); + if ( !album->tracks( m_mode, m_collection ).isEmpty() ) + onTracksAdded( album->tracks( m_mode, m_collection ), parent ); } @@ -774,12 +316,7 @@ TreeModel::onCollectionChanged() void TreeModel::onArtistsAdded( const QList& artists ) { - emit loadingFinished(); - if ( !artists.count() ) - { - emit itemCountChanged( rowCount( QModelIndex() ) ); - return; - } + finishLoading(); int c = rowCount( QModelIndex() ); QPair< int, int > crows; @@ -788,27 +325,26 @@ TreeModel::onArtistsAdded( const QList& artists ) emit beginInsertRows( QModelIndex(), crows.first, crows.second ); - TreeModelItem* artistitem; + PlayableItem* artistitem; foreach( const artist_ptr& artist, artists ) { - artistitem = new TreeModelItem( artist, m_rootItem ); - artistitem->index = createIndex( m_rootItem->children.count() - 1, 0, artistitem ); + artistitem = new PlayableItem( artist, rootItem() ); + artistitem->index = createIndex( rootItem()->children.count() - 1, 0, artistitem ); connect( artistitem, SIGNAL( dataChanged() ), SLOT( onDataChanged() ) ); } emit endInsertRows(); - emit itemCountChanged( rowCount( QModelIndex() ) ); } void TreeModel::onTracksAdded( const QList& tracks, const QModelIndex& parent ) { - emit loadingFinished(); + finishLoading(); if ( !tracks.count() ) return; - TreeModelItem* parentItem = itemFromIndex( parent ); + PlayableItem* parentItem = itemFromIndex( parent ); QPair< int, int > crows; int c = rowCount( parent ); @@ -817,14 +353,10 @@ TreeModel::onTracksAdded( const QList& tracks, const QModel emit beginInsertRows( parent, crows.first, crows.second ); - TreeModelItem* item = 0; + PlayableItem* item = 0; foreach( const query_ptr& query, tracks ) { - if ( query->numResults() ) - item = new TreeModelItem( query->results().first(), parentItem ); - else - item = new TreeModelItem( query, parentItem ); - + item = new PlayableItem( query, parentItem ); item->index = createIndex( parentItem->children.count() - 1, 0, item ); connect( item, SIGNAL( dataChanged() ), SLOT( onDataChanged() ) ); @@ -835,140 +367,25 @@ TreeModel::onTracksAdded( const QList& tracks, const QModel void -TreeModel::onTracksFound( const QList& tracks, const QVariant& variant ) +TreeModel::onTracksFound( const QList& tracks, Tomahawk::ModelMode mode, Tomahawk::collection_ptr collection ) { - QList< QVariant > rows = variant.toList(); - QModelIndex idx = index( rows.first().toUInt(), 0, index( rows.at( 1 ).toUInt(), 0, QModelIndex() ) ); - - onTracksAdded( tracks, idx ); -} - - -void -TreeModel::infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData requestData, QVariant output ) -{ - if ( requestData.caller != m_infoId ) - { + if ( mode != m_mode || collection != m_collection ) return; - } - switch ( requestData.type ) - { - case Tomahawk::InfoSystem::InfoAlbumSongs: - { - m_receivedInfoData.append( requestData.input.value< Tomahawk::InfoSystem::InfoStringHash >() ); + Tomahawk::Album* album = qobject_cast( sender() ); - QVariantMap returnedData = output.value< QVariantMap >(); - if ( !returnedData.isEmpty() ) - { - emit loadingFinished(); - - QList< QVariant > rows = requestData.customData[ "rows" ].toList(); - QModelIndex idx = index( rows.first().toUInt(), 0, index( rows.at( 1 ).toUInt(), 0, QModelIndex() ) ); - if ( rowCount( idx ) ) - return; - - Tomahawk::InfoSystem::InfoStringHash inputInfo; - inputInfo = requestData.input.value< Tomahawk::InfoSystem::InfoStringHash >(); - - QStringList tracks = returnedData[ "tracks" ].toStringList(); - QList ql; - - //TODO: Figure out how to do this with a multi-disk album without breaking the - // current behaviour. I just know too little about InfoSystem to deal with - // it right now, I've only taken the liberty of adding Query::setDiscNumber - // which should make this easier. --Teo 11/2011 - unsigned int trackNo = 1; - - foreach ( const QString& trackName, tracks ) - { - query_ptr query = Query::get( inputInfo[ "artist" ], trackName, inputInfo[ "album" ] ); - query->setAlbumPos( trackNo++ ); - ql << query; - } - Pipeline::instance()->resolve( ql ); - - onTracksAdded( ql, idx ); - } - else if ( m_receivedInfoData.count() == 2 /* FIXME */ ) - { - // If the second load got no data, but the first load did, don't do anything - QList< QVariant > rows = requestData.customData[ "rows" ].toList(); - QModelIndex idx = index( rows.first().toUInt(), 0, index( rows.at( 1 ).toUInt(), 0, QModelIndex() ) ); - if ( rowCount( idx ) ) - return; - - if ( requestData.customData[ "refetch" ].toBool() ) - { - setMode( DatabaseMode ); - - Tomahawk::InfoSystem::InfoStringHash inputInfo; - inputInfo = requestData.input.value< InfoSystem::InfoStringHash >(); - artist_ptr artist = Artist::get( inputInfo[ "artist" ], false ); - album_ptr album = Album::get( artist, inputInfo[ "album" ], false ); - - addTracks( album, QModelIndex() ); - } - else - emit loadingFinished(); - } - - break; - } - - default: - { - Q_ASSERT( false ); - break; - } - } -} - - -void -TreeModel::onPlaybackStarted( const Tomahawk::result_ptr& result ) -{ - TreeModelItem* oldEntry = itemFromIndex( m_currentIndex ); - if ( oldEntry && ( oldEntry->result().isNull() || oldEntry->result().data() != result.data() ) ) - { - oldEntry->setIsPlaying( false ); - } -} - - -void -TreeModel::onPlaybackStopped() -{ - TreeModelItem* oldEntry = itemFromIndex( m_currentIndex ); - if ( oldEntry ) - { - oldEntry->setIsPlaying( false ); - } -} - - -void -TreeModel::onDataChanged() -{ - TreeModelItem* p = (TreeModelItem*)sender(); - emit dataChanged( p->index, p->index.sibling( p->index.row(), columnCount( QModelIndex() ) - 1 ) ); -} - - -void -TreeModel::setColumnStyle( TreeModel::ColumnStyle style ) -{ - m_columnStyle = style; + QModelIndex idx = indexFromAlbum( album->weakRef().toStrongRef() ); + onTracksAdded( tracks, idx ); } QModelIndex TreeModel::indexFromArtist( const Tomahawk::artist_ptr& artist ) const { - for ( int i = 0; i < rowCount(); i++ ) + for ( int i = 0; i < rowCount( QModelIndex() ); i++ ) { QModelIndex idx = index( i, 0, QModelIndex() ); - TreeModelItem* item = itemFromIndex( idx ); + PlayableItem* item = itemFromIndex( idx ); if ( item && item->artist() == artist ) { return idx; @@ -977,3 +394,21 @@ TreeModel::indexFromArtist( const Tomahawk::artist_ptr& artist ) const return QModelIndex(); } + + +QModelIndex +TreeModel::indexFromAlbum( const Tomahawk::album_ptr& album ) const +{ + QModelIndex artistIdx = indexFromArtist( album->artist() ); + for ( int i = 0; i < rowCount( artistIdx ); i++ ) + { + QModelIndex idx = index( i, 0, artistIdx ); + PlayableItem* item = itemFromIndex( idx ); + if ( item && item->album() == album ) + { + return idx; + } + } + + return QModelIndex(); +} diff --git a/src/libtomahawk/playlist/TreeModel.h b/src/libtomahawk/playlist/TreeModel.h index 94d3b9a9a..6fb91d3c3 100644 --- a/src/libtomahawk/playlist/TreeModel.h +++ b/src/libtomahawk/playlist/TreeModel.h @@ -27,68 +27,28 @@ #include "Album.h" #include "Query.h" #include "Result.h" +#include "PlayableModel.h" #include "PlaylistInterface.h" #include "database/DatabaseCommand_AllArtists.h" -#include "TreeModelItem.h" -#include "infosystem/InfoSystem.h" - #include "DllMacro.h" #include "Typedefs.h" class QMetaData; -class DLLEXPORT TreeModel : public QAbstractItemModel +class PlayableItem; + +class DLLEXPORT TreeModel : public PlayableModel { Q_OBJECT public: - enum Columns { - Name = 0, - Composer, - Duration, - Bitrate, - Age, - Year, - Filesize, - Origin, - AlbumPosition - }; - - enum ColumnStyle - { AllColumns = 0, TrackOnly }; - explicit TreeModel( QObject* parent = 0 ); virtual ~TreeModel(); - virtual QModelIndex index( int row, int column, const QModelIndex& parent ) const; - virtual QModelIndex parent( const QModelIndex& child ) const; - - virtual bool isReadOnly() const { return true; } - - virtual int trackCount() const { return rowCount( QModelIndex() ); } - virtual int albumCount() const { return rowCount( QModelIndex() ); } - - virtual bool hasChildren( const QModelIndex& parent = QModelIndex() ) const; - virtual int rowCount( const QModelIndex& parent = QModelIndex() ) const; - virtual int columnCount( const QModelIndex& parent = QModelIndex() ) const; - virtual Tomahawk::ModelMode mode() const { return m_mode; } virtual void setMode( Tomahawk::ModelMode mode ); - virtual QVariant data( const QModelIndex& index, int role = Qt::DisplayRole ) const; - virtual QVariant headerData( int section, Qt::Orientation orientation, int role ) const; - - virtual void clear(); - virtual void removeIndex( const QModelIndex& index ); - virtual void removeIndexes( const QList& indexes ); - - virtual QMimeData* mimeData( const QModelIndexList& indexes ) const; - virtual QStringList mimeTypes() const; - virtual Qt::ItemFlags flags( const QModelIndex& index ) const; - - virtual QPersistentModelIndex currentItem() { return m_currentIndex; } - Tomahawk::collection_ptr collection() const; void addAllCollections(); @@ -101,46 +61,14 @@ public: void getCover( const QModelIndex& index ); - ColumnStyle columnStyle() const { return m_columnStyle; } - void setColumnStyle( ColumnStyle style ); - - virtual QString title() const { return m_title; } - virtual QString description() const { return m_description; } - virtual QPixmap icon() const { return m_icon; } - virtual void setTitle( const QString& title ) { m_title = title; } - virtual void setDescription( const QString& description ) { m_description = description; } - virtual void setIcon( const QPixmap& pixmap ) { m_icon = pixmap; } - QModelIndex indexFromArtist( const Tomahawk::artist_ptr& artist ) const; - TreeModelItem* itemFromIndex( const QModelIndex& index ) const - { - if ( index.isValid() ) - { - return static_cast( index.internalPointer() ); - } - else - { - return m_rootItem; - } - } + QModelIndex indexFromAlbum( const Tomahawk::album_ptr& album ) const; public slots: - virtual void setCurrentItem( const QModelIndex& index ); - - virtual void setRepeatMode( Tomahawk::PlaylistInterface::RepeatMode /*mode*/ ) {} - virtual void setShuffled( bool /*shuffled*/ ) {} - void addAlbums( const QModelIndex& parent, const QList& albums ); signals: - void repeatModeChanged( Tomahawk::PlaylistInterface::RepeatMode mode ); - void shuffleModeChanged( bool enabled ); - void modeChanged( Tomahawk::ModelMode mode ); - void itemCountChanged( unsigned int items ); - - void loadingStarted(); - void loadingFinished(); protected: bool canFetchMore( const QModelIndex& parent ) const; @@ -150,33 +78,16 @@ private slots: void onArtistsAdded( const QList& artists ); void onAlbumsFound( const QList& albums, Tomahawk::ModelMode mode ); void onTracksAdded( const QList& tracks, const QModelIndex& index ); - void onTracksFound( const QList& tracks, const QVariant& variant ); - - void infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData requestData, QVariant output ); - - void onPlaybackStarted( const Tomahawk::result_ptr& result ); - void onPlaybackStopped(); - - void onDataChanged(); + void onTracksFound( const QList& tracks, Tomahawk::ModelMode mode, Tomahawk::collection_ptr collection ); void onSourceAdded( const Tomahawk::source_ptr& source ); void onCollectionChanged(); private: - QPersistentModelIndex m_currentIndex; - TreeModelItem* m_rootItem; - QString m_infoId; - - QString m_title; - QString m_description; - QPixmap m_icon; - ColumnStyle m_columnStyle; Tomahawk::ModelMode m_mode; + Tomahawk::collection_ptr m_collection; QList m_artistsFilter; - - Tomahawk::collection_ptr m_collection; - QList m_receivedInfoData; }; #endif // ALBUMMODEL_H diff --git a/src/libtomahawk/playlist/TreeProxyModel.cpp b/src/libtomahawk/playlist/TreeProxyModel.cpp index 57adf9630..8d181af65 100644 --- a/src/libtomahawk/playlist/TreeProxyModel.cpp +++ b/src/libtomahawk/playlist/TreeProxyModel.cpp @@ -27,56 +27,35 @@ #include "database/Database.h" #include "database/DatabaseImpl.h" #include "database/DatabaseCommand_AllAlbums.h" +#include "PlayableItem.h" #include "utils/Logger.h" TreeProxyModel::TreeProxyModel( QObject* parent ) - : QSortFilterProxyModel( parent ) + : PlayableProxyModel( parent ) , m_artistsFilterCmd( 0 ) , m_model( 0 ) { - setFilterCaseSensitivity( Qt::CaseInsensitive ); - setSortCaseSensitivity( Qt::CaseInsensitive ); - setDynamicSortFilter( true ); - - setSourceTreeModel( 0 ); -} - - -QPersistentModelIndex -TreeProxyModel::currentIndex() const -{ - if ( !m_model ) - return QPersistentModelIndex(); - - return mapFromSource( m_model->currentItem() ); } void -TreeProxyModel::setSourceModel( QAbstractItemModel* sourceModel ) +TreeProxyModel::setSourcePlayableModel( TreeModel* model ) { - Q_UNUSED( sourceModel ); - qDebug() << "Explicitly use setSourceTreeModel instead"; - Q_ASSERT( false ); -} - - -void -TreeProxyModel::setSourceTreeModel( TreeModel* sourceModel ) -{ - m_model = sourceModel; - - if ( m_model ) + if ( sourceModel() ) { - if ( m_model->metaObject()->indexOfSignal( "trackCountChanged(uint)" ) > -1 ) - connect( m_model, SIGNAL( trackCountChanged( unsigned int ) ), SIGNAL( sourceTrackCountChanged( unsigned int ) ) ); + disconnect( m_model, SIGNAL( rowsInserted( QModelIndex, int, int ) ), this, SLOT( onRowsInserted( QModelIndex, int, int ) ) ); + disconnect( m_model, SIGNAL( modelReset() ), this, SLOT( onModelReset() ) ); + } + PlayableProxyModel::setSourcePlayableModel( model ); + m_model = model; + + if ( sourceModel() ) + { connect( m_model, SIGNAL( rowsInserted( QModelIndex, int, int ) ), SLOT( onRowsInserted( QModelIndex, int, int ) ) ); connect( m_model, SIGNAL( modelReset() ), SLOT( onModelReset() ) ); } - - QSortFilterProxyModel::setSourceModel( sourceModel ); } @@ -88,7 +67,7 @@ TreeProxyModel::onRowsInserted( const QModelIndex& parent, int /* start */, int if ( sender() != m_model ) return; - TreeModelItem* pi = m_model->itemFromIndex( m_model->index( parent.row(), 0, parent.parent() ) ); + PlayableItem* pi = m_model->itemFromIndex( m_model->index( parent.row(), 0, parent.parent() ) ); if ( pi->artist().isNull() ) return; @@ -113,7 +92,7 @@ TreeProxyModel::onModelReset() void -TreeProxyModel::newFilterFromPlaylistInterface( const QString &pattern ) +TreeProxyModel::newFilterFromPlaylistInterface( const QString& pattern ) { emit filteringStarted(); @@ -208,7 +187,7 @@ TreeProxyModel::filterFinished() bool TreeProxyModel::filterAcceptsRow( int sourceRow, const QModelIndex& sourceParent ) const { - TreeModelItem* item = sourceModel()->itemFromIndex( sourceModel()->index( sourceRow, 0, sourceParent ) ); + PlayableItem* item = sourceModel()->itemFromIndex( sourceModel()->index( sourceRow, 0, sourceParent ) ); Q_ASSERT( item ); if ( m_model->mode() == Tomahawk::DatabaseMode && !item->result().isNull() ) @@ -231,7 +210,7 @@ TreeProxyModel::filterAcceptsRow( int sourceRow, const QModelIndex& sourceParent if ( i == sourceRow ) continue; - TreeModelItem* ti = sourceModel()->itemFromIndex( sourceModel()->index( i, 0, sourceParent ) ); + PlayableItem* ti = sourceModel()->itemFromIndex( sourceModel()->index( i, 0, sourceParent ) ); if ( ti->name() == item->name() && ( ti->result()->albumpos() == item->result()->albumpos() || ti->result()->albumpos() == 0 || item->result()->albumpos() == 0 ) ) @@ -275,8 +254,8 @@ TreeProxyModel::filterAcceptsRow( int sourceRow, const QModelIndex& sourceParent bool TreeProxyModel::lessThan( const QModelIndex& left, const QModelIndex& right ) const { - TreeModelItem* p1 = sourceModel()->itemFromIndex( left ); - TreeModelItem* p2 = sourceModel()->itemFromIndex( right ); + PlayableItem* p1 = sourceModel()->itemFromIndex( left ); + PlayableItem* p2 = sourceModel()->itemFromIndex( right ); if ( !p1 ) return true; @@ -338,35 +317,8 @@ TreeProxyModel::lessThan( const QModelIndex& left, const QModelIndex& right ) co } -void -TreeProxyModel::removeIndex( const QModelIndex& index ) -{ - qDebug() << Q_FUNC_INFO; - - if ( !sourceModel() ) - return; - if ( index.column() > 0 ) - return; - - sourceModel()->removeIndex( mapToSource( index ) ); -} - - -void -TreeProxyModel::removeIndexes( const QList& indexes ) -{ - if ( !sourceModel() ) - return; - - foreach( const QModelIndex& idx, indexes ) - { - removeIndex( idx ); - } -} - - QString -TreeProxyModel::textForItem( TreeModelItem* item ) const +TreeProxyModel::textForItem( PlayableItem* item ) const { if ( !item ) return QString(); diff --git a/src/libtomahawk/playlist/TreeProxyModel.h b/src/libtomahawk/playlist/TreeProxyModel.h index e02fc6cc6..bca8676b0 100644 --- a/src/libtomahawk/playlist/TreeProxyModel.h +++ b/src/libtomahawk/playlist/TreeProxyModel.h @@ -20,10 +20,9 @@ #ifndef TREEPROXYMODEL_H #define TREEPROXYMODEL_H -#include - #include "PlaylistInterface.h" #include "TreeModel.h" +#include "PlayableProxyModel.h" #include "DllMacro.h" @@ -34,7 +33,7 @@ namespace Tomahawk class TreeProxyModelPlaylistInterface; } -class DLLEXPORT TreeProxyModel : public QSortFilterProxyModel +class DLLEXPORT TreeProxyModel : public PlayableProxyModel { Q_OBJECT @@ -42,28 +41,13 @@ public: explicit TreeProxyModel( QObject* parent = 0 ); virtual ~TreeProxyModel() {} - virtual TreeModel* sourceModel() const { return m_model; } - virtual void setSourceTreeModel( TreeModel* sourceModel ); - virtual void setSourceModel( QAbstractItemModel* sourceModel ); + virtual void setSourcePlayableModel( TreeModel* model ); - virtual QPersistentModelIndex currentIndex() const; - virtual void setCurrentIndex( const QModelIndex& index ) { m_model->setCurrentItem( mapToSource( index ) ); } - - virtual void newFilterFromPlaylistInterface( const QString &pattern ); - - virtual void removeIndex( const QModelIndex& index ); - virtual void removeIndexes( const QList& indexes ); - - virtual int albumCount() const { return rowCount( QModelIndex() ); } - - virtual TreeModelItem* itemFromIndex( const QModelIndex& index ) const { return sourceModel()->itemFromIndex( index ); } + virtual void newFilterFromPlaylistInterface( const QString& pattern ); virtual Tomahawk::playlistinterface_ptr playlistInterface(); signals: - void filterChanged( const QString& filter ); - void filteringStarted(); - void filteringFinished(); protected: bool filterAcceptsRow( int sourceRow, const QModelIndex& sourceParent ) const; @@ -79,7 +63,7 @@ private slots: private: void filterFinished(); - QString textForItem( TreeModelItem* item ) const; + QString textForItem( PlayableItem* item ) const; mutable QMap< QPersistentModelIndex, Tomahawk::result_ptr > m_cache; @@ -87,8 +71,7 @@ private: QList m_albumsFilter; DatabaseCommand_AllArtists* m_artistsFilterCmd; - QString m_filter; - + QString m_filter; TreeModel* m_model; Tomahawk::playlistinterface_ptr m_playlistInterface; diff --git a/src/libtomahawk/playlist/TreeProxyModelPlaylistInterface.cpp b/src/libtomahawk/playlist/TreeProxyModelPlaylistInterface.cpp index c1d7ac574..294cba009 100644 --- a/src/libtomahawk/playlist/TreeProxyModelPlaylistInterface.cpp +++ b/src/libtomahawk/playlist/TreeProxyModelPlaylistInterface.cpp @@ -26,6 +26,7 @@ #include "database/Database.h" #include "database/DatabaseImpl.h" #include "database/DatabaseCommand_AllAlbums.h" +#include "PlayableItem.h" #include "utils/Logger.h" using namespace Tomahawk; @@ -33,7 +34,7 @@ using namespace Tomahawk; TreeProxyModelPlaylistInterface::TreeProxyModelPlaylistInterface( TreeProxyModel *proxyModel ) : PlaylistInterface() , m_proxyModel( proxyModel ) - , m_repeatMode( PlaylistInterface::NoRepeat ) + , m_repeatMode( PlaylistModes::NoRepeat ) , m_shuffled( false ) { } @@ -115,11 +116,11 @@ TreeProxyModelPlaylistInterface::siblingItem( int itemsAway, bool readOnly ) } else { - if ( m_repeatMode != PlaylistInterface::RepeatOne ) + if ( m_repeatMode != PlaylistModes::RepeatOne ) idx = proxyModel->index( idx.row() + ( itemsAway > 0 ? 1 : -1 ), 0, idx.parent() ); } - if ( !idx.isValid() && m_repeatMode == PlaylistInterface::RepeatAll ) + if ( !idx.isValid() && m_repeatMode == PlaylistModes::RepeatAll ) { if ( itemsAway > 0 ) { @@ -136,7 +137,7 @@ TreeProxyModelPlaylistInterface::siblingItem( int itemsAway, bool readOnly ) // Try to find the next available PlaylistItem (with results) while ( idx.isValid() ) { - TreeModelItem* item = proxyModel->itemFromIndex( proxyModel->mapToSource( idx ) ); + PlayableItem* item = proxyModel->itemFromIndex( proxyModel->mapToSource( idx ) ); if ( item && !item->result().isNull() && item->result()->isOnline() ) { qDebug() << "Next PlaylistItem found:" << item->result()->url(); @@ -161,7 +162,7 @@ TreeProxyModelPlaylistInterface::currentItem() const return Tomahawk::result_ptr(); TreeProxyModel* proxyModel = m_proxyModel.data(); - TreeModelItem* item = proxyModel->itemFromIndex( proxyModel->mapToSource( proxyModel->currentIndex() ) ); + PlayableItem* item = proxyModel->itemFromIndex( proxyModel->mapToSource( proxyModel->currentIndex() ) ); if ( item && !item->result().isNull() && item->result()->isOnline() ) return item->result(); return Tomahawk::result_ptr(); diff --git a/src/libtomahawk/playlist/TreeProxyModelPlaylistInterface.h b/src/libtomahawk/playlist/TreeProxyModelPlaylistInterface.h index ae6abf293..2367f5e06 100644 --- a/src/libtomahawk/playlist/TreeProxyModelPlaylistInterface.h +++ b/src/libtomahawk/playlist/TreeProxyModelPlaylistInterface.h @@ -57,12 +57,12 @@ public: virtual void sendTrackCount() { emit trackCountChanged( trackCount() ); } - virtual PlaylistInterface::RepeatMode repeatMode() const { return m_repeatMode; } + virtual PlaylistModes::RepeatMode repeatMode() const { return m_repeatMode; } virtual bool shuffled() const { return m_shuffled; } - virtual PlaylistInterface::ViewMode viewMode() const { return PlaylistInterface::Tree; } + virtual PlaylistModes::ViewMode viewMode() const { return PlaylistModes::Tree; } signals: - void repeatModeChanged( Tomahawk::PlaylistInterface::RepeatMode mode ); + void repeatModeChanged( Tomahawk::PlaylistModes::RepeatMode mode ); void shuffleModeChanged( bool enabled ); void trackCountChanged( unsigned int tracks ); @@ -75,13 +75,13 @@ signals: void nextTrackReady(); public slots: - virtual void setRepeatMode( Tomahawk::PlaylistInterface::RepeatMode mode ) { m_repeatMode = mode; emit repeatModeChanged( mode ); } + virtual void setRepeatMode( Tomahawk::PlaylistModes::RepeatMode mode ) { m_repeatMode = mode; emit repeatModeChanged( mode ); } virtual void setShuffled( bool enabled ) { m_shuffled = enabled; emit shuffleModeChanged( enabled ); } private: QWeakPointer< TreeProxyModel > m_proxyModel; - RepeatMode m_repeatMode; + PlaylistModes::RepeatMode m_repeatMode; bool m_shuffled; }; diff --git a/src/libtomahawk/playlist/ArtistView.cpp b/src/libtomahawk/playlist/TreeView.cpp similarity index 80% rename from src/libtomahawk/playlist/ArtistView.cpp rename to src/libtomahawk/playlist/TreeView.cpp index fec374771..ae6580f13 100644 --- a/src/libtomahawk/playlist/ArtistView.cpp +++ b/src/libtomahawk/playlist/TreeView.cpp @@ -17,7 +17,7 @@ * along with Tomahawk. If not, see . */ -#include "ArtistView.h" +#include "TreeView.h" #include #include @@ -31,10 +31,13 @@ #include "ContextMenu.h" #include "TomahawkSettings.h" -#include "TreeHeader.h" +#include "ViewHeader.h" #include "TreeItemDelegate.h" #include "TreeModel.h" +#include "PlayableItem.h" +#include "Source.h" #include "ViewManager.h" +#include "utils/TomahawkUtilsGui.h" #include "utils/Logger.h" #define SCROLL_TIMEOUT 280 @@ -42,18 +45,21 @@ using namespace Tomahawk; -ArtistView::ArtistView( QWidget* parent ) +TreeView::TreeView( QWidget* parent ) : QTreeView( parent ) - , m_header( new TreeHeader( this ) ) + , m_header( new ViewHeader( this ) ) , m_overlay( new OverlayWidget( this ) ) , m_model( 0 ) , m_proxyModel( 0 ) -// , m_delegate( 0 ) - , m_loadingSpinner( new AnimatedSpinner( this ) ) + , m_loadingSpinner( new LoadingSpinner( this ) ) , m_updateContextView( true ) , m_contextMenu( new ContextMenu( this ) ) , m_showModes( true ) { + setFrameShape( QFrame::NoFrame ); + setAttribute( Qt::WA_MacShowFocusRect, 0 ); + + setContentsMargins( 0, 0, 0, 0 ); setAlternatingRowColors( true ); setDragEnabled( true ); setDropIndicatorShown( false ); @@ -87,19 +93,19 @@ ArtistView::ArtistView( QWidget* parent ) connect( &m_timer, SIGNAL( timeout() ), SLOT( onScrollTimeout() ) ); connect( this, SIGNAL( doubleClicked( QModelIndex ) ), SLOT( onItemActivated( QModelIndex ) ) ); - connect( this, SIGNAL( customContextMenuRequested( const QPoint& ) ), SLOT( onCustomContextMenu( const QPoint& ) ) ); + connect( this, SIGNAL( customContextMenuRequested( QPoint ) ), SLOT( onCustomContextMenu( QPoint ) ) ); connect( m_contextMenu, SIGNAL( triggered( int ) ), SLOT( onMenuTriggered( int ) ) ); } -ArtistView::~ArtistView() +TreeView::~TreeView() { - qDebug() << Q_FUNC_INFO; + tDebug() << Q_FUNC_INFO; } void -ArtistView::setProxyModel( TreeProxyModel* model ) +TreeView::setProxyModel( TreeProxyModel* model ) { m_proxyModel = model; TreeItemDelegate* del = new TreeItemDelegate( this, m_proxyModel ); @@ -111,7 +117,7 @@ ArtistView::setProxyModel( TreeProxyModel* model ) void -ArtistView::setModel( QAbstractItemModel* model ) +TreeView::setModel( QAbstractItemModel* model ) { Q_UNUSED( model ); qDebug() << "Explicitly use setPlaylistModel instead"; @@ -120,28 +126,26 @@ ArtistView::setModel( QAbstractItemModel* model ) void -ArtistView::setTreeModel( TreeModel* model ) +TreeView::setTreeModel( TreeModel* model ) { m_model = model; if ( m_proxyModel ) { - m_proxyModel->setSourceTreeModel( model ); + m_proxyModel->setSourcePlayableModel( model ); m_proxyModel->sort( 0 ); } - connect( m_model, SIGNAL( loadingStarted() ), m_loadingSpinner, SLOT( fadeIn() ) ); - connect( m_model, SIGNAL( loadingFinished() ), m_loadingSpinner, SLOT( fadeOut() ) ); connect( m_proxyModel, SIGNAL( filteringStarted() ), SLOT( onFilteringStarted() ) ); connect( m_proxyModel, SIGNAL( filteringFinished() ), m_loadingSpinner, SLOT( fadeOut() ) ); - connect( m_model, SIGNAL( itemCountChanged( unsigned int ) ), SLOT( onItemCountChanged( unsigned int ) ) ); connect( m_proxyModel, SIGNAL( filteringFinished() ), SLOT( onFilterChangeFinished() ) ); connect( m_proxyModel, SIGNAL( rowsInserted( QModelIndex, int, int ) ), SLOT( onViewChanged() ) ); guid(); // this will set the guid on the header - if ( model->columnStyle() == TreeModel::TrackOnly ) + m_header->setDefaultColumnWeights( model->columnWeights() ); + if ( model->style() == PlayableModel::Large ) { setHeaderHidden( true ); setHorizontalScrollBarPolicy( Qt::ScrollBarAlwaysOff ); @@ -151,11 +155,28 @@ ArtistView::setTreeModel( TreeModel* model ) setHeaderHidden( false ); setHorizontalScrollBarPolicy( Qt::ScrollBarAsNeeded ); } + + emit modelChanged(); + +/* setColumnHidden( PlayableModel::Score, true ); // Hide score column per default + setColumnHidden( PlayableModel::Origin, true ); // Hide origin column per default + setColumnHidden( PlayableModel::Composer, true ); //Hide composer column per default + + setGuid( QString( "collectionview/%1" ).arg( model->columnCount() ) ); + sortByColumn( PlayableModel::Artist, Qt::AscendingOrder );*/ } void -ArtistView::onViewChanged() +TreeView::setEmptyTip( const QString& tip ) +{ + m_emptyTip = tip; + m_overlay->setText( tip ); +} + + +void +TreeView::onViewChanged() { if ( m_timer.isActive() ) m_timer.stop(); @@ -165,7 +186,7 @@ ArtistView::onViewChanged() void -ArtistView::onScrollTimeout() +TreeView::onScrollTimeout() { if ( m_timer.isActive() ) m_timer.stop(); @@ -193,14 +214,14 @@ ArtistView::onScrollTimeout() void -ArtistView::currentChanged( const QModelIndex& current, const QModelIndex& previous ) +TreeView::currentChanged( const QModelIndex& current, const QModelIndex& previous ) { QTreeView::currentChanged( current, previous ); if ( !m_updateContextView ) return; - TreeModelItem* item = m_model->itemFromIndex( m_proxyModel->mapToSource( current ) ); + PlayableItem* item = m_model->itemFromIndex( m_proxyModel->mapToSource( current ) ); if ( item ) { if ( !item->result().isNull() ) @@ -216,15 +237,15 @@ ArtistView::currentChanged( const QModelIndex& current, const QModelIndex& previ void -ArtistView::onItemActivated( const QModelIndex& index ) +TreeView::onItemActivated( const QModelIndex& index ) { - TreeModelItem* item = m_model->itemFromIndex( m_proxyModel->mapToSource( index ) ); + PlayableItem* item = m_model->itemFromIndex( m_proxyModel->mapToSource( index ) ); if ( item ) { if ( !item->artist().isNull() ) ViewManager::instance()->show( item->artist() ); else if ( !item->album().isNull() ) - ViewManager::instance()->show( item->album(), m_model->mode() ); + ViewManager::instance()->show( item->album() ); else if ( !item->result().isNull() && item->result()->isOnline() ) { m_model->setCurrentItem( item->index ); @@ -235,7 +256,7 @@ ArtistView::onItemActivated( const QModelIndex& index ) void -ArtistView::keyPressEvent( QKeyEvent* event ) +TreeView::keyPressEvent( QKeyEvent* event ) { QTreeView::keyPressEvent( event ); @@ -250,7 +271,7 @@ ArtistView::keyPressEvent( QKeyEvent* event ) void -ArtistView::resizeEvent( QResizeEvent* event ) +TreeView::resizeEvent( QResizeEvent* event ) { QTreeView::resizeEvent( event ); m_header->checkState(); @@ -266,24 +287,7 @@ ArtistView::resizeEvent( QResizeEvent* event ) void -ArtistView::onItemCountChanged( unsigned int items ) -{ - if ( items == 0 ) - { - if ( m_model->collection().isNull() || ( !m_model->collection().isNull() && m_model->collection()->source()->isLocal() ) ) - m_overlay->setText( tr( "After you have scanned your music collection you will find your tracks right here." ) ); - else - m_overlay->setText( tr( "This collection is currently empty." ) ); - - m_overlay->show(); - } - else - m_overlay->hide(); -} - - -void -ArtistView::onFilterChangeFinished() +TreeView::onFilterChangeFinished() { if ( selectedIndexes().count() ) scrollTo( selectedIndexes().at( 0 ), QAbstractItemView::PositionAtCenter ); @@ -294,13 +298,22 @@ ArtistView::onFilterChangeFinished() m_overlay->show(); } else + { if ( model()->trackCount() ) + { m_overlay->hide(); + } + else + { + m_overlay->setText( m_emptyTip ); + m_overlay->show(); + } + } } void -ArtistView::onFilteringStarted() +TreeView::onFilteringStarted() { m_overlay->hide(); m_loadingSpinner->fadeIn(); @@ -308,7 +321,7 @@ ArtistView::onFilteringStarted() void -ArtistView::startDrag( Qt::DropActions supportedActions ) +TreeView::startDrag( Qt::DropActions supportedActions ) { QList pindexes; QModelIndexList indexes; @@ -348,7 +361,7 @@ ArtistView::startDrag( Qt::DropActions supportedActions ) void -ArtistView::onCustomContextMenu( const QPoint& pos ) +TreeView::onCustomContextMenu( const QPoint& pos ) { m_contextMenu->clear(); @@ -368,7 +381,7 @@ ArtistView::onCustomContextMenu( const QPoint& pos ) if ( index.column() || selectedIndexes().contains( index.parent() ) ) continue; - TreeModelItem* item = m_proxyModel->itemFromIndex( m_proxyModel->mapToSource( index ) ); + PlayableItem* item = m_proxyModel->itemFromIndex( m_proxyModel->mapToSource( index ) ); if ( item && !item->result().isNull() ) queries << item->result()->toQuery(); @@ -389,7 +402,7 @@ ArtistView::onCustomContextMenu( const QPoint& pos ) void -ArtistView::onMenuTriggered( int action ) +TreeView::onMenuTriggered( int action ) { switch ( action ) { @@ -404,7 +417,7 @@ ArtistView::onMenuTriggered( int action ) bool -ArtistView::jumpToCurrentTrack() +TreeView::jumpToCurrentTrack() { if ( !m_proxyModel ) return false; @@ -415,7 +428,7 @@ ArtistView::jumpToCurrentTrack() QString -ArtistView::guid() const +TreeView::guid() const { if ( m_guid.isEmpty() ) { diff --git a/src/libtomahawk/playlist/ArtistView.h b/src/libtomahawk/playlist/TreeView.h similarity index 89% rename from src/libtomahawk/playlist/ArtistView.h rename to src/libtomahawk/playlist/TreeView.h index 90c76e66f..8bd34cb03 100644 --- a/src/libtomahawk/playlist/ArtistView.h +++ b/src/libtomahawk/playlist/TreeView.h @@ -17,8 +17,8 @@ * along with Tomahawk. If not, see . */ -#ifndef ARTISTVIEW_H -#define ARTISTVIEW_H +#ifndef TREEVIEW_H +#define TREEVIEW_H #include #include @@ -36,18 +36,18 @@ namespace Tomahawk class ContextMenu; }; -class TreeHeader; +class ViewHeader; class AnimatedSpinner; class OverlayWidget; class TreeModel; -class DLLEXPORT ArtistView : public QTreeView, public Tomahawk::ViewPage +class DLLEXPORT TreeView : public QTreeView, public Tomahawk::ViewPage { Q_OBJECT public: - explicit ArtistView( QWidget* parent = 0 ); - ~ArtistView(); + explicit TreeView( QWidget* parent = 0 ); + ~TreeView(); virtual QString guid() const; virtual void setGuid( const QString& guid ) { m_guid = guid; } @@ -57,11 +57,12 @@ public: TreeModel* model() const { return m_model; } TreeProxyModel* proxyModel() const { return m_proxyModel; } OverlayWidget* overlay() const { return m_overlay; } - // PlaylistItemDelegate* delegate() { return m_delegate; } void setModel( QAbstractItemModel* model ); void setTreeModel( TreeModel* model ); + void setEmptyTip( const QString& tip ); + virtual QWidget* widget() { return this; } virtual Tomahawk::playlistinterface_ptr playlistInterface() const { return proxyModel()->playlistInterface(); } @@ -83,6 +84,9 @@ public: public slots: void onItemActivated( const QModelIndex& index ); +signals: + void modelChanged(); + protected: virtual void startDrag( Qt::DropActions supportedActions ); virtual void resizeEvent( QResizeEvent* event ); @@ -93,7 +97,6 @@ protected slots: virtual void currentChanged( const QModelIndex& current, const QModelIndex& previous ); private slots: - void onItemCountChanged( unsigned int items ); void onFilterChangeFinished(); void onFilteringStarted(); void onViewChanged(); @@ -103,11 +106,10 @@ private slots: void onMenuTriggered( int action ); private: - TreeHeader* m_header; + ViewHeader* m_header; OverlayWidget* m_overlay; TreeModel* m_model; TreeProxyModel* m_proxyModel; -// PlaylistItemDelegate* m_delegate; AnimatedSpinner* m_loadingSpinner; bool m_updateContextView; @@ -115,9 +117,10 @@ private: QModelIndex m_contextMenuIndex; Tomahawk::ContextMenu* m_contextMenu; + QString m_emptyTip; bool m_showModes; QTimer m_timer; mutable QString m_guid; }; -#endif // ARTISTVIEW_H +#endif // TREEVIEW_H diff --git a/src/libtomahawk/playlist/XspfUpdater.cpp b/src/libtomahawk/playlist/XspfUpdater.cpp index a997648bf..22c43fd4a 100644 --- a/src/libtomahawk/playlist/XspfUpdater.cpp +++ b/src/libtomahawk/playlist/XspfUpdater.cpp @@ -22,6 +22,7 @@ #include "utils/XspfLoader.h" #include "Pipeline.h" #include "utils/TomahawkUtils.h" +#include "Source.h" #include diff --git a/src/libtomahawk/playlist/dynamic/DynamicModel.cpp b/src/libtomahawk/playlist/dynamic/DynamicModel.cpp index 30fb755b0..a75a92f86 100644 --- a/src/libtomahawk/playlist/dynamic/DynamicModel.cpp +++ b/src/libtomahawk/playlist/dynamic/DynamicModel.cpp @@ -340,7 +340,7 @@ DynamicModel::remove(const QModelIndex& idx, bool moreToCome) { // if the user is manually removing the last one, re-add as we're a station newTrackLoading(); } - TrackModel::remove( idx ); + PlayableModel::remove( idx ); } else PlaylistModel::remove( idx, moreToCome ); diff --git a/src/libtomahawk/playlist/dynamic/DynamicPlaylistRevision.cpp b/src/libtomahawk/playlist/dynamic/DynamicPlaylistRevision.cpp index 10bbaf4ed..bbc85b57a 100644 --- a/src/libtomahawk/playlist/dynamic/DynamicPlaylistRevision.cpp +++ b/src/libtomahawk/playlist/dynamic/DynamicPlaylistRevision.cpp @@ -19,6 +19,7 @@ #include "DynamicPlaylistRevision.h" #include "dynamic/DynamicControl.h" +#include "Source.h" using namespace Tomahawk; diff --git a/src/libtomahawk/playlist/dynamic/DynamicView.cpp b/src/libtomahawk/playlist/dynamic/DynamicView.cpp index 4b326344d..a1643195d 100644 --- a/src/libtomahawk/playlist/dynamic/DynamicView.cpp +++ b/src/libtomahawk/playlist/dynamic/DynamicView.cpp @@ -26,11 +26,11 @@ #include #include "PlaylistModel.h" -#include "TrackProxyModel.h" -#include "TrackHeader.h" +#include "PlayableProxyModel.h" #include "DynamicModel.h" #include "widgets/OverlayWidget.h" #include "utils/Logger.h" +#include "Source.h" using namespace Tomahawk; @@ -48,10 +48,6 @@ DynamicView::DynamicView( QWidget* parent ) , m_fadebg( false ) , m_fadeOnly( false ) { - setContentsMargins( 0, 0, 0, 0 ); - setFrameShape( QFrame::NoFrame ); - setAttribute( Qt::WA_MacShowFocusRect, 0 ); - m_fadeOutAnim.setDuration( FADE_LENGTH ); m_fadeOutAnim.setCurveShape( QTimeLine::LinearCurve ); m_fadeOutAnim.setFrameRange( 100, 0 ); @@ -70,18 +66,17 @@ DynamicView::DynamicView( QWidget* parent ) DynamicView::~DynamicView() { - } void -DynamicView::setDynamicModel( DynamicModel* model) +DynamicView::setDynamicModel( DynamicModel* model ) { m_model = model; PlaylistView::setPlaylistModel( m_model ); connect( m_model, SIGNAL( trackCountChanged( unsigned int ) ), SLOT( onTrackCountChanged( unsigned int ) ) ); - connect( m_model, SIGNAL( checkForOverflow() ), this, SLOT( checkForOverflow() ) ); + connect( m_model, SIGNAL( checkForOverflow() ), SLOT( checkForOverflow() ) ); } diff --git a/src/libtomahawk/playlist/dynamic/GeneratorFactory.cpp b/src/libtomahawk/playlist/dynamic/GeneratorFactory.cpp index 8dfb24b0a..c534c6066 100644 --- a/src/libtomahawk/playlist/dynamic/GeneratorFactory.cpp +++ b/src/libtomahawk/playlist/dynamic/GeneratorFactory.cpp @@ -20,6 +20,7 @@ #include "dynamic/GeneratorInterface.h" #include "utils/Logger.h" +#include "Source.h" using namespace Tomahawk; diff --git a/src/libtomahawk/playlist/dynamic/GeneratorInterface.cpp b/src/libtomahawk/playlist/dynamic/GeneratorInterface.cpp index fe4064006..4b6bd1aca 100644 --- a/src/libtomahawk/playlist/dynamic/GeneratorInterface.cpp +++ b/src/libtomahawk/playlist/dynamic/GeneratorInterface.cpp @@ -20,6 +20,7 @@ #include "dynamic/GeneratorInterface.h" #include "utils/Logger.h" +#include "Source.h" Tomahawk::GeneratorInterface::GeneratorInterface( QObject* parent ) diff --git a/src/libtomahawk/playlist/dynamic/database/DatabaseGenerator.cpp b/src/libtomahawk/playlist/dynamic/database/DatabaseGenerator.cpp index 4a84950c4..ee4c8d137 100644 --- a/src/libtomahawk/playlist/dynamic/database/DatabaseGenerator.cpp +++ b/src/libtomahawk/playlist/dynamic/database/DatabaseGenerator.cpp @@ -20,8 +20,9 @@ #include "DatabaseControl.h" #include "utils/Logger.h" -#include -#include +#include "Source.h" +#include "database/DatabaseCommand_GenericSelect.h" +#include "database/Database.h" using namespace Tomahawk; diff --git a/src/libtomahawk/playlist/dynamic/echonest/EchonestSteerer.cpp b/src/libtomahawk/playlist/dynamic/echonest/EchonestSteerer.cpp index ff1c39fa2..82a44d3c0 100644 --- a/src/libtomahawk/playlist/dynamic/echonest/EchonestSteerer.cpp +++ b/src/libtomahawk/playlist/dynamic/echonest/EchonestSteerer.cpp @@ -28,6 +28,7 @@ #include #include "echonest/Playlist.h" +#include "Source.h" #include "dynamic/widgets/DynamicWidget.h" #include "utils/TomahawkUtils.h" @@ -65,6 +66,15 @@ EchonestSteerer::EchonestSteerer( QWidget* parent ) // m_steerBottom->setFont( f ); // m_textL->addWidget( m_steerBottom ); + + QPalette p = m_steerTop->palette(); +#ifdef Q_OS_MAC + p.setBrush( QPalette::WindowText, Qt::white ); +#else + p.setBrush( QPalette::WindowText, palette().highlightedText() ); +#endif + m_steerTop->setPalette( p ); + m_layout->addLayout( m_textL, 1 ); m_amplifier = new QComboBox( this ); diff --git a/src/libtomahawk/playlist/dynamic/widgets/CollapsibleControls.cpp b/src/libtomahawk/playlist/dynamic/widgets/CollapsibleControls.cpp index da26e824f..728638e56 100644 --- a/src/libtomahawk/playlist/dynamic/widgets/CollapsibleControls.cpp +++ b/src/libtomahawk/playlist/dynamic/widgets/CollapsibleControls.cpp @@ -25,6 +25,7 @@ #include "dynamic/DynamicControl.h" #include "utils/TomahawkUtils.h" #include "widgets/ElidedLabel.h" +#include "Source.h" #include #include diff --git a/src/libtomahawk/playlist/dynamic/widgets/DynamicControlList.cpp b/src/libtomahawk/playlist/dynamic/widgets/DynamicControlList.cpp index 2c10de64f..3f1e8c429 100644 --- a/src/libtomahawk/playlist/dynamic/widgets/DynamicControlList.cpp +++ b/src/libtomahawk/playlist/dynamic/widgets/DynamicControlList.cpp @@ -31,6 +31,7 @@ #include "dynamic/GeneratorInterface.h" #include "utils/TomahawkUtils.h" #include "utils/Logger.h" +#include "Source.h" using namespace Tomahawk; diff --git a/src/libtomahawk/playlist/dynamic/widgets/DynamicWidget.cpp b/src/libtomahawk/playlist/dynamic/widgets/DynamicWidget.cpp index eabad53e9..bf323dcb9 100644 --- a/src/libtomahawk/playlist/dynamic/widgets/DynamicWidget.cpp +++ b/src/libtomahawk/playlist/dynamic/widgets/DynamicWidget.cpp @@ -29,7 +29,8 @@ #include "DynamicControlList.h" #include "dynamic/DynamicModel.h" -#include "TrackProxyModel.h" +#include "PlayableProxyModel.h" +#include "PlayableItem.h" #include "dynamic/GeneratorInterface.h" #include "dynamic/GeneratorFactory.h" #include "Pipeline.h" @@ -422,7 +423,7 @@ DynamicWidget::steeringChanged() for ( int i = 0; i < m_view->proxyModel()->rowCount( QModelIndex() ); ++i ) { const QModelIndex cur = m_view->proxyModel()->index( i, 0, QModelIndex() ); - TrackModelItem* item = m_view->proxyModel()->itemFromIndex( m_view->proxyModel()->mapToSource( cur ) ); + PlayableItem* item = m_view->proxyModel()->itemFromIndex( m_view->proxyModel()->mapToSource( cur ) ); if ( item && item->isPlaying() ) { playing = cur; diff --git a/src/libtomahawk/playlist/topbar/TopBar.cpp b/src/libtomahawk/playlist/topbar/TopBar.cpp index 2c878932d..41adb5e7f 100644 --- a/src/libtomahawk/playlist/topbar/TopBar.cpp +++ b/src/libtomahawk/playlist/topbar/TopBar.cpp @@ -116,8 +116,8 @@ TopBar::TopBar( QWidget* parent ) connect( ViewManager::instance(), SIGNAL( filterAvailable( bool ) ), SLOT( setFilterVisible( bool ) ) ); - connect( ViewManager::instance(), SIGNAL( modeChanged( Tomahawk::PlaylistInterface::ViewMode ) ), - SLOT( onModeChanged( Tomahawk::PlaylistInterface::ViewMode ) ) ); + connect( ViewManager::instance(), SIGNAL( modeChanged( Tomahawk::PlaylistModes::ViewMode ) ), + SLOT( onModeChanged( Tomahawk::PlaylistModes::ViewMode ) ) ); } @@ -300,20 +300,20 @@ TopBar::setFilter( const QString& filter ) void -TopBar::onModeChanged( Tomahawk::PlaylistInterface::ViewMode mode ) +TopBar::onModeChanged( Tomahawk::PlaylistModes::ViewMode mode ) { qDebug() << Q_FUNC_INFO << mode; switch ( mode ) { - case Tomahawk::PlaylistInterface::Flat: + case Tomahawk::PlaylistModes::Flat: onFlatMode(); break; - case Tomahawk::PlaylistInterface::Tree: + case Tomahawk::PlaylistModes::Tree: onArtistMode(); break; - case Tomahawk::PlaylistInterface::Album: + case Tomahawk::PlaylistModes::Album: onAlbumMode(); break; diff --git a/src/libtomahawk/playlist/topbar/TopBar.h b/src/libtomahawk/playlist/topbar/TopBar.h index 55a1da462..6cb50563b 100644 --- a/src/libtomahawk/playlist/topbar/TopBar.h +++ b/src/libtomahawk/playlist/topbar/TopBar.h @@ -63,7 +63,7 @@ public slots: void setFilter( const QString& filter ); private slots: - void onModeChanged( Tomahawk::PlaylistInterface::ViewMode mode ); + void onModeChanged( Tomahawk::PlaylistModes::ViewMode mode ); void onFlatMode(); void onArtistMode(); void onAlbumMode(); diff --git a/src/libtomahawk/resolvers/QtScriptResolver.cpp b/src/libtomahawk/resolvers/QtScriptResolver.cpp index 3cacfe102..b99a8e3cf 100644 --- a/src/libtomahawk/resolvers/QtScriptResolver.cpp +++ b/src/libtomahawk/resolvers/QtScriptResolver.cpp @@ -131,6 +131,7 @@ QtScriptResolverHelper::setResolverConfig( const QVariantMap& config ) m_resolverConfig = config; } + QString QtScriptResolverHelper::hmac( const QByteArray& key, const QByteArray &input ) { @@ -156,6 +157,7 @@ QtScriptResolverHelper::hmac( const QByteArray& key, const QByteArray &input ) #endif } + QString QtScriptResolverHelper::md5( const QByteArray& input ) { @@ -163,6 +165,7 @@ QtScriptResolverHelper::md5( const QByteArray& input ) return QString::fromLatin1( digest.toHex() ); } + void QtScriptResolverHelper::addCustomUrlHandler( const QString& protocol, const QString& callbackFuncName ) { @@ -172,6 +175,7 @@ QtScriptResolverHelper::addCustomUrlHandler( const QString& protocol, const QStr m_urlCallback = callbackFuncName; } + QSharedPointer< QIODevice > QtScriptResolverHelper::customIODeviceFactory( const Tomahawk::result_ptr& result ) { @@ -253,6 +257,7 @@ QtScriptResolver::running() const return m_ready && !m_stopped; } + void QtScriptResolver::reload() { @@ -312,6 +317,7 @@ QtScriptResolver::init() m_ready = true; } + void QtScriptResolver::start() { @@ -361,8 +367,7 @@ QtScriptResolver::resolve( const Tomahawk::query_ptr& query ) } QVariantMap m = m_engine->mainFrame()->evaluateJavaScript( eval ).toMap(); - - if( m.isEmpty() ) + if ( m.isEmpty() ) { // if the resolver doesn't return anything, async api is used return; @@ -422,6 +427,7 @@ QtScriptResolver::parseResultVariantList( const QVariantList& reslist ) Q_ASSERT( !rp->mimetype().isEmpty() ); } + rp->setResolvedBy( this ); results << rp; } diff --git a/src/libtomahawk/resolvers/ScriptResolver.cpp b/src/libtomahawk/resolvers/ScriptResolver.cpp index 6e66fb6d6..3b7c58276 100644 --- a/src/libtomahawk/resolvers/ScriptResolver.cpp +++ b/src/libtomahawk/resolvers/ScriptResolver.cpp @@ -69,13 +69,14 @@ ScriptResolver::~ScriptResolver() msg[ "_msgtype" ] = "quit"; sendMessage( msg ); - bool finished = m_proc.waitForFinished( 2500 ); // might call handleMsg + bool finished = m_proc.state() != QProcess::Running || m_proc.waitForFinished( 2500 ); // might call handleMsg Tomahawk::Pipeline::instance()->removeResolver( this ); if ( !finished || m_proc.state() == QProcess::Running ) { qDebug() << "External resolver didn't exit after waiting 2s for it to die, killing forcefully"; + Q_ASSERT(false); #ifdef Q_OS_WIN m_proc.kill(); #else @@ -287,6 +288,7 @@ ScriptResolver::handleMsg( const QByteArray& msg ) Q_ASSERT( !rp->mimetype().isEmpty() ); } + rp->setResolvedBy( this ); results << rp; } diff --git a/src/libtomahawk/sip/SipPlugin.cpp b/src/libtomahawk/sip/SipPlugin.cpp index e8eb41ddd..009dbccdf 100644 --- a/src/libtomahawk/sip/SipPlugin.cpp +++ b/src/libtomahawk/sip/SipPlugin.cpp @@ -22,6 +22,7 @@ #include "sip/SipPlugin.h" #include "utils/Logger.h" +#include "Source.h" SipPlugin::SipPlugin() : QObject() {} diff --git a/src/libtomahawk/taghandlers/tag.cpp b/src/libtomahawk/taghandlers/tag.cpp index 698cc0e07..6084900f6 100644 --- a/src/libtomahawk/taghandlers/tag.cpp +++ b/src/libtomahawk/taghandlers/tag.cpp @@ -34,6 +34,7 @@ #include #include #include +#include #include @@ -56,6 +57,12 @@ namespace Tomahawk if( file->tag() ) t = new OggTag( f.tag(), file->tag() ); } + else if( TagLib::RIFF::AIFF::File *file = + dynamic_cast< TagLib::RIFF::AIFF::File * >( f.file() ) ) + { + if( file->tag() ) + t = new ID3v2Tag( f.tag(), file->tag() ); + } else if( TagLib::Ogg::Speex::File *file = dynamic_cast< TagLib::Ogg::Speex::File * >( f.file() ) ) { diff --git a/src/libtomahawk/thirdparty/kdsingleapplicationguard/kdlockedsharedmemorypointer.cpp b/src/libtomahawk/thirdparty/kdsingleapplicationguard/kdlockedsharedmemorypointer.cpp index e1fe10a4d..56ab8ebe4 100644 --- a/src/libtomahawk/thirdparty/kdsingleapplicationguard/kdlockedsharedmemorypointer.cpp +++ b/src/libtomahawk/thirdparty/kdsingleapplicationguard/kdlockedsharedmemorypointer.cpp @@ -31,9 +31,9 @@ void * KDLockedSharedMemoryPointerBase::get() { const void * KDLockedSharedMemoryPointerBase::get() const { return mem ? mem->data() : 0 ; } - + size_t KDLockedSharedMemoryPointerBase::byteSize() const { - return mem->size(); + return mem ? mem->size() : 0; } /*! @@ -137,7 +137,7 @@ size_t KDLockedSharedMemoryPointerBase::byteSize() const { (The exception safety of this class has not been evaluated yet.) - KDLockedSharedMemoryArray is a smart immutable pointer, which gives convenient and safe access to array data stored in a QSharedMemory + KDLockedSharedMemoryArray is a smart immutable pointer, which gives convenient and safe access to array data stored in a QSharedMemory data segment. The content of a KDLockedSharedMemoryArray cannot be changed during it's lifetime. @@ -248,7 +248,7 @@ size_t KDLockedSharedMemoryPointerBase::byteSize() const { /*! \fn KDLockedSharedMemoryArray::size_type KDLockedSharedMemoryArray::size() const - Returns the size of this array. The size is calculated from the storage size of T and + Returns the size of this array. The size is calculated from the storage size of T and the size of the shared memory segment. \since_f 2.2 */ @@ -461,7 +461,7 @@ KDAB_UNITTEST_SIMPLE( KDLockedSharedMemoryPointer, "kdcoretools" ) { assertEqual( a[ i ].n, i ); assertEqual( a.front().n, 0u ); assertEqual( a.back().n, a.size() - 1 ); - + std::copy( v.begin(), v.end(), a.rbegin() ); for( uint i = 0; i < a.size(); ++i ) assertEqual( a[ i ].n, a.size() - 1 - i ); diff --git a/src/libtomahawk/thirdparty/kdsingleapplicationguard/kdsingleapplicationguard.cpp b/src/libtomahawk/thirdparty/kdsingleapplicationguard/kdsingleapplicationguard.cpp index b7c04c2d6..b1a13ac8c 100644 --- a/src/libtomahawk/thirdparty/kdsingleapplicationguard/kdsingleapplicationguard.cpp +++ b/src/libtomahawk/thirdparty/kdsingleapplicationguard/kdsingleapplicationguard.cpp @@ -1,93 +1,301 @@ #include "kdsingleapplicationguard.h" +#if QT_VERSION >= 0x040400 || defined(DOXYGEN_RUN) +#ifndef QT_NO_SHAREDMEMORY + +#include "kdsharedmemorylocker.h" +#include "kdlockedsharedmemorypointer.h" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#ifndef Q_WS_WIN +#include +#include +#endif + +#ifdef Q_WS_WIN +#include +typedef signed int ssize_t; +#endif + +using namespace kdtools; + +#ifndef KDSINGLEAPPLICATIONGUARD_TIMEOUT_SECONDS +#define KDSINGLEAPPLICATIONGUARD_TIMEOUT_SECONDS 10 +#endif + #ifndef KDSINGLEAPPLICATIONGUARD_NUMBER_OF_PROCESSES #define KDSINGLEAPPLICATIONGUARD_NUMBER_OF_PROCESSES 10 #endif #ifndef KDSINGLEAPPLICATIONGUARD_MAX_COMMAND_LINE -#define KDSINGLEAPPLICATIONGUARD_MAX_COMMAND_LINE 1024 +#define KDSINGLEAPPLICATIONGUARD_MAX_COMMAND_LINE 32768 #endif +static unsigned int KDSINGLEAPPLICATIONGUARD_SHM_VERSION = 0; +Q_GLOBAL_STATIC_WITH_ARGS( int, registerInstanceType, + (qRegisterMetaType()) ) -KDSingleApplicationGuard::Instance::Instance( const QStringList& args, qint64 p ) -: arguments( args ), -pid( p ) -{ -} +/*! + \class KDSingleApplicationGuard::Instance + \relates KDSingleApplicationGuard + \ingroup core + \brief Information about instances a KDSingleApplicationGuard knows about -#if QT_VERSION < 0x040400 + Instance represents instances of applications under + KDSingleApplicationGuard protection, and allows access to their + pid() and the arguments() they were started with. +*/ -class KDSingleApplicationGuard::Private -{ +class KDSingleApplicationGuard::Instance::Private : public QSharedData { + friend class ::KDSingleApplicationGuard::Instance; +public: + Private( const QStringList & args, bool truncated, qint64 pid ) + : pid( pid ), arguments( args ), truncated( truncated ) {} + +private: + qint64 pid; + QStringList arguments; + bool truncated; }; -KDSingleApplicationGuard::KDSingleApplicationGuard( QCoreApplication*, Policy ) -{ - qWarning( "KD Tools was compiled with a Qt version prior to 4.4. SingleApplicationGuard won't work." ); -} - -KDSingleApplicationGuard::~KDSingleApplicationGuard() -{ -} - -void KDSingleApplicationGuard::shutdownOtherInstances() -{ -} - -void KDSingleApplicationGuard::killOtherInstances() -{ -} - -void KDSingleApplicationGuard::timerEvent( QTimerEvent* ) -{ -} -#else - -#include -#include - -#include "kdsharedmemorylocker.h" -#include "kdlockedsharedmemorypointer.h" - -#include -#include -#include - -#ifndef Q_WS_WIN -#include -#endif - -using namespace kdtools; +struct ProcessInfo; /*! - * \class KDSingleApplicationGuard KDSingleApplicationGuard - * \brief A guard to protect an application from having several instances. - * - * KDSingleApplicationGuard can be used to make sure only one instance of an - * application is running at the same time. - * - * \note As KDSingleApplicationGuard uses QSharedMemory Qt 4.4 or later is required + \internal + */ +class KDSingleApplicationGuard::Private +{ + friend class ::KDSingleApplicationGuard; + friend class ::KDSingleApplicationGuard::Instance; + friend struct ::ProcessInfo; + KDSingleApplicationGuard * const q; +public: + Private( Policy policy, KDSingleApplicationGuard* qq ); + ~Private(); + + void create( const QStringList& arguments ); + + bool checkOperational( const char * function, const char * act ) const; + bool checkOperationalPrimary( const char * function, const char * act ) const; + + struct segmentheader + { + size_t size : 16; + }; + + static void sharedmem_free( char* ); + static char* sharedmem_malloc( size_t size ); + +private: + void shutdownInstance(); + void poll(); + +private: + static KDSingleApplicationGuard* primaryInstance; + +private: + QBasicTimer timer; + QSharedMemory mem; + int id; + Policy policy; + bool operational; + bool exitRequested; +}; + +/*! + \internal +*/ +KDSingleApplicationGuard::Instance::Instance( const QStringList & args, bool truncated, qint64 p ) + : d( new Private( args, truncated, p ) ) +{ + d->ref.ref(); + (void)registerInstanceType(); +} + +/*! + Default constructor. Constructs in Instance that is \link isNull() + null\endlink. + + \sa isNull() +*/ +KDSingleApplicationGuard::Instance::Instance() : d( 0 ) {} + +/*! + Copy constructor. +*/ +KDSingleApplicationGuard::Instance::Instance( const Instance & other ) + : d( other.d ) +{ + if ( d ) + d->ref.ref(); +} + +/*! + Destructor. +*/ +KDSingleApplicationGuard::Instance::~Instance() +{ + if ( d && !d->ref.deref() ) + delete d; +} + +/*! + \fn KDSingleApplicationGuard::Instance::swap( Instance & other ) + + Swaps the contents of this and \a other. + + This function never throws exceptions. +*/ + +/*! + \fn KDSingleApplicationGuard::Instance::operator=( Instance other ) + + Assigns the contents of \a other to this. + + This function is strongly exception-safe. +*/ + +/*! + \fn std::swap( KDSingleApplicationGuard::Instance & lhs, KDSingleApplicationGuard::Instance & rhs ) + \relates KDSingleApplicationGuard::Instance + + Specialisation of std::swap() for + KDSingleApplicationGuard::Instance. Calls swap(). +*/ + +/*! + \fn qSwap( KDSingleApplicationGuard::Instance & lhs, KDSingleApplicationGuard::Instance & rhs ) + \relates KDSingleApplicationGuard::Instance + + Specialisation of qSwap() for + KDSingleApplicationGuard::Instance. Calls swap(). +*/ + +/*! + \fn KDSingleApplicationGuard::Instance::isNull() const + + Returns whether this instance is null. +*/ + +/*! + Returns whether this instance is valid. A valid instance is neither + null, nor does it have a negative PID. +*/ +bool KDSingleApplicationGuard::Instance::isValid() const +{ + return d && d->pid >= 0 ; +} + +/*! + Returns whether the #arguments are complete (\c false) or not (\c + true), e.g. because they have been truncated due to limited storage + space. + + \sa arguments() +*/ +bool KDSingleApplicationGuard::Instance::areArgumentsTruncated() const +{ + return d && d->truncated; +} + +/*! + Returns the arguments that this instance was started with. + + \sa areArgumentsTruncated() +*/ +const QStringList & KDSingleApplicationGuard::Instance::arguments() const +{ + if ( d ) + return d->arguments; + static const QStringList empty; + return empty; +} + +/*! + Returns the process-id (PID) of this instance. +*/ +qint64 KDSingleApplicationGuard::Instance::pid() const +{ + if ( d ) + return d->pid; + else + return -1; +} + +/*! + \class KDSingleApplicationGuard KDSingleApplicationGuard + \ingroup core + \brief A guard to protect an application from having several instances. + + KDSingleApplicationGuard can be used to make sure only one instance of an + application is running at the same time. + + \note As KDSingleApplicationGuard currently uses QSharedMemory, Qt + 4.4 or later is required. */ /*! - * \fn void KDSingleApplicationGuard::instanceStarted() - * This signal is emitted by the primary instance when ever one other - * instance was started. + \fn void KDSingleApplicationGuard::instanceStarted(const KDSingleApplicationGuard::Instance & instance) + + This signal is emitted by the primary instance whenever another + instance \a instance started. */ /*! - * \fn void KDSingleApplicationGuard::instanceExited() - * This signal is emitted by the primary instance when ever one other - * instance was exited. + \fn void KDSingleApplicationGuard::instanceExited(const KDSingleApplicationGuard::Instance & instance) + + This signal is emitted by the primary instance whenever another + instance \a instance exited. */ /*! - * \fn void KDSingleApplicationGuard::becamePrimaryInstance() - * This signal is emitted, when the current running application gets the new - * primary application. The old primary application has quit. + \fn void KDSingleApplicationGuard::raiseRequested() + + This signal is emitted when the current running application is requested + to raise its main window. +*/ + +/*! + \fn void KDSingleApplicationGuard::exitRequested() + + This signal is emitted when the current running application has been asked to exit + by calling kill on the instance. +*/ + +/*! + \fn void KDSingleApplicationGuard::becamePrimaryInstance() + + This signal is emitted when the current running application becomes + the new primary application. The old primary application has quit. */ +/*! + \fn void KDSingleApplicationGuard::becameSecondaryInstance() + + This signal is emmited when the primary instance became secondary instance. + This happens when the instance doesn't update its status for some (default 10) seconds. Another instance + got primary instance in that case. + */ + +/*! + \fn void KDSingleApplicationGuard::policyChanged( KDSingleApplicationGuard::Policy policy ) + + This signal is emitted when the #policy of the system changes. +*/ + enum Command { NoCommand = 0x00, @@ -96,259 +304,530 @@ enum Command FreeInstance = 0x04, ShutDownCommand = 0x08, KillCommand = 0x10, - BecomePrimaryCommand = 0x20 + BecomePrimaryCommand = 0x20, + RaiseCommand = 0x40 }; -Q_DECLARE_FLAGS( Commands, Command ) -Q_DECLARE_OPERATORS_FOR_FLAGS( Commands ) +static const quint16 PrematureEndOfOptions = -1; +static const quint16 RegularEndOfOptions = -2; struct ProcessInfo { + static const size_t MarkerSize = sizeof(quint16); + explicit ProcessInfo( Command c = FreeInstance, const QStringList& arguments = QStringList(), qint64 p = -1 ) - : command( c ), - pid( p ) + : pid( p ), + command( c ), + timestamp( 0 ), + commandline( 0 ) { - std::fill_n( commandline, KDSINGLEAPPLICATIONGUARD_MAX_COMMAND_LINE, '\0' ); - - int argpos = 0; - for( QStringList::const_iterator it = arguments.begin(); it != arguments.end(); ++it ) - { - const QByteArray arg = it->toLatin1(); - const int count = qMin( KDSINGLEAPPLICATIONGUARD_MAX_COMMAND_LINE - argpos, arg.count() ); - std::copy( arg.begin(), arg.begin() + count, commandline + argpos ); - argpos += arg.count() + 1; // makes sure there's a \0 between every parameter - } + setArguments( arguments ); } - QStringList arguments() const - { - QStringList result; + void setArguments( const QStringList & arguments ); + QStringList arguments( bool * prematureEnd ) const; - QByteArray arg; - for( int i = 0; i < KDSINGLEAPPLICATIONGUARD_MAX_COMMAND_LINE; ++i ) - { - if( commandline[ i ] == '\0' && !arg.isEmpty() ) - { - result.push_back( QString::fromLatin1( arg ) ); - arg.clear(); - } - else if( !commandline[ i ] == '\0' ) - { - arg.push_back( commandline[ i ] ); - } - } - - return result; - } - - Commands command; - char commandline[ KDSINGLEAPPLICATIONGUARD_MAX_COMMAND_LINE ]; qint64 pid; + quint32 command; + quint32 timestamp; + char* commandline; }; -bool operator==( const ProcessInfo& lhs, const ProcessInfo& rhs ) +static inline bool operator==( const ProcessInfo & lhs, const ProcessInfo & rhs ) { return lhs.command == rhs.command && - ::memcmp( lhs.commandline, rhs.commandline, KDSINGLEAPPLICATIONGUARD_MAX_COMMAND_LINE ) == 0; + ( lhs.commandline == rhs.commandline || ( lhs.commandline != 0 && rhs.commandline != 0 && ::strcmp( lhs.commandline, rhs.commandline ) == 0 ) ); } -bool operator!=( const ProcessInfo& lhs, const ProcessInfo& rhs ) +static inline bool operator!=( const ProcessInfo & lhs, const ProcessInfo & rhs ) { return !operator==( lhs, rhs ); } /*! - * This struct contains information about the managed process system. - * \internal + This struct contains information about the managed process system. + \internal */ struct InstanceRegister { - InstanceRegister( KDSingleApplicationGuard::Policy policy = KDSingleApplicationGuard::NoPolicy ) - : policy( policy ) + explicit InstanceRegister( KDSingleApplicationGuard::Policy policy = KDSingleApplicationGuard::NoPolicy ) + : policy( policy ), + maxInstances( KDSINGLEAPPLICATIONGUARD_NUMBER_OF_PROCESSES ), + version( 0 ) { - std::fill_n( info, KDSINGLEAPPLICATIONGUARD_NUMBER_OF_PROCESSES, ProcessInfo() ); + std::fill_n( commandLines, KDSINGLEAPPLICATIONGUARD_MAX_COMMAND_LINE, 0 ); ::memcpy( magicCookie, "kdsingleapp", 12 ); } /*! - * Returns wheter this register was properly initialized by the first instance. - */ + Returns whether this register was properly initialized by the first instance. + */ bool isValid() const { return ::strcmp( magicCookie, "kdsingleapp" ) == 0; } - ProcessInfo info[ KDSINGLEAPPLICATIONGUARD_NUMBER_OF_PROCESSES ]; - KDSingleApplicationGuard::Policy policy; char magicCookie[ 12 ]; + unsigned int policy : 8; + quint32 maxInstances : 20; + unsigned int version : 4; + ProcessInfo info[ KDSINGLEAPPLICATIONGUARD_NUMBER_OF_PROCESSES ]; + + char commandLines[ KDSINGLEAPPLICATIONGUARD_MAX_COMMAND_LINE ]; + + Q_DISABLE_COPY( InstanceRegister ) }; -bool operator==( const InstanceRegister& lhs, const InstanceRegister& rhs ) +void ProcessInfo::setArguments( const QStringList & arguments ) { - if( lhs.policy != rhs.policy ) - return false; + if( commandline != 0 ) + KDSingleApplicationGuard::Private::sharedmem_free( commandline ); - for( int i = 0; i < KDSINGLEAPPLICATIONGUARD_NUMBER_OF_PROCESSES; ++i ) - if( lhs.info[ i ] != rhs.info[ i ] ) - return false; + commandline = 0; + if( arguments.isEmpty() ) + return; - return true; -} - -/*! - * \internal - */ -class KDSingleApplicationGuard::Private -{ -public: - Private( KDSingleApplicationGuard* qq ) - : q( qq ), - id( -1 ) + size_t totalsize = MarkerSize; + Q_FOREACH( const QString& arg, arguments ) { - if( primaryInstance == 0 ) - primaryInstance = q; + const QByteArray utf8 = arg.toUtf8(); + totalsize += utf8.size() + MarkerSize; + } + InstanceRegister* const reg = reinterpret_cast( KDSingleApplicationGuard::Private::primaryInstance->d->mem.data() ); + this->commandline = KDSingleApplicationGuard::Private::sharedmem_malloc( totalsize ); + if( this->commandline == 0 ) + { + qWarning("KDSingleApplicationguard: out of memory when trying to save arguments.\n"); + return; } - ~Private() - { - if( primaryInstance == q ) - primaryInstance = 0; - } + char* const commandline = this->commandline + reinterpret_cast(reg->commandLines); - void shutdownInstance() + int argpos = 0; + Q_FOREACH( const QString & arg, arguments ) { - KDLockedSharedMemoryPointer< InstanceRegister > instances( &q->d->mem ); - instances->info[ q->d->id ].command = ExitedInstance; - - if( q->isPrimaryInstance() ) - { - // ohh... we need a new primary instance... - for( int i = 1; i < KDSINGLEAPPLICATIONGUARD_NUMBER_OF_PROCESSES; ++i ) - { - if( ( instances->info[ i ].command & ( FreeInstance | ExitedInstance | ShutDownCommand | KillCommand ) ) == 0 ) - { - instances->info[ i ].command |= BecomePrimaryCommand; - return; - } - } - // none found? then my species is dead :-( + const QByteArray utf8 = arg.toUtf8(); + const int required = MarkerSize + utf8.size() + MarkerSize ; + const int available = KDSINGLEAPPLICATIONGUARD_MAX_COMMAND_LINE - argpos ; + if ( required > available || utf8.size() > std::numeric_limits::max() ) { + // write a premature-eoo marker, and quit + memcpy( commandline + argpos, &PrematureEndOfOptions, MarkerSize ); + argpos += MarkerSize; + qWarning( "KDSingleApplicationGuard: argument list is too long (bytes required: %d, used: %d, available: %d", + required, argpos - 2, available ); + return; + } else { + const quint16 len16 = utf8.size(); + // write the size of the data... + memcpy( commandline + argpos, &len16, MarkerSize ); + argpos += MarkerSize; + // then the data + memcpy( commandline + argpos, utf8.data(), len16 ); + argpos += len16; } } + const ssize_t available = KDSINGLEAPPLICATIONGUARD_MAX_COMMAND_LINE - argpos; + assert( available >= static_cast( MarkerSize ) ); + memcpy( commandline + argpos, &RegularEndOfOptions, MarkerSize ); + argpos += MarkerSize; +} - static KDSingleApplicationGuard* primaryInstance; +QStringList ProcessInfo::arguments( bool * prematureEnd ) const +{ + QStringList result; + if( commandline == 0 ) + { + if( prematureEnd ) + *prematureEnd = true; + return result; + } -private: - KDSingleApplicationGuard* const q; + InstanceRegister* const reg = reinterpret_cast( KDSingleApplicationGuard::Private::primaryInstance->d->mem.data() ); + const char* const commandline = this->commandline + reinterpret_cast(reg->commandLines); -public: - Policy policy; - QSharedMemory mem; - int id; + int argpos = 0; + while ( true ) { + const int available = KDSINGLEAPPLICATIONGUARD_MAX_COMMAND_LINE - argpos ; + assert( available >= 2 ); + + quint16 marker; + memcpy( &marker, commandline + argpos, MarkerSize ); + argpos += MarkerSize; + + if ( marker == PrematureEndOfOptions ) { + if ( prematureEnd ) *prematureEnd = true; + break; + } + if ( marker == RegularEndOfOptions ) { + if ( prematureEnd ) *prematureEnd = false; + break; + } + + const int requested = MarkerSize + marker + MarkerSize ; + if ( requested > available ) { + const long long int p = pid; + qWarning( "KDSingleApplicationGuard: inconsistency detected when parsing command-line argument for process %lld", p ); + if ( prematureEnd ) *prematureEnd = true; + break; + } + + result.push_back( QString::fromUtf8( commandline + argpos, marker ) ); + argpos += marker; + } + + return result; +} + +KDSingleApplicationGuard::Private::~Private() +{ + if( primaryInstance == q ) + primaryInstance = 0; +} + +bool KDSingleApplicationGuard::Private::checkOperational( const char * function, const char * act ) const +{ + assert( function ); + assert( act ); + if ( !operational ) + qWarning( "KDSingleApplicationGuard::%s: need to be operational to %s", function, act ); + return operational; +} + +bool KDSingleApplicationGuard::Private::checkOperationalPrimary( const char * function, const char * act ) const +{ + if ( !checkOperational( function, act ) ) + return false; + if ( id != 0 ) + qWarning( "KDSingleApplicationGuard::%s: need to be primary to %s", function, act ); + return id == 0; +} + +struct segmentheader +{ + size_t size : 16; }; +void KDSingleApplicationGuard::Private::sharedmem_free( char* pointer ) +{ + InstanceRegister* const reg = reinterpret_cast( KDSingleApplicationGuard::Private::primaryInstance->d->mem.data() ); + char* const heap = reg->commandLines; + char* const heap_ptr = heap + reinterpret_cast(pointer) - sizeof( segmentheader ); + const segmentheader* const header = reinterpret_cast< const segmentheader* >( heap_ptr ); + const size_t size = header->size; + + char* end = heap + KDSINGLEAPPLICATIONGUARD_MAX_COMMAND_LINE; + + std::copy( heap_ptr + size, end, heap_ptr ); + std::fill( end - size, end, 0 ); + + for( uint i = 0; i < reg->maxInstances; ++i ) + { + if( reg->info[ i ].commandline > pointer ) + reg->info[ i ].commandline -= size + sizeof( segmentheader ); + } +} + +char* KDSingleApplicationGuard::Private::sharedmem_malloc( size_t size ) +{ + InstanceRegister* const reg = reinterpret_cast( KDSingleApplicationGuard::Private::primaryInstance->d->mem.data() ); + char* heap = reg->commandLines; + + while( heap + sizeof( segmentheader ) + size < reg->commandLines + KDSINGLEAPPLICATIONGUARD_MAX_COMMAND_LINE ) + { + segmentheader* const header = reinterpret_cast< segmentheader* >( heap ); + if( header->size == 0 ) + { + header->size = size; + return heap + sizeof( segmentheader ) - reinterpret_cast(reg->commandLines); + } + heap += sizeof( header ) + header->size; + } + return 0; +} + +void KDSingleApplicationGuard::Private::shutdownInstance() +{ + KDLockedSharedMemoryPointer< InstanceRegister > instances( &q->d->mem ); + instances->info[ q->d->id ].command |= ExitedInstance; + + if( q->isPrimaryInstance() ) + { + // ohh... we need a new primary instance... + for ( int i = 1, end = instances->maxInstances ; i < end ; ++i ) + { + if( ( instances->info[ i ].command & ( FreeInstance | ExitedInstance | ShutDownCommand | KillCommand ) ) == 0 ) + { + instances->info[ i ].command |= BecomePrimaryCommand; + return; + } + } + // none found? then my species is dead :-( + } +} + KDSingleApplicationGuard* KDSingleApplicationGuard::Private::primaryInstance = 0; -#ifndef Q_WS_WIN -void SIGINT_handler( int sig ) +/*! + Requests that the instance kills itself (by emitting exitRequested). + + If the instance has since exited, does nothing. + + \sa shutdown(), raise() +*/ +void KDSingleApplicationGuard::Instance::kill() { - if( sig == SIGINT && KDSingleApplicationGuard::Private::primaryInstance != 0 ) - KDSingleApplicationGuard::Private::primaryInstance->d->shutdownInstance(); + KDLockedSharedMemoryPointer< InstanceRegister > instances( &KDSingleApplicationGuard::Private::primaryInstance->d->mem ); + for ( int i = 0, end = instances->maxInstances ; i < end ; ++i ) + { + if( instances->info[ i ].pid != d->pid ) + continue; + if( ( instances->info[ i ].command & ( FreeInstance | ExitedInstance ) ) == 0 ) + instances->info[ i ].command = KillCommand; + } +} + +/*! + Requests that the instance shuts itself down (by calling QCoreApplication::quit()). + + If the instance has since exited, does nothing. + + \sa kill(), raise() +*/ +void KDSingleApplicationGuard::Instance::shutdown() +{ + KDLockedSharedMemoryPointer< InstanceRegister > instances( &KDSingleApplicationGuard::Private::primaryInstance->d->mem ); + for ( int i = 0, end = instances->maxInstances ; i < end ; ++i ) + { + if( instances->info[ i ].pid != d->pid ) + continue; + if( ( instances->info[ i ].command & ( FreeInstance | ExitedInstance ) ) == 0 ) + instances->info[ i ].command = ShutDownCommand; + } +} + +/*! + + Requests that the instance raises its main window. + + The effects are implementation-defined: the KDSingleApplicationGuard + corresponding to the instance will emit its \link + KDSingleApplicationGuard::raiseRequested() raiseRequested()\endlink + signal. + + If the instance has since exited, does nothing. + + \sa kill(), shutdown() +*/ +void KDSingleApplicationGuard::Instance::raise() +{ + KDLockedSharedMemoryPointer< InstanceRegister > instances( &KDSingleApplicationGuard::Private::primaryInstance->d->mem ); + for ( int i = 0, end = instances->maxInstances ; i < end ; ++i ) + { + if( instances->info[ i ].pid != d->pid ) + continue; + if( ( instances->info[ i ].command & ( FreeInstance | ExitedInstance ) ) == 0 ) + instances->info[ i ].command = RaiseCommand; + } +} + + +#ifndef Q_WS_WIN +// static +void KDSingleApplicationGuard::SIGINT_handler( int sig ) +{ + if( sig == SIGINT && Private::primaryInstance != 0 ) + Private::primaryInstance->d->shutdownInstance(); ::exit( 1 ); } #endif /*! - * Creates a new KDSingleApplicationGuard guarding \a parent from mulitply instances. - * If \a policy is AutoKillOtherInstances (the default), all instances, which try to start, - * are killed automatically and instanceStarted() is emitted. - * If \a policy is NoPolicy, the other instance will run and instanceStarted() is emitted. - */ -KDSingleApplicationGuard::KDSingleApplicationGuard( QCoreApplication* parent, Policy policy ) -: QObject( parent ), -d( new Private( this ) ) + \enum KDSingleApplicationGuard::Policy + + Defines the policy that a KDSingleApplicationGuard can enforce: +*/ + +/*! + \var KDSingleApplicationGuard::NoPolicy + + instanceStarted() is emitted, and the new instance allowed to continue. +*/ + +/*! + \var KDSingleApplicationGuard::AutoKillOtherInstances + + instanceStarted() is emitted, and the new instance is killed (Instance::kill()). +*/ + +/*! + Creates a new KDSingleApplicationGuard with arguments + QCoreApplication::arguments() and policy AutoKillOtherInstances, + passing \a parent to the base class constructor, as usual. +*/ +KDSingleApplicationGuard::KDSingleApplicationGuard( QObject * parent ) + : QObject( parent ), d( new Private( AutoKillOtherInstances, this ) ) { - const QString name = parent->applicationName(); - Q_ASSERT_X( !name.isEmpty(), "KDSingleApplicationGuard::KDSingleApplicationGuard", "applicationName must not be emty" ); - d->mem.setKey( name ); - - // if another instance crashed, the shared memory segment is still there on Unix - // the following lines trigger deletion in that case - #ifndef Q_WS_WIN - d->mem.attach(); - d->mem.detach(); - #endif - - d->policy = policy; - - const bool created = d->mem.create( sizeof( InstanceRegister ) ); - if( !created ) - { - if( !d->mem.attach() ) - { - qWarning( "KDSingleApplicationGuard: Could neither create nor attach to shared memory segment." ); - return; - } - - // lets wait till the other instance initialized the register - bool initialized = false; - while( !initialized ) - { - const KDLockedSharedMemoryPointer< InstanceRegister > instances( &d->mem ); - initialized = instances->isValid(); - } - } - - bool killMyself = false; - { - KDLockedSharedMemoryPointer< InstanceRegister > instances( &d->mem ); - - if( !created ) - { - // we're _not_ the first instance - // but the - bool killOurSelf = false; - - // find a new slot... - d->id = std::find( instances->info, instances->info + KDSINGLEAPPLICATIONGUARD_NUMBER_OF_PROCESSES, ProcessInfo() ) - instances->info; - ProcessInfo& info = instances->info[ d->id ]; - info = ProcessInfo( NewInstance, parent->arguments(), QCoreApplication::applicationPid() ); - killOurSelf = instances->policy == AutoKillOtherInstances; - d->policy = instances->policy; - - // but the signal that we tried to start was sent to the primary application - if( killOurSelf ) - { - info.command |= ExitedInstance; - killMyself = true; - } - } - else - { - // ok.... we are the first instance - InstanceRegister reg( policy ); // create a new list - d->id = 0; // our id = 0 - // and we've no command - reg.info[ 0 ] = ProcessInfo( NoCommand, parent->arguments(), QCoreApplication::applicationPid() ); - *instances = reg; // push this is the process list into shared memory - } - } - // call exit after we let the locker release our memory, as exit() is not guaranteed to clean up objects on the stack - if ( killMyself ) - exit( 1 ); - - #ifndef Q_WS_WIN - ::signal( SIGINT, SIGINT_handler ); - #endif - - // now listen for commands - startTimer( 750 ); + d->create( QCoreApplication::arguments() ); } /*! - * Destroys this SingleApplicationGuard. - * If this instance has been the primary instance and no other instance is existing anymore, - * the application is shut down completely. Otherwise the destructor selects another instance to - * be the primary instances. + Creates a new KDSingleApplicationGuard with arguments + QCoreApplication::arguments() and policy \a policy, passing \a + parent to the base class constructor, as usual. +*/ +KDSingleApplicationGuard::KDSingleApplicationGuard( Policy policy, QObject * parent ) + : QObject( parent ), d( new Private( policy, this ) ) +{ + d->create( QCoreApplication::arguments() ); +} + +/*! + Creates a new KDSingleApplicationGuard with arguments \a arguments + and policy AutoKillOtherInstances, passing \a parent to the base + class constructor, as usual. +*/ +KDSingleApplicationGuard::KDSingleApplicationGuard( const QStringList & arguments, QObject * parent ) + : QObject( parent ), d( new Private( AutoKillOtherInstances, this ) ) +{ + d->create( arguments ); +} + +/*! + Creates a new KDSingleApplicationGuard with arguments \a arguments + and policy \a policy, passing \a parent to the base class + constructor, as usual. +*/ +KDSingleApplicationGuard::KDSingleApplicationGuard( const QStringList & arguments, Policy policy, QObject * parent ) + : QObject( parent ), d( new Private( policy, this ) ) +{ + d->create( arguments ); +} + +KDSingleApplicationGuard::Private::Private( Policy policy_, KDSingleApplicationGuard * qq ) + : q( qq ), + id( -1 ), + policy( policy_ ), + operational( false ), + exitRequested( false ) +{ +} + +void KDSingleApplicationGuard::Private::create( const QStringList & arguments ) +{ + if ( !QCoreApplication::instance() ) { + qWarning( "KDSingleApplicationGuard: you need to construct a Q(Core)Application before you can construct a KDSingleApplicationGuard" ); + return; + } + + const QString name = QCoreApplication::applicationName(); + if ( name.isEmpty() ) { + qWarning( "KDSingleApplicationGuard: QCoreApplication::applicationName must not be emty" ); + return; + } + + (void)registerInstanceType(); + if ( primaryInstance == 0 ) + primaryInstance = q; + + mem.setKey( name ); + + // if another instance crashed, the shared memory segment is still there on Unix + // the following lines trigger deletion in that case +#ifndef Q_WS_WIN + mem.attach(); + mem.detach(); +#endif + + const bool created = mem.create( sizeof( InstanceRegister ) ); + if( !created ) + { + QString errorMsg; + if( mem.error() != QSharedMemory::NoError && mem.error() != QSharedMemory::AlreadyExists ) + errorMsg += QString::fromLatin1( "QSharedMemomry::create() failed: %1" ).arg( mem.errorString() ); + + if( !mem.attach() ) + { + if( mem.error() != QSharedMemory::NoError ) + errorMsg += QString::fromLatin1( "QSharedMemomry::attach() failed: %1" ).arg( mem.errorString() ); + + qWarning( "KDSingleApplicationGuard: Could neither create nor attach to shared memory segment." ); + qWarning( "%s\n", errorMsg.toLocal8Bit().constData() ); + return; + } + + const int maxWaitMSecs = 1000 * 60; // stop waiting after 60 seconds + QTime waitTimer; + waitTimer.start(); + + // lets wait till the other instance initialized the register + bool initialized = false; + while( !initialized && waitTimer.elapsed() < maxWaitMSecs ) + { + const KDLockedSharedMemoryPointer< InstanceRegister > instances( &mem ); + initialized = instances->isValid(); +#ifdef Q_WS_WIN + ::Sleep(20); +#else + usleep(20000); +#endif + } + + const KDLockedSharedMemoryPointer< InstanceRegister > instances( &mem ); + if ( instances->version != 0 ) { + qWarning( "KDSingleApplicationGuard: Detected version mismatch. " + "Highest supported version: %ud, actual version: %ud", + KDSINGLEAPPLICATIONGUARD_SHM_VERSION, instances->version ); + return; + } + + } + + + KDLockedSharedMemoryPointer< InstanceRegister > instances( &mem ); + + if( !created ) + { + assert( instances->isValid() ); + + // we're _not_ the first instance + // but the + bool killOurSelf = false; + + // find a new slot... + id = std::find( instances->info, instances->info + instances->maxInstances, ProcessInfo() ) - instances->info; + ProcessInfo& info = instances->info[ id ]; + info = ProcessInfo( NewInstance, arguments, QCoreApplication::applicationPid() ); + killOurSelf = instances->policy == AutoKillOtherInstances; + policy = static_cast( instances->policy ); + + // but the signal that we tried to start was sent to the primary application + if( killOurSelf ) + exitRequested = true; + } + else + { + // ok.... we are the first instance + new ( instances.get() ) InstanceRegister( policy ); // create a new list (in shared memory) + id = 0; // our id = 0 + // and we've no command + instances->info[ 0 ] = ProcessInfo( NoCommand, arguments, QCoreApplication::applicationPid() ); + } + +#ifndef Q_WS_WIN + ::signal( SIGINT, SIGINT_handler ); +#endif + + // now listen for commands + timer.start( 750, q ); + + operational = true; +} + +/*! + Destroys this SingleApplicationGuard. + If this instance has been the primary instance and no other instance is existing anymore, + the application is shut down completely. Otherwise the destructor selects another instance to + be the primary instances. */ KDSingleApplicationGuard::~KDSingleApplicationGuard() { @@ -359,14 +838,48 @@ KDSingleApplicationGuard::~KDSingleApplicationGuard() } /*! - * \property KDSingleApplicationGuard::primaryInstance - * Determines wheter this instance is the primary instance. - * The primary instance is the first instance which was started or an instance which - * got selected by KDSingleApplicationGuard's destructor, when the primary instance was - * shut down. - * - * Get this property's value using %isPrimaryInstance(), and monitor changes to it - * using becamePrimaryInstance(). + \property KDSingleApplicationGuard::operational + + Contains whether this KDSingleApplicationGuard is operational. + + A non-operational KDSingleApplicationGuard cannot be used in any meaningful way. + + Reasons for a KDSingleApplicationGuard being non-operational include: + \li it was constructed before QApplication (or at least QCoreApplication) was constructed + \li it failed to create or attach to the shared memory segment that is used for communication + + Get this property's value using %isOperational(). +*/ +bool KDSingleApplicationGuard::isOperational() const +{ + return d->operational; +} + +/*! + \property KDSingleApplicationGuard::exitRequested + + Contains wheter this istance has been requested to exit. This will happen when this instance + was just started, but the policy is AutoKillOtherInstances or by explicitely calling kill on + this instance(). + + Get this property's value using %isExitRequested(). +*/ +bool KDSingleApplicationGuard::isExitRequested() const +{ + return d->exitRequested; +}; + +/*! + \property KDSingleApplicationGuard::primaryInstance + + Contains whether this instance is the primary instance. + + The primary instance is the first instance which was started or else the instance which + got selected by KDSingleApplicationGuard's destructor, when the primary instance was + shut down. + + Get this property's value using %isPrimaryInstance(), and monitor changes to it + using becamePrimaryInstance(). */ bool KDSingleApplicationGuard::isPrimaryInstance() const { @@ -374,12 +887,12 @@ bool KDSingleApplicationGuard::isPrimaryInstance() const } /*! - * \property KDSingleApplicationGuard::Policy - * Specifies the policy KDSingleApplicationGuard is using when new instances are started. - * This can only be set in the primary instance. - * - * Get this property's value using %policy(), set it using %setPolicy(), and monitor changes - * to it using policyChanged(). + \property KDSingleApplicationGuard::policy + Specifies the policy KDSingleApplicationGuard is using when new instances are started. + This can only be set in the primary instance. + + Get this property's value using %policy(), set it using %setPolicy(), and monitor changes + to it using policyChanged(). */ KDSingleApplicationGuard::Policy KDSingleApplicationGuard::policy() const { @@ -388,42 +901,58 @@ KDSingleApplicationGuard::Policy KDSingleApplicationGuard::policy() const void KDSingleApplicationGuard::setPolicy( Policy policy ) { - Q_ASSERT( isPrimaryInstance() ); + if ( !d->checkOperationalPrimary( "setPolicy", "change the policy" ) ) + return; + if( d->policy == policy ) return; d->policy = policy; - emit policyChanged(); + emit policyChanged( policy ); KDLockedSharedMemoryPointer< InstanceRegister > instances( &d->mem ); instances->policy = policy; } /*! - * Returns a list of all currently running instances. + Returns a list of all currently running instances. */ -QList< KDSingleApplicationGuard::Instance > KDSingleApplicationGuard::instances() const +QVector +KDSingleApplicationGuard::instances() const { - QList< Instance > result; + if ( !d->checkOperational( "instances", "report on other instances" ) ) + return QVector(); + + if ( Private::primaryInstance == 0 ) { + Private::primaryInstance = const_cast( this ); + } + + QVector result; const KDLockedSharedMemoryPointer< InstanceRegister > instances( const_cast< QSharedMemory* >( &d->mem ) ); - for( int i = 0; i < KDSINGLEAPPLICATIONGUARD_NUMBER_OF_PROCESSES; ++i ) + for ( int i = 0, end = instances->maxInstances ; i < end ; ++i ) { const ProcessInfo& info = instances->info[ i ]; if( ( info.command & ( FreeInstance | ExitedInstance ) ) == 0 ) - result.push_back( Instance( info.arguments(), info.pid ) ); + { + bool truncated; + const QStringList arguments = info.arguments( &truncated ); + result.push_back( Instance( arguments, truncated, info.pid ) ); + } } return result; } /*! - * Shuts down all other instances. This can only be called from the - * the primary instance. - * Shut down is done gracefully via QCoreApplication::quit(). + Shuts down all other instances. This can only be called from the + the primary instance. + Shut down is done gracefully via QCoreApplication::quit(). */ void KDSingleApplicationGuard::shutdownOtherInstances() { - Q_ASSERT( isPrimaryInstance() ); + if ( !d->checkOperationalPrimary( "shutdownOtherInstances", "shut other instances down" ) ) + return; + KDLockedSharedMemoryPointer< InstanceRegister > instances( &d->mem ); - for( int i = 1; i < KDSINGLEAPPLICATIONGUARD_NUMBER_OF_PROCESSES; ++i ) + for ( int i = 1, end = instances->maxInstances ; i < end ; ++i ) { if( ( instances->info[ i ].command & ( FreeInstance | ExitedInstance ) ) == 0 ) instances->info[ i ].command = ShutDownCommand; @@ -431,58 +960,91 @@ void KDSingleApplicationGuard::shutdownOtherInstances() } /*! - * Kills all other instances. This can only be called from the - * the primary instance. - * Killing is done via exit(1) + Kills all other instances. This can only be called from the + the primary instance. + Killing is done via emitting exitRequested. It's up to the receiving + instance to react properly. */ void KDSingleApplicationGuard::killOtherInstances() { - Q_ASSERT( isPrimaryInstance() ); + if ( !d->checkOperationalPrimary( "killOtherInstances", "kill other instances" ) ) + return; + KDLockedSharedMemoryPointer< InstanceRegister > instances( &d->mem ); - for( int i = 1; i < KDSINGLEAPPLICATIONGUARD_NUMBER_OF_PROCESSES; ++i ) + for ( int i = 1, end = instances->maxInstances ; i < end ; ++i ) { if( ( instances->info[ i ].command & ( FreeInstance | ExitedInstance ) ) == 0 ) instances->info[ i ].command = KillCommand; } } -/*! - * \reimp - */ -void KDSingleApplicationGuard::timerEvent( QTimerEvent* event ) +bool KDSingleApplicationGuard::event( QEvent * event ) { - Q_UNUSED( event ) + if ( event->type() == QEvent::Timer ) { + const QTimerEvent * const te = static_cast( event ); + if ( te->timerId() == d->timer.timerId() ) { + d->poll(); + return true; + } + } + return QObject::event( event ); +} - if( isPrimaryInstance() ) +void KDSingleApplicationGuard::Private::poll() { + + const quint32 now = QDateTime::currentDateTime().toTime_t(); + + if ( primaryInstance == 0 ) { + primaryInstance = q; + } + + if ( q->isPrimaryInstance() ) { // only the primary instance will get notified about new instances - QList< Instance > exitedInstances; - QList< Instance > startedInstances; + QVector< Instance > exitedInstances; + QVector< Instance > startedInstances; { - KDLockedSharedMemoryPointer< InstanceRegister > instances( &d->mem ); + KDLockedSharedMemoryPointer< InstanceRegister > instances( &mem ); - for( int i = 1; i < KDSINGLEAPPLICATIONGUARD_NUMBER_OF_PROCESSES; ++i ) + if( instances->info[ id ].pid != QCoreApplication::applicationPid() ) + { + for ( int i = 1, end = instances->maxInstances ; i < end && id == 0 ; ++i ) + { + if( instances->info[ i ].pid == QCoreApplication::applicationPid() ) + id = i; + } + emit q->becameSecondaryInstance(); + return; + } + + instances->info[ id ].timestamp = now; + + for ( int i = 1, end = instances->maxInstances ; i < end ; ++i ) { ProcessInfo& info = instances->info[ i ]; if( info.command & NewInstance ) { - startedInstances.push_back( Instance( info.arguments(), info.pid ) ); + bool truncated; + const QStringList arguments = info.arguments( &truncated ); + startedInstances.push_back( Instance( arguments, truncated, info.pid ) ); info.command &= ~NewInstance; // clear NewInstance flag } - else if( info.command & ExitedInstance ) + if( info.command & ExitedInstance ) { - exitedInstances.push_back( Instance( info.arguments(), info.pid ) ); + bool truncated; + const QStringList arguments = info.arguments( &truncated ); + exitedInstances.push_back( Instance( arguments, truncated, info.pid ) ); info.command = FreeInstance; // set FreeInstance flag } } } // one signal for every new instance - _after_ the memory segment was unlocked again - for( QList< Instance >::const_iterator it = startedInstances.begin(); it != startedInstances.end(); ++it ) - emit instanceStarted( *it ); - for( QList< Instance >::const_iterator it = exitedInstances.begin(); it != exitedInstances.end(); ++it ) - emit instanceExited( *it ); + for( QVector< Instance >::const_iterator it = startedInstances.constBegin(); it != startedInstances.constEnd(); ++it ) + emit q->instanceStarted( *it ); + for( QVector< Instance >::const_iterator it = exitedInstances.constBegin(); it != exitedInstances.constEnd(); ++it ) + emit q->instanceExited( *it ); } else { @@ -492,58 +1054,83 @@ void KDSingleApplicationGuard::timerEvent( QTimerEvent* event ) bool policyDidChange = false; { - KDLockedSharedMemoryPointer< InstanceRegister > instances( &d->mem ); + KDLockedSharedMemoryPointer< InstanceRegister > instances( &mem ); - policyDidChange = instances->policy != d->policy; - d->policy = instances->policy; + const Policy oldPolicy = policy; + policy = static_cast( instances->policy ); + policyDidChange = policy != oldPolicy; - if( instances->info[ d->id ].command & BecomePrimaryCommand ) + // check for the primary instance health status + if( now - instances->info[ 0 ].timestamp > KDSINGLEAPPLICATIONGUARD_TIMEOUT_SECONDS ) { - // we became primary! - instances->info[ 0 ] = instances->info[ d->id ]; - instances->info[ d->id ] = ProcessInfo(); // change our id to 0 and declare the old slot as free - d->id = 0; - emit becamePrimaryInstance(); + std::swap( instances->info[ 0 ], instances->info[ id ] ); + id = 0; + instances->info[ id ].timestamp = now; + emit q->becamePrimaryInstance(); + instances->info[ id ].command &= ~BecomePrimaryCommand; // afterwards, reset the flag } - killOurSelf = instances->info[ d->id ].command & KillCommand; // check for kill command - shutDownOurSelf = instances->info[ d->id ].command & ShutDownCommand; // check for shut down command - instances->info[ d->id ].command &= ~( KillCommand | ShutDownCommand | BecomePrimaryCommand ); // reset both flags + if( instances->info[ id ].command & BecomePrimaryCommand ) + { + // we became primary! + instances->info[ 0 ] = instances->info[ id ]; + instances->info[ id ] = ProcessInfo(); // change our id to 0 and declare the old slot as free + id = 0; + instances->info[ id ].timestamp = now; + emit q->becamePrimaryInstance(); + } + + if( instances->info[ id ].command & RaiseCommand ) + { + // raise ourself! + emit q->raiseRequested(); + instances->info[ id ].command &= ~RaiseCommand; // afterwards, reset the flag + } + + + killOurSelf = instances->info[ id ].command & KillCommand; // check for kill command + shutDownOurSelf = instances->info[ id ].command & ShutDownCommand; // check for shut down command + instances->info[ id ].command &= ~( KillCommand | ShutDownCommand | BecomePrimaryCommand ); // reset both flags if( killOurSelf ) { - instances->info[ d->id ].command |= ExitedInstance; // upon kill, we have to set the ExitedInstance flag - d->id = -1; // becauso our d'tor won't be called anymore + instances->info[ id ].command |= ExitedInstance; // upon kill, we have to set the ExitedInstance flag + id = -1; // becauso our d'tor won't be called anymore } } if( killOurSelf ) // kill our self takes precedence - exit( 1 ); + { + exitRequested = true; + emit q->exitRequested(); + } else if( shutDownOurSelf ) qApp->quit(); else if( policyDidChange ) - emit policyChanged(); + emit q->policyChanged( policy ); } } +#include "moc_kdsingleapplicationguard.cpp" + #ifdef KDTOOLSCORE_UNITTESTS #include +#include "kdautopointer.h" + #include #include #include #include -Q_DECLARE_METATYPE( KDSingleApplicationGuard::Instance ); - -static void wait( int msec ) +static void wait( int msec, QSignalSpy * spy=0, int expectedCount=INT_MAX ) { QTime t; t.start(); - while( t.elapsed() < msec ) + while ( ( !spy || spy->count() < expectedCount ) && t.elapsed() < msec ) { - qApp->processEvents( QEventLoop::WaitForMoreEvents, msec - t.elapsed() ); + qApp->processEvents( QEventLoop::WaitForMoreEvents, qMax( 10, msec - t.elapsed() ) ); } } @@ -560,19 +1147,33 @@ static std::ostream& operator<<( std::ostream& stream, const QStringList& list ) return stream; } +namespace { + class ApplicationNameSaver { + Q_DISABLE_COPY( ApplicationNameSaver ) + const QString oldname; + public: + explicit ApplicationNameSaver( const QString & name ) + : oldname( QCoreApplication::applicationName() ) + { + QCoreApplication::setApplicationName( name ); + } + ~ApplicationNameSaver() { + QCoreApplication::setApplicationName( oldname ); + } + }; +} KDAB_UNITTEST_SIMPLE( KDSingleApplicationGuard, "kdcoretools" ) { // set it to an unique name - qApp->setApplicationName( QUuid::createUuid().toString() ); + const ApplicationNameSaver saver( QUuid::createUuid().toString() ); - qRegisterMetaType< KDSingleApplicationGuard::Instance >(); - - KDSingleApplicationGuard* guard3 = 0; - QSignalSpy* spy3 = 0; + KDAutoPointer guard3; + KDAutoPointer spy3; + KDAutoPointer spy4; { - KDSingleApplicationGuard guard1( qApp ); + KDSingleApplicationGuard guard1; assertEqual( guard1.policy(), KDSingleApplicationGuard::AutoKillOtherInstances ); assertEqual( guard1.instances().count(), 1 ); assertTrue( guard1.isPrimaryInstance() ); @@ -580,48 +1181,67 @@ KDAB_UNITTEST_SIMPLE( KDSingleApplicationGuard, "kdcoretools" ) { guard1.setPolicy( KDSingleApplicationGuard::NoPolicy ); assertEqual( guard1.policy(), KDSingleApplicationGuard::NoPolicy ); - QSignalSpy spy1( &guard1, SIGNAL( instanceStarted( KDSingleApplicationGuard::Instance ) ) ); + QSignalSpy spy1( &guard1, SIGNAL(instanceStarted(KDSingleApplicationGuard::Instance)) ); - KDSingleApplicationGuard guard2( qApp ); + KDSingleApplicationGuard guard2; assertEqual( guard1.instances().count(), 2 ); assertEqual( guard2.instances().count(), 2 ); assertEqual( guard2.policy(), KDSingleApplicationGuard::NoPolicy ); assertFalse( guard2.isPrimaryInstance() ); - wait( 1000 ); + wait( 1000, &spy1, 1 ); assertEqual( spy1.count(), 1 ); - guard3 = new KDSingleApplicationGuard( qApp ); - spy3 = new QSignalSpy( guard3, SIGNAL( becamePrimaryInstance() ) ); + guard3.reset( new KDSingleApplicationGuard ); + spy3.reset( new QSignalSpy( guard3.get(), SIGNAL(becamePrimaryInstance()) ) ); + spy4.reset( new QSignalSpy( guard3.get(), SIGNAL(instanceExited(KDSingleApplicationGuard::Instance) ) ) ); assertFalse( guard3->isPrimaryInstance() ); } - wait( 1000 ); + wait( 1000, spy3.get(), 1 ); + wait( 1000, spy4.get(), 1 ); assertEqual( spy3->count(), 1 ); assertEqual( guard3->instances().count(), 1 ); assertTrue( guard3->isPrimaryInstance() ); + guard3.reset( new KDSingleApplicationGuard ); - assertEqual( guard3->instances().first().arguments, qApp->arguments() ); + assertEqual( guard3->instances().first().arguments(), qApp->arguments() ); - QSignalSpy spyStarted( guard3, SIGNAL( instanceStarted( KDSingleApplicationGuard::Instance ) ) ); - QSignalSpy spyExited( guard3, SIGNAL( instanceExited( KDSingleApplicationGuard::Instance ) ) ); + QSignalSpy spyStarted( guard3.get(), SIGNAL(instanceStarted(KDSingleApplicationGuard::Instance)) ); + QSignalSpy spyExited( guard3.get(), SIGNAL(instanceExited(KDSingleApplicationGuard::Instance)) ); { - KDSingleApplicationGuard guard1( qApp ); - KDSingleApplicationGuard guard2( qApp ); + KDSingleApplicationGuard guard1; + KDSingleApplicationGuard guard2; - wait( 1000 ); + wait( 1000, &spyStarted, 2 ); assertEqual( spyStarted.count(), 2 ); } - wait( 1000 ); + wait( 1000, &spyExited, 2 ); assertEqual( spyExited.count(), 2 ); - delete spy3; - delete guard3; + spyStarted.clear(); + spyExited.clear(); + + { + // check arguments-too-long handling: + QStringList args; + for ( unsigned int i = 0, end = KDSINGLEAPPLICATIONGUARD_MAX_COMMAND_LINE/16 ; i != end ; ++i ) + args.push_back( QLatin1String( "0123456789ABCDEF" ) ); + KDSingleApplicationGuard guard3( args ); + + wait( 1000, &spyStarted, 1 ); + + const QVector instances = guard3.instances(); + assertEqual( instances.size(), 2 ); + + assertTrue( instances[1].areArgumentsTruncated() ); + } } #endif // KDTOOLSCORE_UNITTESTS -#endif // QT_VERSION < 0x040400 +#endif // QT_NO_SHAREDMEMORY +#endif // QT_VERSION >= 0x040400 || defined(DOXYGEN_RUN) diff --git a/src/libtomahawk/thirdparty/kdsingleapplicationguard/kdsingleapplicationguard.h b/src/libtomahawk/thirdparty/kdsingleapplicationguard/kdsingleapplicationguard.h index 8acc80ac3..5f526e16d 100644 --- a/src/libtomahawk/thirdparty/kdsingleapplicationguard/kdsingleapplicationguard.h +++ b/src/libtomahawk/thirdparty/kdsingleapplicationguard/kdsingleapplicationguard.h @@ -2,24 +2,29 @@ #define __KDTOOLSCORE_KDSINGLEAPPLICATIONGUARD_H__ #include + +#if QT_VERSION >= 0x040400 || defined(DOXYGEN_RUN) +#ifndef QT_NO_SHAREDMEMORY + #include +#include #include "pimpl_ptr.h" #include "DllMacro.h" -class QCoreApplication; +#include -#ifndef Q_WS_WIN -void SIGINT_handler( int sig ); -#endif +template class QVector; +class QCoreApplication; class DLLEXPORT KDSingleApplicationGuard : public QObject { Q_OBJECT -#ifndef Q_WS_WIN - friend void ::SIGINT_handler( int ); -#endif - + Q_ENUMS( Policy ) + Q_PROPERTY( bool operational READ isOperational ) + Q_PROPERTY( bool exitRequested READ isExitRequested ) + Q_PROPERTY( bool primaryInstance READ isPrimaryInstance NOTIFY becamePrimaryInstance ) + Q_PROPERTY( Policy policy READ policy WRITE setPolicy NOTIFY policyChanged ) public: enum Policy { @@ -27,49 +32,112 @@ public: AutoKillOtherInstances = 1 }; - Q_PROPERTY( bool primaryInstance READ isPrimaryInstance NOTIFY becamePrimaryInstance ) - Q_PROPERTY( Policy policy READ policy WRITE setPolicy NOTIFY policyChanged ) - - explicit KDSingleApplicationGuard( QCoreApplication* parent, Policy policy = AutoKillOtherInstances ); + explicit KDSingleApplicationGuard( QObject * parent=0 ); + explicit KDSingleApplicationGuard( Policy policy, QObject * parent=0 ); + explicit KDSingleApplicationGuard( const QStringList & arguments, QObject * parent=0 ); + explicit KDSingleApplicationGuard( const QStringList & arguments, Policy policy, QObject * parent=0 ); ~KDSingleApplicationGuard(); + bool isOperational() const; + + bool isExitRequested() const; + bool isPrimaryInstance() const; - + Policy policy() const; void setPolicy( Policy policy ); - struct Instance - { - Instance( const QStringList& arguments = QStringList(), qint64 pid = -1 ); + class Instance; - QStringList arguments; - qint64 pid; - }; - - QList< Instance > instances() const; + QVector instances() const; Q_SIGNALS: - void instanceStarted( KDSingleApplicationGuard::Instance instance ); - void instanceExited( KDSingleApplicationGuard::Instance instance ); + void instanceStarted( const KDSingleApplicationGuard::Instance & instance ); + void instanceExited( const KDSingleApplicationGuard::Instance & instance ); + void exitRequested(); + void raiseRequested(); void becamePrimaryInstance(); - void policyChanged(); + void becameSecondaryInstance(); + void policyChanged( KDSingleApplicationGuard::Policy policy ); public Q_SLOTS: void shutdownOtherInstances(); void killOtherInstances(); protected: - void timerEvent( QTimerEvent* event ); + /*! \reimp */ bool event( QEvent * event ); private: +#ifndef Q_WS_WIN + static void SIGINT_handler( int ); +#endif + +private: + friend struct ProcessInfo; + class Private; kdtools::pimpl_ptr< Private > d; }; -#if QT_VERSION < 0x040400 -#ifdef Q_CC_GNU -#warning "Can't use KDSingleApplicationGuard with Qt versions prior to 4.4" -#endif -#endif +class DLLEXPORT KDSingleApplicationGuard::Instance { + friend class ::KDSingleApplicationGuard; + friend class ::KDSingleApplicationGuard::Private; + Instance( const QStringList &, bool, qint64 ); +public: + Instance(); + Instance( const Instance & other ); + ~Instance(); -#endif + void swap( Instance & other ) { + std::swap( d, other.d ); + } + + Instance & operator=( Instance other ) { + swap( other ); + return *this; + } + + bool isNull() const { return !d; } + bool isValid() const; + + bool areArgumentsTruncated() const; + + const QStringList & arguments() const; + qint64 pid() const; + + void shutdown(); + void kill(); + void raise(); + +private: + class Private; + Private * d; +}; + +namespace std { + template <> + inline void swap( KDSingleApplicationGuard::Instance & lhs, + KDSingleApplicationGuard::Instance & rhs ) + { + lhs.swap( rhs ); + } +} // namespace std + +QT_BEGIN_NAMESPACE + +template <> +inline void qSwap( KDSingleApplicationGuard::Instance & lhs, + KDSingleApplicationGuard::Instance & rhs ) +{ + lhs.swap( rhs ); +} +Q_DECLARE_METATYPE( KDSingleApplicationGuard::Instance ) +Q_DECLARE_TYPEINFO( KDSingleApplicationGuard::Instance, Q_MOVABLE_TYPE ); + +QT_END_NAMESPACE + + +#endif // QT_NO_SHAREDMEMORY +#endif // QT_VERSION >= 0x040400 || defined(DOXYGEN_RUN) + +#endif /* __KDTOOLSCORE_KDSINGLEAPPLICATIONGUARD_H__ */ diff --git a/src/libtomahawk/utils/AnimatedSpinner.cpp b/src/libtomahawk/utils/AnimatedSpinner.cpp index 7cdb7dda0..2d66e7273 100644 --- a/src/libtomahawk/utils/AnimatedSpinner.cpp +++ b/src/libtomahawk/utils/AnimatedSpinner.cpp @@ -28,7 +28,10 @@ #include #include -AnimatedSpinner::AnimatedSpinner( QWidget *parent ) +#include "utils/Logger.h" + + +AnimatedSpinner::AnimatedSpinner( QWidget* parent ) : QWidget( parent ) , m_showHide( new QTimeLine ) , m_animation( new QTimeLine ) @@ -38,7 +41,7 @@ AnimatedSpinner::AnimatedSpinner( QWidget *parent ) } -AnimatedSpinner::AnimatedSpinner( const QSize size, bool autoStart ) +AnimatedSpinner::AnimatedSpinner( const QSize& size, bool autoStart ) : QWidget() , m_showHide( new QTimeLine ) , m_animation( new QTimeLine ) @@ -57,12 +60,14 @@ AnimatedSpinner::AnimatedSpinner( const QSize size, bool autoStart ) void AnimatedSpinner::init() { + m_autoCenter = true; m_showHide->setDuration( 300 ); m_showHide->setStartFrame( 0 ); m_showHide->setEndFrame( 100 ); m_showHide->setUpdateInterval( 20 ); - if( parentWidget() ) + + if ( parentWidget() ) connect( m_showHide, SIGNAL( frameChanged( int ) ), this, SLOT( update() ) ); else connect( m_showHide, SIGNAL( frameChanged( int ) ), this, SLOT( updatePixmap() ) ); @@ -86,13 +91,12 @@ AnimatedSpinner::init() else size = m_pixmap.size(); - /// Radius is best-fit line with points (13x13, 2), (28x28, 5), (48x48, 10) - m_radius = qRound( ( 23. * ( size.width() - 5.) ) / 100. ); - m_armLength = size.width()/2 - m_radius; + m_radius = qRound( ( 23. * ( size.width() - 5. ) ) / 100. ); + m_armLength = size.width() / 2 - m_radius; /// Arm width is best-fit line with points (13x13, 1), (28x28, 2), (48x48, 5) - m_armWidth = qRound( (116.*size.width() - 781.)/1000. ); + m_armWidth = qRound( ( 116. * size.width() - 781. ) / 1000. ); m_border = 2; m_armRect = QRect( m_radius, 0, m_armLength, m_armWidth ); @@ -101,12 +105,12 @@ AnimatedSpinner::init() void -AnimatedSpinner::paintEvent(QPaintEvent *event) +AnimatedSpinner::paintEvent( QPaintEvent* event ) { - Q_UNUSED(event); - if ( parentWidget() ) + Q_UNUSED( event ); + if ( m_autoCenter && parentWidget() ) { - QPoint center( ( parentWidget()->width() / 2 ) - ( width() / 2 ), ( parentWidget()->height() / 2 ) - ( height() / 2 ) ); + QPoint center = parentWidget()->contentsRect().center() - QPoint( sizeHint().width() / 2, sizeHint().height() / 2 ); if ( center != pos() ) { move( center ); @@ -114,7 +118,7 @@ AnimatedSpinner::paintEvent(QPaintEvent *event) } } - QPainter p(this); + QPainter p( this ); drawFrame( &p, rect() ); } @@ -142,8 +146,7 @@ AnimatedSpinner::drawFrame( QPainter* p, const QRect& rect ) p->setOpacity( (qreal)m_showHide->currentValue() ); } - p->setRenderHint(QPainter::Antialiasing, true); - + p->setRenderHint( QPainter::Antialiasing, true ); p->translate( rect.center() + QPoint( 0, 1 ) ); // center const qreal stepRadius = (360 + 2*m_armWidth) / segmentCount(); @@ -206,19 +209,17 @@ AnimatedSpinner::hideFinished() QSize AnimatedSpinner::sizeHint() const { - return QSize(48, 48); + return QSize( 48, 48 ); } void AnimatedSpinner::frameChanged( int frame ) { - - if ( m_currentIndex == frame || frame > segmentCount()-1 ) + if ( m_currentIndex == frame || frame > segmentCount() - 1 ) return; m_currentIndex = frame; - Q_ASSERT( frame >= 0 && frame < m_colors.size() ); // calculate colors, save a factor from 1 to 0 behind the current item @@ -250,7 +251,7 @@ AnimatedSpinner::colorForSegment( int seg ) const // Base color is 101, 101, 101 Q_ASSERT( seg < m_colors.size() ); const int comp = 101 + m_colors[seg] * ( 126 ); - return QColor(comp, comp, comp, 255); + return QColor( comp, comp, comp, 255 ); } @@ -259,3 +260,27 @@ AnimatedSpinner::segmentCount() const { return 11; } + + +LoadingSpinner::LoadingSpinner( QAbstractItemView* parent ) + : AnimatedSpinner( parent ) + , m_parent( parent ) +{ + if ( m_parent->model() ) + { + connect( m_parent->model(), SIGNAL( loadingStarted() ), SLOT( fadeIn() ), Qt::UniqueConnection ); + connect( m_parent->model(), SIGNAL( loadingFinished() ), SLOT( fadeOut() ), Qt::UniqueConnection ); + } + connect( m_parent, SIGNAL( modelChanged() ), SLOT( onViewModelChanged() ) ); +} + + +void +LoadingSpinner::onViewModelChanged() +{ + if ( m_parent->model() ) + { + connect( m_parent->model(), SIGNAL( loadingStarted() ), SLOT( fadeIn() ), Qt::UniqueConnection ); + connect( m_parent->model(), SIGNAL( loadingFinished() ), SLOT( fadeOut() ), Qt::UniqueConnection ); + } +} diff --git a/src/libtomahawk/utils/AnimatedSpinner.h b/src/libtomahawk/utils/AnimatedSpinner.h index e738d9bb0..19ec3090e 100644 --- a/src/libtomahawk/utils/AnimatedSpinner.h +++ b/src/libtomahawk/utils/AnimatedSpinner.h @@ -26,6 +26,7 @@ #include #include #include +#include class QTimeLine; class QHideEvent; @@ -42,13 +43,16 @@ class QTimerEvent; class DLLEXPORT AnimatedSpinner : public QWidget { Q_OBJECT + public: - explicit AnimatedSpinner( QWidget *parent = 0 ); // widget mode - AnimatedSpinner( const QSize size, bool autoStart ); // pixmap mode + explicit AnimatedSpinner( QWidget* parent = 0 ); // widget mode + AnimatedSpinner( const QSize& size, bool autoStart ); // pixmap mode QSize sizeHint() const; QPixmap pixmap() const { return m_pixmap; } + + void setAutoCenter( bool enabled ) { m_autoCenter = enabled; } public slots: void fadeIn(); @@ -82,6 +86,22 @@ private: int m_currentIndex; QVector m_colors; QPixmap m_pixmap; + bool m_autoCenter; +}; + + +class LoadingSpinner : public AnimatedSpinner +{ + Q_OBJECT + +public: + explicit LoadingSpinner( QAbstractItemView* parent = 0 ); // widget mode + +private slots: + void onViewModelChanged(); + +private: + QAbstractItemView* m_parent; }; #endif diff --git a/src/libtomahawk/utils/BinaryExtractWorker.cpp b/src/libtomahawk/utils/BinaryExtractWorker.cpp new file mode 100644 index 000000000..0c4353676 --- /dev/null +++ b/src/libtomahawk/utils/BinaryExtractWorker.cpp @@ -0,0 +1,105 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2010-2011, Christian Muehlhaeuser + * Copyright 2010-2011, Leo Franchi + * Copyright 2010-2012, Jeff Mitchell + * + * Tomahawk is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Tomahawk is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Tomahawk. If not, see . + */ + +#include "BinaryExtractWorker.h" + +#include +#include +#include +#include + +#include "utils/TomahawkUtils.h" +#include "utils/Logger.h" + + +namespace TomahawkUtils +{ + +void +BinaryExtractWorker::run() +{ + ScopedDeleter deleter( this ); + +#ifdef Q_OS_MAC + // Platform-specific handling of resolver payload now. We know it's good + // Unzip the file. + QFileInfo info( m_zipFileName ); + QDir tmpDir = QDir::tempPath(); + if ( !tmpDir.mkdir( info.baseName() ) ) + { + qWarning() << "Failed to create temporary directory to unzip in:" << tmpDir.absolutePath(); + return; + } + tmpDir.cd( info.baseName() ); + TomahawkUtils::unzipFileInFolder( info.absoluteFilePath(), tmpDir ); + + // On OSX it just contains 1 file, the resolver executable itself. For now. We just copy it to + // the Tomahawk.app/Contents/MacOS/ folder alongside the Tomahawk executable. + const QString dest = QCoreApplication::applicationDirPath(); + // Find the filename + const QDir toList( tmpDir.absolutePath() ); + const QStringList files = toList.entryList( QStringList(), QDir::Files ); + Q_ASSERT( files.size() == 1 ); + + const QString src = toList.absoluteFilePath( files.first() ); + qDebug() << "OS X: Copying binary resolver from to:" << src << dest; + + copyWithAuthentication( src, dest, m_receiver ); + + return; +#elif defined(Q_OS_WIN) || defined(Q_OS_LINUX) + // We unzip directly to the target location, just like normal attica resolvers + Q_ASSERT( m_receiver ); + if ( !m_receiver ) + return; + + const QString resolverId = m_receiver->property( "resolverid" ).toString(); + + Q_ASSERT( !resolverId.isEmpty() ); + if ( resolverId.isEmpty() ) + return; + + const QDir resolverPath( extractScriptPayload( m_zipFileName, resolverId ) ); + +#ifdef Q_OS_WIN + const QStringList files = resolverPath.entryList( QStringList() << "*.exe", QDir::Files ); +#elif defined(Q_OS_LINUX) + const QStringList files = resolverPath.entryList( QStringList() << "*_tomahawkresolver", QDir::Files ); +#endif + + qDebug() << "Found executables in unzipped binary resolver dir:" << files; + Q_ASSERT( files.size() == 1 ); + if ( files.size() < 1 ) + return; + + const QString resolverToUse = resolverPath.absoluteFilePath( files.first() ); + +#ifdef Q_OS_LINUX + QProcess p; + p.start( "chmod", QStringList() << "744" << resolverToUse, QIODevice::ReadOnly ); + p.waitForFinished(); +#endif + + QMetaObject::invokeMethod( m_receiver, "installSucceeded", Qt::QueuedConnection, Q_ARG( QString, resolverToUse ) ); + +#endif +} + +} diff --git a/src/libtomahawk/playlist/CollectionProxyModel.h b/src/libtomahawk/utils/BinaryExtractWorker.h similarity index 59% rename from src/libtomahawk/playlist/CollectionProxyModel.h rename to src/libtomahawk/utils/BinaryExtractWorker.h index ab2d06dc3..52fceb301 100644 --- a/src/libtomahawk/playlist/CollectionProxyModel.h +++ b/src/libtomahawk/utils/BinaryExtractWorker.h @@ -1,6 +1,7 @@ /* === This file is part of Tomahawk Player - === * * Copyright 2010-2011, Christian Muehlhaeuser + * Copyright 2010-2011, Leo Franchi * Copyright 2010-2012, Jeff Mitchell * * Tomahawk is free software: you can redistribute it and/or modify @@ -17,24 +18,42 @@ * along with Tomahawk. If not, see . */ -#ifndef COLLECTIONPROXYMODEL_H -#define COLLECTIONPROXYMODEL_H +#ifndef BINARYEXTRACTWORKER_H +#define BINARYEXTRACTWORKER_H -#include "TrackProxyModel.h" -#include "TrackProxyModelPlaylistInterface.h" +#include #include "DllMacro.h" -class DLLEXPORT CollectionProxyModel : public TrackProxyModel +namespace TomahawkUtils { -Q_OBJECT +class ScopedDeleter +{ public: - explicit CollectionProxyModel( QObject* parent = 0 ); - virtual ~CollectionProxyModel() {} - - virtual Tomahawk::playlistinterface_ptr playlistInterface(); + ScopedDeleter( QObject* o ) : m_o( 0 ) {} + ~ScopedDeleter() { m_o->deleteLater(); } +private: + QObject* m_o; }; -#endif // COLLECTIONPROXYMODEL_H + +class BinaryExtractWorker : public QThread +{ + Q_OBJECT +public: + BinaryExtractWorker( const QString& zipFilename, QObject* receiver ) : m_zipFileName( zipFilename ), m_receiver( receiver ) {} + virtual ~BinaryExtractWorker() {} + +protected: + virtual void run(); + +private: + QString m_zipFileName; + QObject* m_receiver; +}; + +} + +#endif \ No newline at end of file diff --git a/src/libtomahawk/utils/BinaryInstallerHelper.cpp b/src/libtomahawk/utils/BinaryInstallerHelper.cpp new file mode 100644 index 000000000..d061053ca --- /dev/null +++ b/src/libtomahawk/utils/BinaryInstallerHelper.cpp @@ -0,0 +1,83 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2012 Leo Franchi + * + * Tomahawk is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Tomahawk is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Tomahawk. If not, see . + */ + +#include "BinaryInstallerHelper.h" + +#include "accounts/AccountManager.h" +#include "TomahawkSettingsGui.h" +#include + +BinaryInstallerHelper::BinaryInstallerHelper( QTemporaryFile* tempFile, const QString& resolverId, bool createAccount, AtticaManager* manager ) + : QObject( manager ) + , m_tempFile( tempFile ) + , m_resolverId( resolverId ) + , m_createAccount( createAccount ) + , m_manager( QWeakPointer< AtticaManager >( manager ) ) +{ + Q_ASSERT( m_tempFile ); + Q_ASSERT( !m_resolverId.isEmpty() ); + Q_ASSERT( !m_manager.isNull() ); + + setProperty( "resolverid", m_resolverId ); +} + + +BinaryInstallerHelper::~BinaryInstallerHelper() +{ + Q_ASSERT( m_tempFile ); + delete m_tempFile; +} + + +void +BinaryInstallerHelper::installSucceeded( const QString& path ) +{ + qDebug() << Q_FUNC_INFO << "install of binary resolver succeeded, enabling: " << path; + + if ( m_manager.isNull() ) + return; + + if ( m_createAccount ) + { + Tomahawk::Accounts::Account* acct = Tomahawk::Accounts::AccountManager::instance()->accountFromPath( path ); + + Tomahawk::Accounts::AccountManager::instance()->addAccount( acct ); + TomahawkSettings::instance()->addAccount( acct->accountId() ); + Tomahawk::Accounts::AccountManager::instance()->enableAccount( acct ); + } + + m_manager.data()->m_resolverStates[ m_resolverId ].scriptPath = path; + m_manager.data()->m_resolverStates[ m_resolverId ].state = AtticaManager::Installed; + + TomahawkSettingsGui::instanceGui()->setAtticaResolverStates( m_manager.data()->m_resolverStates ); + emit m_manager.data()->resolverInstalled( m_resolverId ); + emit m_manager.data()->resolverStateChanged( m_resolverId ); + + deleteLater(); +} +void BinaryInstallerHelper::installFailed() +{ + qDebug() << Q_FUNC_INFO << "install failed"; + + if ( m_manager.isNull() ) + return; + + m_manager.data()->resolverInstallationFailed( m_resolverId ); + + deleteLater(); +} diff --git a/src/libtomahawk/utils/BinaryInstallerHelper.h b/src/libtomahawk/utils/BinaryInstallerHelper.h new file mode 100644 index 000000000..33fbba7ce --- /dev/null +++ b/src/libtomahawk/utils/BinaryInstallerHelper.h @@ -0,0 +1,45 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2012 Leo Franchi + * + * Tomahawk is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Tomahawk is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Tomahawk. If not, see . + */ +#ifndef BINARY_INSTALLER_HELPER +#define BINARY_INSTALLER_HELPER + +#include "AtticaManager.h" + +#include + +class QTemporaryFile; +class BinaryInstallerHelper : public QObject +{ + Q_OBJECT +public: + explicit BinaryInstallerHelper( QTemporaryFile* tempFile, const QString& resolverId, bool createAccount, AtticaManager* manager ); + + virtual ~BinaryInstallerHelper(); + +public slots: + void installSucceeded( const QString& path ); + void installFailed(); + +private: + QTemporaryFile* m_tempFile; + QString m_resolverId; + bool m_createAccount; + QWeakPointer m_manager; +}; + +#endif \ No newline at end of file diff --git a/src/libtomahawk/utils/Closure.h b/src/libtomahawk/utils/Closure.h index f071c0e28..591712358 100644 --- a/src/libtomahawk/utils/Closure.h +++ b/src/libtomahawk/utils/Closure.h @@ -1,5 +1,6 @@ -/* This file is part of Clementine. +/* This file is part of Tomabawk. Copyright 2011, David Sansome + Copyright 2012, Leo Franchi Clementine is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/libtomahawk/utils/DropJobNotifier.cpp b/src/libtomahawk/utils/DropJobNotifier.cpp index 535c5a295..085e42a99 100644 --- a/src/libtomahawk/utils/DropJobNotifier.cpp +++ b/src/libtomahawk/utils/DropJobNotifier.cpp @@ -32,25 +32,25 @@ #include #include -class QNetworkReply; - using namespace Tomahawk; + DropJobNotifier::DropJobNotifier( QPixmap servicePixmap, QString service, DropJob::DropType type, QNetworkReply* job ) : JobStatusItem() , m_type( "unknown" ) , m_job( 0 ) - , m_pixmap ( servicePixmap ) - , m_service ( service ) + , m_pixmap( servicePixmap ) + , m_service( service ) { init( type ); - if( m_service.isEmpty() ) + if ( m_service.isEmpty() ) m_service = "DropJob"; connect( job, SIGNAL( finished() ), this, SLOT( setFinished() ) ); } + DropJobNotifier::DropJobNotifier( QPixmap pixmap, DropJob::DropType type ) : JobStatusItem() , m_job( 0 ) @@ -61,23 +61,24 @@ DropJobNotifier::DropJobNotifier( QPixmap pixmap, DropJob::DropType type ) DropJobNotifier::~DropJobNotifier() -{} +{ +} + void DropJobNotifier::init( DropJob::DropType type ) { - if( type == DropJob::Playlist ) - m_type = "playlist"; + if ( type == DropJob::Playlist ) + m_type = tr( "playlist" ); - if( type == DropJob::Artist ) - m_type = "artist"; + if ( type == DropJob::Artist ) + m_type = tr( "artist" ); - if( type == DropJob::Track ) - m_type = "track"; - - if( type == DropJob::Album ) - m_type = "album"; + if ( type == DropJob::Track ) + m_type = tr( "track" ); + if ( type == DropJob::Album ) + m_type = tr( "album" ); } @@ -87,6 +88,7 @@ DropJobNotifier::rightColumnText() const return QString(); } + QPixmap DropJobNotifier::icon() const { @@ -108,6 +110,7 @@ DropJobNotifier::mainText() const } } + void DropJobNotifier::setFinished() { diff --git a/src/utils/GuiHelpers.cpp b/src/libtomahawk/utils/GuiHelpers.cpp similarity index 98% rename from src/utils/GuiHelpers.cpp rename to src/libtomahawk/utils/GuiHelpers.cpp index 8f3d3c5ba..a682f8b9c 100644 --- a/src/utils/GuiHelpers.cpp +++ b/src/libtomahawk/utils/GuiHelpers.cpp @@ -22,7 +22,8 @@ #include "accounts/Account.h" #include "accounts/AccountManager.h" -#include "DelegateConfigWrapper.h" +#include "accounts/DelegateConfigWrapper.h" +#include "TomahawkSettings.h" namespace TomahawkUtils { @@ -168,4 +169,4 @@ openAccountConfig( Tomahawk::Accounts::Account* account, QWidget* parent, bool s } // namespace TomahawkUtils -#include "GuiHelpers.moc" \ No newline at end of file +#include "GuiHelpers.moc" diff --git a/src/utils/GuiHelpers.h b/src/libtomahawk/utils/GuiHelpers.h similarity index 80% rename from src/utils/GuiHelpers.h rename to src/libtomahawk/utils/GuiHelpers.h index f17ccd43c..9fe8f3873 100644 --- a/src/utils/GuiHelpers.h +++ b/src/libtomahawk/utils/GuiHelpers.h @@ -19,6 +19,8 @@ #ifndef TOMAHAWK_GUI_HELPERS_H #define TOMAHAWK_GUI_HELPERS_H +#include "DllMacro.h" + class QWidget; namespace Tomahawk { namespace Accounts { @@ -29,8 +31,8 @@ namespace Tomahawk { namespace TomahawkUtils { - void createAccountFromFactory( Tomahawk::Accounts::AccountFactory*, QWidget* parent ); - void openAccountConfig( Tomahawk::Accounts::Account*, QWidget* parent, bool showDelete = false ); + DLLEXPORT void createAccountFromFactory( Tomahawk::Accounts::AccountFactory*, QWidget* parent ); + DLLEXPORT void openAccountConfig( Tomahawk::Accounts::Account*, QWidget* parent, bool showDelete = false ); } -#endif \ No newline at end of file +#endif diff --git a/src/libtomahawk/utils/Logger.cpp b/src/libtomahawk/utils/Logger.cpp index 4f4efd7f5..a34df872d 100644 --- a/src/libtomahawk/utils/Logger.cpp +++ b/src/libtomahawk/utils/Logger.cpp @@ -35,6 +35,7 @@ #define RELEASE_LEVEL_THRESHOLD 0 #define DEBUG_LEVEL_THRESHOLD LOGEXTRA +#define LOG_SQL_QUERIES 1 using namespace std; ofstream logfile; @@ -65,9 +66,19 @@ log( const char *msg, unsigned int debugLevel, bool toDisk = true ) if ( debugLevel > DEBUG_LEVEL_THRESHOLD ) toDisk = false; #endif + + #ifdef LOG_SQL_QUERIES + if ( debugLevel == LOGSQL ) + toDisk = true; + #endif if ( toDisk || (int)debugLevel <= s_threshold ) { + #ifdef LOG_SQL_QUERIES + if ( debugLevel == LOGSQL ) + logfile << "TSQLQUERY: "; + #endif + logfile << QTime::currentTime().toString().toAscii().data() << " [" << QString::number( debugLevel ).toAscii().data() << "]: " << msg << endl; logfile.flush(); } diff --git a/src/libtomahawk/utils/Logger.h b/src/libtomahawk/utils/Logger.h index a95fee6ef..6c1f69fdb 100644 --- a/src/libtomahawk/utils/Logger.h +++ b/src/libtomahawk/utils/Logger.h @@ -24,6 +24,13 @@ #include "DllMacro.h" +#define LOGDEBUG 1 +#define LOGINFO 2 +#define LOGEXTRA 5 +#define LOGVERBOSE 8 +#define LOGTHIRDPARTY 9 +#define LOGSQL 10 + namespace Logger { class DLLEXPORT TLog : public QDebug @@ -40,7 +47,15 @@ namespace Logger class DLLEXPORT TDebug : public TLog { public: - TDebug( unsigned int debugLevel = 1 ) : TLog( debugLevel ) + TDebug( unsigned int debugLevel = LOGDEBUG ) : TLog( debugLevel ) + { + } + }; + + class DLLEXPORT TSqlLog : public TLog + { + public: + TSqlLog() : TLog( LOGSQL ) { } }; @@ -51,11 +66,6 @@ namespace Logger #define tLog Logger::TLog #define tDebug Logger::TDebug - -#define LOGDEBUG 1 -#define LOGINFO 2 -#define LOGEXTRA 5 -#define LOGVERBOSE 8 -#define LOGTHIRDPARTY 9 +#define tSqlLog Logger::TSqlLog #endif // TOMAHAWK_LOGGER_H diff --git a/src/libtomahawk/utils/PixmapDelegateFader.cpp b/src/libtomahawk/utils/PixmapDelegateFader.cpp index 9148150b1..b03e57f6d 100644 --- a/src/libtomahawk/utils/PixmapDelegateFader.cpp +++ b/src/libtomahawk/utils/PixmapDelegateFader.cpp @@ -1,8 +1,9 @@ /* === This file is part of Tomahawk Player - === * * Copyright 2010-2012, Leo Franchi - * Copyright 2012, Jeff Mitchell - * + * Copyright 2012, Jeff Mitchell + * Copyright 2010-2012, Christian Muehlhaeuser + * * Tomahawk is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or @@ -19,6 +20,7 @@ #include "PixmapDelegateFader.h" #include "TomahawkUtilsGui.h" +#include "Source.h" #include #include @@ -27,10 +29,9 @@ using namespace Tomahawk; -#define COVER_FADEIN 1000 - QWeakPointer< TomahawkUtils::SharedTimeLine > PixmapDelegateFader::s_stlInstance = QWeakPointer< TomahawkUtils::SharedTimeLine >(); + QWeakPointer< TomahawkUtils::SharedTimeLine > PixmapDelegateFader::stlInstance() { @@ -45,13 +46,10 @@ PixmapDelegateFader::PixmapDelegateFader( const artist_ptr& artist, const QSize& : m_artist( artist ) , m_size( size ) , m_mode( mode ) - , m_startFrame( 0 ) - , m_connectedToStl( false ) - , m_fadePct( 100 ) { if ( !m_artist.isNull() ) { - connect( m_artist.data(), SIGNAL( updated() ), SLOT( trackChanged() ) ); + connect( m_artist.data(), SIGNAL( updated() ), SLOT( artistChanged() ) ); connect( m_artist.data(), SIGNAL( coverChanged() ), SLOT( artistChanged() ) ); m_currentReference = m_artist->cover( size, forceLoad ); } @@ -59,17 +57,15 @@ PixmapDelegateFader::PixmapDelegateFader( const artist_ptr& artist, const QSize& init(); } + PixmapDelegateFader::PixmapDelegateFader( const album_ptr& album, const QSize& size, TomahawkUtils::ImageMode mode, bool forceLoad ) : m_album( album ) , m_size( size ) , m_mode( mode ) - , m_startFrame( 0 ) - , m_connectedToStl( false ) - , m_fadePct( 100 ) { if ( !m_album.isNull() ) { - connect( m_album.data(), SIGNAL( updated() ), SLOT( trackChanged() ) ); + connect( m_album.data(), SIGNAL( updated() ), SLOT( albumChanged() ) ); connect( m_album.data(), SIGNAL( coverChanged() ), SLOT( albumChanged() ) ); m_currentReference = m_album->cover( size, forceLoad ); } @@ -82,9 +78,6 @@ PixmapDelegateFader::PixmapDelegateFader( const query_ptr& track, const QSize& s : m_track( track ) , m_size( size ) , m_mode( mode ) - , m_startFrame( 0 ) - , m_connectedToStl( false ) - , m_fadePct( 100 ) { if ( !m_track.isNull() ) { @@ -99,17 +92,40 @@ PixmapDelegateFader::PixmapDelegateFader( const query_ptr& track, const QSize& s PixmapDelegateFader::~PixmapDelegateFader() { - } void PixmapDelegateFader::init() { + if ( m_currentReference.isNull() ) + m_defaultImage = true; + + m_startFrame = 0; + m_fadePct = 100; + m_connectedToStl = false; + m_current = QPixmap( m_size ); m_current.fill( Qt::transparent ); - - if ( m_currentReference.isNull() ) + + setSize( m_size ); + if ( m_defaultImage ) + return; + + stlInstance().data()->setUpdateInterval( 20 ); + m_startFrame = stlInstance().data()->currentFrame(); + m_connectedToStl = true; + m_fadePct = 0; + connect( stlInstance().data(), SIGNAL( frameChanged( int ) ), SLOT( onAnimationStep( int ) ) ); +} + + +void +PixmapDelegateFader::setSize( const QSize& size ) +{ + m_size = size; + + if ( m_defaultImage ) { // No cover loaded yet, use default and don't fade in if ( !m_album.isNull() ) @@ -117,16 +133,19 @@ PixmapDelegateFader::init() else if ( !m_artist.isNull() ) m_current = m_currentReference = TomahawkUtils::defaultPixmap( TomahawkUtils::DefaultArtistImage, m_mode, m_size ); else if ( !m_track.isNull() ) - m_current = m_currentReference = TomahawkUtils::defaultPixmap( TomahawkUtils::DefaultTrackImage, m_mode, m_size ); - - return; + m_current = m_currentReference = TomahawkUtils::defaultPixmap( TomahawkUtils::DefaultArtistImage, m_mode, m_size ); + } + else + { + if ( !m_album.isNull() ) + m_currentReference = m_album->cover( m_size ); + else if ( !m_artist.isNull() ) + m_currentReference = m_artist->cover( m_size ); + else if ( !m_track.isNull() ) + m_currentReference = m_track->cover( m_size ); } - stlInstance().data()->setUpdateInterval( 20 ); - m_startFrame = stlInstance().data()->currentFrame(); - m_connectedToStl = true; - m_fadePct = 0; - connect( stlInstance().data(), SIGNAL( frameChanged( int ) ), this, SLOT( onAnimationStep( int ) ) ); + emit repaintRequest(); } @@ -139,6 +158,7 @@ PixmapDelegateFader::albumChanged() QMetaObject::invokeMethod( this, "setPixmap", Qt::QueuedConnection, Q_ARG( QPixmap, m_album->cover( m_size ) ) ); } + void PixmapDelegateFader::artistChanged() { @@ -165,22 +185,23 @@ PixmapDelegateFader::setPixmap( const QPixmap& pixmap ) if ( pixmap.isNull() ) return; - QByteArray ba; - QBuffer buffer( &ba ); - buffer.open( QIODevice::WriteOnly ); - pixmap.save( &buffer, "PNG" ); - QString newImageMd5 = TomahawkUtils::md5( buffer.data() ); + m_defaultImage = false; + QCryptographicHash hash( QCryptographicHash::Md5 ); + const QImage img = pixmap.toImage(); + hash.addData( (const char*)img.constBits(), img.byteCount() ); + const QString newImageMd5 = hash.result(); + if ( m_oldImageMd5 == newImageMd5 ) return; m_oldImageMd5 = newImageMd5; - + if ( m_connectedToStl ) { m_pixmapQueue.enqueue( pixmap ); return; } - + m_oldReference = m_currentReference; m_currentReference = pixmap; @@ -201,7 +222,7 @@ PixmapDelegateFader::onAnimationStep( int step ) if ( m_fadePct == 100.0 ) QTimer::singleShot( 0, this, SLOT( onAnimationFinished() ) ); - + const qreal opacity = m_fadePct / 100.0; const qreal oldOpacity = ( 100.0 - m_fadePct ) / 100.0; m_current.fill( Qt::transparent ); @@ -281,7 +302,6 @@ PixmapDelegateFader::onAnimationFinished() } - QPixmap PixmapDelegateFader::currentPixmap() const { diff --git a/src/libtomahawk/utils/PixmapDelegateFader.h b/src/libtomahawk/utils/PixmapDelegateFader.h index 9d4b71229..f79c58774 100644 --- a/src/libtomahawk/utils/PixmapDelegateFader.h +++ b/src/libtomahawk/utils/PixmapDelegateFader.h @@ -1,7 +1,7 @@ /* === This file is part of Tomahawk Player - === * * Copyright 2011-2012, Leo Franchi - * Copyright 2012, Jeff Mitchell + * Copyright 2012, Jeff Mitchell * * Tomahawk is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -23,6 +23,7 @@ #include "Artist.h" #include "Album.h" #include "Query.h" +#include "utils/SharedTimeLine.h" #include #include @@ -51,8 +52,12 @@ public: virtual ~PixmapDelegateFader(); + QSize size() const { return m_size; } QPixmap currentPixmap() const; +public slots: + void setSize( const QSize& size ); + signals: void repaintRequest(); @@ -77,6 +82,7 @@ private: bool m_connectedToStl; float m_fadePct; QString m_oldImageMd5; + bool m_defaultImage; QQueue m_pixmapQueue; diff --git a/src/libtomahawk/utils/SharedTimeLine.cpp b/src/libtomahawk/utils/SharedTimeLine.cpp new file mode 100644 index 000000000..cb48befed --- /dev/null +++ b/src/libtomahawk/utils/SharedTimeLine.cpp @@ -0,0 +1,64 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2010-2011, Christian Muehlhaeuser + * Copyright 2010-2011, Leo Franchi + * Copyright 2010-2012, Jeff Mitchell + * + * Tomahawk is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Tomahawk is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Tomahawk. If not, see . + */ + +#include "SharedTimeLine.h" + + +namespace TomahawkUtils +{ + +SharedTimeLine::SharedTimeLine() + : QObject( 0 ) + , m_refcount( 0 ) +{ + m_timeline.setCurveShape( QTimeLine::LinearCurve ); + m_timeline.setFrameRange( 0, INT_MAX ); + m_timeline.setDuration( INT_MAX ); + m_timeline.setUpdateInterval( 40 ); + connect( &m_timeline, SIGNAL( frameChanged( int ) ), SIGNAL( frameChanged( int ) ) ); +} + + +void +SharedTimeLine::connectNotify( const char* signal ) +{ + if ( signal == QMetaObject::normalizedSignature( SIGNAL( frameChanged( int ) ) ) ) { + m_refcount++; + if ( m_timeline.state() != QTimeLine::Running ) + m_timeline.start(); + } +} + + +void +SharedTimeLine::disconnectNotify( const char* signal ) +{ + if ( signal == QMetaObject::normalizedSignature( SIGNAL( frameChanged( int ) ) ) ) + { + m_refcount--; + if ( m_timeline.state() == QTimeLine::Running && m_refcount == 0 ) + { + m_timeline.stop(); + deleteLater(); + } + } +} + +} \ No newline at end of file diff --git a/src/libtomahawk/playlist/PlaylistProxyModel.h b/src/libtomahawk/utils/SharedTimeLine.h similarity index 54% rename from src/libtomahawk/playlist/PlaylistProxyModel.h rename to src/libtomahawk/utils/SharedTimeLine.h index ec8ce154f..5d8d59602 100644 --- a/src/libtomahawk/playlist/PlaylistProxyModel.h +++ b/src/libtomahawk/utils/SharedTimeLine.h @@ -1,6 +1,7 @@ /* === This file is part of Tomahawk Player - === * * Copyright 2010-2011, Christian Muehlhaeuser + * Copyright 2010-2011, Leo Franchi * Copyright 2010-2012, Jeff Mitchell * * Tomahawk is free software: you can redistribute it and/or modify @@ -17,22 +18,43 @@ * along with Tomahawk. If not, see . */ -#ifndef PLAYLISTPROXYMODEL_H -#define PLAYLISTPROXYMODEL_H +#ifndef SHAREDTIMELINE_H +#define SHAREDTIMELINE_H -#include "TrackProxyModel.h" +#include +#include #include "DllMacro.h" -class DLLEXPORT PlaylistProxyModel : public TrackProxyModel +namespace TomahawkUtils { -Q_OBJECT -public: - explicit PlaylistProxyModel( QObject* parent = 0 ); - virtual ~PlaylistProxyModel() {} +class DLLEXPORT SharedTimeLine : public QObject +{ + Q_OBJECT - virtual Tomahawk::playlistinterface_ptr playlistInterface(); + public: + SharedTimeLine(); + + virtual ~SharedTimeLine() {} + + int currentFrame() { return m_timeline.currentFrame(); } + + void setUpdateInterval( int msec ) { if ( msec != m_timeline.updateInterval() ) m_timeline.setUpdateInterval( msec ); } + + signals: + void frameChanged( int ); + + protected slots: + virtual void connectNotify( const char *signal ); + + virtual void disconnectNotify( const char *signal ); + + private: + int m_refcount; + QTimeLine m_timeline; }; -#endif // PLAYLISTPROXYMODEL_H +} + +#endif \ No newline at end of file diff --git a/src/libtomahawk/utils/ShortenedLinkParser.cpp b/src/libtomahawk/utils/ShortenedLinkParser.cpp index 09bf6929e..6452e1b0c 100644 --- a/src/libtomahawk/utils/ShortenedLinkParser.cpp +++ b/src/libtomahawk/utils/ShortenedLinkParser.cpp @@ -26,6 +26,7 @@ #include "jobview/ErrorStatusMessage.h" #include "jobview/JobStatusModel.h" #include "jobview/JobStatusView.h" +#include "Source.h" #include diff --git a/src/libtomahawk/utils/TomahawkCache.cpp b/src/libtomahawk/utils/TomahawkCache.cpp index 08e441ed9..51e58f313 100644 --- a/src/libtomahawk/utils/TomahawkCache.cpp +++ b/src/libtomahawk/utils/TomahawkCache.cpp @@ -19,6 +19,7 @@ #include "TomahawkCache.h" #include "TomahawkSettings.h" +#include "Source.h" #include "utils/Logger.h" #include @@ -100,7 +101,7 @@ QVariant Cache::getData ( const QString& identifier, const QString& key ) return data.data; } - tDebug( LOGVERBOSE ) << Q_FUNC_INFO << "No such client" << identifier; + tDebug( LOGVERBOSE ) << Q_FUNC_INFO << "No such key" << key; return QVariant(); } diff --git a/src/libtomahawk/utils/TomahawkUtils.cpp b/src/libtomahawk/utils/TomahawkUtils.cpp index 8fcf170af..83cfbf821 100644 --- a/src/libtomahawk/utils/TomahawkUtils.cpp +++ b/src/libtomahawk/utils/TomahawkUtils.cpp @@ -24,6 +24,9 @@ #include "utils/TomahawkUtils.h" #include "utils/Logger.h" +#include "Source.h" +#include "BinaryExtractWorker.h" +#include "SharedTimeLine.h" #ifdef LIBLASTFM_FOUND #include @@ -38,8 +41,12 @@ #include #include #include +#include -#ifdef Q_WS_WIN +#include +#include + +#ifdef Q_OS_WIN #include #include #endif @@ -49,6 +56,10 @@ #include #endif +#ifdef QCA2_FOUND + #include +#endif + namespace TomahawkUtils { static quint64 s_infosystemRequestId = 0; @@ -167,13 +178,6 @@ appLogDir() } -QString -sqlEscape( QString sql ) -{ - return sql.replace( "'", "''" ); -} - - QString timeToString( int seconds ) { @@ -292,17 +296,31 @@ extensionToMimetype( const QString& extension ) s_ext2mime.insert( "mp3", "audio/mpeg" ); s_ext2mime.insert( "ogg", "application/ogg" ); s_ext2mime.insert( "oga", "application/ogg" ); - s_ext2mime.insert( "flac", "audio/flac" ); s_ext2mime.insert( "mpc", "audio/x-musepack" ); s_ext2mime.insert( "wma", "audio/x-ms-wma" ); s_ext2mime.insert( "aac", "audio/mp4" ); s_ext2mime.insert( "m4a", "audio/mp4" ); s_ext2mime.insert( "mp4", "audio/mp4" ); + s_ext2mime.insert( "flac", "audio/flac" ); + s_ext2mime.insert( "aiff", "audio/aiff" ); + s_ext2mime.insert( "aif", "audio/aiff" ); } return s_ext2mime.value( extension, "unknown" ); } + +void +msleep( unsigned int ms ) +{ + #ifdef WIN32 + Sleep( ms ); + #else + ::usleep( ms * 1000 ); + #endif +} + + static QMutex s_noProxyHostsMutex; static QStringList s_noProxyHosts; @@ -402,15 +420,15 @@ proxyFactory( bool makeClone, bool noMutexLocker ) { if ( s_threadProxyFactoryHash.contains( QThread::currentThread() ) ) return s_threadProxyFactoryHash[ QThread::currentThread() ]; - - if ( !s_threadProxyFactoryHash.contains( TOMAHAWK_APPLICATION::instance()->thread() ) ) - return 0; } // create a new proxy factory for this thread - TomahawkUtils::NetworkProxyFactory *mainProxyFactory = s_threadProxyFactoryHash[ TOMAHAWK_APPLICATION::instance()->thread() ]; TomahawkUtils::NetworkProxyFactory *newProxyFactory = new TomahawkUtils::NetworkProxyFactory(); - *newProxyFactory = *mainProxyFactory; + if ( s_threadProxyFactoryHash.contains( TOMAHAWK_APPLICATION::instance()->thread() ) ) + { + TomahawkUtils::NetworkProxyFactory *mainProxyFactory = s_threadProxyFactoryHash[ TOMAHAWK_APPLICATION::instance()->thread() ]; + *newProxyFactory = *mainProxyFactory; + } if ( !makeClone ) s_threadProxyFactoryHash[ QThread::currentThread() ] = newProxyFactory; @@ -453,7 +471,10 @@ nam() { QMutexLocker locker( &s_namAccessMutex ); if ( s_threadNamHash.contains( QThread::currentThread() ) ) + { + //tDebug() << Q_FUNC_INFO << "Found current thread in nam hash"; return s_threadNamHash[ QThread::currentThread() ]; + } if ( !s_threadNamHash.contains( TOMAHAWK_APPLICATION::instance()->thread() ) ) { @@ -465,6 +486,7 @@ nam() else return 0; } + tDebug() << Q_FUNC_INFO << "Found gui thread in nam hash"; // Create a nam for this thread based on the main thread's settings but with its own proxyfactory QNetworkAccessManager *mainNam = s_threadNamHash[ TOMAHAWK_APPLICATION::instance()->thread() ]; @@ -476,7 +498,9 @@ nam() s_threadNamHash[ QThread::currentThread() ] = newNam; - tDebug( LOGEXTRA ) << "created new nam for thread " << QThread::currentThread(); + tDebug( LOGEXTRA ) << Q_FUNC_INFO << "created new nam for thread " << QThread::currentThread(); + //QNetworkProxy proxy = dynamic_cast< TomahawkUtils::NetworkProxyFactory* >( newNam->proxyFactory() )->proxy(); + //tDebug() << Q_FUNC_INFO << "reply proxy properties: " << proxy.type() << proxy.hostName() << proxy.port(); return newNam; } @@ -513,6 +537,7 @@ setNam( QNetworkAccessManager* nam, bool noMutexLocker ) s_noProxyHostsMutex.unlock(); } + QNetworkProxyFactory::setApplicationProxyFactory( proxyFactory ); nam->setProxyFactory( proxyFactory ); s_threadNamHash[ QThread::currentThread() ] = nam; s_threadProxyFactoryHash[ QThread::currentThread() ] = proxyFactory; @@ -639,41 +664,174 @@ crash() } -SharedTimeLine::SharedTimeLine() - : QObject( 0 ) - , m_refcount( 0 ) +bool +verifyFile( const QString &filePath, const QString &signature ) { - m_timeline.setCurveShape( QTimeLine::LinearCurve ); - m_timeline.setFrameRange( 0, INT_MAX ); - m_timeline.setDuration( INT_MAX ); - m_timeline.setUpdateInterval( 40 ); - connect( &m_timeline, SIGNAL( frameChanged( int ) ), SIGNAL( frameChanged( int ) ) ); -} + QCA::Initializer init; -void -SharedTimeLine::connectNotify( const char* signal ) -{ - if ( signal == QMetaObject::normalizedSignature( SIGNAL( frameChanged( int ) ) ) ) { - m_refcount++; - if ( m_timeline.state() != QTimeLine::Running ) - m_timeline.start(); - } -} - - -void -SharedTimeLine::disconnectNotify( const char* signal ) -{ - if ( signal == QMetaObject::normalizedSignature( SIGNAL( frameChanged( int ) ) ) ) + if( !QCA::isSupported( "sha1" ) ) { - m_refcount--; - if ( m_timeline.state() == QTimeLine::Running && m_refcount == 0 ) - { - m_timeline.stop(); - deleteLater(); - } + qWarning() << "SHA1 not supported by QCA, aborting."; + return false; } + + // The signature for the resolver.zip was created like so: + // openssl dgst -sha1 -binary < "#{tarball}" | openssl dgst -dss1 -sign "#{ARGV[2]}" | openssl enc -base64 + // which means we need to decode it with QCA's DSA public key signature verification tools + // The input data is: + // file -> SHA1 binary format -> DSS1/DSA signed -> base64 encoded. + + // Step 1: Load the public key + // Public key is in :/data/misc/tomahawk_pubkey.pem + QFile f( ":/data/misc/tomahawk_pubkey.pem" ); + if ( !f.open( QIODevice::ReadOnly ) ) + { + qWarning() << "Unable to read public key from resources!"; + return false; + } + + const QString pubkeyData = QString::fromUtf8( f.readAll() ); + QCA::ConvertResult conversionResult; + QCA::PublicKey publicKey = QCA::PublicKey::fromPEM( pubkeyData, &conversionResult ); + if ( QCA::ConvertGood != conversionResult) + { + qWarning() << "Public key reading/loading failed! Tried to load public key:" << pubkeyData; + return false; + } + + if ( !publicKey.canVerify() ) + { + qWarning() << "Loaded Tomahawk public key but cannot use it to verify! What is up...."; + return false; + } + + // Step 2: Get the SHA1 of the file contents + QFile toVerify( filePath ); + if ( !toVerify.exists() || !toVerify.open( QIODevice::ReadOnly ) ) + { + qWarning() << "Failed to open file we are trying to verify!" << filePath; + return false; + } + + const QByteArray fileHashData = QCA::Hash( "sha1" ).hash( toVerify.readAll() ).toByteArray(); + toVerify.close(); + + // Step 3: Base64 decode the signature + QCA::Base64 decoder( QCA::Decode ); + const QByteArray decodedSignature = decoder.decode( QCA::SecureArray( signature.trimmed().toUtf8() ) ).toByteArray(); + if ( decodedSignature.isEmpty() ) + { + qWarning() << "Got empty signature after we tried to decode it from Base64:" << signature.trimmed().toUtf8() << decodedSignature.toBase64(); + return false; + } + + // Step 4: Do the actual verifying! + const bool result = publicKey.verifyMessage( fileHashData, decodedSignature, QCA::EMSA1_SHA1, QCA::DERSequence ); + if ( !result ) + { + qWarning() << "File" << filePath << "FAILED VERIFICATION against our input signature!"; + return false; + } + + qDebug() << "Successfully verified signature of downloaded file:" << filePath; + + return true; +} + + +QString +extractScriptPayload( const QString& filename, const QString& resolverId ) +{ + // uses QuaZip to extract the temporary zip file to the user's tomahawk data/resolvers directory + QDir resolverDir = appDataDir(); + if ( !resolverDir.mkpath( QString( "atticaresolvers/%1" ).arg( resolverId ) ) ) + { + tLog() << "Failed to mkdir resolver save dir: " << TomahawkUtils::appDataDir().absoluteFilePath( QString( "atticaresolvers/%1" ).arg( resolverId ) ); + return QString(); + } + resolverDir.cd( QString( "atticaresolvers/%1" ).arg( resolverId ) ); + + if ( !unzipFileInFolder( filename, resolverDir ) ) + { + qWarning() << "Failed to unzip resolver. Ooops."; + return QString(); + } + + return resolverDir.absolutePath(); +} + + +bool +unzipFileInFolder( const QString &zipFileName, const QDir &folder ) +{ + Q_ASSERT( !zipFileName.isEmpty() ); + Q_ASSERT( folder.exists() ); + + QuaZip zipFile( zipFileName ); + if ( !zipFile.open( QuaZip::mdUnzip ) ) + { + qWarning() << "Failed to QuaZip open:" << zipFile.getZipError(); + return false; + } + + if ( !zipFile.goToFirstFile() ) + { + tLog() << "Failed to go to first file in zip archive: " << zipFile.getZipError(); + return false; + } + + tDebug() << "Unzipping files to:" << folder.absolutePath(); + + QuaZipFile fileInZip( &zipFile ); + do + { + QuaZipFileInfo info; + zipFile.getCurrentFileInfo( &info ); + + if ( !fileInZip.open( QIODevice::ReadOnly ) ) + { + tLog() << "Failed to open file inside zip archive:" << info.name << zipFile.getZipName() << "with error:" << zipFile.getZipError(); + continue; + } + + QFile out( folder.absoluteFilePath( fileInZip.getActualFileName() ) ); + + // make dir if there is one needed + QStringList parts = fileInZip.getActualFileName().split( "/" ); + if ( parts.size() > 1 ) + { + QStringList dirs = parts.mid( 0, parts.size() - 1 ); + QString dirPath = dirs.join( "/" ); // QDir translates / to \ internally if necessary + folder.mkpath( dirPath ); + } + + tDebug() << "Writing to output file..." << out.fileName(); + if ( !out.open( QIODevice::WriteOnly ) ) + { + tLog() << "Failed to open zip extract file:" << out.errorString() << info.name; + fileInZip.close(); + continue; + } + + + out.write( fileInZip.readAll() ); + out.close(); + fileInZip.close(); + + } while ( zipFile.goToNextFile() ); + + return true; +} + + +void +extractBinaryResolver( const QString& zipFilename, QObject* receiver ) +{ + BinaryExtractWorker* worker = new BinaryExtractWorker( zipFilename, receiver ); + worker->start( QThread::LowPriority ); } } // ns + +#include "TomahawkUtils.moc" diff --git a/src/libtomahawk/utils/TomahawkUtils.h b/src/libtomahawk/utils/TomahawkUtils.h index 8269d3134..5da2fab30 100644 --- a/src/libtomahawk/utils/TomahawkUtils.h +++ b/src/libtomahawk/utils/TomahawkUtils.h @@ -26,12 +26,10 @@ #include #include #include -#include #include #define RESPATH ":/data/" - class QDir; class QNetworkAccessManager; @@ -53,39 +51,14 @@ namespace TomahawkUtils NowPlayingSpeaker, InfoIcon }; + enum ImageMode { Original, CoverInCase, AvatarInFrame, - ScaledCover - }; - - - class DLLEXPORT SharedTimeLine : public QObject - { - Q_OBJECT - - public: - SharedTimeLine(); - - virtual ~SharedTimeLine() {} - - int currentFrame() { return m_timeline.currentFrame(); } - - void setUpdateInterval( int msec ) { if ( msec != m_timeline.updateInterval() ) m_timeline.setUpdateInterval( msec ); } - - signals: - void frameChanged( int ); - - protected slots: - virtual void connectNotify( const char *signal ); - - virtual void disconnectNotify( const char *signal ); - - private: - int m_refcount; - QTimeLine m_timeline; + ScaledCover, + Grid }; @@ -113,7 +86,7 @@ namespace TomahawkUtils QStringList m_noProxyHosts; QNetworkProxy m_proxy; }; - + DLLEXPORT QString appFriendlyVersion(); @@ -121,11 +94,12 @@ namespace TomahawkUtils DLLEXPORT QDir appDataDir(); DLLEXPORT QDir appLogDir(); - DLLEXPORT QString sqlEscape( QString sql ); DLLEXPORT QString timeToString( int seconds ); DLLEXPORT QString ageToString( const QDateTime& time, bool appendAgoString = false ); DLLEXPORT QString filesizeToString( unsigned int size ); DLLEXPORT QString extensionToMimetype( const QString& extension ); + + DLLEXPORT void msleep( unsigned int ms ); DLLEXPORT bool newerVersion( const QString& oldVersion, const QString& newVersion ); DLLEXPORT NetworkProxyFactory* proxyFactory( bool makeClone = false, bool noMutexLocker = false ); @@ -136,7 +110,18 @@ namespace TomahawkUtils DLLEXPORT QString md5( const QByteArray& data ); DLLEXPORT bool removeDirectory( const QString& dir ); - + + DLLEXPORT bool verifyFile( const QString& filePath, const QString& signature ); + DLLEXPORT QString extractScriptPayload( const QString& filename, const QString& resolverId ); + DLLEXPORT bool unzipFileInFolder( const QString& zipFileName, const QDir& folder ); + + // Extracting may be asynchronous, pass in a receiver object with the following slots: + // extractSucceeded( const QString& path ) and extractFailed() to be notified/ + DLLEXPORT void extractBinaryResolver( const QString& zipFilename, QObject* receiver ); + + // Used by the above, not exported + void copyWithAuthentication( const QString& srcFile, const QDir dest, QObject* receiver ); + /** * This helper is designed to help "update" an existing playlist with a newer revision of itself. * To avoid re-loading the whole playlist and re-resolving tracks that are the same in the old playlist, diff --git a/src/libtomahawk/utils/TomahawkUtilsGui.cpp b/src/libtomahawk/utils/TomahawkUtilsGui.cpp index 5e9f5478d..ccf8cdf2c 100644 --- a/src/libtomahawk/utils/TomahawkUtilsGui.cpp +++ b/src/libtomahawk/utils/TomahawkUtilsGui.cpp @@ -23,7 +23,8 @@ #include "Query.h" #include "Result.h" #include "Logger.h" -#include "TrackModelItem.h" +#include "PlayableItem.h" +#include "Source.h" #include #include @@ -61,23 +62,24 @@ createDragPixmap( MediaType type, int itemCount ) { xCount = 5; size = 16; - } else if( itemCount > 9 ) + } + else if( itemCount > 9 ) { xCount = 4; size = 22; } - if( itemCount < xCount ) + if ( itemCount < xCount ) { xCount = itemCount; } int yCount = itemCount / xCount; - if( itemCount % xCount != 0 ) + if ( itemCount % xCount != 0 ) { ++yCount; } - if( yCount > xCount ) + if ( yCount > xCount ) { yCount = xCount; } @@ -104,7 +106,7 @@ createDragPixmap( MediaType type, int itemCount ) int x = 0; int y = 0; - for( int i = 0; i < itemCount; ++i ) + for ( int i = 0; i < itemCount; ++i ) { painter.drawPixmap( x, y, pixmap ); @@ -125,9 +127,29 @@ createDragPixmap( MediaType type, int itemCount ) } +void +drawShadowText( QPainter* painter, const QRect& rect, const QString& text, const QTextOption& textOption ) +{ + painter->save(); + + painter->drawText( rect, text, textOption ); + +/* QFont font = painter->font(); + font.setPixelSize( font.pixelSize() + 2 ); + painter->setFont( font ); + + painter->setPen( Qt::black ); + painter->drawText( rect, text, textOption );*/ + + painter->restore(); +} + + void drawBackgroundAndNumbers( QPainter* painter, const QString& text, const QRect& figRectIn ) { + painter->save(); + QRect figRect = figRectIn; if ( text.length() == 1 ) figRect.adjust( -painter->fontMetrics().averageCharWidth(), 0, 0, 0 ); @@ -154,15 +176,13 @@ drawBackgroundAndNumbers( QPainter* painter, const QString& text, const QRect& f ppath.arcTo( leftArcRect, 270, 180 ); painter->drawPath( ppath ); - painter->setPen( origpen ); - -#ifdef Q_WS_MAC figRect.adjust( -1, 0, 0, 0 ); -#endif - QTextOption to( Qt::AlignCenter ); + painter->setPen( origpen ); painter->setPen( Qt::white ); - painter->drawText( figRect.adjusted( -5, 0, 6, 0 ), text, to ); + painter->drawText( figRect.adjusted( -5, 0, 6, 0 ), text, QTextOption( Qt::AlignCenter ) ); + + painter->restore(); } @@ -276,10 +296,10 @@ QPixmap createAvatarFrame( const QPixmap &avatar ) { QPixmap frame( ":/data/images/avatar_frame.png" ); - QPixmap scaledAvatar = avatar.scaled( frame.height() * 75 / 100, frame.width() * 75 / 100, Qt::KeepAspectRatio, Qt::SmoothTransformation ); + QPixmap scaledAvatar = avatar.scaled( frame.height() * 75 / 100, frame.width() * 75 / 100, Qt::IgnoreAspectRatio, Qt::SmoothTransformation ); QPainter painter( &frame ); - painter.drawPixmap( (frame.height() - scaledAvatar.height()) / 2, (frame.width() - scaledAvatar.width()) / 2, scaledAvatar ); + painter.drawPixmap( ( frame.height() - scaledAvatar.height() ) / 2, ( frame.width() - scaledAvatar.width() ) / 2, scaledAvatar ); return frame; } @@ -338,11 +358,16 @@ defaultPixmap( ImageType type, ImageMode mode, const QSize& size ) case DefaultAlbumCover: if ( mode == CoverInCase ) pixmap = QPixmap( RESPATH "images/no-album-art-placeholder.png" ); + else if ( mode == Grid ) + pixmap = QPixmap( RESPATH "images/album-placeholder-grid.png" ); else pixmap = QPixmap( RESPATH "images/no-album-no-case.png" ); break; case DefaultArtistImage: + if ( mode == Grid ) + pixmap = QPixmap( RESPATH "images/artist-placeholder-grid.png" ); + else pixmap = QPixmap( RESPATH "images/no-artist-image-placeholder.png" ); break; @@ -386,8 +411,10 @@ defaultPixmap( ImageType type, ImageMode mode, const QSize& size ) void -prepareStyleOption( QStyleOptionViewItemV4* option, const QModelIndex& index, TrackModelItem* item ) +prepareStyleOption( QStyleOptionViewItemV4* option, const QModelIndex& index, PlayableItem* item ) { + Q_UNUSED( index ); + if ( item->isPlaying() ) { option->palette.setColor( QPalette::Highlight, option->palette.color( QPalette::Mid ) ); @@ -404,7 +431,7 @@ prepareStyleOption( QStyleOptionViewItemV4* option, const QModelIndex& index, Tr else { float opacity = 0.0; - if ( item->query()->results().count() ) + if ( !item->query()->results().isEmpty() ) opacity = item->query()->results().first()->score(); opacity = qMax( (float)0.3, opacity ); diff --git a/src/libtomahawk/utils/TomahawkUtilsGui.h b/src/libtomahawk/utils/TomahawkUtilsGui.h index 48f03cb2f..1c89f8bce 100644 --- a/src/libtomahawk/utils/TomahawkUtilsGui.h +++ b/src/libtomahawk/utils/TomahawkUtilsGui.h @@ -22,11 +22,12 @@ #include #include +#include #include "TomahawkUtils.h" #include "DllMacro.h" -class TrackModelItem; +class PlayableItem; class QStyleOptionViewItemV4; class QPainter; class QColor; @@ -47,7 +48,9 @@ namespace TomahawkUtils DLLEXPORT QColor alphaBlend( const QColor& colorFrom, const QColor& colorTo, float opacity ); DLLEXPORT QPixmap createDragPixmap( MediaType type, int itemCount = 1 ); + DLLEXPORT void drawShadowText( QPainter* p, const QRect& rect, const QString& text, const QTextOption& textOption ); DLLEXPORT void drawBackgroundAndNumbers( QPainter* p, const QString& text, const QRect& rect ); + DLLEXPORT void unmarginLayout( QLayout* layout ); DLLEXPORT int headerHeight(); @@ -55,7 +58,7 @@ namespace TomahawkUtils DLLEXPORT QPixmap defaultPixmap( ImageType type, ImageMode mode = TomahawkUtils::Original, const QSize& size = QSize( 0, 0 ) ); - DLLEXPORT void prepareStyleOption( QStyleOptionViewItemV4* option, const QModelIndex& index, TrackModelItem* item ); + DLLEXPORT void prepareStyleOption( QStyleOptionViewItemV4* option, const QModelIndex& index, PlayableItem* item ); } diff --git a/src/libtomahawk/playlist/TreeHeader.h b/src/libtomahawk/utils/TomahawkUtils_Mac.h similarity index 58% rename from src/libtomahawk/playlist/TreeHeader.h rename to src/libtomahawk/utils/TomahawkUtils_Mac.h index d80cf872c..4274803e8 100644 --- a/src/libtomahawk/playlist/TreeHeader.h +++ b/src/libtomahawk/utils/TomahawkUtils_Mac.h @@ -1,6 +1,6 @@ /* === This file is part of Tomahawk Player - === * - * Copyright 2010-2011, Christian Muehlhaeuser + * Copyright 2012, Leo Franchi . */ -#ifndef TREEHEADER_H -#define TREEHEADER_H +#ifndef TOMAHAWKUTILS_MAC_H +#define TOMAHAWKUTILS_MAC_H -#include "ViewHeader.h" -#include "DllMacro.h" +#include -class ArtistView; +#import -class DLLEXPORT TreeHeader : public ViewHeader +@interface MoveDelegate : NSObject { -Q_OBJECT + QObject* receiver; + QString path; +} +- (void)setReceiver:(QObject*)receiver; +- (void)setMoveTo:(QString)path; +- (void)moveFinished; +- (void)moveFailedWithError:(NSError *)error; +@end -public: - explicit TreeHeader( ArtistView* parent = 0 ); - ~TreeHeader(); - -private: - ArtistView* m_parent; -}; - -#endif +#endif // TOMAHAWKUTILS_MAC_H diff --git a/src/libtomahawk/utils/TomahawkUtils_Mac.mm b/src/libtomahawk/utils/TomahawkUtils_Mac.mm index aeac94866..28b03e0e3 100644 --- a/src/libtomahawk/utils/TomahawkUtils_Mac.mm +++ b/src/libtomahawk/utils/TomahawkUtils_Mac.mm @@ -1,6 +1,77 @@ -#include "TomahawkUtils.h" +/* === This file is part of Tomahawk Player - === + * + * Copyright 2012, Leo Franchi . + */ + +#include "mac/FileHelpers.h" #import +#import + +#include "TomahawkUtils.h" +#include "TomahawkUtils_Mac.h" + +#include +#include + +@implementation MoveDelegate + + +-(void) setReceiver:(QObject*) object +{ + receiver = object; +} + +-(void) setMoveTo:(QString) p +{ + path = p; +} + +- (void)moveFinished +{ + // HACK since I can't figure out how to get QuaZip to maintain executable permissions after unzip (nor find the info) + // we set the binary to executable here + + NSLog(@"Move succeeded!, handling result"); + + NSFileManager *manager = [[[NSFileManager alloc] init] autorelease]; + NSError* error; + NSDictionary* attrs = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithInt:0755], NSFilePosixPermissions, nil]; + + NSString* target = [[NSString alloc] initWithBytes:path.toUtf8() length:path.length() encoding: NSUTF8StringEncoding]; + NSLog(@"Changing permissions to executable for: %@", target); + BOOL success = [manager setAttributes:attrs ofItemAtPath:target error:&error]; + if (!success) { + NSLog( @"Failed to do chmod +x of moved resolver! %@", [[error userInfo] objectForKey: NSLocalizedDescriptionKey] ); + } + + if ( receiver ) + QMetaObject::invokeMethod(receiver, "installSucceeded", Qt::DirectConnection, Q_ARG(QString, path)); + + [target release]; + +} + +- (void)moveFailedWithError:(NSError *)error +{ + NSLog(@"Move failed, handling result"); + if ( receiver ) + QMetaObject::invokeMethod(receiver, "installFailed", Qt::DirectConnection); +} +@end namespace TomahawkUtils { @@ -10,4 +81,31 @@ bringToFront() { [NSApp activateIgnoringOtherApps:YES]; } +void +copyWithAuthentication( const QString& srcFile, const QDir dest, QObject* receiver ) +{ + /** + On OS X, we have to do the following: + 1) Authenticate to be able to have write access to the /Applications folder + 2) Copy file to dest + 5) Call result slots on receiver object + */ + + MoveDelegate* del = [[MoveDelegate alloc] init]; + [del setReceiver: receiver]; + + // Get the filename + path to save for later + QFileInfo srcInfo( srcFile ); + const QString resultingPath = dest.absoluteFilePath( srcInfo.fileName() ); + [del setMoveTo: resultingPath]; + + const QFileInfo info( srcFile ); + const QString destPath = dest.absoluteFilePath( info.fileName() ); + + NSString* src = [[NSString alloc] initWithBytes: srcFile.toUtf8() length: srcFile.length() encoding: NSUTF8StringEncoding]; + NSString* destStr = [[NSString alloc] initWithBytes: destPath.toUtf8() length: destPath.length() encoding: NSUTF8StringEncoding]; + [FileHelpers moveFile:src to:destStr withDelegate:del]; +} + + } diff --git a/src/libtomahawk/utils/WidgetDragFilter.cpp b/src/libtomahawk/utils/WidgetDragFilter.cpp index 46797ca46..3d427d097 100644 --- a/src/libtomahawk/utils/WidgetDragFilter.cpp +++ b/src/libtomahawk/utils/WidgetDragFilter.cpp @@ -38,34 +38,42 @@ WidgetDragFilter::WidgetDragFilter( QObject* parent ) bool WidgetDragFilter::eventFilter( QObject* obj, QEvent* event ) { - if( m_target.isNull() || m_target.data() != obj ) + if ( m_target.isNull() || m_target.data() != obj ) return false; - if( event->type() == QEvent::MouseButtonPress ) { + if ( event->type() == QEvent::MouseButtonPress ) + { QMouseEvent *mouseEvent = static_cast( event ); - if( !canDrag( obj, mouseEvent ) ) + if ( !canDrag( obj, mouseEvent ) ) return false; - if( !( mouseEvent->modifiers() == Qt::NoModifier && mouseEvent->button() == Qt::LeftButton ) ) + if ( !( mouseEvent->modifiers() == Qt::NoModifier && mouseEvent->button() == Qt::LeftButton ) ) return false; + m_dragPoint = mouseEvent->pos(); m_dragStarted = true; return false; - } else if( event->type() == QEvent::MouseMove ) { - if( !m_dragStarted ) + } + else if ( event->type() == QEvent::MouseMove ) + { + if ( !m_dragStarted ) return false; - QMouseEvent* e = static_cast(event); - if( !canDrag( obj, e ) ) { - m_dragStarted = false; + QMouseEvent* e = static_cast(event); + if ( !canDrag( obj, e ) ) + { + m_dragStarted = false; return false; } - if( e->buttons().testFlag( Qt::LeftButton ) ) { + + if ( e->buttons().testFlag( Qt::LeftButton ) ) + { m_target.data()->window()->move( m_target.data()->window()->pos() + ( e->pos() - m_dragPoint ) ); return true; } - } else if( event->type() == QEvent::MouseButtonRelease ) { - m_dragStarted = false; } + else if ( event->type() == QEvent::MouseButtonRelease ) + m_dragStarted = false; + return false; } @@ -76,38 +84,39 @@ WidgetDragFilter::eventFilter( QObject* obj, QEvent* event ) bool WidgetDragFilter::canDrag( QObject* obj, QMouseEvent* ev ) const { - if( !obj->isWidgetType() ) + if ( !obj->isWidgetType() ) return false; QWidget* w = static_cast< QWidget* >( obj ); - if( QWidget::mouseGrabber() ) + if ( QWidget::mouseGrabber() ) return false; - if( w->cursor().shape() != Qt::ArrowCursor ) + if ( w->cursor().shape() != Qt::ArrowCursor ) return false; // Now we check various things about the child position and mouse QPoint position( ev->pos() ); QWidget* child = w->childAt( position ); - if( child && child->cursor().shape() != Qt::ArrowCursor ) return false; + if ( child && child->cursor().shape() != Qt::ArrowCursor ) + return false; // Don't want to drag menubars when selecting an action - if( QMenuBar* menuBar = qobject_cast( w ) ) + if ( QMenuBar* menuBar = qobject_cast( w ) ) { // check if there is an active action - if( menuBar->activeAction() && menuBar->activeAction()->isEnabled() ) return false; + if ( menuBar->activeAction() && menuBar->activeAction()->isEnabled() ) + return false; // check if action at position exists and is enabled - if( QAction* action = menuBar->actionAt( position ) ) + if ( QAction* action = menuBar->actionAt( position ) ) { - if( action->isSeparator() ) return true; - if( action->isEnabled() ) return false; + if ( action->isSeparator() ) + return true; + if ( action->isEnabled() ) + return false; } - // return true in all other cases - return true; - } return true; diff --git a/src/libtomahawk/utils/XspfGenerator.cpp b/src/libtomahawk/utils/XspfGenerator.cpp index ba0fb9cf6..72355f618 100644 --- a/src/libtomahawk/utils/XspfGenerator.cpp +++ b/src/libtomahawk/utils/XspfGenerator.cpp @@ -26,6 +26,7 @@ #include "Playlist.h" #include "Query.h" #include "utils/Logger.h" +#include "Source.h" using namespace Tomahawk; diff --git a/src/libtomahawk/widgets/AnimatedSplitter.cpp b/src/libtomahawk/widgets/AnimatedSplitter.cpp index 93a941c83..539433136 100644 --- a/src/libtomahawk/widgets/AnimatedSplitter.cpp +++ b/src/libtomahawk/widgets/AnimatedSplitter.cpp @@ -63,6 +63,7 @@ AnimatedSplitter::addWidget( AnimatedWidget* widget ) connect( widget, SIGNAL( hideWidget() ), SLOT( onHideRequest() ) ); connect( widget, SIGNAL( sizeHintChanged( QSize ) ), SLOT( onShowRequest() ) ); connect( widget, SIGNAL( sizeChanged( QSize ) ), SLOT( onSizeChanged( QSize ) ) ); + connect( widget, SIGNAL( resizeBy( QPoint ) ), SLOT( onResizeRequest( QPoint ) ) ); connect( this, SIGNAL( shown( QWidget*, bool ) ), widget, SLOT( onShown( QWidget*, bool ) ) ); connect( this, SIGNAL( hidden( QWidget*, bool ) ), widget, SLOT( onHidden( QWidget*, bool ) ) ); @@ -70,32 +71,9 @@ AnimatedSplitter::addWidget( AnimatedWidget* widget ) void -AnimatedSplitter::onShowRequest() +AnimatedSplitter::changeSize( QWidget* child, const QSize& size ) { - AnimatedWidget* w = (AnimatedWidget*)(sender()); - if ( indexOf( w ) > 0 ) - show( indexOf( w ) ); - else - qDebug() << "Could not find widget:" << sender(); -} - - -void -AnimatedSplitter::onHideRequest() -{ - AnimatedWidget* w = (AnimatedWidget*)(sender()); - if ( indexOf( w ) > 0 ) - hide( indexOf( w ) ); - else - qDebug() << "Could not find widget:" << sender(); -} - - -void -AnimatedSplitter::onSizeChanged( const QSize& size ) -{ - AnimatedWidget* w = (AnimatedWidget*)(sender()); - int wi = indexOf( w ); + int wi = indexOf( child ); QList< int > sizes; for ( int i = 0; i < count(); i ++ ) @@ -127,6 +105,58 @@ AnimatedSplitter::onSizeChanged( const QSize& size ) } +void +AnimatedSplitter::onResizeRequest( const QPoint& delta ) +{ + AnimatedWidget* w = (AnimatedWidget*)(sender()); + if ( indexOf( w ) > 0 ) + { + int newheight = w->height() + delta.y(); + if ( newheight <= w->hiddenSize().height() ) + { + w->hide(); + } + else + changeSize( w, QSize( w->width(), newheight ) ); + } + else + Q_ASSERT( false ); +} + + +void +AnimatedSplitter::onShowRequest() +{ + AnimatedWidget* w = (AnimatedWidget*)(sender()); + if ( indexOf( w ) > 0 ) + show( indexOf( w ) ); + else + Q_ASSERT( false ); +} + + +void +AnimatedSplitter::onHideRequest() +{ + AnimatedWidget* w = (AnimatedWidget*)(sender()); + if ( indexOf( w ) > 0 ) + hide( indexOf( w ) ); + else + Q_ASSERT( false ); +} + + +void +AnimatedSplitter::onSizeChanged( const QSize& size ) +{ + AnimatedWidget* w = (AnimatedWidget*)(sender()); + if ( indexOf( w ) > 0 ) + changeSize( w, size ); + else + Q_ASSERT( false ); +} + + void AnimatedSplitter::setGreedyWidget( int index ) { @@ -247,6 +277,8 @@ AnimatedWidget::onAnimationFinished() { setFixedHeight( hiddenSize().height() ); } + + emit animationFinished(); } diff --git a/src/libtomahawk/widgets/AnimatedSplitter.h b/src/libtomahawk/widgets/AnimatedSplitter.h index 4e47d77b9..d113e769e 100644 --- a/src/libtomahawk/widgets/AnimatedSplitter.h +++ b/src/libtomahawk/widgets/AnimatedSplitter.h @@ -49,11 +49,14 @@ protected: virtual QSplitterHandle* createHandle(); private slots: + void changeSize( QWidget* child, const QSize& size ); + void onShowRequest(); void onHideRequest(); void onSizeChanged( const QSize& size ); - + void onResizeRequest( const QPoint& delta ); + private: int m_greedyIndex; }; @@ -95,10 +98,16 @@ public slots: virtual void onShown( QWidget*, bool animated ); virtual void onHidden( QWidget*, bool animated ); + virtual void hide() { emit hideWidget(); } + virtual void show() { emit showWidget(); } + signals: void showWidget(); void hideWidget(); + + void animationFinished(); + void resizeBy( const QPoint& delta ); void sizeChanged( const QSize& size ); void sizeHintChanged( const QSize& size ); void hiddenSizeChanged(); diff --git a/src/libtomahawk/widgets/ChartDataLoader.cpp b/src/libtomahawk/widgets/ChartDataLoader.cpp index 07a69112e..760df389c 100644 --- a/src/libtomahawk/widgets/ChartDataLoader.cpp +++ b/src/libtomahawk/widgets/ChartDataLoader.cpp @@ -17,6 +17,8 @@ */ #include "ChartDataLoader.h" +#include "Source.h" + using namespace Tomahawk; diff --git a/src/libtomahawk/widgets/CheckDirTree.cpp b/src/libtomahawk/widgets/CheckDirTree.cpp index 6bed37868..9c7d878c6 100644 --- a/src/libtomahawk/widgets/CheckDirTree.cpp +++ b/src/libtomahawk/widgets/CheckDirTree.cpp @@ -21,6 +21,7 @@ #include "utils/Logger.h" #include "TomahawkSettings.h" +#include "Source.h" #include #include diff --git a/src/libtomahawk/widgets/FadingPixmap.cpp b/src/libtomahawk/widgets/FadingPixmap.cpp index 68ac58581..b64f42e02 100644 --- a/src/libtomahawk/widgets/FadingPixmap.cpp +++ b/src/libtomahawk/widgets/FadingPixmap.cpp @@ -1,7 +1,7 @@ /* === This file is part of Tomahawk Player - === * * Copyright 2011 - 2012, Christian Muehlhaeuser - * Copyright 2012, Jeff Mitchell + * Copyright 2012, Jeff Mitchell * * Tomahawk is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/libtomahawk/widgets/FadingPixmap.h b/src/libtomahawk/widgets/FadingPixmap.h index 9943cc41e..5e4928817 100644 --- a/src/libtomahawk/widgets/FadingPixmap.h +++ b/src/libtomahawk/widgets/FadingPixmap.h @@ -1,7 +1,7 @@ /* === This file is part of Tomahawk Player - === * * Copyright 2011 - 2012, Christian Muehlhaeuser - * Copyright 2012, Jeff Mitchell + * Copyright 2012, Jeff Mitchell * * Tomahawk is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -20,6 +20,7 @@ #ifndef FADINGPIXMAP_H #define FADINGPIXMAP_H +#include "utils/SharedTimeLine.h" #include "utils/TomahawkUtils.h" #include diff --git a/src/libtomahawk/widgets/HeaderLabel.cpp b/src/libtomahawk/widgets/HeaderLabel.cpp index cfd3542a9..edec62258 100644 --- a/src/libtomahawk/widgets/HeaderLabel.cpp +++ b/src/libtomahawk/widgets/HeaderLabel.cpp @@ -20,17 +20,20 @@ #include #include +#include #include "utils/Logger.h" #include "utils/StyleHelper.h" #include "utils/TomahawkUtilsGui.h" - static const int s_defaultFontSize = 12; + HeaderLabel::HeaderLabel( QWidget* parent ) : QLabel( parent ) , m_parent( parent ) + , m_pressed( false ) + , m_moved( false ) { QFont f( font() ); f.setBold( true ); @@ -38,6 +41,7 @@ HeaderLabel::HeaderLabel( QWidget* parent ) setFont( f ); setFixedHeight( TomahawkUtils::headerHeight() ); + setMouseTracking( true ); } @@ -52,6 +56,7 @@ HeaderLabel::sizeHint() const return QLabel::sizeHint(); } + int HeaderLabel::defaultFontSize() { @@ -63,7 +68,14 @@ void HeaderLabel::mousePressEvent( QMouseEvent* event ) { QFrame::mousePressEvent( event ); - m_time.start(); + + if ( !m_moved ) + { + m_time.start(); + + m_pressed = true; + m_dragPoint = event->pos(); + } } @@ -71,8 +83,27 @@ void HeaderLabel::mouseReleaseEvent( QMouseEvent* event ) { QFrame::mouseReleaseEvent( event ); - if ( m_time.elapsed() < qApp->doubleClickInterval() ) + + if ( !m_moved && m_time.elapsed() < qApp->doubleClickInterval() ) emit clicked(); + + m_pressed = false; + m_moved = false; +} + + +void +HeaderLabel::mouseMoveEvent( QMouseEvent* event ) +{ + if ( m_pressed ) + { + QPoint delta = m_dragPoint - event->pos(); + if ( abs( delta.y() ) > 3 ) + { + m_moved = true; + emit resized( delta ); + } + } } diff --git a/src/libtomahawk/widgets/HeaderLabel.h b/src/libtomahawk/widgets/HeaderLabel.h index 4d015d5c9..88f244c7e 100644 --- a/src/libtomahawk/widgets/HeaderLabel.h +++ b/src/libtomahawk/widgets/HeaderLabel.h @@ -43,6 +43,7 @@ public: signals: void clicked(); + void resized( const QPoint& delta ); protected: // void changeEvent( QEvent* e ); @@ -50,10 +51,15 @@ protected: void mousePressEvent( QMouseEvent* event ); void mouseReleaseEvent( QMouseEvent* event ); + void mouseMoveEvent( QMouseEvent* event ); private: QWidget* m_parent; QTime m_time; + + QPoint m_dragPoint; + bool m_pressed; + bool m_moved; }; #endif // HEADERLABEL_H diff --git a/src/libtomahawk/widgets/NewReleasesWidget.cpp b/src/libtomahawk/widgets/NewReleasesWidget.cpp index f3ae283fe..c4e7631bc 100644 --- a/src/libtomahawk/widgets/NewReleasesWidget.cpp +++ b/src/libtomahawk/widgets/NewReleasesWidget.cpp @@ -38,8 +38,7 @@ #include "playlist/PlaylistModel.h" #include "playlist/TreeProxyModel.h" #include "playlist/PlaylistChartItemDelegate.h" -#include "widgets/OverlayWidget.h" -#include "utils/TomahawkUtils.h" +#include "utils/TomahawkUtilsGui.h" #include "utils/Logger.h" #include "Pipeline.h" @@ -60,9 +59,6 @@ NewReleasesWidget::NewReleasesWidget( QWidget* parent ) { ui->setupUi( this ); - ui->albumsView->setFrameShape( QFrame::NoFrame ); - ui->albumsView->setAttribute( Qt::WA_MacShowFocusRect, 0 ); - TomahawkUtils::unmarginLayout( layout() ); TomahawkUtils::unmarginLayout( ui->verticalLayout_2 ); TomahawkUtils::unmarginLayout( ui->breadCrumbLeft->layout() ); @@ -76,7 +72,6 @@ NewReleasesWidget::NewReleasesWidget( QWidget* parent ) connect( ui->breadCrumbLeft, SIGNAL( activateIndex( QModelIndex ) ), SLOT( leftCrumbIndexChanged(QModelIndex) ) ); - //m_playlistInterface = Tomahawk::playlistinterface_ptr( new ChartsPlaylistInterface( this ) ); m_workerThread = new QThread( this ); @@ -190,13 +185,12 @@ NewReleasesWidget::infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData request if ( type == "albums" ) { - loader->setType( ChartDataLoader::Album ); loader->setData( returnedData[ "albums" ].value< QList< Tomahawk::InfoSystem::InfoStringHash > >() ); connect( loader, SIGNAL( albums( Tomahawk::ChartDataLoader*, QList< Tomahawk::album_ptr > ) ), this, SLOT( newReleasesLoaded( Tomahawk::ChartDataLoader*, QList ) ) ); - AlbumModel* albumModel = new AlbumModel( ui->albumsView ); + PlayableModel* albumModel = new PlayableModel( ui->albumsView ); m_albumModels[ releaseId ] = albumModel; @@ -348,12 +342,13 @@ NewReleasesWidget::parseNode( QStandardItem* parentItem, const QString &label, c void -NewReleasesWidget::setLeftViewAlbums( AlbumModel* model ) +NewReleasesWidget::setLeftViewAlbums( PlayableModel* model ) { - ui->albumsView->setAlbumModel( model ); + ui->albumsView->setPlayableModel( model ); ui->albumsView->proxyModel()->sort( -1 ); // disable sorting, must be called after artistsViewLeft->setTreeModel } + void NewReleasesWidget::newReleasesLoaded( ChartDataLoader* loader, const QList< album_ptr >& albums ) { @@ -361,7 +356,7 @@ NewReleasesWidget::newReleasesLoaded( ChartDataLoader* loader, const QList< albu Q_ASSERT( m_albumModels.contains( chartId ) ); if ( m_albumModels.contains( chartId ) ) - m_albumModels[ chartId ]->addAlbums( albums ); + m_albumModels[ chartId ]->append( albums ); m_workers.remove( loader ); loader->deleteLater(); diff --git a/src/libtomahawk/widgets/NewReleasesWidget.h b/src/libtomahawk/widgets/NewReleasesWidget.h index f61afb398..5e6c21cf7 100644 --- a/src/libtomahawk/widgets/NewReleasesWidget.h +++ b/src/libtomahawk/widgets/NewReleasesWidget.h @@ -30,8 +30,6 @@ #include "infosystem/InfoSystem.h" #include "ViewPage.h" -#include "utils/TomahawkUtils.h" - #include "DllMacro.h" class QSortFilterProxyModel; @@ -39,9 +37,9 @@ class QStandardItemModel; class QStandardItem; class TreeModel; class PlaylistModel; -class OverlayWidget; class TreeProxyModel; class AlbumModel; +class PlayableModel; namespace Ui { @@ -97,7 +95,7 @@ private slots: private: void setLeftViewArtists( TreeModel* artistModel ); - void setLeftViewAlbums( AlbumModel* albumModel ); + void setLeftViewAlbums( PlayableModel* albumModel ); void setLeftViewTracks( PlaylistModel* trackModel ); @@ -115,7 +113,7 @@ private: QSet< Tomahawk::ChartDataLoader* > m_workers; // Cache our model data - QHash< QString, AlbumModel* > m_albumModels; + QHash< QString, PlayableModel* > m_albumModels; QString m_queueItemToShow; QSet< QString > m_queuedFetches; QTimer* m_timer; diff --git a/src/libtomahawk/widgets/NewReleasesWidget.ui b/src/libtomahawk/widgets/NewReleasesWidget.ui index 1dc057717..f1e8a1b9e 100644 --- a/src/libtomahawk/widgets/NewReleasesWidget.ui +++ b/src/libtomahawk/widgets/NewReleasesWidget.ui @@ -16,7 +16,7 @@ - + true @@ -29,9 +29,9 @@ - AlbumView + GridView QListView -
playlist/AlbumView.h
+
playlist/GridView.h
Tomahawk::Breadcrumb diff --git a/src/libtomahawk/widgets/OverlayWidget.cpp b/src/libtomahawk/widgets/OverlayWidget.cpp index 83e43a2fc..d3e896d64 100644 --- a/src/libtomahawk/widgets/OverlayWidget.cpp +++ b/src/libtomahawk/widgets/OverlayWidget.cpp @@ -22,6 +22,7 @@ #include #include +#include "PlayableProxyModel.h" #include "utils/Logger.h" #define CORNER_ROUNDNESS 8.0 @@ -32,22 +33,28 @@ OverlayWidget::OverlayWidget( QWidget* parent ) : QWidget( parent ) // this is on purpose! - , m_opacity( 0.00 ) , m_parent( parent ) + , m_itemView( 0 ) { - resize( 380, 128 ); - setAttribute( Qt::WA_TranslucentBackground, true ); + init(); +} - setOpacity( m_opacity ); - m_timer.setSingleShot( true ); - connect( &m_timer, SIGNAL( timeout() ), this, SLOT( hide() ) ); +OverlayWidget::OverlayWidget( QAbstractItemView* parent ) + : QWidget( parent ) // this is on purpose! + , m_parent( parent ) + , m_itemView( parent ) +{ + init(); -#ifdef Q_WS_MAC - QFont f( font() ); - f.setPointSize( f.pointSize() - 2 ); - setFont( f ); -#endif + if ( m_itemView->model() ) + { + connect( m_itemView->model(), SIGNAL( rowsInserted( QModelIndex, int, int ) ), SLOT( onViewChanged() ), Qt::UniqueConnection ); + connect( m_itemView->model(), SIGNAL( rowsRemoved( QModelIndex, int, int ) ), SLOT( onViewChanged() ), Qt::UniqueConnection ); + connect( m_itemView->model(), SIGNAL( loadingStarted() ), SLOT( onViewChanged() ), Qt::UniqueConnection ); + connect( m_itemView->model(), SIGNAL( loadingFinished() ), SLOT( onViewChanged() ), Qt::UniqueConnection ); + } + connect( m_itemView, SIGNAL( modelChanged() ), SLOT( onViewModelChanged() ) ); } @@ -56,6 +63,27 @@ OverlayWidget::~OverlayWidget() } +void +OverlayWidget::init() +{ + installEventFilter( m_parent ); + setAcceptDrops( true ); + + setAttribute( Qt::WA_TranslucentBackground, true ); + m_opacity = 0.00; + setOpacity( m_opacity ); + + m_timer.setSingleShot( true ); + connect( &m_timer, SIGNAL( timeout() ), this, SLOT( hide() ) ); + +#ifdef Q_WS_MAC + QFont f( font() ); + f.setPointSize( f.pointSize() - 2 ); + setFont( f ); +#endif +} + + void OverlayWidget::setOpacity( qreal opacity ) { @@ -78,6 +106,7 @@ void OverlayWidget::setText( const QString& text ) { m_text = text; + onViewChanged(); } @@ -92,7 +121,7 @@ OverlayWidget::show( int timeoutSecs ) animation->setEndValue( 1.0 ); animation->start(); - if( timeoutSecs > 0 ) + if ( timeoutSecs > 0 ) m_timer.start( timeoutSecs * 1000 ); } @@ -120,11 +149,61 @@ OverlayWidget::shown() const } +void +OverlayWidget::onViewChanged() +{ + if ( !m_itemView ) + return; + + PlayableProxyModel* model = qobject_cast( m_itemView->model() ); + if ( !model ) + return; + + if ( m_text.isEmpty() || model->rowCount( QModelIndex() ) || model->isLoading() ) + { + hide(); + } + else + { + show(); + } +} + + +void +OverlayWidget::onViewModelChanged() +{ + if ( !m_itemView ) + return; + + if ( m_itemView->model() ) + { + connect( m_itemView->model(), SIGNAL( rowsInserted( QModelIndex, int, int ) ), SLOT( onViewChanged() ), Qt::UniqueConnection ); + connect( m_itemView->model(), SIGNAL( rowsRemoved( QModelIndex, int, int ) ), SLOT( onViewChanged() ), Qt::UniqueConnection ); + connect( m_itemView->model(), SIGNAL( loadingStarted() ), SLOT( onViewChanged() ), Qt::UniqueConnection ); + connect( m_itemView->model(), SIGNAL( loadingFinished() ), SLOT( onViewChanged() ), Qt::UniqueConnection ); + + onViewChanged(); + } +} + + void OverlayWidget::paintEvent( QPaintEvent* event ) { Q_UNUSED( event ); + { + QSize maxiSize = QSize( (double)m_parent->width() * 0.70, (double)m_parent->height() * 0.70 ); + QSize prefSize = QSize( 380, 128 ); + int width = qMin( maxiSize.width(), prefSize.width() ); + int height = qMin( maxiSize.height(), prefSize.height() ); + QSize newSize = QSize( width, height ); + + if ( newSize != size() ) + resize( newSize ); + } + QPoint center( ( m_parent->width() - width() ) / 2, ( m_parent->height() - height() ) / 2 ); if ( center != pos() ) { diff --git a/src/libtomahawk/widgets/OverlayWidget.h b/src/libtomahawk/widgets/OverlayWidget.h index 91de2d2bf..c618cd118 100644 --- a/src/libtomahawk/widgets/OverlayWidget.h +++ b/src/libtomahawk/widgets/OverlayWidget.h @@ -32,6 +32,7 @@ Q_PROPERTY( qreal opacity READ opacity WRITE setOpacity ) public: OverlayWidget( QWidget* parent ); + OverlayWidget( QAbstractItemView* parent ); ~OverlayWidget(); qreal opacity() const { return m_opacity; } @@ -49,12 +50,20 @@ public slots: protected: // void changeEvent( QEvent* e ); void paintEvent( QPaintEvent* event ); + +private slots: + void onViewChanged(); + void onViewModelChanged(); private: + void init(); + QString m_text; qreal m_opacity; QWidget* m_parent; + QAbstractItemView* m_itemView; + QTimer m_timer; }; diff --git a/src/libtomahawk/widgets/QueryLabel.cpp b/src/libtomahawk/widgets/QueryLabel.cpp index 586cd3c38..82b40d084 100644 --- a/src/libtomahawk/widgets/QueryLabel.cpp +++ b/src/libtomahawk/widgets/QueryLabel.cpp @@ -30,6 +30,7 @@ #include "ContextMenu.h" #include "utils/TomahawkUtilsGui.h" #include "utils/Logger.h" +#include "Source.h" #define BOXMARGIN 2 #define DASH " - " @@ -80,7 +81,7 @@ void QueryLabel::init() { m_contextMenu = new ContextMenu( this ); - m_contextMenu->setSupportedActions( ContextMenu::ActionQueue | ContextMenu::ActionCopyLink | ContextMenu::ActionStopAfter | ContextMenu::ActionLove ); + m_contextMenu->setSupportedActions( ContextMenu::ActionQueue | ContextMenu::ActionCopyLink | ContextMenu::ActionStopAfter | ContextMenu::ActionLove | ContextMenu::ActionPage ); m_hoverType = None; setContentsMargins( 0, 0, 0, 0 ); @@ -88,7 +89,7 @@ QueryLabel::init() m_useCustomPen = false; m_useCustomFont = false; - m_align = Qt::AlignLeft; + m_align = Qt::AlignLeft | Qt::AlignVCenter; m_mode = Qt::ElideMiddle; } diff --git a/src/libtomahawk/widgets/RecentPlaylistsModel.cpp b/src/libtomahawk/widgets/RecentPlaylistsModel.cpp index 573f02e07..ba0eca7bd 100644 --- a/src/libtomahawk/widgets/RecentPlaylistsModel.cpp +++ b/src/libtomahawk/widgets/RecentPlaylistsModel.cpp @@ -63,6 +63,8 @@ RecentPlaylistsModel::onRefresh() if ( m_timer->isActive() ) m_timer->stop(); + emit loadingStarted(); + DatabaseCommand_LoadAllSortedPlaylists* cmd = new DatabaseCommand_LoadAllSortedPlaylists( source_ptr() ); cmd->setLimit( 15 ); cmd->setSortOrder( DatabaseCommand_LoadAllPlaylists::ModificationTime ); @@ -117,6 +119,7 @@ RecentPlaylistsModel::playlistsLoaded( const QList Copyright (C) 2011 Jeff Mitchell - + This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or @@ -25,6 +25,7 @@ #include "SourceList.h" #include "utils/Logger.h" #include "dynamic/DynamicPlaylist.h" +#include "Playlist.h" using namespace Tomahawk; @@ -37,7 +38,7 @@ RecentlyPlayedPlaylistsModel::RecentlyPlayedPlaylistsModel( QObject* parent ) loadFromSettings(); connect( SourceList::instance(), SIGNAL( sourceAdded( Tomahawk::source_ptr ) ), this, SLOT( onSourceAdded( Tomahawk::source_ptr ) ), Qt::QueuedConnection ); - connect( TomahawkSettings::instance(), SIGNAL( recentlyPlayedPlaylistAdded( Tomahawk::playlist_ptr ) ), this, SLOT( plAdded( Tomahawk::playlist_ptr ) ) ); + connect( TomahawkSettings::instance(), SIGNAL( recentlyPlayedPlaylistAdded( QString, int ) ), this, SLOT( plAdded( QString, int ) ) ); connect( AudioEngine::instance(),SIGNAL( playlistChanged( Tomahawk::playlistinterface_ptr ) ), this, SLOT( playlistChanged( Tomahawk::playlistinterface_ptr ) ), Qt::QueuedConnection ); emit emptinessChanged( m_recplaylists.isEmpty() ); @@ -217,8 +218,21 @@ RecentlyPlayedPlaylistsModel::rowCount( const QModelIndex& ) const void -RecentlyPlayedPlaylistsModel::plAdded( const playlist_ptr& pl ) +RecentlyPlayedPlaylistsModel::plAdded( const QString& plguid, int sId ) { + source_ptr source = SourceList::instance()->get( sId ); + if ( source.isNull() ) + return; + + playlist_ptr pl = source->collection()->playlist( plguid ); + if ( pl.isNull() ) + pl = source->collection()->autoPlaylist( plguid ); + if ( pl.isNull() ) + pl = source->collection()->station( plguid ); + + if ( pl.isNull() ) + return; + onPlaylistsRemoved( QList< playlist_ptr >() << pl ); beginInsertRows( QModelIndex(), 0, 0 ); @@ -235,7 +249,7 @@ RecentlyPlayedPlaylistsModel::playlistChanged( Tomahawk::playlistinterface_ptr p // ARG if ( pli.isNull() ) return; - + if ( Playlist *pl = dynamic_cast< Playlist* >( pli.data() ) ) { // look for it, qsharedpointer fail playlist_ptr ptr; diff --git a/src/libtomahawk/widgets/RecentlyPlayedPlaylistsModel.h b/src/libtomahawk/widgets/RecentlyPlayedPlaylistsModel.h index f280afecc..7e14f4cf8 100644 --- a/src/libtomahawk/widgets/RecentlyPlayedPlaylistsModel.h +++ b/src/libtomahawk/widgets/RecentlyPlayedPlaylistsModel.h @@ -1,7 +1,7 @@ /* Copyright (C) 2011 Leo Franchi Copyright (C) 2011 Jeff Mitchell - + This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or @@ -47,14 +47,14 @@ signals: public slots: void sourceOnline(); - + private slots: void playlistChanged( Tomahawk::playlistinterface_ptr ); void onSourceAdded( const Tomahawk::source_ptr& source ); void onPlaylistsRemoved( QList ); void loadFromSettings(); - void plAdded( const Tomahawk::playlist_ptr& ); + void plAdded( const QString& plid, int sid ); void playlistRevisionLoaded(); private: diff --git a/src/libtomahawk/widgets/SearchWidget.cpp b/src/libtomahawk/widgets/SearchWidget.cpp index 30a9a84c5..81f794204 100644 --- a/src/libtomahawk/widgets/SearchWidget.cpp +++ b/src/libtomahawk/widgets/SearchWidget.cpp @@ -25,12 +25,11 @@ #include "SourceList.h" #include "ViewManager.h" -#include "playlist/AlbumModel.h" +#include "playlist/PlayableModel.h" #include "playlist/PlaylistModel.h" -#include "widgets/OverlayWidget.h" #include "utils/AnimatedSpinner.h" -#include "utils/TomahawkUtils.h" +#include "utils/TomahawkUtilsGui.h" #include "utils/Logger.h" @@ -44,37 +43,19 @@ SearchWidget::SearchWidget( const QString& search, QWidget* parent ) ui->resultsView->setGuid( "searchwidget" ); m_resultsModel = new PlaylistModel( ui->resultsView ); ui->resultsView->setPlaylistModel( m_resultsModel ); - ui->resultsView->overlay()->setEnabled( false ); ui->resultsView->sortByColumn( PlaylistModel::Score, Qt::DescendingOrder ); - m_albumsModel = new AlbumModel( ui->albumView ); - ui->albumView->setAlbumModel( m_albumsModel ); + m_albumsModel = new PlayableModel( ui->albumView ); + ui->albumView->setPlayableModel( m_albumsModel ); - m_artistsModel = new AlbumModel( ui->artistView ); - ui->artistView->setAlbumModel( m_artistsModel ); - - ui->artistView->setAutoFitItems( false ); - ui->albumView->setAutoFitItems( false ); - ui->artistView->setSpacing( 8 ); - ui->albumView->setSpacing( 8 ); + m_artistsModel = new PlayableModel( ui->artistView ); + ui->artistView->setPlayableModel( m_artistsModel ); ui->artistView->proxyModel()->sort( -1 ); ui->albumView->proxyModel()->sort( -1 ); TomahawkUtils::unmarginLayout( ui->verticalLayout ); - ui->artistView->setContentsMargins( 0, 0, 0, 0 ); - ui->artistView->setFrameShape( QFrame::NoFrame ); - ui->artistView->setAttribute( Qt::WA_MacShowFocusRect, 0 ); - - ui->albumView->setContentsMargins( 0, 0, 0, 0 ); - ui->albumView->setFrameShape( QFrame::NoFrame ); - ui->albumView->setAttribute( Qt::WA_MacShowFocusRect, 0 ); - - ui->resultsView->setContentsMargins( 0, 0, 0, 0 ); - ui->resultsView->setFrameShape( QFrame::NoFrame ); - ui->resultsView->setAttribute( Qt::WA_MacShowFocusRect, 0 ); - ui->resultsView->loadingSpinner()->fadeIn(); m_queries << Tomahawk::Query::get( search, uuid() ); @@ -151,22 +132,22 @@ SearchWidget::onResultsFound( const QList& results ) albums << result->album(); } - m_artistsModel->addArtists( artists ); - m_albumsModel->addAlbums( albums ); + m_artistsModel->append( artists ); + m_albumsModel->append( albums ); } void SearchWidget::onAlbumsFound( const QList& albums ) { - m_albumsModel->addAlbums( albums ); + m_albumsModel->append( albums ); } void SearchWidget::onArtistsFound( const QList& artists ) { - m_artistsModel->addArtists( artists ); + m_artistsModel->append( artists ); } diff --git a/src/libtomahawk/widgets/SearchWidget.h b/src/libtomahawk/widgets/SearchWidget.h index b7d026740..70f942bfe 100644 --- a/src/libtomahawk/widgets/SearchWidget.h +++ b/src/libtomahawk/widgets/SearchWidget.h @@ -31,7 +31,7 @@ #include "DllMacro.h" class QPushButton; -class AlbumModel; +class PlayableModel; class PlaylistModel; namespace Ui @@ -77,8 +77,8 @@ private: QString m_search; - AlbumModel* m_artistsModel; - AlbumModel* m_albumsModel; + PlayableModel* m_artistsModel; + PlayableModel* m_albumsModel; PlaylistModel* m_resultsModel; QList< Tomahawk::query_ptr > m_queries; }; diff --git a/src/libtomahawk/widgets/SearchWidget.ui b/src/libtomahawk/widgets/SearchWidget.ui index d1f81af0c..e2df0a078 100644 --- a/src/libtomahawk/widgets/SearchWidget.ui +++ b/src/libtomahawk/widgets/SearchWidget.ui @@ -29,8 +29,8 @@ 1 - - + + @@ -44,9 +44,9 @@
playlist/PlaylistView.h
- AlbumView + GridView QListView -
playlist/AlbumView.h
+
playlist/GridView.h
diff --git a/src/libtomahawk/widgets/SocialPlaylistWidget.cpp b/src/libtomahawk/widgets/SocialPlaylistWidget.cpp index b646fb106..cd42b8dfe 100644 --- a/src/libtomahawk/widgets/SocialPlaylistWidget.cpp +++ b/src/libtomahawk/widgets/SocialPlaylistWidget.cpp @@ -23,6 +23,7 @@ #include "database/DatabaseCommand_LoadDynamicPlaylist.h" #include "database/Database.h" #include "SourceList.h" +#include "PlayableModel.h" #include "dynamic/GeneratorInterface.h" #include "dynamic/database/DatabaseGenerator.h" #include "utils/Logger.h" @@ -36,7 +37,7 @@ QString SocialPlaylistWidget::s_mostPlayedPlaylistsQuery = "asd"; QString SocialPlaylistWidget::s_topForeignTracksQuery = "select track.name, artist.name, count(*) as counter from (select track from playback_log group by track, source), track, artist where track not in (select track from playback_log where source is null group by track) and track.id = track and artist.id = track.artist group by track order by counter desc"; SocialPlaylistWidget::SocialPlaylistWidget ( QWidget* parent ) - : QWidget ( parent ) + : QWidget( parent ) , ui( new Ui_SocialPlaylistWidget ) , m_topForeignTracksModel( 0 ) , m_popularNewAlbumsModel( 0 ) @@ -53,10 +54,6 @@ SocialPlaylistWidget::SocialPlaylistWidget ( QWidget* parent ) ui->mostPlayedPlaylists->setFrameShape( QFrame::NoFrame ); ui->mostPlayedPlaylists->setAttribute( Qt::WA_MacShowFocusRect, 0 ); - ui->newTracksView->setFrameShape( QFrame::NoFrame ); - ui->newTracksView->setAttribute( Qt::WA_MacShowFocusRect, 0 ); - ui->newAlbumsView->setFrameShape( QFrame::NoFrame ); - ui->newAlbumsView->setAttribute( Qt::WA_MacShowFocusRect, 0 ); TomahawkUtils::unmarginLayout( layout() ); TomahawkUtils::unmarginLayout( ui->verticalLayout->layout() ); @@ -72,11 +69,11 @@ SocialPlaylistWidget::SocialPlaylistWidget ( QWidget* parent ) m_topForeignTracksModel = new PlaylistModel( ui->newTracksView ); ui->newTracksView->setPlaylistModel( m_topForeignTracksModel ); - m_topForeignTracksModel->setStyle( TrackModel::Short ); + m_topForeignTracksModel->setStyle( PlayableModel::Short ); ui->newTracksView->overlay()->setEnabled( false ); - m_popularNewAlbumsModel = new AlbumModel( ui->newAlbumsView ); - ui->newAlbumsView->setAlbumModel( m_popularNewAlbumsModel ); + m_popularNewAlbumsModel = new PlayableModel( ui->newAlbumsView ); + ui->newAlbumsView->setPlayableModel( m_popularNewAlbumsModel ); // TODO run the genericselect command // m_recentAlbumsModel->addFilteredCollection( collection_ptr(), 20, DatabaseCommand_AllAlbums::ModificationTime ); /* @@ -94,7 +91,6 @@ SocialPlaylistWidget::SocialPlaylistWidget ( QWidget* parent ) SocialPlaylistWidget::~SocialPlaylistWidget() { - } @@ -144,7 +140,7 @@ SocialPlaylistWidget::popularAlbumsFetched( QList< album_ptr > albums ) { m_popularNewAlbumsModel->clear(); - m_popularNewAlbumsModel->addAlbums( albums ); + m_popularNewAlbumsModel->append( albums ); } diff --git a/src/libtomahawk/widgets/SocialPlaylistWidget.h b/src/libtomahawk/widgets/SocialPlaylistWidget.h index 0afb813da..14ae0b7f4 100644 --- a/src/libtomahawk/widgets/SocialPlaylistWidget.h +++ b/src/libtomahawk/widgets/SocialPlaylistWidget.h @@ -38,7 +38,7 @@ #include "Album.h" #include "Query.h" -class AlbumModel; +class PlayableModel; class PlaylistModel; class TreeModel; @@ -81,7 +81,7 @@ private: Ui_SocialPlaylistWidget *ui; PlaylistModel* m_topForeignTracksModel; - AlbumModel* m_popularNewAlbumsModel; + PlayableModel* m_popularNewAlbumsModel; QString m_title; QString m_description; diff --git a/src/libtomahawk/widgets/SocialPlaylistWidget.ui b/src/libtomahawk/widgets/SocialPlaylistWidget.ui index bf1015e98..1dfc5327b 100644 --- a/src/libtomahawk/widgets/SocialPlaylistWidget.ui +++ b/src/libtomahawk/widgets/SocialPlaylistWidget.ui @@ -36,7 +36,7 @@
- + @@ -92,9 +92,9 @@
widgets/HeaderLabel.h
- AlbumView + GridView QListView -
playlist/AlbumView.h
+
playlist/GridView.h
PlaylistWidget diff --git a/src/libtomahawk/widgets/WelcomeWidget.cpp b/src/libtomahawk/widgets/WelcomeWidget.cpp index 12a18a6b6..0280b3285 100644 --- a/src/libtomahawk/widgets/WelcomeWidget.cpp +++ b/src/libtomahawk/widgets/WelcomeWidget.cpp @@ -32,6 +32,7 @@ #include "playlist/AlbumModel.h" #include "playlist/RecentlyPlayedModel.h" #include "widgets/OverlayWidget.h" +#include "utils/AnimatedSpinner.h" #include "utils/TomahawkUtils.h" #include "utils/Logger.h" #include "dynamic/GeneratorInterface.h" @@ -54,10 +55,6 @@ WelcomeWidget::WelcomeWidget( QWidget* parent ) ui->playlistWidget->setFrameShape( QFrame::NoFrame ); ui->playlistWidget->setAttribute( Qt::WA_MacShowFocusRect, 0 ); - ui->tracksView->setFrameShape( QFrame::NoFrame ); - ui->tracksView->setAttribute( Qt::WA_MacShowFocusRect, 0 ); - ui->additionsView->setFrameShape( QFrame::NoFrame ); - ui->additionsView->setAttribute( Qt::WA_MacShowFocusRect, 0 ); TomahawkUtils::unmarginLayout( layout() ); TomahawkUtils::unmarginLayout( ui->verticalLayout->layout() ); @@ -72,12 +69,12 @@ WelcomeWidget::WelcomeWidget( QWidget* parent ) updatePlaylists(); m_tracksModel = new RecentlyPlayedModel( source_ptr(), ui->tracksView ); - m_tracksModel->setStyle( TrackModel::ShortWithAvatars ); + m_tracksModel->setStyle( PlayableModel::ShortWithAvatars ); ui->tracksView->overlay()->setEnabled( false ); ui->tracksView->setPlaylistModel( m_tracksModel ); m_recentAlbumsModel = new AlbumModel( ui->additionsView ); - ui->additionsView->setAlbumModel( m_recentAlbumsModel ); + ui->additionsView->setPlayableModel( m_recentAlbumsModel ); ui->additionsView->proxyModel()->sort( -1 ); connect( SourceList::instance(), SIGNAL( ready() ), SLOT( onSourcesReady() ) ); @@ -245,11 +242,11 @@ PlaylistDelegate::paint( QPainter* painter, const QStyleOptionViewItem& option, // right edge 10px past right edge of pixmapRect // bottom edge flush with bottom of pixmap QRect rect( pixmapRect.right() - width , 0, width - 8, 0 ); - rect.adjust( -1, 0, 0, 0 ); + rect.adjust( -2, 0, 0, 0 ); rect.setTop( pixmapRect.bottom() - painter->fontMetrics().height() - 1 ); rect.setBottom( pixmapRect.bottom() + 1 ); - QColor figColor( 153, 153, 153 ); + QColor figColor( "#464b55" ); painter->setPen( figColor ); painter->setBrush( figColor ); @@ -311,4 +308,14 @@ PlaylistWidget::PlaylistWidget( QWidget* parent ) : QListView( parent ) { m_overlay = new OverlayWidget( this ); + LoadingSpinner* spinner = new LoadingSpinner( this ); } + + +void +PlaylistWidget::setModel( QAbstractItemModel* model ) +{ + QListView::setModel( model ); + emit modelChanged(); +} + diff --git a/src/libtomahawk/widgets/WelcomeWidget.h b/src/libtomahawk/widgets/WelcomeWidget.h index bb5754fce..658f6e8aa 100644 --- a/src/libtomahawk/widgets/WelcomeWidget.h +++ b/src/libtomahawk/widgets/WelcomeWidget.h @@ -64,12 +64,20 @@ private: QPixmap m_playlistIcon, m_autoIcon, m_stationIcon, m_defaultAvatar; }; + class DLLEXPORT PlaylistWidget : public QListView { +Q_OBJECT + public: PlaylistWidget( QWidget* parent = 0 ); OverlayWidget* overlay() const { return m_overlay; } + + virtual void setModel( QAbstractItemModel* model ); + +signals: + void modelChanged(); private: OverlayWidget* m_overlay; diff --git a/src/libtomahawk/widgets/WelcomeWidget.ui b/src/libtomahawk/widgets/WelcomeWidget.ui index a46ad774f..e91e39612 100644 --- a/src/libtomahawk/widgets/WelcomeWidget.ui +++ b/src/libtomahawk/widgets/WelcomeWidget.ui @@ -36,7 +36,7 @@ - + true @@ -94,9 +94,9 @@
widgets/HeaderLabel.h
- AlbumView + GridView QListView -
playlist/AlbumView.h
+
playlist/GridView.h
PlaylistView diff --git a/src/libtomahawk/widgets/WhatsHotWidget.cpp b/src/libtomahawk/widgets/WhatsHotWidget.cpp index ea55cf65a..2d2c8ddef 100644 --- a/src/libtomahawk/widgets/WhatsHotWidget.cpp +++ b/src/libtomahawk/widgets/WhatsHotWidget.cpp @@ -34,11 +34,11 @@ #include "audio/AudioEngine.h" #include "dynamic/GeneratorInterface.h" +#include "playlist/PlayableModel.h" #include "playlist/PlaylistModel.h" #include "playlist/TreeProxyModel.h" #include "playlist/PlaylistChartItemDelegate.h" -#include "widgets/OverlayWidget.h" -#include "utils/TomahawkUtils.h" +#include "utils/TomahawkUtilsGui.h" #include "utils/Logger.h" #include "Pipeline.h" @@ -59,9 +59,6 @@ WhatsHotWidget::WhatsHotWidget( QWidget* parent ) { ui->setupUi( this ); - ui->albumsView->setFrameShape( QFrame::NoFrame ); - ui->albumsView->setAttribute( Qt::WA_MacShowFocusRect, 0 ); - TomahawkUtils::unmarginLayout( layout() ); TomahawkUtils::unmarginLayout( ui->stackLeft->layout() ); TomahawkUtils::unmarginLayout( ui->horizontalLayout->layout() ); @@ -78,9 +75,6 @@ WhatsHotWidget::WhatsHotWidget( QWidget* parent ) connect( ui->breadCrumbLeft, SIGNAL( activateIndex( QModelIndex ) ), SLOT( leftCrumbIndexChanged(QModelIndex) ) ); - ui->tracksViewLeft->setFrameShape( QFrame::NoFrame ); - ui->tracksViewLeft->setAttribute( Qt::WA_MacShowFocusRect, 0 ); - ui->tracksViewLeft->overlay()->setEnabled( false ); ui->tracksViewLeft->setHeaderHidden( true ); ui->tracksViewLeft->setHorizontalScrollBarPolicy( Qt::ScrollBarAlwaysOff ); PlaylistChartItemDelegate* del = new PlaylistChartItemDelegate( ui->tracksViewLeft, ui->tracksViewLeft->proxyModel() ); @@ -93,8 +87,6 @@ WhatsHotWidget::WhatsHotWidget( QWidget* parent ) artistsProxy->setDynamicSortFilter( true ); ui->artistsViewLeft->setProxyModel( artistsProxy ); - ui->artistsViewLeft->setFrameShape( QFrame::NoFrame ); - ui->artistsViewLeft->setAttribute( Qt::WA_MacShowFocusRect, 0 ); ui->artistsViewLeft->setHorizontalScrollBarPolicy( Qt::ScrollBarAlwaysOff ); ui->artistsViewLeft->header()->setVisible( true ); @@ -179,9 +171,15 @@ WhatsHotWidget::infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData requestDat if ( requestData.caller != s_whatsHotIdentifier ) return; + if ( output.isNull() ) + { + tDebug( LOGVERBOSE ) << Q_FUNC_INFO << "Info came back empty"; + return; + } + if ( !output.canConvert< QVariantMap >() ) { - tDebug( LOGVERBOSE ) << Q_FUNC_INFO << "WhatsHot: Could not parse output"; + tDebug( LOGVERBOSE ) << Q_FUNC_INFO << "WhatsHot: Could not parse output into a map"; return; } @@ -264,7 +262,7 @@ WhatsHotWidget::infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData requestDat TreeModel* artistsModel = new TreeModel( ui->artistsViewLeft ); artistsModel->setMode( InfoSystemMode ); - artistsModel->setColumnStyle( TreeModel::AllColumns ); + artistsModel->setStyle( PlayableModel::Collection ); m_artistModels[ chartId ] = artistsModel; @@ -273,13 +271,12 @@ WhatsHotWidget::infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData requestDat } else if ( type == "albums" ) { - loader->setType( ChartDataLoader::Album ); loader->setData( returnedData[ "albums" ].value< QList< Tomahawk::InfoSystem::InfoStringHash > >() ); connect( loader, SIGNAL( albums( Tomahawk::ChartDataLoader*, QList< Tomahawk::album_ptr > ) ), this, SLOT( chartAlbumsLoaded( Tomahawk::ChartDataLoader*, QList< Tomahawk::album_ptr > ) ) ); - AlbumModel* albumModel = new AlbumModel( ui->albumsView ); + PlayableModel* albumModel = new PlayableModel( ui->albumsView ); m_albumModels[ chartId ] = albumModel; @@ -288,14 +285,13 @@ WhatsHotWidget::infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData requestDat } else if ( type == "tracks" ) { - loader->setType( ChartDataLoader::Track ); loader->setData( returnedData[ "tracks" ].value< QList< Tomahawk::InfoSystem::InfoStringHash > >() ); connect( loader, SIGNAL( tracks( Tomahawk::ChartDataLoader*, QList< Tomahawk::query_ptr > ) ), this, SLOT( chartTracksLoaded( Tomahawk::ChartDataLoader*, QList< Tomahawk::query_ptr > ) ) ); PlaylistModel* trackModel = new PlaylistModel( ui->tracksViewLeft ); - trackModel->setStyle( TrackModel::Large ); + trackModel->setStyle( PlayableModel::Large ); m_trackModels[ chartId ] = trackModel; @@ -453,9 +449,9 @@ WhatsHotWidget::parseNode( QStandardItem* parentItem, const QString &label, cons void -WhatsHotWidget::setLeftViewAlbums( AlbumModel* model ) +WhatsHotWidget::setLeftViewAlbums( PlayableModel* model ) { - ui->albumsView->setAlbumModel( model ); + ui->albumsView->setPlayableModel( model ); ui->albumsView->proxyModel()->sort( -1 ); // disable sorting, must be called after artistsViewLeft->setTreeModel ui->stackLeft->setCurrentIndex( 2 ); } @@ -523,7 +519,7 @@ WhatsHotWidget::chartAlbumsLoaded( ChartDataLoader* loader, const QList< album_p Q_ASSERT( m_albumModels.contains( chartId ) ); if ( m_albumModels.contains( chartId ) ) - m_albumModels[ chartId ]->addAlbums( albums ); + m_albumModels[ chartId ]->append( albums ); m_workers.remove( loader ); loader->deleteLater(); diff --git a/src/libtomahawk/widgets/WhatsHotWidget.h b/src/libtomahawk/widgets/WhatsHotWidget.h index cb8a8f635..851b4c281 100644 --- a/src/libtomahawk/widgets/WhatsHotWidget.h +++ b/src/libtomahawk/widgets/WhatsHotWidget.h @@ -29,8 +29,6 @@ #include "infosystem/InfoSystem.h" #include "ViewPage.h" -#include "utils/TomahawkUtils.h" - #include "DllMacro.h" class QSortFilterProxyModel; @@ -38,9 +36,8 @@ class QStandardItemModel; class QStandardItem; class TreeModel; class PlaylistModel; -class OverlayWidget; class TreeProxyModel; -class AlbumModel; +class PlayableModel; namespace Ui { @@ -98,7 +95,7 @@ private slots: private: void setLeftViewArtists( TreeModel* artistModel ); - void setLeftViewAlbums( AlbumModel* albumModel ); + void setLeftViewAlbums( PlayableModel* albumModel ); void setLeftViewTracks( PlaylistModel* trackModel ); @@ -116,7 +113,7 @@ private: QSet< Tomahawk::ChartDataLoader* > m_workers; // Cache our model data - QHash< QString, AlbumModel* > m_albumModels; + QHash< QString, PlayableModel* > m_albumModels; QHash< QString, TreeModel* > m_artistModels; QHash< QString, PlaylistModel* > m_trackModels; QString m_queueItemToShow; diff --git a/src/libtomahawk/widgets/WhatsHotWidget.ui b/src/libtomahawk/widgets/WhatsHotWidget.ui index 95e51f4d3..88eb41339 100644 --- a/src/libtomahawk/widgets/WhatsHotWidget.ui +++ b/src/libtomahawk/widgets/WhatsHotWidget.ui @@ -33,7 +33,7 @@ - + 320 @@ -47,7 +47,7 @@ - + true @@ -64,14 +64,14 @@ - AlbumView + GridView QListView -
playlist/AlbumView.h
+
playlist/GridView.h
- ArtistView + TreeView QTreeView -
ArtistView.h
+
TreeView.h
PlaylistView diff --git a/src/libtomahawk/widgets/WhatsHotWidget_p.h b/src/libtomahawk/widgets/WhatsHotWidget_p.h index c72c1fde1..8d830eb31 100644 --- a/src/libtomahawk/widgets/WhatsHotWidget_p.h +++ b/src/libtomahawk/widgets/WhatsHotWidget_p.h @@ -40,10 +40,10 @@ public: : PlaylistInterface() , m_w( w ) { - connect( m_w->ui->tracksViewLeft->proxyModel()->playlistInterface().data(), SIGNAL( repeatModeChanged( Tomahawk::PlaylistInterface::RepeatMode ) ), - SLOT( anyRepeatModeChanged( Tomahawk::PlaylistInterface::RepeatMode ) ) ); - connect( m_w->ui->artistsViewLeft->proxyModel()->playlistInterface().data(), SIGNAL( repeatModeChanged( Tomahawk::PlaylistInterface::RepeatMode ) ), - SLOT( anyRepeatModeChanged( Tomahawk::PlaylistInterface::RepeatMode ) ) ); + connect( m_w->ui->tracksViewLeft->proxyModel()->playlistInterface().data(), SIGNAL( repeatModeChanged( Tomahawk::PlaylistModes::RepeatMode ) ), + SLOT( anyRepeatModeChanged( Tomahawk::PlaylistModes::RepeatMode ) ) ); + connect( m_w->ui->artistsViewLeft->proxyModel()->playlistInterface().data(), SIGNAL( repeatModeChanged( Tomahawk::PlaylistModes::RepeatMode ) ), + SLOT( anyRepeatModeChanged( Tomahawk::PlaylistModes::RepeatMode ) ) ); connect( m_w->ui->tracksViewLeft->proxyModel()->playlistInterface().data(), SIGNAL( shuffleModeChanged( bool ) ), SLOT( anyShuffleChanged( bool ) ) ); @@ -53,7 +53,7 @@ public: virtual ~ChartsPlaylistInterface() {} // Any one is fine, we keep them all synched - virtual RepeatMode repeatMode() const { return m_w->ui->tracksViewLeft->proxyModel()->playlistInterface()->repeatMode(); } + virtual PlaylistModes::RepeatMode repeatMode() const { return m_w->ui->tracksViewLeft->proxyModel()->playlistInterface()->repeatMode(); } virtual bool shuffled() const { return m_w->ui->tracksViewLeft->proxyModel()->playlistInterface()->shuffled(); } @@ -72,7 +72,7 @@ public: } public slots: - virtual void setRepeatMode( RepeatMode mode ) + virtual void setRepeatMode( PlaylistModes::RepeatMode mode ) { m_w->ui->tracksViewLeft->proxyModel()->playlistInterface()->setRepeatMode( mode ); m_w->ui->artistsViewLeft->proxyModel()->playlistInterface()->setRepeatMode( mode ); @@ -85,7 +85,7 @@ public slots: } signals: - void repeatModeChanged( Tomahawk::PlaylistInterface::RepeatMode mode ); + void repeatModeChanged( Tomahawk::PlaylistModes::RepeatMode mode ); void shuffleModeChanged( bool enabled ); void trackCountChanged( unsigned int tracks ); @@ -93,7 +93,7 @@ signals: void nextTrackReady(); private slots: - void anyRepeatModeChanged( Tomahawk::PlaylistInterface::RepeatMode mode ) + void anyRepeatModeChanged( Tomahawk::PlaylistModes::RepeatMode mode ) { emit repeatModeChanged( mode ); } diff --git a/src/libtomahawk/widgets/infowidgets/AlbumInfoWidget.cpp b/src/libtomahawk/widgets/infowidgets/AlbumInfoWidget.cpp index a1f920271..3dc71d192 100644 --- a/src/libtomahawk/widgets/infowidgets/AlbumInfoWidget.cpp +++ b/src/libtomahawk/widgets/infowidgets/AlbumInfoWidget.cpp @@ -25,63 +25,40 @@ #include "ViewManager.h" #include "database/Database.h" #include "playlist/TreeModel.h" -#include "playlist/AlbumModel.h" +#include "playlist/PlayableModel.h" +#include "Source.h" #include "database/DatabaseCommand_AllTracks.h" #include "database/DatabaseCommand_AllAlbums.h" -#include "utils/TomahawkUtils.h" +#include "utils/TomahawkUtilsGui.h" #include "utils/Logger.h" -#include "widgets/OverlayButton.h" -#include "widgets/OverlayWidget.h" - using namespace Tomahawk; -AlbumInfoWidget::AlbumInfoWidget( const Tomahawk::album_ptr& album, ModelMode startingMode, QWidget* parent ) +AlbumInfoWidget::AlbumInfoWidget( const Tomahawk::album_ptr& album, QWidget* parent ) : QWidget( parent ) , ui( new Ui::AlbumInfoWidget ) - , m_infoId( uuid() ) { ui->setupUi( this ); - ui->albumsView->setFrameShape( QFrame::NoFrame ); - ui->albumsView->setAttribute( Qt::WA_MacShowFocusRect, 0 ); - ui->tracksView->setFrameShape( QFrame::NoFrame ); - ui->tracksView->setAttribute( Qt::WA_MacShowFocusRect, 0 ); - TomahawkUtils::unmarginLayout( layout() ); TomahawkUtils::unmarginLayout( ui->verticalLayout ); TomahawkUtils::unmarginLayout( ui->verticalLayout_2 ); - m_albumsModel = new AlbumModel( ui->albumsView ); - ui->albumsView->setAlbumModel( m_albumsModel ); + m_albumsModel = new PlayableModel( ui->albumsView ); + ui->albumsView->setPlayableModel( m_albumsModel ); + ui->albumsView->setEmptyTip( tr( "Sorry, we could not find any other albums for this artist!" ) ); m_tracksModel = new TreeModel( ui->tracksView ); - m_tracksModel->setMode( startingMode ); + m_tracksModel->setMode( Mixed ); ui->tracksView->setTreeModel( m_tracksModel ); ui->tracksView->setRootIsDecorated( false ); + ui->tracksView->setEmptyTip( tr( "Sorry, we could not find any tracks for this album!" ) ); m_pixmap = TomahawkUtils::defaultPixmap( TomahawkUtils::DefaultAlbumCover, TomahawkUtils::ScaledCover, QSize( 48, 48 ) ); - m_button = new OverlayButton( ui->tracksView ); - m_button->setCheckable( true ); - m_button->setChecked( m_tracksModel->mode() == InfoSystemMode ); - if ( m_button->isChecked() ) - m_button->setText( tr( "Click to show SuperCollection Tracks" ) ); - else - m_button->setText( tr( "Click to show Official Tracks" ) ); - - m_buttonAlbums = new OverlayButton( ui->albumsView ); - m_buttonAlbums->setCheckable( true ); - m_buttonAlbums->setChecked( true ); - m_buttonAlbums->setText( tr( "Click to show SuperCollection Albums" ) ); - m_buttonAlbums->show(); - - connect( m_button, SIGNAL( clicked() ), SLOT( onModeToggle() ) ); - connect( m_buttonAlbums, SIGNAL( clicked() ), SLOT( onAlbumsModeToggle() ) ); - connect( m_tracksModel, SIGNAL( modeChanged( Tomahawk::ModelMode ) ), SLOT( setMode( Tomahawk::ModelMode ) ) ); connect( m_tracksModel, SIGNAL( loadingStarted() ), SLOT( onLoadingStarted() ) ); connect( m_tracksModel, SIGNAL( loadingFinished() ), SLOT( onLoadingFinished() ) ); @@ -102,54 +79,15 @@ AlbumInfoWidget::playlistInterface() const } -void -AlbumInfoWidget::setMode( ModelMode mode ) -{ - m_button->setChecked( mode == InfoSystemMode ); - - if ( m_tracksModel->mode() != mode ) - onModeToggle(); - - if ( mode == InfoSystemMode ) - m_button->setText( tr( "Click to show SuperCollection Tracks" ) ); - else - m_button->setText( tr( "Click to show Official Tracks" ) ); -} - - -void -AlbumInfoWidget::onModeToggle() -{ - m_tracksModel->setMode( m_button->isChecked() ? InfoSystemMode : DatabaseMode ); - m_tracksModel->addTracks( m_album, QModelIndex() ); -} - - -void -AlbumInfoWidget::onAlbumsModeToggle() -{ - if ( m_buttonAlbums->isChecked() ) - m_buttonAlbums->setText( tr( "Click to show SuperCollection Albums" ) ); - else - m_buttonAlbums->setText( tr( "Click to show Official Albums" ) ); - - loadAlbums(); -} - - void AlbumInfoWidget::onLoadingStarted() { - m_button->setEnabled( false ); - m_button->hide(); } void AlbumInfoWidget::onLoadingFinished() { - m_button->setEnabled( true ); - m_button->show(); } @@ -211,14 +149,16 @@ AlbumInfoWidget::load( const album_ptr& album ) void AlbumInfoWidget::loadAlbums( bool autoRefetch ) { + Q_UNUSED( autoRefetch ); + m_albumsModel->clear(); connect( m_album->artist().data(), SIGNAL( albumsAdded( QList, Tomahawk::ModelMode ) ), SLOT( gotAlbums( QList ) ) ); - ModelMode mode = m_buttonAlbums->isChecked() ? InfoSystemMode : DatabaseMode; - gotAlbums( m_album->artist()->albums( mode ) ); - + if ( !m_album->artist()->albums( Mixed ).isEmpty() ) + gotAlbums( m_album->artist()->albums( Mixed ) ); + /* tDebug() << "Auto refetching"; m_buttonAlbums->setChecked( false ); onAlbumsModeToggle();*/ @@ -243,7 +183,7 @@ AlbumInfoWidget::gotAlbums( const QList& albums ) if ( al.contains( m_album ) ) al.removeAll( m_album ); - m_albumsModel->addAlbums( al ); + m_albumsModel->append( al ); } diff --git a/src/libtomahawk/widgets/infowidgets/AlbumInfoWidget.h b/src/libtomahawk/widgets/infowidgets/AlbumInfoWidget.h index 958748a51..9d00c2847 100644 --- a/src/libtomahawk/widgets/infowidgets/AlbumInfoWidget.h +++ b/src/libtomahawk/widgets/infowidgets/AlbumInfoWidget.h @@ -39,9 +39,8 @@ #include "DllMacro.h" #include "Typedefs.h" -class AlbumModel; +class PlayableModel; class TreeModel; -class OverlayButton; namespace Ui { @@ -53,7 +52,7 @@ class DLLEXPORT AlbumInfoWidget : public QWidget, public Tomahawk::ViewPage Q_OBJECT public: - AlbumInfoWidget( const Tomahawk::album_ptr& album, Tomahawk::ModelMode startingMode = Tomahawk::InfoSystemMode, QWidget* parent = 0 ); + AlbumInfoWidget( const Tomahawk::album_ptr& album, QWidget* parent = 0 ); ~AlbumInfoWidget(); virtual QWidget* widget() { return this; } @@ -73,8 +72,6 @@ public: virtual bool isBeingPlayed() const; public slots: - void setMode( Tomahawk::ModelMode mode ); - /** \brief Loads information for a given album. * \param album The album that you want to load information for. * @@ -98,29 +95,21 @@ private slots: void gotAlbums( const QList& albums ); void onAlbumCoverUpdated(); - void onModeToggle(); - void onAlbumsModeToggle(); - void onLoadingStarted(); void onLoadingFinished(); private: - Ui::AlbumInfoWidget *ui; + Ui::AlbumInfoWidget* ui; Tomahawk::album_ptr m_album; - AlbumModel* m_albumsModel; + PlayableModel* m_albumsModel; TreeModel* m_tracksModel; - OverlayButton* m_button; - OverlayButton* m_buttonAlbums; - QString m_title; QString m_description; QString m_longDescription; QPixmap m_pixmap; - - QString m_infoId; }; #endif // ALBUMINFOWIDGET_H diff --git a/src/libtomahawk/widgets/infowidgets/AlbumInfoWidget.ui b/src/libtomahawk/widgets/infowidgets/AlbumInfoWidget.ui index 9ad34c135..ec146f484 100644 --- a/src/libtomahawk/widgets/infowidgets/AlbumInfoWidget.ui +++ b/src/libtomahawk/widgets/infowidgets/AlbumInfoWidget.ui @@ -22,7 +22,7 @@ 1 - + @@ -33,7 +33,7 @@
- +
@@ -48,14 +48,14 @@
widgets/HeaderLabel.h
- ArtistView + TreeView QTreeView -
ArtistView.h
+
TreeView.h
- AlbumView + GridView QListView -
playlist/AlbumView.h
+
playlist/GridView.h
diff --git a/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp b/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp index d3b71898d..8bbcda7bb 100644 --- a/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp +++ b/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp @@ -22,10 +22,11 @@ #include "ui_ArtistInfoWidget.h" #include "audio/AudioEngine.h" -#include "playlist/TrackHeader.h" +#include "playlist/PlayableModel.h" #include "playlist/TreeModel.h" #include "playlist/PlaylistModel.h" #include "playlist/TreeProxyModel.h" +#include "Source.h" #include "database/DatabaseCommand_AllTracks.h" #include "database/DatabaseCommand_AllAlbums.h" @@ -34,9 +35,6 @@ #include "utils/TomahawkUtilsGui.h" #include "utils/Logger.h" -#include "widgets/OverlayButton.h" -#include "widgets/OverlayWidget.h" - #include "Pipeline.h" using namespace Tomahawk; @@ -46,56 +44,37 @@ ArtistInfoWidget::ArtistInfoWidget( const Tomahawk::artist_ptr& artist, QWidget* : QWidget( parent ) , ui( new Ui::ArtistInfoWidget ) , m_artist( artist ) - , m_infoId( uuid() ) { ui->setupUi( this ); m_plInterface = Tomahawk::playlistinterface_ptr( new MetaPlaylistInterface( this ) ); - ui->albums->setFrameShape( QFrame::NoFrame ); - ui->albums->setAttribute( Qt::WA_MacShowFocusRect, 0 ); - ui->relatedArtists->setFrameShape( QFrame::NoFrame ); - ui->relatedArtists->setAttribute( Qt::WA_MacShowFocusRect, 0 ); - ui->topHits->setFrameShape( QFrame::NoFrame ); - ui->topHits->setAttribute( Qt::WA_MacShowFocusRect, 0 ); - TomahawkUtils::unmarginLayout( layout() ); TomahawkUtils::unmarginLayout( ui->layoutWidget->layout() ); TomahawkUtils::unmarginLayout( ui->layoutWidget1->layout() ); TomahawkUtils::unmarginLayout( ui->layoutWidget2->layout() ); TomahawkUtils::unmarginLayout( ui->albumHeader->layout() ); - m_albumsModel = new TreeModel( ui->albums ); - m_albumsModel->setMode( InfoSystemMode ); - ui->albums->setTreeModel( m_albumsModel ); + m_albumsModel = new PlayableModel( ui->albums ); + ui->albums->setPlayableModel( m_albumsModel ); + ui->topHits->setEmptyTip( tr( "Sorry, we could not find any albums for this artist!" ) ); - m_relatedModel = new TreeModel( ui->relatedArtists ); - m_relatedModel->setColumnStyle( TreeModel::TrackOnly ); - ui->relatedArtists->setTreeModel( m_relatedModel ); - ui->relatedArtists->setSortingEnabled( false ); + m_relatedModel = new PlayableModel( ui->relatedArtists ); + ui->relatedArtists->setPlayableModel( m_relatedModel ); ui->relatedArtists->proxyModel()->sort( -1 ); + ui->topHits->setEmptyTip( tr( "Sorry, we could not find any related artists!" ) ); m_topHitsModel = new PlaylistModel( ui->topHits ); - m_topHitsModel->setStyle( TrackModel::Short ); - ui->topHits->setTrackModel( m_topHitsModel ); + m_topHitsModel->setStyle( PlayableModel::Short ); + ui->topHits->setPlayableModel( m_topHitsModel ); ui->topHits->setSortingEnabled( false ); + ui->topHits->setEmptyTip( tr( "Sorry, we could not find any top hits for this artist!" ) ); m_pixmap = TomahawkUtils::defaultPixmap( TomahawkUtils::DefaultArtistImage, TomahawkUtils::ScaledCover, QSize( 48, 48 ) ); - m_button = new OverlayButton( ui->albums ); - m_button->setText( tr( "Click to show SuperCollection Albums" ) ); - m_button->setCheckable( true ); - m_button->setChecked( true ); - - connect( m_button, SIGNAL( clicked() ), SLOT( onModeToggle() ) ); - connect( m_albumsModel, SIGNAL( modeChanged( Tomahawk::ModelMode ) ), SLOT( setMode( Tomahawk::ModelMode ) ) ); connect( m_albumsModel, SIGNAL( loadingStarted() ), SLOT( onLoadingStarted() ) ); connect( m_albumsModel, SIGNAL( loadingFinished() ), SLOT( onLoadingFinished() ) ); - connect( Tomahawk::InfoSystem::InfoSystem::instance(), - SIGNAL( info( Tomahawk::InfoSystem::InfoRequestData, QVariant ) ), - SLOT( infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData, QVariant ) ) ); - load( artist ); } @@ -113,42 +92,15 @@ ArtistInfoWidget::playlistInterface() const } -void -ArtistInfoWidget::setMode( ModelMode mode ) -{ - m_button->setChecked( mode == InfoSystemMode ); - - if ( m_albumsModel->mode() != mode ) - onModeToggle(); - - if ( mode == InfoSystemMode ) - m_button->setText( tr( "Click to show SuperCollection Albums" ) ); - else - m_button->setText( tr( "Click to show Official Albums" ) ); -} - - -void -ArtistInfoWidget::onModeToggle() -{ - m_albumsModel->setMode( m_button->isChecked() ? InfoSystemMode : DatabaseMode ); - m_albumsModel->fetchAlbums( m_artist ); -} - - void ArtistInfoWidget::onLoadingStarted() { - m_button->setEnabled( false ); - m_button->hide(); } void ArtistInfoWidget::onLoadingFinished() { - m_button->setEnabled( true ); - m_button->show(); } @@ -188,35 +140,39 @@ void ArtistInfoWidget::load( const artist_ptr& artist ) { if ( !m_artist.isNull() ) + { disconnect( m_artist.data(), SIGNAL( updated() ), this, SLOT( onArtistImageUpdated() ) ); + disconnect( m_artist.data(), SIGNAL( similarArtistsLoaded() ), this, SLOT( onSimilarArtistsLoaded() ) ); + disconnect( m_artist.data(), SIGNAL( biographyLoaded() ), this, SLOT( onBiographyLoaded() ) ); + disconnect( m_artist.data(), SIGNAL( albumsAdded( QList, Tomahawk::ModelMode ) ), + this, SLOT( onAlbumsFound( QList, Tomahawk::ModelMode ) ) ); + disconnect( m_artist.data(), SIGNAL( tracksAdded( QList, Tomahawk::ModelMode, Tomahawk::collection_ptr ) ), + this, SLOT( onTracksFound( QList, Tomahawk::ModelMode ) ) ); + } m_artist = artist; m_title = artist->name(); - - m_albumsModel->fetchAlbums( artist ); - - Tomahawk::InfoSystem::InfoStringHash artistInfo; - artistInfo["artist"] = artist->name(); - - Tomahawk::InfoSystem::InfoRequestData requestData; - requestData.caller = m_infoId; - requestData.customData = QVariantMap(); - - requestData.input = artist->name(); - requestData.type = Tomahawk::InfoSystem::InfoArtistBiography; - Tomahawk::InfoSystem::InfoSystem::instance()->getInfo( requestData ); - - requestData.input = QVariant::fromValue< Tomahawk::InfoSystem::InfoStringHash >( artistInfo ); - - 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 ); + connect( m_artist.data(), SIGNAL( biographyLoaded() ), SLOT( onBiographyLoaded() ) ); + connect( m_artist.data(), SIGNAL( similarArtistsLoaded() ), SLOT( onSimilarArtistsLoaded() ) ); connect( m_artist.data(), SIGNAL( updated() ), SLOT( onArtistImageUpdated() ) ); + connect( m_artist.data(), SIGNAL( albumsAdded( QList, Tomahawk::ModelMode ) ), + SLOT( onAlbumsFound( QList, Tomahawk::ModelMode ) ) ); + connect( m_artist.data(), SIGNAL( tracksAdded( QList, Tomahawk::ModelMode, Tomahawk::collection_ptr ) ), + SLOT( onTracksFound( QList, Tomahawk::ModelMode ) ) ); + + if ( !m_artist->albums( Mixed ).isEmpty() ) + onAlbumsFound( m_artist->albums( Mixed ), Mixed ); + + if ( !m_artist->tracks().isEmpty() ) + onTracksFound( m_artist->tracks(), Mixed ); + + if ( !m_artist->similarArtists().isEmpty() ) + onSimilarArtistsLoaded(); + + if ( !m_artist->biography().isEmpty() ) + onBiographyLoaded(); + onArtistImageUpdated(); } @@ -224,76 +180,33 @@ ArtistInfoWidget::load( const artist_ptr& artist ) void ArtistInfoWidget::onAlbumsFound( const QList& albums, ModelMode mode ) { + Q_UNUSED( mode ); + + m_albumsModel->append( albums ); } void -ArtistInfoWidget::infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData requestData, QVariant output ) +ArtistInfoWidget::onTracksFound( const QList& queries, ModelMode mode ) { - if ( requestData.caller != m_infoId ) - return; + Q_UNUSED( mode ); - InfoSystem::InfoStringHash trackInfo; - trackInfo = requestData.input.value< InfoSystem::InfoStringHash >(); + m_topHitsModel->append( queries ); +} - if ( output.canConvert< QVariantMap >() ) - { - const QString artist = requestData.input.toString(); - if ( trackInfo["artist"] != m_artist->name() && artist != m_artist->name() ) - { - qDebug() << "Returned info was for:" << trackInfo["artist"] << "- was looking for:" << m_artist->name(); - return; - } - } - QVariantMap returnedData = output.value< QVariantMap >(); - switch ( requestData.type ) - { - case InfoSystem::InfoArtistBiography: - { - QVariantMap bmap = output.toMap(); +void +ArtistInfoWidget::onSimilarArtistsLoaded() +{ + m_relatedModel->append( m_artist->similarArtists() ); +} - foreach ( const QString& source, bmap.keys() ) - { - if ( m_longDescription.isEmpty() || source == "last.fm" ) - m_longDescription = bmap[ source ].toHash()[ "text" ].toString(); - } - emit longDescriptionChanged( m_longDescription ); - break; - } - case InfoSystem::InfoArtistSongs: - { - const QStringList tracks = returnedData["tracks"].toStringList(); - - QList< query_ptr > queries; - int i = 0; - foreach ( const QString& track, tracks ) - { - queries << Query::get( m_artist->name(), track, QString() ); - Pipeline::instance()->resolve( queries ); - - if ( ++i == 15 ) - break; - } - - m_topHitsModel->append( queries ); - break; - } - - case InfoSystem::InfoArtistSimilars: - { - const QStringList artists = returnedData["artists"].toStringList(); - foreach ( const QString& artist, artists ) - { - m_relatedModel->addArtists( Artist::get( artist ) ); - } - break; - } - - default: - return; - } +void +ArtistInfoWidget::onBiographyLoaded() +{ + m_longDescription = m_artist->biography(); + emit longDescriptionChanged( m_longDescription ); } diff --git a/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.h b/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.h index 5469a0c3d..6c00ab0f2 100644 --- a/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.h +++ b/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.h @@ -35,13 +35,11 @@ #include "Typedefs.h" #include "PlaylistInterface.h" #include "ViewPage.h" -#include "infosystem/InfoSystem.h" #include "DllMacro.h" +class PlayableModel; class PlaylistModel; -class TreeModel; -class OverlayButton; namespace Ui { @@ -91,14 +89,13 @@ protected: void changeEvent( QEvent* e ); private slots: - void setMode( Tomahawk::ModelMode mode ); - - void infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData requestData, QVariant output ); void onArtistImageUpdated(); + void onBiographyLoaded(); void onAlbumsFound( const QList& albums, Tomahawk::ModelMode mode ); + void onTracksFound( const QList& queries, Tomahawk::ModelMode mode ); + void onSimilarArtistsLoaded(); - void onModeToggle(); void onLoadingStarted(); void onLoadingFinished(); @@ -107,17 +104,14 @@ private: Tomahawk::artist_ptr m_artist; - TreeModel* m_relatedModel; - TreeModel* m_albumsModel; + PlayableModel* m_relatedModel; + PlayableModel* m_albumsModel; PlaylistModel* m_topHitsModel; Tomahawk::playlistinterface_ptr m_plInterface; - OverlayButton* m_button; - QString m_title; QString m_description; QString m_longDescription; - QString m_infoId; QPixmap m_pixmap; friend class MetaPlaylistInterface; diff --git a/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.ui b/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.ui index b951a3558..246a3e593 100644 --- a/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.ui +++ b/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.ui @@ -57,10 +57,7 @@ - - - true - + @@ -82,7 +79,7 @@ - + @@ -112,9 +109,9 @@
widgets/ToggleButton.h
- ArtistView - QTreeView -
ArtistView.h
+ GridView + QListView +
GridView.h
diff --git a/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget_p.h b/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget_p.h index d697b56e0..10f6776b4 100644 --- a/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget_p.h +++ b/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget_p.h @@ -25,6 +25,7 @@ #include "PlaylistInterface.h" #include "TreeProxyModel.h" #include "Result.h" +#include "Typedefs.h" #include @@ -36,12 +37,12 @@ public: : PlaylistInterface() , m_w( w ) { - connect( m_w->ui->albums->proxyModel()->playlistInterface().data(), SIGNAL( repeatModeChanged( Tomahawk::PlaylistInterface::RepeatMode ) ), - SLOT( anyRepeatModeChanged( Tomahawk::PlaylistInterface::RepeatMode ) ) ); - connect( m_w->ui->relatedArtists->proxyModel()->playlistInterface().data(), SIGNAL( repeatModeChanged( Tomahawk::PlaylistInterface::RepeatMode ) ), - SLOT( anyRepeatModeChanged( Tomahawk::PlaylistInterface::RepeatMode ) ) ); - connect( m_w->ui->topHits->proxyModel()->playlistInterface().data(), SIGNAL( repeatModeChanged( Tomahawk::PlaylistInterface::RepeatMode ) ), - SLOT( anyRepeatModeChanged( Tomahawk::PlaylistInterface::RepeatMode ) ) ); + connect( m_w->ui->albums->proxyModel()->playlistInterface().data(), SIGNAL( repeatModeChanged( Tomahawk::PlaylistModes::RepeatMode ) ), + SLOT( anyRepeatModeChanged( Tomahawk::PlaylistModes::RepeatMode ) ) ); + connect( m_w->ui->relatedArtists->proxyModel()->playlistInterface().data(), SIGNAL( repeatModeChanged( Tomahawk::PlaylistModes::RepeatMode ) ), + SLOT( anyRepeatModeChanged( Tomahawk::PlaylistModes::RepeatMode ) ) ); + connect( m_w->ui->topHits->proxyModel()->playlistInterface().data(), SIGNAL( repeatModeChanged( Tomahawk::PlaylistModes::RepeatMode ) ), + SLOT( anyRepeatModeChanged( Tomahawk::PlaylistModes::RepeatMode ) ) ); connect( m_w->ui->albums->proxyModel()->playlistInterface().data(), SIGNAL( shuffleModeChanged( bool ) ), SLOT( anyShuffleChanged( bool ) ) ); @@ -54,7 +55,7 @@ public: // Any one is fine, we keep them all synched - virtual RepeatMode repeatMode() const { return m_w->ui->albums->proxyModel()->playlistInterface()->repeatMode(); } + virtual Tomahawk::PlaylistModes::RepeatMode repeatMode() const { return m_w->ui->albums->proxyModel()->playlistInterface()->repeatMode(); } virtual bool shuffled() const { return m_w->ui->albums->proxyModel()->playlistInterface()->shuffled(); } @@ -73,7 +74,7 @@ public: } public slots: - virtual void setRepeatMode( RepeatMode mode ) + virtual void setRepeatMode( Tomahawk::PlaylistModes::RepeatMode mode ) { m_w->ui->albums->proxyModel()->playlistInterface()->setRepeatMode( mode ); m_w->ui->relatedArtists->proxyModel()->playlistInterface()->setRepeatMode( mode ); @@ -91,7 +92,7 @@ signals: void nextTrackReady(); private slots: - void anyRepeatModeChanged( Tomahawk::PlaylistInterface::RepeatMode mode ) + void anyRepeatModeChanged( Tomahawk::PlaylistModes::RepeatMode mode ) { emit repeatModeChanged( mode ); } diff --git a/src/libtomahawk/widgets/infowidgets/SourceInfoWidget.cpp b/src/libtomahawk/widgets/infowidgets/SourceInfoWidget.cpp index c250dafda..fb02f275a 100644 --- a/src/libtomahawk/widgets/infowidgets/SourceInfoWidget.cpp +++ b/src/libtomahawk/widgets/infowidgets/SourceInfoWidget.cpp @@ -23,18 +23,15 @@ #include "ViewManager.h" #include "playlist/AlbumModel.h" -#include "playlist/CollectionFlatModel.h" #include "playlist/RecentlyAddedModel.h" #include "playlist/RecentlyPlayedModel.h" #include "database/Database.h" #include "database/DatabaseCommand_AllAlbums.h" -#include "utils/TomahawkUtils.h" +#include "utils/TomahawkUtilsGui.h" #include "utils/Logger.h" -#include "widgets/OverlayWidget.h" - SourceInfoWidget::SourceInfoWidget( const Tomahawk::source_ptr& source, QWidget* parent ) : QWidget( parent ) @@ -43,13 +40,6 @@ SourceInfoWidget::SourceInfoWidget( const Tomahawk::source_ptr& source, QWidget* { ui->setupUi( this ); - ui->historyView->setFrameShape( QFrame::NoFrame ); - ui->historyView->setAttribute( Qt::WA_MacShowFocusRect, 0 ); - ui->recentAlbumView->setFrameShape( QFrame::NoFrame ); - ui->recentAlbumView->setAttribute( Qt::WA_MacShowFocusRect, 0 ); - ui->recentCollectionView->setFrameShape( QFrame::NoFrame ); - ui->recentCollectionView->setAttribute( Qt::WA_MacShowFocusRect, 0 ); - TomahawkUtils::unmarginLayout( layout() ); TomahawkUtils::unmarginLayout( ui->horizontalLayout ); TomahawkUtils::unmarginLayout( ui->verticalLayout ); @@ -59,19 +49,17 @@ SourceInfoWidget::SourceInfoWidget( const Tomahawk::source_ptr& source, QWidget* ui->splitter->setStretchFactor( 0, 0 ); ui->splitter->setStretchFactor( 1, 1 ); - ui->historyView->overlay()->setEnabled( false ); - m_recentTracksModel = new RecentlyAddedModel( source, ui->recentCollectionView ); - m_recentTracksModel->setStyle( TrackModel::Short ); - ui->recentCollectionView->setTrackModel( m_recentTracksModel ); - ui->recentCollectionView->sortByColumn( TrackModel::Age, Qt::DescendingOrder ); + m_recentTracksModel->setStyle( PlayableModel::Short ); + ui->recentCollectionView->setPlayableModel( m_recentTracksModel ); + ui->recentCollectionView->sortByColumn( PlayableModel::Age, Qt::DescendingOrder ); m_historyModel = new RecentlyPlayedModel( source, ui->historyView ); - m_historyModel->setStyle( TrackModel::Short ); + m_historyModel->setStyle( PlayableModel::Short ); ui->historyView->setPlaylistModel( m_historyModel ); m_recentAlbumModel = new AlbumModel( ui->recentAlbumView ); - ui->recentAlbumView->setAlbumModel( m_recentAlbumModel ); + ui->recentAlbumView->setPlayableModel( m_recentAlbumModel ); ui->recentAlbumView->proxyModel()->sort( -1 ); onCollectionChanged(); diff --git a/src/libtomahawk/widgets/infowidgets/SourceInfoWidget.ui b/src/libtomahawk/widgets/infowidgets/SourceInfoWidget.ui index e11eeaa15..de7638fff 100644 --- a/src/libtomahawk/widgets/infowidgets/SourceInfoWidget.ui +++ b/src/libtomahawk/widgets/infowidgets/SourceInfoWidget.ui @@ -32,7 +32,7 @@ - + 0 @@ -76,7 +76,7 @@ - + @@ -107,9 +107,9 @@
widgets/HeaderLabel.h
- AlbumView + GridView QListView -
playlist/AlbumView.h
+
playlist/GridView.h
PlaylistView @@ -117,9 +117,9 @@
playlist/PlaylistView.h
- CollectionView + TrackView QTreeView -
playlist/CollectionView.h
+
playlist/TrackView.h
diff --git a/src/libtomahawk/widgets/infowidgets/TrackInfoWidget.cpp b/src/libtomahawk/widgets/infowidgets/TrackInfoWidget.cpp index 33783121f..20837b978 100644 --- a/src/libtomahawk/widgets/infowidgets/TrackInfoWidget.cpp +++ b/src/libtomahawk/widgets/infowidgets/TrackInfoWidget.cpp @@ -23,9 +23,10 @@ #include "ViewManager.h" #include "SourceList.h" -#include "playlist/AlbumModel.h" +#include "playlist/PlayableModel.h" +#include "audio/AudioEngine.h" -#include "utils/TomahawkUtils.h" +#include "utils/TomahawkUtilsGui.h" #include "utils/Logger.h" using namespace Tomahawk; @@ -34,49 +35,69 @@ using namespace Tomahawk; TrackInfoWidget::TrackInfoWidget( const Tomahawk::query_ptr& query, QWidget* parent ) : QWidget( parent ) , ui( new Ui::TrackInfoWidget ) - , m_infoId( uuid() ) { ui->setupUi( this ); + QPalette pal = palette(); + pal.setColor( QPalette::Window, QColor( "#323435" ) ); + + setPalette( pal ); + setAutoFillBackground( true ); + layout()->setSpacing( 0 ); - ui->headerWidget->setStyleSheet( "QWidget#headerWidget { background-image: url(" RESPATH "images/playlist-header-tiled.png); }" ); - ui->tracksWidget->setStyleSheet( "background-color: #bababa;" ); + ui->tracksWidget->setStyleSheet( "QWidget#tracksWidget { background-color: #323435; }" ); +// ui->headerWidget->setStyleSheet( "QWidget#headerWidget { background-image: url(" RESPATH "images/playlist-header-tiled.png); }" ); +// ui->headerWidget->setStyleSheet( "background-color: #323435;" ); +// ui->tracksWidget->setStyleSheet( "background-color: #323435;" ); ui->statsLabel->setStyleSheet( "QLabel { background-image:url(); border: 2px solid #dddddd; background-color: #faf9f9; border-radius: 4px; padding: 12px; }" ); + ui->lyricsView->setStyleSheet( "QTextBrowser#lyricsView { background-color: transparent; }" ); + + ui->lyricsView->setFrameShape( QFrame::NoFrame ); + ui->lyricsView->setAttribute( Qt::WA_MacShowFocusRect, 0 ); QFont f = font(); f.setBold( true ); f.setPixelSize( 18 ); ui->trackLabel->setFont( f ); - ui->similarTracksLabel->setFont( f ); +// ui->similarTracksLabel->setFont( f ); f.setPixelSize( 14 ); ui->artistLabel->setFont( f ); ui->albumLabel->setFont( f ); ui->byLabel->setFont( f ); ui->fromLabel->setFont( f ); - + f.setPixelSize( 12 ); ui->statsLabel->setFont( f ); - ui->similarTracksView->setFrameShape( QFrame::NoFrame ); - ui->similarTracksView->setAttribute( Qt::WA_MacShowFocusRect, 0 ); - ui->similarTracksView->setStyleSheet( "QListView { background-color: transparent; } QListView::item { background-color: transparent; }" ); +// ui->similarTracksView->setStyleSheet( "QListView { background-color: transparent; } QListView::item { background-color: transparent; }" ); QPalette p = ui->trackLabel->palette(); p.setColor( QPalette::Foreground, Qt::white ); + p.setColor( QPalette::Text, Qt::white ); + ui->trackLabel->setPalette( p ); ui->artistLabel->setPalette( p ); ui->albumLabel->setPalette( p ); ui->byLabel->setPalette( p ); ui->fromLabel->setPalette( p ); + ui->lyricsView->setPalette( p ); +// ui->similarTracksLabel->setPalette( p ); - m_albumsModel = new AlbumModel( ui->similarTracksView ); - ui->similarTracksView->setAlbumModel( m_albumsModel ); + ui->artistLabel->setType( QueryLabel::Artist ); + ui->albumLabel->setType( QueryLabel::Album ); + + m_relatedTracksModel = new PlayableModel( ui->similarTracksView ); + ui->similarTracksView->setPlayableModel( m_relatedTracksModel ); ui->similarTracksView->proxyModel()->sort( -1 ); + ui->similarTracksView->setEmptyTip( tr( "Sorry, but we could not find similar tracks for this song!" ) ); m_pixmap = TomahawkUtils::defaultPixmap( TomahawkUtils::DefaultAlbumCover, TomahawkUtils::ScaledCover, QSize( 48, 48 ) ); load( query ); + + connect( ui->artistLabel, SIGNAL( clickedArtist() ), SLOT( onArtistClicked() ) ); + connect( ui->albumLabel, SIGNAL( clickedAlbum() ), SLOT( onAlbumClicked() ) ); } @@ -115,6 +136,8 @@ TrackInfoWidget::load( const query_ptr& query ) if ( !m_query.isNull() ) { + disconnect( m_query.data(), SIGNAL( lyricsLoaded() ), this, SLOT( onLyricsLoaded() ) ); + disconnect( m_query.data(), SIGNAL( similarTracksLoaded() ), this, SLOT( onSimilarTracksLoaded() ) ); disconnect( m_query.data(), SIGNAL( statsLoaded() ), this, SLOT( onStatsLoaded() ) ); disconnect( m_query.data(), SIGNAL( updated() ), this, SLOT( onCoverUpdated() ) ); disconnect( m_artist.data(), SIGNAL( statsLoaded() ), this, SLOT( onStatsLoaded() ) ); @@ -123,22 +146,25 @@ TrackInfoWidget::load( const query_ptr& query ) connect( m_artist.data(), SIGNAL( similarArtistsLoaded() ), SLOT( onSimilarArtistsLoaded() ) ); connect( m_artist.data(), SIGNAL( statsLoaded() ), SLOT( onStatsLoaded() ) ); + connect( m_query.data(), SIGNAL( lyricsLoaded() ), SLOT( onLyricsLoaded() ) ); connect( m_query.data(), SIGNAL( similarTracksLoaded() ), SLOT( onSimilarTracksLoaded() ) ); connect( m_query.data(), SIGNAL( updated() ), SLOT( onCoverUpdated() ) ); connect( m_query.data(), SIGNAL( statsLoaded() ), SLOT( onStatsLoaded() ) ); m_artist->loadStats(); m_query->loadStats(); + m_query->lyrics(); onCoverUpdated(); ui->trackLabel->setText( query->track() ); - ui->artistLabel->setText( query->artist() ); - ui->albumLabel->setText( query->album() ); + ui->artistLabel->setQuery( query ); + ui->albumLabel->setQuery( query ); ui->fromLabel->setVisible( !query->album().isEmpty() ); - m_query->similarTracks(); - m_albumsModel->addArtists( m_artist->similarArtists() ); - m_albumsModel->clear(); + m_relatedTracksModel->clear(); + + if ( !m_query->similarTracks().isEmpty() ) + onSimilarTracksLoaded(); } @@ -171,12 +197,12 @@ TrackInfoWidget::onStatsLoaded() { stats += "\n" + tr( "You first listened to it on %1." ).arg( QDateTime::fromTime_t( history.first().timestamp ).toString( "dd MMM yyyy" ) ); } - + if ( artistCounter ) stats += "\n" + tr( "You've listened to %1 %n time(s).", "", artistCounter ).arg( m_artist->name() ); else stats += "\n" + tr( "You've never listened to %1 before." ).arg( m_artist->name() ); - + ui->statsLabel->setText( stats ); } @@ -184,16 +210,38 @@ TrackInfoWidget::onStatsLoaded() void TrackInfoWidget::onSimilarArtistsLoaded() { - Artist* artist = qobject_cast( sender() ); +/* Artist* artist = qobject_cast( sender() ); -// m_albumsModel->addArtists( artist->similarArtists() ); + m_relatedArtistsModel->addArtists( artist->similarArtists() );*/ } void TrackInfoWidget::onSimilarTracksLoaded() { - m_albumsModel->addQueries( m_query->similarTracks() ); + m_relatedTracksModel->append( m_query->similarTracks() ); +} + + +void +TrackInfoWidget::onLyricsLoaded() +{ + ui->lyricsView->setHtml( m_query->lyrics().join( "
" ) ); +} + + +void +TrackInfoWidget::onArtistClicked() +{ + ViewManager::instance()->show( Artist::get( m_query->artist(), false ) ); +} + + +void +TrackInfoWidget::onAlbumClicked() +{ + artist_ptr artist = Artist::get( m_query->artist(), false ); + ViewManager::instance()->show( Album::get( artist, m_query->album(), false ) ); } diff --git a/src/libtomahawk/widgets/infowidgets/TrackInfoWidget.h b/src/libtomahawk/widgets/infowidgets/TrackInfoWidget.h index d03968dcd..dc9c3de64 100644 --- a/src/libtomahawk/widgets/infowidgets/TrackInfoWidget.h +++ b/src/libtomahawk/widgets/infowidgets/TrackInfoWidget.h @@ -39,7 +39,7 @@ #include "DllMacro.h" #include "Typedefs.h" -class AlbumModel; +class PlayableModel; namespace Ui { @@ -80,6 +80,10 @@ private slots: void onStatsLoaded(); void onSimilarArtistsLoaded(); void onSimilarTracksLoaded(); + void onLyricsLoaded(); + + void onArtistClicked(); + void onAlbumClicked(); private: Ui::TrackInfoWidget *ui; @@ -87,10 +91,9 @@ private: Tomahawk::query_ptr m_query; Tomahawk::artist_ptr m_artist; - AlbumModel* m_albumsModel; + PlayableModel* m_relatedTracksModel; QString m_title; QPixmap m_pixmap; - QString m_infoId; }; #endif // TRACKINFOWIDGET_H diff --git a/src/libtomahawk/widgets/infowidgets/TrackInfoWidget.ui b/src/libtomahawk/widgets/infowidgets/TrackInfoWidget.ui index d6387b683..088f5b234 100644 --- a/src/libtomahawk/widgets/infowidgets/TrackInfoWidget.ui +++ b/src/libtomahawk/widgets/infowidgets/TrackInfoWidget.ui @@ -13,7 +13,7 @@ Form - + 0 @@ -83,7 +83,7 @@ - + Artist @@ -107,7 +107,7 @@ - + Album @@ -162,11 +162,43 @@ + + + + Lyrics + + + 8 + + + + + + + false + + + + + 0 + + + 0 + + + 4 + + + 0 + + + 0 + - + Similar Tracks @@ -176,7 +208,7 @@ - + 0 @@ -192,9 +224,19 @@ - AlbumView + GridView QListView -
playlist/AlbumView.h
+
playlist/GridView.h
+
+ + HeaderLabel + QLabel +
widgets/HeaderLabel.h
+
+ + QueryLabel + QLabel +
widgets/QueryLabel.h
diff --git a/src/mac/TomahawkApp_Mac.h b/src/mac/TomahawkApp_Mac.h index e0957177f..e258c6719 100644 --- a/src/mac/TomahawkApp_Mac.h +++ b/src/mac/TomahawkApp_Mac.h @@ -23,6 +23,7 @@ // copyright David Sansome 2010 class QString; +class QObject; namespace Tomahawk { @@ -43,7 +44,9 @@ void setShortcutHandler(Tomahawk::MacShortcutHandler* engine); // used for opening files with tomahawk void setApplicationHandler(PlatformInterface* handler); void checkForUpdates(); -void enableFullscreen(); + +// Pass in a QObject with slots "fullScreenEntered() and fullScreenExited() in order to be notified +void enableFullscreen( QObject* notifier ); }; diff --git a/src/mac/TomahawkApp_Mac.mm b/src/mac/TomahawkApp_Mac.mm index 6187fab4e..c9ab976a6 100644 --- a/src/mac/TomahawkApp_Mac.mm +++ b/src/mac/TomahawkApp_Mac.mm @@ -43,6 +43,8 @@ #include #include +#include +#include @interface MacApplication :NSApplication { AppDelegate* delegate_; @@ -164,14 +166,14 @@ NSAppleEventManager *em = [NSAppleEventManager sharedAppleEventManager]; [em setEventHandler:self - andSelector:@selector(getUrl:withReplyEvent:) - forEventClass:kInternetEventClass - andEventID:kAEGetURL]; + andSelector:@selector(getUrl:withReplyEvent:) + forEventClass:kInternetEventClass + andEventID:kAEGetURL]; [em setEventHandler:self - andSelector:@selector(getUrl:withReplyEvent:) - forEventClass:'WWW!' - andEventID:'OURL']; + andSelector:@selector(getUrl:withReplyEvent:) + forEventClass:'WWW!' + andEventID:'OURL']; NSString *bundleID = [[NSBundle mainBundle] bundleIdentifier]; OSStatus httpResult = LSSetDefaultHandlerForURLScheme((CFStringRef)@"tomahawk", (CFStringRef)bundleID); @@ -251,11 +253,15 @@ void Tomahawk::checkForUpdates() { #ifdef LION #define SET_LION_FULLSCREEN NSWindowCollectionBehaviorFullScreenPrimary +#define LION_FULLSCREEN_ENTER_NOTIFICATION_VALUE NSWindowWillEnterFullScreenNotification +#define LION_FULLSCREEN_EXIT_NOTIFICATION_VALUE NSWindowDidExitFullScreenNotification #else #define SET_LION_FULLSCREEN (NSUInteger)(1 << 7) // Defined as NSWindowCollectionBehaviorFullScreenPrimary in lion's NSWindow.h +#define LION_FULLSCREEN_ENTER_NOTIFICATION_VALUE @"NSWindowWillEnterFullScreenNotification" +#define LION_FULLSCREEN_EXIT_NOTIFICATION_VALUE @"NSWindowDidExitFullScreenNotification" #endif -void Tomahawk::enableFullscreen() +void Tomahawk::enableFullscreen( QObject* receiver ) { // We don't support anything below leopard, so if it's not [snow] leopard it must be lion // Can't check for lion as Qt 4.7 doesn't have the enum val, not checking for Unknown as it will be lion @@ -273,6 +279,24 @@ void Tomahawk::enableFullscreen() NSView *nsview = (NSView *)w->winId(); NSWindow *nswindow = [nsview window]; [nswindow setCollectionBehavior:SET_LION_FULLSCREEN]; + + if ( !receiver ) + continue; + + [[NSNotificationCenter defaultCenter] addObserverForName:LION_FULLSCREEN_ENTER_NOTIFICATION_VALUE + object:nswindow + queue:nil + usingBlock:^(NSNotification * note) { + NSLog(@"Became Full Screen!"); + QMetaObject::invokeMethod( receiver, "fullScreenEntered", Qt::DirectConnection ); + }]; + [[NSNotificationCenter defaultCenter] addObserverForName:LION_FULLSCREEN_EXIT_NOTIFICATION_VALUE + object:nswindow + queue:nil + usingBlock:^(NSNotification * note) { + NSLog(@"Left Full Screen!"); + QMetaObject::invokeMethod( receiver, "fullScreenExited", Qt::DirectConnection ); + }]; } } } diff --git a/src/main.cpp b/src/main.cpp index fe0332d1f..870bded10 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -23,8 +23,7 @@ #include "UbuntuUnityHack.h" #include "TomahawkSettings.h" #include "config.h" - -#include +#include "utils/Logger.h" #ifdef Q_WS_MAC #include "TomahawkApp_Mac.h" @@ -37,50 +36,53 @@ #include "breakpad/BreakPad.h" #endif + #ifdef Q_OS_WIN // code from patch attached to QTBUG-19064 by Honglei Zhang -LRESULT QT_WIN_CALLBACK qt_LowLevelKeyboardHookProc(int nCode, WPARAM wParam, LPARAM lParam); +LRESULT QT_WIN_CALLBACK qt_LowLevelKeyboardHookProc( int nCode, WPARAM wParam, LPARAM lParam ); HHOOK hKeyboardHook; HINSTANCE hGuiLibInstance; -LRESULT QT_WIN_CALLBACK qt_LowLevelKeyboardHookProc(int nCode, WPARAM wParam, LPARAM lParam) +LRESULT QT_WIN_CALLBACK qt_LowLevelKeyboardHookProc( int nCode, WPARAM wParam, LPARAM lParam ) { LPKBDLLHOOKSTRUCT kbHookStruct = reinterpret_cast(lParam); - switch(kbHookStruct->vkCode){ - case VK_VOLUME_MUTE: - case VK_VOLUME_DOWN: - case VK_VOLUME_UP: - case VK_MEDIA_NEXT_TRACK: - case VK_MEDIA_PREV_TRACK: - case VK_MEDIA_STOP: - case VK_MEDIA_PLAY_PAUSE: - case VK_LAUNCH_MEDIA_SELECT: - // send message - { - HWND hWnd = NULL; - foreach (QWidget *widget, QApplication::topLevelWidgets()) { - // relay message to each top level widgets(window) - // if the window has focus, we don't send a duplicate message - if(QApplication::activeWindow() == widget){ - continue; + switch(kbHookStruct->vkCode) + { + case VK_VOLUME_MUTE: + case VK_VOLUME_DOWN: + case VK_VOLUME_UP: + case VK_MEDIA_NEXT_TRACK: + case VK_MEDIA_PREV_TRACK: + case VK_MEDIA_STOP: + case VK_MEDIA_PLAY_PAUSE: + case VK_LAUNCH_MEDIA_SELECT: + // send message + { + HWND hWnd = NULL; + foreach ( QWidget *widget, QApplication::topLevelWidgets() ) + { + // relay message to each top level widgets(window) + // if the window has focus, we don't send a duplicate message + if ( QApplication::activeWindow() == widget ) + continue; + + hWnd = widget->winId(); + + // generate message and post it to the message queue + LPKBDLLHOOKSTRUCT pKeyboardHookStruct = reinterpret_cast(lParam); + WPARAM _wParam = pKeyboardHookStruct->vkCode; + LPARAM _lParam = MAKELPARAM( pKeyboardHookStruct->scanCode, pKeyboardHookStruct->flags ); + PostMessage( hWnd, wParam, _wParam, _lParam ); } - - hWnd = widget->winId(); - - // generate message and post it to the message queue - LPKBDLLHOOKSTRUCT pKeyboardHookStruct = reinterpret_cast(lParam); - WPARAM _wParam = pKeyboardHookStruct->vkCode; - LPARAM _lParam = MAKELPARAM(pKeyboardHookStruct->scanCode, pKeyboardHookStruct->flags); - PostMessage(hWnd, wParam, _wParam, _lParam); } - } - break; - default: - break; + break; + + default: + break; } - return CallNextHookEx(0, nCode, wParam, lParam); + return CallNextHookEx( 0, nCode, wParam, lParam ); } #include @@ -88,32 +90,29 @@ LRESULT QT_WIN_CALLBACK qt_LowLevelKeyboardHookProc(int nCode, WPARAM wParam, LP #define argv __argv // code taken from AbiWord, (c) AbiSource Inc. -int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, - PSTR szCmdLine, int iCmdShow) +int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow ) { hKeyboardHook = NULL; hGuiLibInstance = hInstance; - // setup keyboard hook to receive multimedia key events when application is at background - hKeyboardHook = SetWindowsHookEx(WH_KEYBOARD_LL,(HOOKPROC) qt_LowLevelKeyboardHookProc, hGuiLibInstance, 0); + hKeyboardHook = SetWindowsHookEx( WH_KEYBOARD_LL,(HOOKPROC) qt_LowLevelKeyboardHookProc, hGuiLibInstance, 0 ); - if (fileno (stdout) != -1 && _get_osfhandle (fileno (stdout)) != -1) + if ( fileno( stdout ) != -1 && _get_osfhandle( fileno( stdout ) ) != -1 ) { /* stdout is fine, presumably redirected to a file or pipe */ } else { typedef BOOL (WINAPI * AttachConsole_t) (DWORD); + AttachConsole_t p_AttachConsole = (AttachConsole_t) GetProcAddress( GetModuleHandleW( L"kernel32.dll" ), "AttachConsole" ); - AttachConsole_t p_AttachConsole = (AttachConsole_t) GetProcAddress (GetModuleHandleW(L"kernel32.dll"), "AttachConsole"); - - if (p_AttachConsole != NULL && p_AttachConsole (ATTACH_PARENT_PROCESS)) + if ( p_AttachConsole != NULL && p_AttachConsole( ATTACH_PARENT_PROCESS ) ) { - _wfreopen (L"CONOUT$", L"w", stdout); - dup2 (fileno (stdout), 1); - _wfreopen (L"CONOUT$", L"w", stderr); - dup2 (fileno (stderr), 2); + _wfreopen ( L"CONOUT$", L"w", stdout ); + dup2( fileno( stdout ), 1 ); + _wfreopen ( L"CONOUT$", L"w", stderr ); + dup2( fileno( stderr ), 2 ); } } #else // Q_OS_WIN @@ -121,7 +120,7 @@ int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, int main( int argc, char *argv[] ) { - #ifdef Q_WS_MAC +#ifdef Q_WS_MAC // Do Mac specific startup to get media keys working. // This must go before QApplication initialisation. Tomahawk::macMain(); @@ -144,34 +143,18 @@ main( int argc, char *argv[] ) new TomahawkSettingsGui( &a ); #endif - #ifndef ENABLE_HEADLESSs #ifdef WITH_BREAKPAD new BreakPad( QDir::tempPath(), TomahawkSettings::instance()->crashReporterEnabled() ); #endif #endif - KDSingleApplicationGuard guard( &a, KDSingleApplicationGuard::AutoKillOtherInstances ); + KDSingleApplicationGuard guard( KDSingleApplicationGuard::AutoKillOtherInstances ); QObject::connect( &guard, SIGNAL( instanceStarted( KDSingleApplicationGuard::Instance ) ), &a, SLOT( instanceStarted( KDSingleApplicationGuard::Instance ) ) ); if ( guard.isPrimaryInstance() ) a.init(); - QString locale = QLocale::system().name(); - if ( locale == "C" ) - locale = "en"; - QTranslator translator; - if ( translator.load( QString( ":/lang/tomahawk_" ) + locale ) ) - { - tDebug() << "Using system locale:" << locale; - } - else - { - tDebug() << "Using default locale, system locale one not found:" << locale; - translator.load( QString( ":/lang/tomahawk_en" ) ); - } - a.installTranslator( &translator ); - if ( argc > 1 ) { QString arg = a.arguments()[ 1 ]; @@ -181,12 +164,13 @@ main( int argc, char *argv[] ) int returnCode = a.exec(); #ifdef Q_OS_WIN // clean up keyboard hook - if( hKeyboardHook ) + if ( hKeyboardHook ) { - UnhookWindowsHookEx(hKeyboardHook); + UnhookWindowsHookEx( hKeyboardHook ); hKeyboardHook = NULL; } #endif + return returnCode; } diff --git a/src/sourcetree/SourceDelegate.cpp b/src/sourcetree/SourceDelegate.cpp index e7ba57897..1f434a924 100644 --- a/src/sourcetree/SourceDelegate.cpp +++ b/src/sourcetree/SourceDelegate.cpp @@ -145,10 +145,13 @@ SourceDelegate::paintDecorations( QPainter* painter, const QStyleOptionViewItem& void SourceDelegate::paintCollection( QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index ) const { - QFont normal = painter->font(); - QFont bold = painter->font(); + QFont normal = option.font; + QFont bold = option.font; bold.setBold( true ); + QFont figFont = bold; + figFont.setFamily( "Arial Bold" ); + figFont.setWeight( QFont::Black ); figFont.setPixelSize( 10 ); SourceTreeItem* item = index.data( SourcesModel::SourceTreeItemRole ).value< SourceTreeItem* >(); @@ -183,17 +186,10 @@ SourceDelegate::paintCollection( QPainter* painter, const QStyleOptionViewItem& QString text = painter->fontMetrics().elidedText( name, Qt::ElideRight, textRect.width() ); painter->drawText( textRect, text ); - bool isPlaying = false; - QString desc = status ? colItem->source()->textStatus() : tr( "Offline" ); + bool isPlaying = !( colItem->source()->currentTrack().isNull() ); + QString desc = colItem->source()->textStatus(); if ( colItem->source().isNull() ) desc = tr( "All available tracks" ); - if ( status && desc.isEmpty() && !colItem->source()->currentTrack().isNull() ) - { - desc = colItem->source()->currentTrack()->artist() + " - " + colItem->source()->currentTrack()->track(); - isPlaying = true; - } - if ( desc.isEmpty() ) - desc = tr( "Online" ); painter->setFont( normal ); textRect = option.rect.adjusted( iconRect.width() + 8, option.rect.height() / 2, -figWidth - 24, -6 ); @@ -218,7 +214,7 @@ SourceDelegate::paintCollection( QPainter* painter, const QStyleOptionViewItem& if ( !colItem->source()->isLocal() ) { realtimeListeningAlongPixmap = - colItem->source()->playlistInterface()->latchMode() == Tomahawk::PlaylistInterface::RealTime ? + colItem->source()->playlistInterface()->latchMode() == Tomahawk::PlaylistModes::RealTime ? m_realtimeLocked : m_realtimeUnlocked; } } @@ -254,7 +250,7 @@ SourceDelegate::paintCollection( QPainter* painter, const QStyleOptionViewItem& { painter->setRenderHint( QPainter::Antialiasing ); - QRect figRect = option.rect.adjusted( option.rect.width() - figWidth - 8, 0, -13, -option.rect.height() + 16 ); + QRect figRect = option.rect.adjusted( option.rect.width() - figWidth - 13, 0, -14, -option.rect.height() + 16 ); int hd = ( option.rect.height() - figRect.height() ) / 2; figRect.adjust( 0, hd, 0, hd ); diff --git a/src/sourcetree/SourceTreeView.cpp b/src/sourcetree/SourceTreeView.cpp index 3e43c36b6..fb2dfbd7b 100644 --- a/src/sourcetree/SourceTreeView.cpp +++ b/src/sourcetree/SourceTreeView.cpp @@ -26,8 +26,9 @@ #include #include #include -#include #include +#include +#include #include "ActionCollection.h" #include "Playlist.h" @@ -42,12 +43,13 @@ #include "TomahawkSettings.h" #include "GlobalActionManager.h" #include "DropJob.h" -#include "utils/Logger.h" #include "items/GenericPageItems.h" #include "items/TemporaryPageItem.h" #include "database/DatabaseCommand_SocialAction.h" #include "database/Database.h" #include "LatchManager.h" +#include "utils/TomahawkUtilsGui.h" +#include "utils/Logger.h" using namespace Tomahawk; @@ -109,7 +111,7 @@ SourceTreeView::SourceTreeView( QWidget* parent ) header()->setResizeMode( 0, QHeaderView::Stretch ); connect( this, SIGNAL( expanded( QModelIndex ) ), SLOT( onItemExpanded( QModelIndex ) ) ); -// connect( selectionModel(), SIGNAL( selectionChanged( QItemSelection, QItemSelection ) ), SLOT( onSelectionChanged() ) ); + connect( selectionModel(), SIGNAL( selectionChanged( QItemSelection, QItemSelection ) ), SLOT( onSelectionChanged() ) ); showOfflineSources( TomahawkSettings::instance()->showOfflineSources() ); @@ -173,7 +175,7 @@ SourceTreeView::setupMenus() connect( latchOffAction, SIGNAL( triggered() ), SLOT( latchOff() ) ); m_latchMenu.addSeparator(); QAction *latchRealtimeAction = ActionCollection::instance()->getAction( "realtimeFollowingAlong" ); - latchRealtimeAction->setChecked( source->playlistInterface()->latchMode() == Tomahawk::PlaylistInterface::RealTime ); + latchRealtimeAction->setChecked( source->playlistInterface()->latchMode() == Tomahawk::PlaylistModes::RealTime ); m_latchMenu.addAction( latchRealtimeAction ); connect( latchRealtimeAction, SIGNAL( toggled( bool ) ), SLOT( latchModeToggled( bool ) ) ); } @@ -189,15 +191,15 @@ SourceTreeView::setupMenus() QAction *copyPlaylistAction = m_playlistMenu.addAction( tr( "&Copy Link" ) ); QAction *deletePlaylistAction = m_playlistMenu.addAction( tr( "&Delete %1" ).arg( SourcesModel::rowTypeToString( type ) ) ); - QString addToText = QString( "Add to my %1" ); + QString addToText; if ( type == SourcesModel::StaticPlaylist ) - addToText = addToText.arg( "playlists" ); + addToText = tr( "Add to my Playlists" ); if ( type == SourcesModel::AutomaticPlaylist ) - addToText = addToText.arg( "Automatic Playlists" ); + addToText = tr( "Add to my Automatic Playlists" ); else if ( type == SourcesModel::Station ) - addToText = addToText.arg( "Stations" ); + addToText = tr( "Add to my Stations" ); - QAction *addToLocalAction = m_roPlaylistMenu.addAction( tr( addToText.toUtf8(), "Adds the given playlist, dynamic playlist, or station to the users's own list" ) ); + QAction *addToLocalAction = m_roPlaylistMenu.addAction( addToText ); m_roPlaylistMenu.addAction( copyPlaylistAction ); deletePlaylistAction->setEnabled( !readonly ); @@ -210,7 +212,6 @@ SourceTreeView::setupMenus() { m_playlistMenu.addSeparator(); - const PlaylistItem* item = itemFromIndex< PlaylistItem >( m_contextMenuIndex ); const playlist_ptr playlist = item->playlist(); foreach ( QAction* action, ActionCollection::instance()->getAction( ActionCollection::LocalPlaylists ) ) @@ -225,7 +226,6 @@ SourceTreeView::setupMenus() } } - if ( type == SourcesModel::StaticPlaylist ) copyPlaylistAction->setText( tr( "&Export Playlist" ) ); @@ -245,6 +245,18 @@ SourceTreeView::showOfflineSources( bool offlineSourcesShown ) } +void +SourceTreeView::onSelectionChanged() +{ + if ( currentIndex() != m_selectedIndex ) + { + selectionModel()->blockSignals( true ); + selectionModel()->select( m_selectedIndex, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Current ); + selectionModel()->blockSignals( false ); + } +} + + void SourceTreeView::onItemActivated( const QModelIndex& index ) { @@ -271,7 +283,7 @@ void SourceTreeView::onItemExpanded( const QModelIndex& idx ) { // make sure to expand children nodes for collections - if( idx.data( SourcesModel::SourceTreeItemTypeRole ) == SourcesModel::Collection ) + if ( idx.data( SourcesModel::SourceTreeItemTypeRole ) == SourcesModel::Collection ) { for( int i = 0; i < model()->rowCount( idx ); i++ ) { @@ -284,6 +296,8 @@ SourceTreeView::onItemExpanded( const QModelIndex& idx ) void SourceTreeView::selectRequest( const QPersistentModelIndex& idx ) { + m_selectedIndex = idx; + if ( !selectionModel()->selectedIndexes().contains( idx ) ) { scrollTo( idx, QTreeView::EnsureVisible ); @@ -319,20 +333,46 @@ SourceTreeView::loadPlaylist() void SourceTreeView::deletePlaylist( const QModelIndex& idxIn ) { - qDebug() << Q_FUNC_INFO; - QModelIndex idx = idxIn.isValid() ? idxIn : m_contextMenuIndex; if ( !idx.isValid() ) return; SourcesModel::RowType type = ( SourcesModel::RowType )model()->data( idx, SourcesModel::SourceTreeItemTypeRole ).toInt(); + QString typeDesc; + switch ( type ) + { + case SourcesModel::StaticPlaylist: + typeDesc = tr( "playlist" ); + break; + + case SourcesModel::AutomaticPlaylist: + typeDesc = tr( "automatic playlist" ); + break; + + case SourcesModel::Station: + typeDesc = tr( "station" ); + break; + + default: + Q_ASSERT( false ); + } + + QMessageBox askDelete( QMessageBox::Question, tr( "Delete %1?", "playlist/station/..." ).arg( typeDesc ), + tr( "Would you like to delete the %1 \"%2\"?", "e.g. Would you like to delete the playlist named Foobar?" ) + .arg( typeDesc ).arg( idx.data().toString() ), + QMessageBox::Yes | QMessageBox::No, this ); + + int r = askDelete.exec(); + if ( r != QMessageBox::Yes ) + return; + if ( type == SourcesModel::StaticPlaylist ) { PlaylistItem* item = itemFromIndex< PlaylistItem >( idx ); playlist_ptr playlist = item->playlist(); Playlist::remove( playlist ); } - else if( type == SourcesModel::AutomaticPlaylist || type == SourcesModel::Station ) + else if ( type == SourcesModel::AutomaticPlaylist || type == SourcesModel::Station ) { DynamicPlaylistItem* item = itemFromIndex< DynamicPlaylistItem >( idx ); dynplaylist_ptr playlist = item->dynPlaylist(); @@ -349,7 +389,7 @@ SourceTreeView::copyPlaylistLink() return; SourcesModel::RowType type = ( SourcesModel::RowType )model()->data( m_contextMenuIndex, SourcesModel::SourceTreeItemTypeRole ).toInt(); - if( type == SourcesModel::AutomaticPlaylist || type == SourcesModel::Station ) + if ( type == SourcesModel::AutomaticPlaylist || type == SourcesModel::Station ) { DynamicPlaylistItem* item = itemFromIndex< DynamicPlaylistItem >( m_contextMenuIndex ); dynplaylist_ptr playlist = item->dynPlaylist(); @@ -357,7 +397,6 @@ SourceTreeView::copyPlaylistLink() } else if ( type == SourcesModel::StaticPlaylist ) { - // Disable toma.hk playlist mode until ready // GlobalActionManager::instance()->getShortLink( playlist ); @@ -385,7 +424,7 @@ SourceTreeView::addToLocal() return; SourcesModel::RowType type = ( SourcesModel::RowType )model()->data( m_contextMenuIndex, SourcesModel::SourceTreeItemTypeRole ).toInt(); - if( type == SourcesModel::AutomaticPlaylist || type == SourcesModel::Station ) + if ( type == SourcesModel::AutomaticPlaylist || type == SourcesModel::Station ) { DynamicPlaylistItem* item = itemFromIndex< DynamicPlaylistItem >( m_contextMenuIndex ); dynplaylist_ptr playlist = item->dynPlaylist(); @@ -418,7 +457,7 @@ SourceTreeView::latchOnOrCatchUp() return; SourcesModel::RowType type = ( SourcesModel::RowType )model()->data( m_contextMenuIndex, SourcesModel::SourceTreeItemTypeRole ).toInt(); - if( type != SourcesModel::Collection ) + if ( type != SourcesModel::Collection ) return; SourceItem* item = itemFromIndex< SourceItem >( m_contextMenuIndex ); @@ -437,7 +476,7 @@ SourceTreeView::latchOff() return; SourcesModel::RowType type = ( SourcesModel::RowType )model()->data( m_contextMenuIndex, SourcesModel::SourceTreeItemTypeRole ).toInt(); - if( type != SourcesModel::Collection ) + if ( type != SourcesModel::Collection ) return; const SourceItem* item = itemFromIndex< SourceItem >( m_contextMenuIndex ); @@ -474,7 +513,7 @@ SourceTreeView::latchModeToggled( bool checked ) return; SourcesModel::RowType type = ( SourcesModel::RowType )model()->data( m_contextMenuIndex, SourcesModel::SourceTreeItemTypeRole ).toInt(); - if( type != SourcesModel::Collection ) + if ( type != SourcesModel::Collection ) return; const SourceItem* item = itemFromIndex< SourceItem >( m_contextMenuIndex ); @@ -486,7 +525,7 @@ SourceTreeView::latchModeToggled( bool checked ) void SourceTreeView::renamePlaylist() { - if( !m_contextMenuIndex.isValid() && !selectionModel()->selectedIndexes().isEmpty() ) + if ( !m_contextMenuIndex.isValid() && !selectionModel()->selectedIndexes().isEmpty() ) edit( selectionModel()->selectedIndexes().first() ); else edit( m_contextMenuIndex ); @@ -509,7 +548,7 @@ SourceTreeView::onCustomContextMenu( const QPoint& pos ) model()->data( m_contextMenuIndex, SourcesModel::SourceTreeItemTypeRole ) == SourcesModel::Station ) { PlaylistItem* item = itemFromIndex< PlaylistItem >( m_contextMenuIndex ); - if( item->playlist()->author()->isLocal() ) + if ( item->playlist()->author()->isLocal() ) m_playlistMenu.exec( mapToGlobal( pos ) ); else m_roPlaylistMenu.exec( mapToGlobal( pos ) ); @@ -586,7 +625,7 @@ SourceTreeView::dragMoveEvent( QDragMoveEvent* event ) m_dropRect = rect; SourceTreeItem* item = itemFromIndex< SourceTreeItem >( index ); - if( item->willAcceptDrag( event->mimeData() ) ) + if ( item->willAcceptDrag( event->mimeData() ) ) { accept = true; @@ -674,7 +713,7 @@ SourceTreeView::dropEvent( QDropEvent* event ) void SourceTreeView::keyPressEvent( QKeyEvent *event ) { - if( ( event->key() == Qt::Key_Delete || event->key() == Qt::Key_Backspace ) && !selectionModel()->selectedIndexes().isEmpty() ) + if ( ( event->key() == Qt::Key_Delete || event->key() == Qt::Key_Backspace ) && !selectionModel()->selectedIndexes().isEmpty() ) { QModelIndex idx = selectionModel()->selectedIndexes().first(); if ( model()->data( idx, SourcesModel::SourceTreeItemTypeRole ) == SourcesModel::StaticPlaylist || @@ -684,11 +723,16 @@ SourceTreeView::keyPressEvent( QKeyEvent *event ) PlaylistItem* item = itemFromIndex< PlaylistItem >( idx ); Q_ASSERT( item ); - if( item->playlist()->author()->isLocal() ) { + if ( item->playlist()->author()->isLocal() ) deletePlaylist( idx ); - } } + event->accept(); } + else + { + event->ignore(); + } + QTreeView::keyPressEvent( event ); } diff --git a/src/sourcetree/SourceTreeView.h b/src/sourcetree/SourceTreeView.h index f9f71af47..388649d3c 100644 --- a/src/sourcetree/SourceTreeView.h +++ b/src/sourcetree/SourceTreeView.h @@ -81,6 +81,7 @@ private slots: void latchModeToggled( bool checked ); void onCustomContextMenu( const QPoint& pos ); + void onSelectionChanged(); protected: void drawRow( QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index ) const; @@ -113,6 +114,8 @@ private: bool m_dragging; QRect m_dropRect; QPersistentModelIndex m_dropIndex; + + QPersistentModelIndex m_selectedIndex; }; #endif // SOURCETREEVIEW_H diff --git a/src/sourcetree/SourcesModel.cpp b/src/sourcetree/SourcesModel.cpp index 607955127..1b532e3b8 100644 --- a/src/sourcetree/SourcesModel.cpp +++ b/src/sourcetree/SourcesModel.cpp @@ -40,7 +40,7 @@ #include "GlobalActionManager.h" #include "DropJob.h" #include "items/PlaylistItems.h" -#include "playlist/ArtistView.h" +#include "playlist/TreeView.h" #include "playlist/PlaylistView.h" #include "playlist/dynamic/widgets/DynamicWidget.h" @@ -101,41 +101,48 @@ SourcesModel::data( const QModelIndex& index, int role ) const if ( !index.isValid() ) return QVariant(); + SourceTreeItem* item = itemFromIndex( index ); + if ( !item ) + return QVariant(); + switch ( role ) { case Qt::SizeHintRole: return QSize( 0, 18 ); case SourceTreeItemRole: - return QVariant::fromValue< SourceTreeItem* >( itemFromIndex( index ) ); + return QVariant::fromValue< SourceTreeItem* >( item ); case SourceTreeItemTypeRole: - return itemFromIndex( index )->type(); + return item->type(); case Qt::DisplayRole: case Qt::EditRole: - return itemFromIndex( index )->text(); + return item->text(); case Qt::DecorationRole: - return itemFromIndex( index )->icon(); + return item->icon(); case SourcesModel::SortRole: - return itemFromIndex( index )->peerSortValue(); + return item->peerSortValue(); case SourcesModel::IDRole: - return itemFromIndex( index )->IDValue(); + return item->IDValue(); case SourcesModel::LatchedOnRole: { - if ( itemFromIndex( index )->type() == Collection ) + if ( item->type() == Collection ) { - SourceItem* cItem = qobject_cast< SourceItem* >( itemFromIndex( index ) ); + SourceItem* cItem = qobject_cast< SourceItem* >( item ); return cItem->localLatchedOn(); } return false; } case SourcesModel::LatchedRealtimeRole: { - if ( itemFromIndex( index )->type() == Collection ) + if ( item->type() == Collection ) { - SourceItem* cItem = qobject_cast< SourceItem* >( itemFromIndex( index ) ); - return cItem->localLatchMode() == Tomahawk::PlaylistInterface::RealTime; + SourceItem* cItem = qobject_cast< SourceItem* >( item ); + return cItem->localLatchMode() == Tomahawk::PlaylistModes::RealTime; } return false; } + case Qt::ToolTipRole: + if ( !item->tooltip().isEmpty() ) + return item->tooltip(); } return QVariant(); } @@ -549,7 +556,7 @@ SourcesModel::linkSourceItemToPage( SourceTreeItem* item, ViewPage* p ) // TODO handle removal m_sourceTreeLinks[ p ] = item; - if( m_viewPageDelayedCacheItem == p ) + if ( p && m_viewPageDelayedCacheItem == p ) emit selectRequest( QPersistentModelIndex( indexFromItem( item ) ) ); if ( QObject* obj = dynamic_cast< QObject* >( p ) ) diff --git a/src/sourcetree/items/CategoryItems.cpp b/src/sourcetree/items/CategoryItems.cpp index d01cc763d..7ef745226 100644 --- a/src/sourcetree/items/CategoryItems.cpp +++ b/src/sourcetree/items/CategoryItems.cpp @@ -28,7 +28,7 @@ #include "SourceTreeView.h" #include "utils/TomahawkUtils.h" #include "widgets/NewPlaylistWidget.h" -#include "widgets/PlaylistTypeSelectorDialog.h" +#include "TomahawkWindow.h" #include #include "utils/Logger.h" #include "DropJob.h" @@ -56,11 +56,13 @@ CategoryAddItem::~CategoryAddItem() QString CategoryAddItem::text() const { - switch( m_categoryType ) { + switch( m_categoryType ) + { case SourcesModel::PlaylistsCategory: - return tr( "New Playlist" ); + return tr( "Create new Playlist" ); + case SourcesModel::StationsCategory: - return tr( "New Station" ); + return tr( "Create new Station" ); } return QString(); @@ -72,46 +74,16 @@ CategoryAddItem::activate() { switch( m_categoryType ) { - case SourcesModel::PlaylistsCategory: { - - PlaylistTypeSelectorDlg* playlistSelectorDlg = new PlaylistTypeSelectorDlg( TomahawkApp::instance()->mainWindow(), Qt::Sheet ); -#ifndef Q_WS_MAC - playlistSelectorDlg->setModal( true ); -#endif - connect( playlistSelectorDlg, SIGNAL( finished( int ) ), this, SLOT( dialogClosed( int ) ) ); - - playlistSelectorDlg->show(); + case SourcesModel::PlaylistsCategory: + APP->mainWindow()->createPlaylist(); break; - } + case SourcesModel::StationsCategory: APP->mainWindow()->createStation(); break; } } -void -CategoryAddItem::dialogClosed( int ret ) -{ - PlaylistTypeSelectorDlg* playlistSelectorDlg = qobject_cast< PlaylistTypeSelectorDlg* >( sender() ); - Q_ASSERT( playlistSelectorDlg ); - - QString playlistName = playlistSelectorDlg->playlistName(); - if ( playlistName.isEmpty() ) - playlistName = tr( "New Playlist" ); - - if ( !playlistSelectorDlg->playlistTypeIsAuto() && ret ) { - - playlist_ptr playlist = Tomahawk::Playlist::create( SourceList::instance()->getLocal(), uuid(), playlistName, "", "", false, QList< query_ptr>() ); - ViewManager::instance()->show( playlist ); - - } else if ( playlistSelectorDlg->playlistTypeIsAuto() && ret ) { - // create Auto Playlist - APP->mainWindow()->createAutomaticPlaylist( playlistName ); - } else if ( !ret ) { - model()->viewPageActivated( ViewManager::instance()->currentPage() ); - } - playlistSelectorDlg->deleteLater(); -} Qt::ItemFlags CategoryAddItem::flags() const @@ -120,8 +92,10 @@ CategoryAddItem::flags() const { case SourcesModel::PlaylistsCategory: return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDropEnabled; + case SourcesModel::StationsCategory: return Qt::ItemIsEnabled | Qt::ItemIsDropEnabled; + default: return Qt::ItemIsEnabled; break; @@ -143,9 +117,11 @@ CategoryAddItem::willAcceptDrag( const QMimeData* data ) const { return true; } + return false; } + SourceTreeItem::DropTypes CategoryAddItem::supportedDropTypes( const QMimeData* data ) const { @@ -283,6 +259,7 @@ CategoryAddItem::dropMimeData( const QMimeData* data, Qt::DropAction ) return true; } + void CategoryAddItem::playlistToRenameLoaded() { diff --git a/src/sourcetree/items/CategoryItems.h b/src/sourcetree/items/CategoryItems.h index 6c03d6d5f..85db4578f 100644 --- a/src/sourcetree/items/CategoryItems.h +++ b/src/sourcetree/items/CategoryItems.h @@ -40,10 +40,10 @@ public: private slots: void parsedDroppedTracks( const QList< Tomahawk::query_ptr >& tracks ); - void dialogClosed( int ret ); // Do the rename only after the revision is loaded void playlistToRenameLoaded(); + private: SourcesModel::CategoryType m_categoryType; QIcon m_icon; diff --git a/src/sourcetree/items/PlaylistItems.cpp b/src/sourcetree/items/PlaylistItems.cpp index f7b37ac5a..24780ce16 100644 --- a/src/sourcetree/items/PlaylistItems.cpp +++ b/src/sourcetree/items/PlaylistItems.cpp @@ -251,18 +251,10 @@ PlaylistItem::parsedDroppedTracks( const QList< query_ptr >& tracks ) void PlaylistItem::onUpdated() { - // No work todo - if ( !m_overlaidIcon.isNull() && m_overlaidUpdaters.operator==( m_playlist->updaters() ) ) - { - emit updated(); - return; - } - const bool newOverlay = createOverlay(); if ( !newOverlay && !m_overlaidIcon.isNull() ) m_overlaidIcon = QIcon(); - emit updated(); } @@ -278,7 +270,7 @@ PlaylistItem::createOverlay() QList< QPixmap > icons; foreach ( PlaylistUpdaterInterface* updater, m_playlist->updaters() ) { - if ( !updater->typeIcon().isNull() ) + if ( updater->sync() && !updater->typeIcon().isNull() ) icons << updater->typeIcon(); } diff --git a/src/sourcetree/items/SourceItem.cpp b/src/sourcetree/items/SourceItem.cpp index 1a4caa914..4847d8512 100644 --- a/src/sourcetree/items/SourceItem.cpp +++ b/src/sourcetree/items/SourceItem.cpp @@ -28,7 +28,6 @@ #include "utils/Logger.h" #include "widgets/SocialPlaylistWidget.h" #include "playlist/CustomPlaylistView.h" -#include "playlist/CollectionView.h" #include "playlist/PlaylistView.h" #include "playlist/RecentlyAddedModel.h" #include "playlist/RecentlyPlayedModel.h" @@ -105,8 +104,8 @@ SourceItem::SourceItem( SourcesModel* mdl, SourceTreeItem* parent, const Tomahaw onStationsAdded( stations ); } - if ( ViewManager::instance()->pageForCollection( source->collection() ) ) - model()->linkSourceItemToPage( this, ViewManager::instance()->pageForCollection( source->collection() ) ); +/* if ( ViewManager::instance()->pageForCollection( source->collection() ) ) + model()->linkSourceItemToPage( this, ViewManager::instance()->pageForCollection( source->collection() ) );*/ m_defaultAvatar = TomahawkUtils::createAvatarFrame( QPixmap( RESPATH "images/user-avatar.png" ) ); @@ -114,7 +113,6 @@ SourceItem::SourceItem( SourcesModel* mdl, SourceTreeItem* parent, const Tomahaw connect( source.data(), SIGNAL( stats( QVariantMap ) ), SIGNAL( updated() ) ); connect( source.data(), SIGNAL( syncedWithDatabase() ), SIGNAL( updated() ) ); - connect( source.data(), SIGNAL( playbackStarted( Tomahawk::query_ptr ) ), SIGNAL( updated() ) ); connect( source.data(), SIGNAL( stateChanged() ), SIGNAL( updated() ) ); connect( source.data(), SIGNAL( offline() ), SIGNAL( updated() ) ); connect( source.data(), SIGNAL( online() ), SIGNAL( updated() ) ); @@ -147,6 +145,16 @@ SourceItem::text() const } +QString +SourceItem::tooltip() const +{ + if ( !m_source.isNull() && !m_source->currentTrack().isNull() ) + return m_source->textStatus(); + + return QString(); +} + + int SourceItem::IDValue() const { @@ -212,13 +220,13 @@ SourceItem::localLatchedOn() const } -Tomahawk::PlaylistInterface::LatchMode +Tomahawk::PlaylistModes::LatchMode SourceItem::localLatchMode() const { if ( !m_source.isNull() && !m_source->isLocal() ) return m_source->playlistInterface()->latchMode(); - return Tomahawk::PlaylistInterface::StayOnSong; + return Tomahawk::PlaylistModes::StayOnSong; } @@ -228,7 +236,7 @@ SourceItem::latchedOff( const source_ptr& from, const source_ptr& to ) if ( from->isLocal() && ( m_source == to || m_source == from ) ) { m_latchedOn = false; - disconnect( m_latchedOnTo->playlistInterface().data(), SIGNAL( latchModeChanged( Tomahawk::PlaylistInterface::LatchMode ) ) ); + disconnect( m_latchedOnTo->playlistInterface().data(), SIGNAL( latchModeChanged( Tomahawk::PlaylistModes::LatchMode ) ) ); m_latchedOnTo.clear(); emit updated(); } @@ -242,14 +250,14 @@ SourceItem::latchedOn( const source_ptr& from, const source_ptr& to ) { m_latchedOn = true; m_latchedOnTo = to; - connect( m_latchedOnTo->playlistInterface().data(), SIGNAL( latchModeChanged( Tomahawk::PlaylistInterface::LatchMode ) ), SLOT( latchModeChanged( Tomahawk::PlaylistInterface::LatchMode ) ) ); + connect( m_latchedOnTo->playlistInterface().data(), SIGNAL( latchModeChanged( Tomahawk::PlaylistModes::LatchMode ) ), SLOT( latchModeChanged( Tomahawk::PlaylistModes::LatchMode ) ) ); emit updated(); } } void -SourceItem::latchModeChanged( Tomahawk::PlaylistInterface::LatchMode mode ) +SourceItem::latchModeChanged( Tomahawk::PlaylistModes::LatchMode mode ) { Q_UNUSED( mode ); emit updated(); @@ -507,6 +515,7 @@ SourceItem::lovedTracksClicked() PlaylistLargeItemDelegate* del = new PlaylistLargeItemDelegate( PlaylistLargeItemDelegate::LovedTracks, view, view->proxyModel() ); connect( del, SIGNAL( updateIndex( QModelIndex ) ), view, SLOT( update( QModelIndex ) ) ); view->setItemDelegate( del ); + view->setEmptyTip( tr( "Sorry, we could not find any loved tracks!" ) ); m_lovedTracksPage = view; } @@ -528,14 +537,14 @@ SourceItem::latestAdditionsClicked() { if ( !m_latestAdditionsPage ) { - CollectionView* cv = new CollectionView( ViewManager::instance()->widget() ); + TrackView* cv = new TrackView( ViewManager::instance()->widget() ); cv->setFrameShape( QFrame::NoFrame ); cv->setAttribute( Qt::WA_MacShowFocusRect, 0 ); RecentlyAddedModel* raModel = new RecentlyAddedModel( m_source, cv ); - raModel->setStyle( TrackModel::Large ); + raModel->setStyle( PlayableModel::Large ); raModel->setTitle( tr( "Latest Additions" ) ); - + if ( m_source->isLocal() ) raModel->setDescription( tr( "Latest additions to your collection" ) ); else @@ -545,8 +554,9 @@ SourceItem::latestAdditionsClicked() connect( del, SIGNAL( updateIndex( QModelIndex ) ), cv, SLOT( update( QModelIndex ) ) ); cv->setItemDelegate( del ); - cv->setTrackModel( raModel ); - cv->sortByColumn( TrackModel::Age, Qt::DescendingOrder ); + cv->setPlayableModel( raModel ); + cv->sortByColumn( PlayableModel::Age, Qt::DescendingOrder ); + cv->setEmptyTip( tr( "Sorry, we could not find any recent additions!" ) ); m_latestAdditionsPage = cv; } @@ -573,7 +583,7 @@ SourceItem::recentPlaysClicked() pv->setAttribute( Qt::WA_MacShowFocusRect, 0 ); RecentlyPlayedModel* raModel = new RecentlyPlayedModel( m_source, pv ); - raModel->setStyle( TrackModel::Large ); + raModel->setStyle( PlayableModel::Large ); raModel->setTitle( tr( "Recently Played Tracks" ) ); if ( m_source->isLocal() ) @@ -586,6 +596,7 @@ SourceItem::recentPlaysClicked() pv->setItemDelegate( del ); pv->setPlaylistModel( raModel ); + pv->setEmptyTip( tr( "Sorry, we could not find any recent plays!" ) ); m_recentPlaysPage = pv; } diff --git a/src/sourcetree/items/SourceItem.h b/src/sourcetree/items/SourceItem.h index 0be1f4e52..cd6711735 100644 --- a/src/sourcetree/items/SourceItem.h +++ b/src/sourcetree/items/SourceItem.h @@ -38,12 +38,13 @@ public: SourceItem( SourcesModel* model, SourceTreeItem* parent, const Tomahawk::source_ptr& source ); virtual QString text() const; + virtual QString tooltip() const; virtual QIcon icon() const; virtual int peerSortValue() const; virtual int IDValue() const; virtual bool localLatchedOn() const; - virtual Tomahawk::PlaylistInterface::LatchMode localLatchMode() const; + virtual Tomahawk::PlaylistModes::LatchMode localLatchMode() const; Tomahawk::source_ptr source() const; @@ -65,7 +66,7 @@ private slots: void latchedOn( const Tomahawk::source_ptr&, const Tomahawk::source_ptr& ); void latchedOff( const Tomahawk::source_ptr&, const Tomahawk::source_ptr& ); - void latchModeChanged( Tomahawk::PlaylistInterface::LatchMode mode ); + void latchModeChanged( Tomahawk::PlaylistModes::LatchMode mode ); void requestExpanding(); diff --git a/src/sourcetree/items/SourceTreeItem.h b/src/sourcetree/items/SourceTreeItem.h index b2a527a05..410542e9e 100644 --- a/src/sourcetree/items/SourceTreeItem.h +++ b/src/sourcetree/items/SourceTreeItem.h @@ -58,6 +58,7 @@ public: // varies depending on the type of the item virtual QString text() const { return QString(); } + virtual QString tooltip() const { return QString(); } virtual Qt::ItemFlags flags() const { return Qt::ItemIsSelectable | Qt::ItemIsEnabled; } virtual QIcon icon() const { return QIcon(); } virtual bool willAcceptDrag( const QMimeData* ) const { return false; } diff --git a/src/sourcetree/items/TemporaryPageItem.cpp b/src/sourcetree/items/TemporaryPageItem.cpp index 8da923333..dc9f4a169 100644 --- a/src/sourcetree/items/TemporaryPageItem.cpp +++ b/src/sourcetree/items/TemporaryPageItem.cpp @@ -20,6 +20,7 @@ #include "ViewManager.h" #include "widgets/infowidgets/AlbumInfoWidget.h" #include "widgets/infowidgets/ArtistInfoWidget.h" +#include "widgets/infowidgets/TrackInfoWidget.h" #include "widgets/SearchWidget.h" using namespace Tomahawk; @@ -34,6 +35,8 @@ TemporaryPageItem::TemporaryPageItem ( SourcesModel* mdl, SourceTreeItem* parent m_icon = QIcon( RESPATH "images/artist-icon.png" ); else if ( dynamic_cast< AlbumInfoWidget* >( page ) ) m_icon = QIcon( RESPATH "images/album-icon.png" ); + else if ( dynamic_cast< TrackInfoWidget* >( page ) ) + m_icon = QIcon( RESPATH "images/track-icon-sidebar.png" ); else if ( dynamic_cast< SearchWidget* >( page ) ) m_icon = QIcon( RESPATH "images/search-icon.png" ); @@ -79,7 +82,7 @@ TemporaryPageItem::IDValue() const void TemporaryPageItem::removeFromList() { - ViewManager::instance()->removeFromHistory( m_page ); + ViewManager::instance()->destroyPage( m_page ); model()->removeSourceItemLink( this ); diff --git a/src/web/Api_v1.cpp b/src/web/Api_v1.cpp index e918ec90f..7008d0e2a 100644 --- a/src/web/Api_v1.cpp +++ b/src/web/Api_v1.cpp @@ -30,6 +30,7 @@ #include "database/DatabaseCommand_ClientAuthValid.h" #include "network/Servent.h" #include "Pipeline.h" +#include "Source.h" using namespace Tomahawk; diff --git a/thirdparty/liblastfm2/src/types/Track.cpp b/thirdparty/liblastfm2/src/types/Track.cpp index e8cfc9fa3..10cc41230 100644 --- a/thirdparty/liblastfm2/src/types/Track.cpp +++ b/thirdparty/liblastfm2/src/types/Track.cpp @@ -78,9 +78,9 @@ lastfm::TrackData::TrackData() rating( 0 ), fpid( -1 ), loved( false ), - null( false ), scrobbleStatus( Track::Null ), - scrobbleError( Track::None ) + scrobbleError( Track::None ), + null( false ) {} lastfm::Track::Track()