diff --git a/.gitignore b/.gitignore index 7a9999082..e3f800ccc 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,5 @@ moc_* *~ /tomahawk thirdparty/qtweetlib/WARNING-twitter-api-keys +.kdev4 +tomahawk.kdev4 diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 000000000..c07550f06 --- /dev/null +++ b/AUTHORS @@ -0,0 +1,16 @@ +Tomahawk is primarily authored by: + +* Christian Muehlhaeuser + +Contributors include: + +* Leo Franchi +* Dominik Schmidt +* Jeff Mitchell +* J Herskowitz +* Alejandro Wainzinger + +Thanks to: + +* Harald Sitter +* Steve Robertson diff --git a/CMakeLists.txt b/CMakeLists.txt index f77ffc6d9..71c62fdea 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,13 +1,17 @@ PROJECT( tomahawk ) CMAKE_MINIMUM_REQUIRED( VERSION 2.8 ) +IF( ${CMAKE_VERSION} VERSION_GREATER 2.8.3 ) + CMAKE_POLICY(SET CMP0017 NEW) +ENDIF( ${CMAKE_VERSION} VERSION_GREATER 2.8.3 ) + ### ### Tomahawk application info ### SET( ORGANIZATION_NAME "Tomahawk" ) SET( ORGANIZATION_DOMAIN "tomahawk-player.org" ) -SET( APPLICATION_NAME "Player" ) -SET( VERSION "0.0.1" ) +SET( APPLICATION_NAME "Tomahawk" ) +SET( VERSION "0.0.3" ) # set paths @@ -18,10 +22,10 @@ SET( THIRDPARTY_DIR ${CMAKE_SOURCE_DIR}/thirdparty ) IF( "${gui}" STREQUAL "no" ) ADD_DEFINITIONS( -DENABLE_HEADLESS ) MESSAGE( STATUS "Building in HEADLESS mode ***" ) - FIND_PACKAGE( Qt4 4.6.0 COMPONENTS QtCore QtXml QtNetwork REQUIRED ) + FIND_PACKAGE( Qt4 4.7.0 COMPONENTS QtCore QtXml QtNetwork REQUIRED ) ELSE() MESSAGE( STATUS "Building full GUI version ***" ) - FIND_PACKAGE( Qt4 4.6.0 COMPONENTS QtGui QtCore QtXml QtNetwork REQUIRED ) + FIND_PACKAGE( Qt4 4.7.0 COMPONENTS QtGui QtCore QtXml QtNetwork REQUIRED ) ENDIF() #deps @@ -29,8 +33,10 @@ INCLUDE( MacroOptionalFindPackage ) INCLUDE( MacroLogFeature ) # required -macro_optional_find_package(LibLastFm 0.3.3) -macro_log_feature(LIBLASTFM_FOUND "LastFm" "Qt library for the Last.fm webservices" "https://github.com/mxcl/liblastfm" FALSE "" "liblastfm is needed for scrobbling tracks to Last.fm and fetching cover artwork") +#While we distribute our own liblastfm2, don't need to look for it +#macro_optional_find_package(LibLastFm 0.3.3) +#macro_log_feature(LIBLASTFM_FOUND "LastFm" "Qt library for the Last.fm webservices" "https://github.com/mxcl/liblastfm" FALSE "" "liblastfm is needed for scrobbling tracks to Last.fm and fetching cover artwork") +set(LIBLASTFM_FOUND true) macro_optional_find_package(LibEchonest 1.1.1) macro_log_feature(LIBECHONEST_FOUND "Echonest" "Qt library for communicating with The Echo Nest" "http://projects.kde.org/libechonest" TRUE "" "libechonest is needed for dynamic playlists and the infosystem") @@ -47,36 +53,48 @@ macro_log_feature(TAGLIB_FOUND "TagLib" "Audio Meta-Data Library" "http://develo # we need pthreads too find_package(Threads) -FIND_PACKAGE( Taglib 1.6.0 REQUIRED ) +find_package(KDE4) +IF(KDE4_FOUND) + #KDE4 adds and removes some compiler flags that we don't like + STRING( REPLACE "-std=iso9899:1990" "" CLEAN_C_FLAGS ${CMAKE_C_FLAGS} ) + SET( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fexceptions" ) +ELSE() + SET( CLEAN_C_FLAGS ${CMAKE_C_FLAGS} ) +ENDIF() + include( CheckTagLibFileName ) check_taglib_filename( COMPLEX_TAGLIB_FILENAME ) # optional -macro_optional_find_package(Jreen) -IF( ENABLE_JREEN AND NOT LIBJREEN_FOUND ) +IF( ENABLE_JREEN ) + macro_optional_find_package(Jreen) + IF( LIBJREEN_FOUND ) + macro_log_feature(JREEN_FOUND "Jreen" "Qt XMPP library" "http://gitorious.org/jreen" FALSE "" "Jreen is needed for the alternative/new Jabber SIP plugin. Built automatically inside Tomahawk, if not installed systemwide and ENABLE_JREEN is true") + ELSE( LIBJREEN_FOUND ) + SET( OLD_C_FLAGS ${CMAKE_C_FLAGS} ) + SET( CMAKE_C_FLAGS ${CLEAN_C_FLAGS} ) ADD_SUBDIRECTORY( thirdparty/jreen ) SET( LIBJREEN_INCLUDE_DIR ${CMAKE_CURRENT_BINARY_DIR}/thirdparty/jreen/include ) IF( UNIX AND NOT APPLE ) SET( LIBJREEN_LIBRARY ${CMAKE_CURRENT_BINARY_DIR}/thirdparty/jreen/libjreen.so ) ENDIF( UNIX AND NOT APPLE ) IF( WIN32 ) - SET( LIBJREEN_LIBRARY ${CMAKE_CURRENT_BINARY_DIR}/thirdparty/jreen/libjreen.dll ) + SET( LIBJREEN_LIBRARY ${CMAKE_CURRENT_BINARY_DIR}/thirdparty/jreen/libjreen.dll ) ENDIF( WIN32 ) SET( LIBJREEN_FOUND true ) MESSAGE(STATUS "Internal libjreen: ${LIBJREEN_INCLUDE_DIR}, ${LIBJREEN_LIBRARY}") -ENDIF( ENABLE_JREEN AND NOT LIBJREEN_FOUND ) + SET( CMAKE_C_FLAGS ${OLD_C_FLAGS} ) + ENDIF( LIBJREEN_FOUND ) +ELSE( LIBJREEN_FOUND ) + macro_optional_find_package(Gloox 1.0) + macro_log_feature(GLOOX_FOUND "Gloox" "A portable high-level Jabber/XMPP library for C++" "http://camaya.net/gloox" FALSE "" "Gloox is needed for the Jabber SIP plugin and the XMPP-Bot") + +ENDIF( ENABLE_JREEN ) IF( WIN32 ) find_library(QTSPARKLE_LIBRARIES qtsparkle) ENDIF( WIN32 ) -macro_log_feature(JREEN_FOUND "Jreen" "Qt XMPP library" "http://gitorious.org/jreen" FALSE "" "Jreen is needed for the alternative/new Jabber SIP plugin. Built automatically inside Tomahawk, if not installed systemwide and ENABLE_JREEN is true") - -macro_optional_find_package(Gloox 1.0) -IF( ENABLE_JREEN ) - set( GLOOX_FOUND false ) -ENDIF( ENABLE_JREEN) -macro_log_feature(GLOOX_FOUND "Gloox" "A portable high-level Jabber/XMPP library for C++" "http://camaya.net/gloox" FALSE "" "Gloox is needed for the Jabber SIP plugin and the XMPP-Bot") #show dep log macro_display_feature_log() MESSAGE("WARNING!") @@ -85,6 +103,19 @@ MESSAGE("add checks for libmad, libvorbis and libflac. Make sure they are instal MESSAGE("") MESSAGE("-----------------------------------------------------------------------------") +SET( INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}" ) + +# make uninstall support +CONFIGURE_FILE( + "${CMAKE_CURRENT_SOURCE_DIR}/cmake_uninstall.cmake.in" + "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake" + IMMEDIATE @ONLY) + +# KDE4 defines an uninstall target for us automatically +IF( NOT KDE4_FOUND ) + ADD_CUSTOM_TARGET(uninstall "${CMAKE_COMMAND}" -P "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake") +ENDIF() + IF( NOT APPLE ) # Make linking as strict on linux as it is on osx. Then we don't break linking on mac so often SET( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wl,--no-undefined" ) @@ -93,3 +124,4 @@ ENDIF( NOT APPLE ) ADD_SUBDIRECTORY( thirdparty ) ADD_SUBDIRECTORY( src/libtomahawk ) ADD_SUBDIRECTORY( src ) +ADD_SUBDIRECTORY( admin ) diff --git a/CMakeModules/FindCLucene.cmake b/CMakeModules/FindCLucene.cmake index 32bc16b84..52116d01f 100644 --- a/CMakeModules/FindCLucene.cmake +++ b/CMakeModules/FindCLucene.cmake @@ -13,7 +13,7 @@ INCLUDE(CheckSymbolExists) INCLUDE(FindLibraryWithDebug) if(NOT CLUCENE_MIN_VERSION) - set(CLUCENE_MIN_VERSION "0.9.19") + set(CLUCENE_MIN_VERSION "0.9.23") endif(NOT CLUCENE_MIN_VERSION) IF(EXISTS ${PROJECT_CMAKE}/CLuceneConfig.cmake) @@ -75,7 +75,14 @@ FIND_PATH(CLUCENE_LIBRARY_DIR PATHS ${TRIAL_LIBRARY_PATHS} ${TRIAL_INCLUDE_PATHS} NO_DEFAULT_PATH) IF (CLUCENE_LIBRARY_DIR) MESSAGE(STATUS "Found CLucene library dir: ${CLUCENE_LIBRARY_DIR}") - INCLUDE(${CLUCENE_LIBRARY_DIR}/CLuceneConfig.cmake/CLuceneConfig.cmake) + # include CLuceneConfig/CLuceneConfig.cmake + IF(EXISTS ${CLUCENE_LIBRARY_DIR}/CLuceneConfig.cmake/CLuceneConfig.cmake) + INCLUDE(${CLUCENE_LIBRARY_DIR}/CLuceneConfig.cmake/CLuceneConfig.cmake) + ENDIF(EXISTS ${CLUCENE_LIBRARY_DIR}/CLuceneConfig.cmake/CLuceneConfig.cmake) + # include CLucene/CLuceneConfig.cmake + IF(EXISTS ${CLUCENE_LIBRARY_DIR}/CLucene/CLuceneConfig.cmake) + INCLUDE(${CLUCENE_LIBRARY_DIR}/CLucene/CLuceneConfig.cmake) + ENDIF(EXISTS ${CLUCENE_LIBRARY_DIR}/CLucene/CLuceneConfig.cmake) IF (CLUCENE_VERSION STRLESS "${CLUCENE_MIN_VERSION}") MESSAGE(ERROR " CLucene version ${CLUCENE_VERSION} is less than the required minimum ${CLUCENE_MIN_VERSION}") SET(CLUCENE_GOOD_VERSION FALSE) diff --git a/ChangeLog b/ChangeLog index ac67013ec..f33fe5ebf 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,51 @@ +Version 0.1.0: + * You can now browse and play collections in a tree-view. + * Watch folders for changes and automatically update your collection. This + is on by default; you can turn it off on the Local Music tab in the + settings dialog. Note that this triggers only on files or folders being + added to or removed from folders; it is not watch individual files as + most OSes can't support enough file watches to handle a normal-sized + music collection. + +Version 0.0.4: + * Fixed a crash situation caused by sources going on- or offline. + +Version 0.0.3: + * Show spinner while resolving playlists. + * Go back to previous page visible when deleting a playlist. + * Fixed issue where automatic playlists and station summaries were not + updated in the playlist header. + * Fixed an issue which caused duplicate items when rescanning. + * Revert change introduced in 0.0.2 causing Twitter protocol to not try + to reconnect to a peer if it couldn't connect the first time the plugin + was connected. This caused confusing (and for most unwanted) behavior. + * Fix crashes in Twitter authentication. + * Properly honor the chosen port number if a static host and port are + marked as preferred. + * Don't automatically try to resolve all incoming playback logs. This + speeds up importing sources a lot. + * Faster painting of playlists with lots of unresolved tracks. + * Prefer local results when results' score is equal. + * (Windows) The tomahawk:// protocol handler works on Windows now. + * (Windows) Fixed launching Tomahawk from Windows installer with admin privileges. + * (Windows) Prevent launching a second instance on Windows. + +Version 0.0.2: + * Don't reconnect to Jabber if the settings dialog is closed successfully + but the Jabber settings haven't changed. + * Don't run a rescan of the local collection if the settings dialog is + closed successfully but the path hasn't changed. + * Don't attempt to connect to unavailable Twitter peers over and over. + * Find Twitter peers if the peer's Got Tomahawk? tweet is not their latest + tweet. + * Got Tomahawk? tweets can now be sent directly to specific users or in + private direct messages. + * Display a helpful message when someone sends a normal instant message to + the Tomahawk XMPP presence. + * Incompatible change: Twitter SIP protocol has changed slightly. 0.0.1 + clients will not be able to talk to newer clients. + * Don't let long playlist or summary names force a large Tomahawk window. + * Tomahawk now asks you to authorize new contacts. + Version 0.0.1: - Features: - * Knocks your socks off + * First public release. diff --git a/README b/README index 20657f168..ca1241e92 100644 --- a/README +++ b/README @@ -1,8 +1,8 @@ Quickstart on Ubuntu -------------------- - $ sudo apt-get install build-essential cmake libtag1c2a libtag1-dev liblastfm-dev libqt4-dev \ - libqt4-sql-sqlite libboost-dev zlib1g-dev libgnutls-dev pkg-config + $ sudo apt-get install build-essential cmake libtag1c2a libtag1-dev libqt4-dev libqt4-sql-sqlite \ + libboost-dev zlib1g-dev libgnutls-dev pkg-config Gloox 1.0 (XMPP library) @@ -29,7 +29,7 @@ QJson (Qt JSON library) $ ./configure && make $ sudo make install -libEchonest 1.1.1 +libEchonest 1.1.4 --------------- See: http://projects.kde.org/projects/playground/libs/libechonest/ @@ -56,7 +56,7 @@ Quickstart on OS X Install homebrew $ ruby -e "$(curl -fsSL https://gist.github.com/raw/323731/install_homebrew.rb)" - $ brew install cmake qt qjson gloox taglib boost liblastfm + $ brew install cmake qt qjson gloox taglib boost Install libEchnoest & CLucene as per the above instructions. @@ -89,23 +89,18 @@ Dependencies TagLib 1.6.2 http://developer.kde.org/~wheeler/taglib.html Boost 1.3x http://www.boost.org/ CLucene 0.9.23 (0.9.21 will fail) http://clucene.sourceforge.net/download.shtml - liblastfm 0.3.3 http://github.com/mxcl/liblastfm/ - libechonest 1.1.1 http://projects.kde.org/projects/playground/libs/libechonest/ + libechonest 1.1.4 http://projects.kde.org/projects/playground/libs/libechonest/ Third party libraries that we ship with our source: MiniUPnP http://miniupnp.free.fr/ + liblastfm 0.4.0 http://github.com/jonocole/liblastfm/ To build the app: ----------------- - $ mkdir build && cd build - - Pick one of the following two choices. If uncertain pick the second one, you probably want a GUI. - $ cmake -Dgui=no .. # enables headless mode, build without GUI - $ cmake .. # normal build including GUI - + $ cmake .. $ make To run the app: diff --git a/admin/CMakeLists.txt b/admin/CMakeLists.txt new file mode 100644 index 000000000..48db60b20 --- /dev/null +++ b/admin/CMakeLists.txt @@ -0,0 +1,3 @@ +IF(WIN32) + INSTALL(DIRECTORY win DESTINATION share/tomahawk/admin ) +ENDIF(WIN32) \ No newline at end of file diff --git a/gen_resources.sh b/admin/gen_resources.sh similarity index 100% rename from gen_resources.sh rename to admin/gen_resources.sh diff --git a/admin/mac/DS_Store.in b/admin/mac/DS_Store.in new file mode 100644 index 000000000..bea3a4bcf Binary files /dev/null and b/admin/mac/DS_Store.in differ diff --git a/admin/mac/Info.plist b/admin/mac/Info.plist index a761cee82..f4fd83c88 100644 --- a/admin/mac/Info.plist +++ b/admin/mac/Info.plist @@ -5,21 +5,21 @@ CFBundleDevelopmentRegion English CFBundleExecutable - tomahawk + Tomahawk CFBundleIdentifier - org.tomahawk-player.org.Tomahawk + org.tomahawk-player.Tomahawk CFBundleInfoDictionaryVersion 6.0 CFBundlePackageType APPL CFBundleVersion - 0.0.1.0 + TOMAHAWK_VERSION CFBundleShortVersionString - 0.0.1 + TOMAHAWK_VERSION CFBundleSignature tomahawk CFBundleIconFile - tomahawk.icns + Tomahawk.icns CFBundleName Tomahawk LSMinimumSystemVersion diff --git a/admin/mac/build-release-osx.sh b/admin/mac/build-release-osx.sh index a399398a1..b283a2e44 100755 --- a/admin/mac/build-release-osx.sh +++ b/admin/mac/build-release-osx.sh @@ -16,6 +16,11 @@ function die { } ################################################################################ +if [ -z $1 ] +then + echo This script expects the version number as a parameter, e.g. 1.0.0 + exit 1 +fi ROOT=`pwd` @@ -31,7 +36,7 @@ echo "Goes here: $QTDIR" export QMAKESPEC='macx-g++' export QTDIR export VERSION -export QTVERSION='4.7.1' +export QTVERSION='4.7.2' ################################################################################ @@ -39,17 +44,34 @@ CLEAN='1' BUILD='1' NOTQUICK='1' CREATEDMG='1' +VERSION=$1 - header addQt + header "Adding Qt to app bundle" cd tomahawk.app $ROOT/../admin/mac/add-Qt-to-bundle.sh \ - 'QtCore QtGui QtXml QtNetwork QtSql' + 'QtCore QtGui QtXml QtNetwork QtSql QtXmlPatterns QtWebKit phonon' - header deposx + header "Running install_name_tool" $ROOT/../admin/mac/deposx.sh - header "Copying Sparkle pubkey and framework, and qt.conf" + + header "Renaming files" + mv Contents/Resources/tomahawkSources.icns Contents/Resources/Tomahawk.icns + mv Contents/MacOS/tomahawk Contents/MacOS/Tomahawk +# cp $ROOT/../admin/mac/Info.plist Contents/Info.plist + + header "Copying Sparkle pubkey & framework, and qt.conf" cp $ROOT/../admin/mac/sparkle_pub.pem Contents/Resources cp -R /Library/Frameworks/Sparkle.framework Contents/Frameworks cp $ROOT/../admin/mac/qt.conf Contents/Resources - header Done! + header "Creating DMG" + cd .. + mv tomahawk.app Tomahawk.app + $ROOT/../admin/mac/create-dmg.sh Tomahawk.app + mv Tomahawk.dmg Tomahawk-$VERSION.dmg + + header "Creating signed Sparkle update" + $ROOT/../admin/mac/sign_bundle.rb $VERSION ~/tomahawk_sparkle_privkey.pem + mv Tomahawk.app tomahawk.app + + header "Done!" diff --git a/admin/mac/create-dmg.sh b/admin/mac/create-dmg.sh new file mode 100755 index 000000000..74c42ddc0 --- /dev/null +++ b/admin/mac/create-dmg.sh @@ -0,0 +1,59 @@ +#!/bin/sh +# author: max@last.fm, muesli@tomahawk-player.org +# brief: Produces a compressed DMG from a bundle directory +# usage: Pass the bundle directory as the only parameter +# note: This script depends on the Tomahawk build system, and must be run from +# the build directory +################################################################################ + + +#if [ -z $VERSION ] +#then +# echo VERSION must be set +# exit 2 +#fi + +if [ -z "$1" ] +then + echo "Please pass the bundle.app directory as the first parameter." + exit 3 +fi +################################################################################ + + +NAME=$(basename "$1" | perl -pe 's/(.*).app/\1/') +IN="$1" +TMP="dmg/$NAME" +OUT="$NAME.dmg" +mkdir -p "$TMP" +################################################################################ + + +# clean up +rm -rf "$TMP" +rm -f "$OUT" + +# create DMG contents and copy files +mkdir -p "$TMP/.background" +cp ../admin/mac/dmg_background.png "$TMP/.background/background.png" +cp ../admin/mac/DS_Store.in "$TMP/.DS_Store" +chmod go-rwx "$TMP/.DS_Store" +ln -s /Applications "$TMP/Applications" +# copies the prepared bundle into the dir that will become the DMG +cp -R "$IN" "$TMP" + +# create +hdiutil makehybrid -hfs -hfs-volume-name Tomahawk -hfs-openfolder "$TMP" "$TMP" -o tmp.dmg +hdiutil convert -format UDZO -imagekey zlib-level=9 tmp.dmg -o "$OUT" + +# cleanup +rm tmp.dmg + +#hdiutil create -srcfolder "$TMP" \ +# -format UDZO -imagekey zlib-level=9 \ +# -scrub \ +# "$OUT" \ +# || die "Error creating DMG :(" + +# done ! +echo 'DMG size:' `du -hs "$OUT" | awk '{print $1}'` diff --git a/admin/mac/deposx.sh b/admin/mac/deposx.sh index 31966f9ed..8a8332936 100755 --- a/admin/mac/deposx.sh +++ b/admin/mac/deposx.sh @@ -62,7 +62,7 @@ function deposx_change function deplib_change { install_name_tool -change /usr/local/Cellar/liblastfm/0.3.3/lib/liblastfm.0.dylib @executable_path/liblastfm.0.dylib $1 - install_name_tool -change /usr/local/Cellar/qjson/0.7.1/lib/libqjson.0.dylib @executable_path/libqjson.0.dylib $1 + install_name_tool -change /usr/local/Cellar/qjson/0.7.1/lib/libqjson.0.7.1.dylib @executable_path/libqjson.0.7.1.dylib $1 install_name_tool -change /usr/local/lib/libechonest.1.1.dylib @executable_path/libechonest.1.1.dylib $1 install_name_tool -change /usr/local/lib/libclucene-core.0.9.23.dylib @executable_path/libclucene-core.0.9.23.dylib $1 install_name_tool -change /usr/local/lib/libclucene-shared.0.9.23.dylib @executable_path/libclucene-shared.0.9.23.dylib $1 @@ -75,13 +75,13 @@ function deplib_change install_name_tool -change /usr/local/Cellar/flac/1.2.1/lib/libFLAC++.6.dylib @executable_path/libFLAC++.6.dylib $1 install_name_tool -change /usr/local/Cellar/flac/1.2.1/lib/libFLAC.8.dylib @executable_path/libFLAC.8.dylib $1 install_name_tool -change $ORIGROOT/src/libtomahawk/libtomahawklib.dylib @executable_path/libtomahawklib.dylib $1 - install_name_tool -change $ORIGROOT/libsip_jabber.dylib @executable_path/libsip_jabber.dylib $1 - install_name_tool -change $ORIGROOT/libsip_twitter.dylib @executable_path/libsip_twitter.dylib $1 - install_name_tool -change $ORIGROOT/libsip_zeroconf.dylib @executable_path/libsip_zeroconf.dylib $1 + install_name_tool -change $ORIGROOT/libtomahawk_sipjabber.dylib @executable_path/libtomahawk_sipjabber.dylib $1 + install_name_tool -change $ORIGROOT/libtomahawk_siptwitter.dylib @executable_path/libtomahawk_siptwitter.dylib $1 + install_name_tool -change $ORIGROOT/libtomahawk_sipzeroconf.dylib @executable_path/libtomahawk_sipzeroconf.dylib $1 install_name_tool -change $ORIGROOT/thirdparty/jdns/libtomahawk_jdns.dylib @executable_path/libtomahawk_jdns.dylib $1 install_name_tool -change $ORIGROOT/thirdparty/qtweetlib/libtomahawk_qtweetlib.dylib @executable_path/libtomahawk_qtweetlib.dylib $1 - install_name_tool -change libqjson.0.dylib @executable_path/libqjson.0.dylib $1 + install_name_tool -change libqjson.0.7.1.dylib @executable_path/libqjson.0.7.1.dylib $1 install_name_tool -change libechonest.1.1.dylib @executable_path/libechonest.1.1.dylib $1 install_name_tool -change libclucene-core.0.9.23.dylib @executable_path/libclucene-core.0.9.23.dylib $1 install_name_tool -change libclucene-shared.0.9.23.dylib @executable_path/libclucene-shared.0.9.23.dylib $1 @@ -99,7 +99,7 @@ do deplib_change "$x" done -import_lib /usr/local/Cellar/qjson/0.7.1/lib/libqjson.0.dylib +import_lib /usr/local/Cellar/qjson/0.7.1/lib/libqjson.0.7.1.dylib import_lib /usr/local/Cellar/liblastfm/0.3.3/lib/liblastfm.0.dylib import_lib /usr/local/Cellar/gloox/1.0/lib/libgloox.8.dylib import_lib /usr/local/Cellar/taglib/1.6.3/lib/libtag.1.dylib @@ -113,9 +113,9 @@ import_lib /usr/local/lib/libechonest.1.1.dylib import_lib /usr/local/lib/libclucene-core.0.9.23.dylib import_lib /usr/local/lib/libclucene-shared.0.9.23.dylib -import_lib ../../libsip_jabber.dylib -import_lib ../../libsip_twitter.dylib -import_lib ../../libsip_zeroconf.dylib +import_lib ../../libtomahawk_sipjabber.dylib +import_lib ../../libtomahawk_siptwitter.dylib +import_lib ../../libtomahawk_sipzeroconf.dylib import_lib ../../src/libtomahawk/libtomahawklib.dylib import_lib ../../thirdparty/jdns/libtomahawk_jdns.dylib import_lib ../../thirdparty/qtweetlib/libtomahawk_qtweetlib.dylib diff --git a/admin/mac/dmg_background.png b/admin/mac/dmg_background.png new file mode 100644 index 000000000..a516605d9 Binary files /dev/null and b/admin/mac/dmg_background.png differ diff --git a/admin/mac/sign_bundle.rb b/admin/mac/sign_bundle.rb index 7ffd00062..aaebb9c0c 100755 --- a/admin/mac/sign_bundle.rb +++ b/admin/mac/sign_bundle.rb @@ -6,9 +6,9 @@ if ARGV.length < 2 exit end -tarball = "tomahawk#{ARGV[0]}.tar.bz2" +tarball = "tomahawk-#{ARGV[0]}.tar.bz2" puts "Zipping: #{tarball}..." -`tar jcvf "#{tarball}" tomahawk.app` +`tar jcvf "#{tarball}" Tomahawk.app` puts "Signing..." puts `openssl dgst -sha1 -binary < "#{tarball}" | openssl dgst -dss1 -sign "#{ARGV[1]}" | openssl enc -base64` diff --git a/admin/mac/sparkle-beta.rss b/admin/mac/sparkle-beta.rss index 604fd8dd8..c0ee02303 100755 --- a/admin/mac/sparkle-beta.rss +++ b/admin/mac/sparkle-beta.rss @@ -2,16 +2,24 @@ Tomahawk Player Changelog - http://download.tomahawk-player.org/sparkle - Most recent changes with links to updates. + http://www.gettomahawk.com + Tomahawk Player Beta en - Version 0.0.1 (Tomahawk Player - It Lives!) + Version 0.0.1 (Tomahawk Player Beta - It Lives?) - https://github.com/tomahawk-player/tomahawk/raw/master/ChangeLog + https://github.com/tomahawk-player/tomahawk/raw/0.0.1/ChangeLog - Fri, 04 Mar 2011 16:05:15 -0500 - + Fri, 25 Mar 2011 00:00:01 +0100 + + + + Version 0.0.2 (Tomahawk Player Beta - It Lives?) + + https://github.com/tomahawk-player/tomahawk/raw/0.0.2/ChangeLog + + Mon, 28 Mar 2011 06:13:01 +0100 + diff --git a/admin/mac/sparkle.rss b/admin/mac/sparkle.rss index 83d400eae..fa6bfafcc 100755 --- a/admin/mac/sparkle.rss +++ b/admin/mac/sparkle.rss @@ -2,16 +2,24 @@ Tomahawk Player Changelog - http://download.tomahawk-player.org/sparkle - Most recent changes with links to updates. + http://www.gettomahawk.com + Tomahawk Player en Version 0.0.1 (Tomahawk Player - It Lives!) - https://github.com/tomahawk-player/tomahawk/raw/master/ChangeLog + https://github.com/tomahawk-player/tomahawk/raw/0.0.1/ChangeLog - Fri, 04 Mar 2011 16:05:15 -0500 - + Fri, 25 Mar 2011 00:00:01 +0100 + + + + Version 0.0.2 (Tomahawk Player - It Lives!) + + https://github.com/tomahawk-player/tomahawk/raw/0.0.2/ChangeLog + + Mon, 28 Mar 2011 06:13:01 +0100 + diff --git a/admin/unix/tomahawk.protocol b/admin/unix/tomahawk.protocol index 3a393aa61..f342fb39c 100644 --- a/admin/unix/tomahawk.protocol +++ b/admin/unix/tomahawk.protocol @@ -1,5 +1,5 @@ [Protocol] -exec=/home/leo/kde/tomahawk/build/tomahawk "%u" +exec=/path/to/binary "%u" protocol=tomahawk input=none output=none diff --git a/admin/win/README.txt b/admin/win/README.txt deleted file mode 100755 index e69de29bb..000000000 diff --git a/admin/win/Toolchain-mingw32-openSUSE.cmake b/admin/win/Toolchain-mingw32-openSUSE.cmake index 5e62dc2e5..80a22964d 100644 --- a/admin/win/Toolchain-mingw32-openSUSE.cmake +++ b/admin/win/Toolchain-mingw32-openSUSE.cmake @@ -2,16 +2,19 @@ SET(CMAKE_SYSTEM_NAME Windows) # specify the cross compiler -SET(CMAKE_C_COMPILER i686-pc-mingw32-gcc) -SET(CMAKE_CXX_COMPILER i686-pc-mingw32-g++) +SET(CMAKE_C_COMPILER i686-w64-mingw32-gcc) +SET(CMAKE_CXX_COMPILER i686-w64-mingw32-g++) # where is the target environment containing libraries -SET(CMAKE_FIND_ROOT_PATH /usr/i686-pc-mingw32/sys-root/mingw) +SET(CMAKE_FIND_ROOT_PATH /usr/i686-w64-mingw32/sys-root/mingw) +SET(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +SET(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) +SET(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) # windres executable for application icon support -set(WINDRES_EXECUTABLE /usr/bin/i686-pc-mingw32-windres) - +SET(WINDRES_EXECUTABLE /usr/bin/i686-w64-mingw32-windres) # libs with broken find modules -set(TAGLIB_FOUND true) -set(TAGLIB_LIBRARIES ${CMAKE_FIND_ROOT_PATH}/lib/libtag.dll.a) +SET(TAGLIB_FOUND true) +SET(TAGLIB_LIBRARIES ${CMAKE_FIND_ROOT_PATH}/lib/libtag.dll.a) +SET(TAGLIB_INCLUDES ${CMAKE_FIND_ROOT_PATH}/include/taglib) diff --git a/admin/win/nsi/RELEASE_NOTES.txt b/admin/win/nsi/RELEASE_NOTES.txt old mode 100755 new mode 100644 index fd3e8f620..adbeaae55 --- a/admin/win/nsi/RELEASE_NOTES.txt +++ b/admin/win/nsi/RELEASE_NOTES.txt @@ -1 +1 @@ -TO DO \ No newline at end of file +See http://github.com/tomahawk-player/tomahawk/blob/stable/ChangeLog diff --git a/admin/win/nsi/installer.ico b/admin/win/nsi/installer.ico old mode 100755 new mode 100644 diff --git a/admin/win/nsi/nsis_processes/bin/Processes.dll b/admin/win/nsi/nsis_processes/bin/Processes.dll new file mode 100755 index 000000000..e532bf8bb Binary files /dev/null and b/admin/win/nsi/nsis_processes/bin/Processes.dll differ diff --git a/admin/win/nsi/nsis_processes/license.rtf b/admin/win/nsi/nsis_processes/license.rtf new file mode 100755 index 000000000..2ce5a58c9 --- /dev/null +++ b/admin/win/nsi/nsis_processes/license.rtf @@ -0,0 +1,35 @@ +{\rtf1\ansi\ansicpg1252\uc1\deff0\stshfdbch0\stshfloch0\stshfhich0\stshfbi0\deflang1033\deflangfe1033{\fonttbl{\f0\froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;}{\f39\fswiss\fcharset0\fprq2{\*\panose 020b0604030504040204}Verdana;} +{\f172\froman\fcharset238\fprq2 Times New Roman CE;}{\f173\froman\fcharset204\fprq2 Times New Roman Cyr;}{\f175\froman\fcharset161\fprq2 Times New Roman Greek;}{\f176\froman\fcharset162\fprq2 Times New Roman Tur;} +{\f177\froman\fcharset177\fprq2 Times New Roman (Hebrew);}{\f178\froman\fcharset178\fprq2 Times New Roman (Arabic);}{\f179\froman\fcharset186\fprq2 Times New Roman Baltic;}{\f180\froman\fcharset163\fprq2 Times New Roman (Vietnamese);} +{\f562\fswiss\fcharset238\fprq2 Verdana CE;}{\f563\fswiss\fcharset204\fprq2 Verdana Cyr;}{\f565\fswiss\fcharset161\fprq2 Verdana Greek;}{\f566\fswiss\fcharset162\fprq2 Verdana Tur;}{\f569\fswiss\fcharset186\fprq2 Verdana Baltic;} +{\f570\fswiss\fcharset163\fprq2 Verdana (Vietnamese);}}{\colortbl;\red0\green0\blue0;\red0\green0\blue255;\red0\green255\blue255;\red0\green255\blue0;\red255\green0\blue255;\red255\green0\blue0;\red255\green255\blue0;\red255\green255\blue255; +\red0\green0\blue128;\red0\green128\blue128;\red0\green128\blue0;\red128\green0\blue128;\red128\green0\blue0;\red128\green128\blue0;\red128\green128\blue128;\red192\green192\blue192;}{\stylesheet{ +\ql \li0\ri0\widctlpar\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \fs24\lang1033\langfe1033\cgrid\langnp1033\langfenp1033 \snext0 Normal;}{\*\cs10 \additive \ssemihidden Default Paragraph Font;}{\* +\ts11\tsrowd\trftsWidthB3\trpaddl108\trpaddr108\trpaddfl3\trpaddft3\trpaddfb3\trpaddfr3\trcbpat1\trcfpat1\tscellwidthfts0\tsvertalt\tsbrdrt\tsbrdrl\tsbrdrb\tsbrdrr\tsbrdrdgl\tsbrdrdgr\tsbrdrh\tsbrdrv +\ql \li0\ri0\widctlpar\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \fs20\lang1024\langfe1024\cgrid\langnp1024\langfenp1024 \snext11 \ssemihidden Normal Table;}{\*\cs15 \additive \ul\cf2 \sbasedon10 \styrsid7485074 Hyperlink;}} +{\*\latentstyles\lsdstimax156\lsdlockeddef0}{\*\rsidtbl \rsid6712196\rsid7485074\rsid11352300\rsid15940516}{\*\generator Microsoft Word 11.0.5604;}{\info{\title Processes v1}{\author Hardwired}{\operator Hardwired}{\creatim\yr2004\mo12\dy12\hr23\min42} +{\revtim\yr2004\mo12\dy12\hr23\min51}{\version2}{\edmins9}{\nofpages1}{\nofwords80}{\nofchars458}{\nofcharsws537}{\vern24689}}\widowctrl\ftnbj\aenddoc\noxlattoyen\expshrtn\noultrlspc\dntblnsbdb\nospaceforul\formshade\horzdoc\dgmargin\dghspace180 +\dgvspace180\dghorigin1800\dgvorigin1440\dghshow1\dgvshow1 +\jexpand\viewkind1\viewscale100\pgbrdrhead\pgbrdrfoot\splytwnine\ftnlytwnine\htmautsp\nolnhtadjtbl\useltbaln\alntblind\lytcalctblwd\lyttblrtgr\lnbrkrule\nobrkwrptbl\snaptogridincell\allowfieldendsel\wrppunct +\asianbrkrule\rsidroot7485074\newtblstyruls\nogrowautofit \fet0\sectd \linex0\endnhere\sectlinegrid360\sectdefaultcl\sftnbj {\*\pnseclvl1\pnucrm\pnstart1\pnindent720\pnhang {\pntxta .}}{\*\pnseclvl2\pnucltr\pnstart1\pnindent720\pnhang {\pntxta .}} +{\*\pnseclvl3\pndec\pnstart1\pnindent720\pnhang {\pntxta .}}{\*\pnseclvl4\pnlcltr\pnstart1\pnindent720\pnhang {\pntxta )}}{\*\pnseclvl5\pndec\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}{\*\pnseclvl6\pnlcltr\pnstart1\pnindent720\pnhang {\pntxtb (} +{\pntxta )}}{\*\pnseclvl7\pnlcrm\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}{\*\pnseclvl8\pnlcltr\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}{\*\pnseclvl9\pnlcrm\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}\pard\plain +\qj \li0\ri0\widctlpar\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0\pararsid7485074 \fs24\lang1033\langfe1033\cgrid\langnp1033\langfenp1033 {\b\f39\insrsid7485074\charrsid7485074 Processes v1.0}{\f39\insrsid7485074\charrsid7485074 .0.1 +\par }{\f39\fs20\insrsid7485074 +\par }\pard \qj \li0\ri0\widctlpar\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0\pararsid15940516 {\f39\fs20\insrsid15940516 This software binaries and source-code are free for any kind of use, including commercial use. }{ +\f39\fs20\insrsid7485074\charrsid7485074 There is no restriction and no guaranty for using}{\f39\fs20\insrsid7485074\charrsid7485074 t}{\f39\fs20\insrsid7485074\charrsid7485074 his software}{\f39\fs20\insrsid7485074\charrsid7485074 and/or it +s source-code. }{\f39\fs20\insrsid15940516 +\par I}{\f39\fs20\insrsid7485074\charrsid7485074 f you use the plug}{\f39\fs20\insrsid7485074\charrsid7485074 -}{\f39\fs20\insrsid7485074\charrsid7485074 in }{\f39\fs20\insrsid7485074\charrsid7485074 and/}{\f39\fs20\insrsid7485074\charrsid7485074 or it}{ +\f39\fs20\insrsid7485074\charrsid7485074 s}{\f39\fs20\insrsid7485074\charrsid7485074 source-code, I would }{\f39\fs20\insrsid7485074\charrsid7485074 appreciate }{\f39\fs20\insrsid7485074\charrsid7485074 if my name is mentioned.}{ +\f39\fs20\insrsid7485074\charrsid7485074 +\par }\pard \qj \li0\ri0\widctlpar\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0\pararsid7485074 {\f39\fs20\insrsid7485074\charrsid7485074 +\par }{\b\f39\fs20\insrsid7485074\charrsid7485074 Andrei Ciubotaru [Hardwired] +\par }{\f39\fs20\insrsid7485074\charrsid7485074 Lead Developer ICode&Ideas SRL (}{\field\flddirty{\*\fldinst {\f39\fs20\insrsid7485074\charrsid7485074 HYPERLINK "http://www.icode.ro/" }{\f39\fs20\insrsid7485074\charrsid7485074 {\*\datafield +00d0c9ea79f9bace118c8200aa004ba90b02000000170000001500000068007400740070003a002f002f007700770077002e00690063006f00640065002e0072006f002f000000e0c9ea79f9bace118c8200aa004ba90b2a00000068007400740070003a002f002f007700770077002e00690063006f00640065002e007200 +6f002f000000}}}{\fldrslt {\cs15\f39\fs20\ul\cf2\insrsid7485074\charrsid7485074 http://www.icode.ro/}}}{\f39\fs20\insrsid7485074\charrsid7485074 ) +\par }{\field{\*\fldinst {\f39\fs20\insrsid7485074 HYPERLINK "hardwiredteks@gmail.com" }{\f39\fs20\insrsid15940516\charrsid7485074 {\*\datafield +00d0c9ea79f9bace118c8200aa004ba90b02000000010000000303000000000000c00000000000004600001800000068617264776972656474656b7340676d61696c2e636f6d00ffffadde000000000000000000000000000000000000000000000000}}}{\fldrslt { +\cs15\f39\fs20\ul\cf2\insrsid7485074\charrsid7485074 hardwiredteks@gmail.com}}}{\f39\fs20\insrsid7485074\charrsid7485074 , }{\field{\*\fldinst {\f39\fs20\insrsid7485074 HYPERLINK "hardwired@icode.ro" }{\f39\fs20\insrsid15940516\charrsid7485074 +{\*\datafield 00d0c9ea79f9bace118c8200aa004ba90b02000000010000000303000000000000c0000000000000460000130000006861726477697265644069636f64652e726f00ffffadde000000000000000000000000000000000000000000000000}}}{\fldrslt { +\cs15\f39\fs20\ul\cf2\insrsid7485074\charrsid7485074 hardwired@icode.ro}}}{\f39\fs20\insrsid7485074\charrsid7485074 +\par }} \ No newline at end of file diff --git a/admin/win/nsi/nsis_processes/readme.txt b/admin/win/nsi/nsis_processes/readme.txt new file mode 100755 index 000000000..8529c39ad --- /dev/null +++ b/admin/win/nsi/nsis_processes/readme.txt @@ -0,0 +1,122 @@ +---------------------------------------------------------------- +---------------------------------------------------------------- +Processes (Processes.dll) +Version: 1.0.1.0 +Release: 24.february.2005 +Description: Nullsoft Installer (NSIS) plug-in for managing?! + Windows processes. + +Copyright: © 2004-2005 Hardwired. No rights reserved. + There is no restriction and no guaranty for using + this software. + +Author: Andrei Ciubotaru [Hardwired] + Lead Developer ICode&Ideas SRL (http://www.icode.ro/) + hardwiredteks@gmail.com, hardwired@icode.ro + +---------------------------------------------------------------- +---------------------------------------------------------------- +INTRODUCTION + + The Need For Plug-in - I need it for the one of my installers. + + Briefly: Use it when you need to find\kill a process when +installing\uninstalling some application. Also, use it when you +need to test the presence of a device driver. + + +SUPPORT + + Supported platforms are: WinNT,Win2K,WinXP and Win2003 Server. + + +DESCRIPTION + + Processes::FindProcess ;without ".exe" + + Searches the currently running processes for the given + process name. + + return: 1 - the process was found + 0 - the process was not found + + Processes::KillProcess ; without ".exe" + + Searches the currently running processes for the given + process name. If the process is found then the it gets + killed. + + return: 1 - the process was found and killed + 0 - the process was not found or the process + cannot be killed (insuficient rights) + + Processes::FindDevice + + Searches the installed devices drivers for the given + device base name. + (important: I said BASE NAME not FILENAME) + + return: 1 - the device driver was found + 0 - the device driver was not found + + +USAGE + + First of all, does not matter where you use it. Ofcourse, the +routines must be called inside of a Section/Function scope. + + Processes::FindProcess "process_name" + Pop $R0 + + StrCmp $R0 "1" make_my_day noooooo + + make_my_day: + ... + + noooooo: + ... + + + Processes::KillProcess "process_name" + Pop $R0 + + StrCmp $R0 "1" dead_meat why_wont_you_die + + dead_meat: + ... + + why_wont_you_die: + ... + + + Processes::FindDevice "device_base_name" + Pop $R0 + + StrCmp $R0 "1" blabla more_blabla + + blabla: + ... + + more_blabla: + ... + + +THANKS + + Sunil Kamath for inspiring me. I wanted to use its FindProcDLL +but my requirements made it imposible. + + Nullsoft for creating this very powerfull installer. One big, +free and full-featured (hmmm... and guiless for the moment) mean +install machine!:) + + ME for being such a great coder... + ... HAHAHAHAHAHAHA! + +ONE MORE THING + + If you use the plugin or it's source-code, I would apreciate +if my name is mentioned. + +---------------------------------------------------------------- +---------------------------------------------------------------- diff --git a/admin/win/nsi/nsis_processes/src/StdAfx.cpp b/admin/win/nsi/nsis_processes/src/StdAfx.cpp new file mode 100755 index 000000000..f38accc8a --- /dev/null +++ b/admin/win/nsi/nsis_processes/src/StdAfx.cpp @@ -0,0 +1,8 @@ +// stdafx.cpp : source file that includes just the standard includes +// KillProcDLL.pch will be the pre-compiled header +// stdafx.obj will contain the pre-compiled type information + +#include "stdafx.h" + +// TODO: reference any additional headers you need in STDAFX.H +// and not in this file diff --git a/admin/win/nsi/nsis_processes/src/StdAfx.h b/admin/win/nsi/nsis_processes/src/StdAfx.h new file mode 100755 index 000000000..dd49f99b9 --- /dev/null +++ b/admin/win/nsi/nsis_processes/src/StdAfx.h @@ -0,0 +1,34 @@ +// stdafx.h : include file for standard system include files, +// or project specific include files that are used frequently, but +// are changed infrequently +// + +#if !defined(AFX_STDAFX_H__780690DC_E128_403D_BC07_780D1B2CC101__INCLUDED_) +#define AFX_STDAFX_H__780690DC_E128_403D_BC07_780D1B2CC101__INCLUDED_ + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 + +#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers + +#include + +#include // String management... + +//From exam28.cpp +#include +//#include + +#ifdef BORLANDC + #include + #include +#endif + +//To make it a NSIS Plug-In +#include "exdll.h" + +//{{AFX_INSERT_LOCATION}} +// Microsoft Visual C++ will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_STDAFX_H__780690DC_E128_403D_BC07_780D1B2CC101__INCLUDED_) diff --git a/admin/win/nsi/nsis_processes/src/exdll.c b/admin/win/nsi/nsis_processes/src/exdll.c new file mode 100755 index 000000000..7092cb840 --- /dev/null +++ b/admin/win/nsi/nsis_processes/src/exdll.c @@ -0,0 +1,37 @@ +#include +#include "exdll.h" + +HINSTANCE g_hInstance; + +HWND g_hwndParent; + +void __declspec(dllexport) myFunction(HWND hwndParent, int string_size, + char *variables, stack_t **stacktop) +{ + g_hwndParent=hwndParent; + + EXDLL_INIT(); + + + // note if you want parameters from the stack, pop them off in order. + // i.e. if you are called via exdll::myFunction file.dat poop.dat + // calling popstring() the first time would give you file.dat, + // and the second time would give you poop.dat. + // you should empty the stack of your parameters, and ONLY your + // parameters. + + // do your stuff here + { + char buf[1024]; + wsprintf(buf,"$0=%s\n",getuservariable(INST_0)); + MessageBox(g_hwndParent,buf,0,MB_OK); + } +} + + + +BOOL WINAPI _DllMainCRTStartup(HANDLE hInst, ULONG ul_reason_for_call, LPVOID lpReserved) +{ + g_hInstance=hInst; + return TRUE; +} diff --git a/admin/win/nsi/nsis_processes/src/exdll.h b/admin/win/nsi/nsis_processes/src/exdll.h new file mode 100755 index 000000000..777d93be5 --- /dev/null +++ b/admin/win/nsi/nsis_processes/src/exdll.h @@ -0,0 +1,136 @@ +#ifndef _EXDLL_H_ +#define _EXDLL_H_ + + + + + +// +// only include this file from one place in your DLL. +// (it is all static, if you use it in two places it will fail) +// +#define EXDLL_INIT() { \ + g_stringsize = string_size; \ + g_stacktop = stacktop; \ + g_variables = variables; } + + + + +// +// For page showing plug-ins +// +#define WM_NOTIFY_OUTER_NEXT (WM_USER+0x8) +#define WM_NOTIFY_CUSTOM_READY (WM_USER+0xd) +#define NOTIFY_BYE_BYE 'x' + +typedef struct _stack_t +{ + struct _stack_t *next; + char text[1]; // this should be the length of string_size +} stack_t; + + +static unsigned int g_stringsize; +static stack_t **g_stacktop; +static char *g_variables; + +enum +{ +INST_0, // $0 +INST_1, // $1 +INST_2, // $2 +INST_3, // $3 +INST_4, // $4 +INST_5, // $5 +INST_6, // $6 +INST_7, // $7 +INST_8, // $8 +INST_9, // $9 +INST_R0, // $R0 +INST_R1, // $R1 +INST_R2, // $R2 +INST_R3, // $R3 +INST_R4, // $R4 +INST_R5, // $R5 +INST_R6, // $R6 +INST_R7, // $R7 +INST_R8, // $R8 +INST_R9, // $R9 +INST_CMDLINE, // $CMDLINE +INST_INSTDIR, // $INSTDIR +INST_OUTDIR, // $OUTDIR +INST_EXEDIR, // $EXEDIR +INST_LANG, // $LANGUAGE +__INST_LAST +}; + + + + + +// +// utility functions (not required but often useful) +// +static int popstring( char *str ) +{ + stack_t *th; + + + if( !g_stacktop || + !*g_stacktop ) + return 1; + + th = (*g_stacktop); + lstrcpy( str, th->text ); + *g_stacktop = th->next; + GlobalFree( (HGLOBAL)th ); + + return 0; +} + + + + +static void pushstring( char *str ) +{ + stack_t *th; + + + if( !g_stacktop ) + return; + + th = (stack_t*)GlobalAlloc( GPTR, sizeof(stack_t) + g_stringsize ); + lstrcpyn( th->text, str, g_stringsize ); + th->next = *g_stacktop; + *g_stacktop = th; +} + + + + + +static char *getuservariable( int varnum ) +{ + if( varnum < 0 || + varnum >= __INST_LAST ) + return NULL; + + return (g_variables + varnum*g_stringsize); +} + + + + + +static void setuservariable( int varnum, char *var ) +{ + if( var != NULL && + varnum >= 0 && + varnum < __INST_LAST ) + lstrcpy( g_variables + varnum*g_stringsize, var ); +} + + + +#endif//_EXDLL_H_ \ No newline at end of file diff --git a/admin/win/nsi/nsis_processes/src/processes.cpp b/admin/win/nsi/nsis_processes/src/processes.cpp new file mode 100755 index 000000000..c15f8f94a --- /dev/null +++ b/admin/win/nsi/nsis_processes/src/processes.cpp @@ -0,0 +1,411 @@ +#include "stdafx.h" +#include "processes.h" +#include "string.h" + + + + + + +//------------------------------------------------------------------------------------------- +// global variables +lpfEnumProcesses EnumProcesses; +lpfEnumProcessModules EnumProcessModules; +lpfGetModuleBaseName GetModuleBaseName; +lpfEnumDeviceDrivers EnumDeviceDrivers; +lpfGetDeviceDriverBaseName GetDeviceDriverBaseName; + +HINSTANCE g_hInstance; +HWND g_hwndParent; +HINSTANCE g_hInstLib; + + + + + +//------------------------------------------------------------------------------------------- +// main DLL entry +BOOL WINAPI _DllMainCRTStartup( HANDLE hInst, + ULONG ul_reason_for_call, + LPVOID lpReserved ) +{ + g_hInstance = (struct HINSTANCE__ *)hInst; + + return TRUE; +} + + + + + +//------------------------------------------------------------------------------------------- +// loads the psapi routines +bool LoadPSAPIRoutines( void ) +{ + if( NULL == (g_hInstLib = LoadLibraryA( "PSAPI.DLL" )) ) + return false; + + EnumProcesses = (lpfEnumProcesses) GetProcAddress( g_hInstLib, "EnumProcesses" ); + EnumProcessModules = (lpfEnumProcessModules) GetProcAddress( g_hInstLib, "EnumProcessModules" ); + GetModuleBaseName = (lpfGetModuleBaseName) GetProcAddress( g_hInstLib, "GetModuleBaseNameA" ); + EnumDeviceDrivers = (lpfEnumDeviceDrivers) GetProcAddress( g_hInstLib, "EnumDeviceDrivers" ); + GetDeviceDriverBaseName = (lpfGetDeviceDriverBaseName) GetProcAddress( g_hInstLib, "GetDeviceDriverBaseNameA" ); + + if( ( NULL == EnumProcesses ) || + ( NULL == EnumProcessModules ) || + ( NULL == EnumDeviceDrivers ) || + ( NULL == GetModuleBaseName ) || + ( NULL == GetDeviceDriverBaseName ) ) + { + FreeLibrary( g_hInstLib ); + + return false; + } + + return true; +} + + + + + +//------------------------------------------------------------------------------------------- +// free the psapi routines +bool FreePSAPIRoutines( void ) +{ + EnumProcesses = NULL; + EnumProcessModules = NULL; + GetModuleBaseName = NULL; + EnumDeviceDrivers = NULL; + + if( FALSE == FreeLibrary( g_hInstLib ) ) + return false; + + return true; +} + + + + + +//------------------------------------------------------------------------------------------- +// find a process by name +// return value: true - process was found +// false - process not found +bool FindProc( char *szProcess ) +{ + char szProcessName[ 1024 ]; + char szCurrentProcessName[ 1024 ]; + DWORD dPID[ 1024 ]; + DWORD dPIDSize( 1024 ); + DWORD dSize( 1024 ); + HANDLE hProcess; + HMODULE phModule[ 1024 ]; + + + // + // make the name lower case + // + memset( szProcessName, 0, 1024*sizeof(char) ); + sprintf( szProcessName, "%s", szProcess ); + strlwr( szProcessName ); + + // + // load PSAPI routines + // + if( false == LoadPSAPIRoutines() ) + return false; + + // + // enumerate processes names + // + if( FALSE == EnumProcesses( dPID, dSize, &dPIDSize ) ) + { + FreePSAPIRoutines(); + + return false; + } + + // + // walk trough and compare see if the process is running + // + for( int k( dPIDSize / sizeof( DWORD ) ); k >= 0; k-- ) + { + memset( szCurrentProcessName, 0, 1024*sizeof(char) ); + + if( NULL != ( hProcess = OpenProcess( PROCESS_ALL_ACCESS, FALSE, dPID[ k ] ) ) ) + { + if( TRUE == EnumProcessModules( hProcess, phModule, sizeof(HMODULE)*1024, &dPIDSize ) ) + if( GetModuleBaseName( hProcess, phModule[ 0 ], szCurrentProcessName, 1024 ) > 0 ) + { + strlwr( szCurrentProcessName ); + + if( NULL != strstr( szCurrentProcessName, szProcessName ) ) + { + FreePSAPIRoutines(); + CloseHandle( hProcess ); + + return true; + } + } + + CloseHandle( hProcess ); + } + } + + // + // free PSAPI routines + // + FreePSAPIRoutines(); + + return false; +} + + + + + +//------------------------------------------------------------------------------------------- +// kills a process by name +// return value: true - process was found +// false - process not found +bool KillProc( char *szProcess ) +{ + char szProcessName[ 1024 ]; + char szCurrentProcessName[ 1024 ]; + DWORD dPID[ 1024 ]; + DWORD dPIDSize( 1024 ); + DWORD dSize( 1024 ); + HANDLE hProcess; + HMODULE phModule[ 1024 ]; + + + // + // make the name lower case + // + memset( szProcessName, 0, 1024*sizeof(char) ); + sprintf( szProcessName, "%s", szProcess ); + strlwr( szProcessName ); + + // + // load PSAPI routines + // + if( false == LoadPSAPIRoutines() ) + return false; + + // + // enumerate processes names + // + if( FALSE == EnumProcesses( dPID, dSize, &dPIDSize ) ) + { + FreePSAPIRoutines(); + + return false; + } + + // + // walk trough and compare see if the process is running + // + for( int k( dPIDSize / sizeof( DWORD ) ); k >= 0; k-- ) + { + memset( szCurrentProcessName, 0, 1024*sizeof(char) ); + + if( NULL != ( hProcess = OpenProcess( PROCESS_ALL_ACCESS, FALSE, dPID[ k ] ) ) ) + { + if( TRUE == EnumProcessModules( hProcess, phModule, sizeof(HMODULE)*1024, &dPIDSize ) ) + if( GetModuleBaseName( hProcess, phModule[ 0 ], szCurrentProcessName, 1024 ) > 0 ) + { + strlwr( szCurrentProcessName ); + + if( NULL != strstr( szCurrentProcessName, szProcessName ) ) + { + FreePSAPIRoutines(); + + // + // kill process + // + if( false == TerminateProcess( hProcess, 0 ) ) + { + CloseHandle( hProcess ); + + return true; + } + + // + // refresh systray + // + UpdateWindow( FindWindow( NULL, "Shell_TrayWnd" ) ); + + // + // refresh desktop window + // + UpdateWindow( GetDesktopWindow() ); + + CloseHandle( hProcess ); + + return true; + } + } + + CloseHandle( hProcess ); + } + } + + // + // free PSAPI routines + // + FreePSAPIRoutines(); + + return false; +} + + + + + +//------------------------------------------------------------------------------------------- +bool FindDev( char *szDriverName ) +{ + char szDeviceName[ 1024 ]; + char szCurrentDeviceName[ 1024 ]; + LPVOID lpDevices[ 1024 ]; + DWORD dDevicesSize( 1024 ); + DWORD dSize( 1024 ); + TCHAR tszCurrentDeviceName[ 1024 ]; + DWORD dNameSize( 1024 ); + + + // + // make the name lower case + // + memset( szDeviceName, 0, 1024*sizeof(char) ); + sprintf( szDeviceName, "%s", strlwr( szDriverName ) ); + + // + // load PSAPI routines + // + if( false == LoadPSAPIRoutines() ) + return false; + + // + // enumerate devices + // + if( FALSE == EnumDeviceDrivers( lpDevices, dSize, &dDevicesSize ) ) + { + FreePSAPIRoutines(); + + return false; + } + + // + // walk trough and compare see if the device driver exists + // + for( int k( dDevicesSize / sizeof( LPVOID ) ); k >= 0; k-- ) + { + memset( szCurrentDeviceName, 0, 1024*sizeof(char) ); + memset( tszCurrentDeviceName, 0, 1024*sizeof(TCHAR) ); + + if( 0 != GetDeviceDriverBaseName( lpDevices[ k ], tszCurrentDeviceName, dNameSize ) ) + { + sprintf( szCurrentDeviceName, "%S", tszCurrentDeviceName ); + + if( 0 == strcmp( strlwr( szCurrentDeviceName ), szDeviceName ) ) + { + FreePSAPIRoutines(); + + return true; + } + } + } + + // + // free PSAPI routines + // + FreePSAPIRoutines(); + + return false; +} + + + + + +//------------------------------------------------------------------------------------------- +extern "C" __declspec(dllexport) void FindProcess( HWND hwndParent, + int string_size, + char *variables, + stack_t **stacktop ) +{ + char szParameter[ 1024 ]; + + + g_hwndParent = hwndParent; + + EXDLL_INIT(); + { + popstring( szParameter ); + + if( true == FindProc( szParameter ) ) + wsprintf( szParameter, "1" ); + else + wsprintf( szParameter, "0" ); + + setuservariable( INST_R0, szParameter ); + } +} + + + + + +//------------------------------------------------------------------------------------------- +extern "C" __declspec(dllexport) void KillProcess( HWND hwndParent, + int string_size, + char *variables, + stack_t **stacktop ) +{ + char szParameter[ 1024 ]; + + + g_hwndParent = hwndParent; + + EXDLL_INIT(); + { + popstring( szParameter ); + + if( true == KillProc( szParameter ) ) + wsprintf( szParameter, "1" ); + else + wsprintf( szParameter, "0" ); + + setuservariable( INST_R0, szParameter ); + } +} + + + + + +//------------------------------------------------------------------------------------------- +extern "C" __declspec(dllexport) void FindDevice( HWND hwndParent, + int string_size, + char *variables, + stack_t **stacktop ) +{ + char szParameter[ 1024 ]; + + + g_hwndParent = hwndParent; + + EXDLL_INIT(); + { + popstring( szParameter ); + + if( true == FindDev( szParameter ) ) + wsprintf( szParameter, "1" ); + else + wsprintf( szParameter, "0" ); + + setuservariable( INST_R0, szParameter ); + } +} diff --git a/admin/win/nsi/nsis_processes/src/processes.h b/admin/win/nsi/nsis_processes/src/processes.h new file mode 100755 index 000000000..9bd069101 --- /dev/null +++ b/admin/win/nsi/nsis_processes/src/processes.h @@ -0,0 +1,49 @@ +#pragma once + + + + + +//------------------------------------------------------------------------------------------- +// PSAPI function pointers +typedef BOOL (WINAPI *lpfEnumProcesses) ( DWORD *, DWORD, DWORD * ); +typedef BOOL (WINAPI *lpfEnumProcessModules) ( HANDLE, HMODULE *, DWORD, LPDWORD ); +typedef DWORD (WINAPI *lpfGetModuleBaseName) ( HANDLE, HMODULE, LPTSTR, DWORD ); +typedef BOOL (WINAPI *lpfEnumDeviceDrivers) ( LPVOID *, DWORD, LPDWORD ); +typedef BOOL (WINAPI *lpfGetDeviceDriverBaseName)( LPVOID, LPTSTR, DWORD ); + + + + + + +//------------------------------------------------------------------------------------------- +// Internal use routines +bool LoadPSAPIRoutines( void ); +bool FreePSAPIRoutines( void ); + +bool FindProc( char *szProcess ); +bool KillProc( char *szProcess ); + +bool FindDev( char *szDriverName ); + + + + + +//------------------------------------------------------------------------------------------- +// Exported routines +extern "C" __declspec(dllexport) void FindProcess( HWND hwndParent, + int string_size, + char *variables, + stack_t **stacktop ); + +extern "C" __declspec(dllexport) void KillProcess( HWND hwndParent, + int string_size, + char *variables, + stack_t **stacktop ); + +extern "C" __declspec(dllexport) void FindDevice( HWND hwndParent, + int string_size, + char *variables, + stack_t **stacktop ); diff --git a/admin/win/nsi/nsis_processes/src/processes.ncb b/admin/win/nsi/nsis_processes/src/processes.ncb new file mode 100755 index 000000000..c1a5f281f Binary files /dev/null and b/admin/win/nsi/nsis_processes/src/processes.ncb differ diff --git a/admin/win/nsi/nsis_processes/src/processes.rc b/admin/win/nsi/nsis_processes/src/processes.rc new file mode 100755 index 000000000..c6e62a3c8 --- /dev/null +++ b/admin/win/nsi/nsis_processes/src/processes.rc @@ -0,0 +1,103 @@ +// Microsoft Visual C++ generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "afxres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""afxres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +VS_VERSION_INFO VERSIONINFO + FILEVERSION 1,0,0,1 + PRODUCTVERSION 1,0,0,1 + FILEFLAGSMASK 0x17L +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x4L + FILETYPE 0x2L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "Comments", "NSIS Plug-in for Windows process management. Only WinNT, Win2K, WinXP and Win2003 Server supported." + VALUE "CompanyName", "Andrei Ciubotaru [Hardwired]" + VALUE "FileDescription", "Windows Processes Management" + VALUE "FileVersion", "1, 0, 0, 1" + VALUE "InternalName", "Processes" + VALUE "LegalCopyright", "Copyright (c) 2004 Hardwired. No rights reserved." + VALUE "OriginalFilename", "Processes.dll" + VALUE "ProductName", "Processes" + VALUE "ProductVersion", "1, 0, 0, 1" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/admin/win/nsi/nsis_processes/src/processes.sln b/admin/win/nsi/nsis_processes/src/processes.sln new file mode 100755 index 000000000..73fc989e2 --- /dev/null +++ b/admin/win/nsi/nsis_processes/src/processes.sln @@ -0,0 +1,21 @@ +Microsoft Visual Studio Solution File, Format Version 8.00 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "processes", "processes.vcproj", "{3438467F-A719-46DC-93E5-137A8B691727}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfiguration) = preSolution + Debug = Debug + Release = Release + EndGlobalSection + GlobalSection(ProjectConfiguration) = postSolution + {3438467F-A719-46DC-93E5-137A8B691727}.Debug.ActiveCfg = Debug|Win32 + {3438467F-A719-46DC-93E5-137A8B691727}.Debug.Build.0 = Debug|Win32 + {3438467F-A719-46DC-93E5-137A8B691727}.Release.ActiveCfg = Release|Win32 + {3438467F-A719-46DC-93E5-137A8B691727}.Release.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + EndGlobalSection + GlobalSection(ExtensibilityAddIns) = postSolution + EndGlobalSection +EndGlobal diff --git a/admin/win/nsi/nsis_processes/src/processes.txt b/admin/win/nsi/nsis_processes/src/processes.txt new file mode 100755 index 000000000..51d11902a --- /dev/null +++ b/admin/win/nsi/nsis_processes/src/processes.txt @@ -0,0 +1,122 @@ +---------------------------------------------------------------- +---------------------------------------------------------------- +Processes (Processes.dll) +Version: 1.0.0.1 +Release: 12.december.2004 +Description:Nullsoft Installer (NSIS) plug-in for managing?! + Windows processes. + +Copyright: © 2004 Hardwired. No rights reserved. + There is no restriction and no guaranty for using + this software. + +Author: Andrei Ciubotaru [Hardwired] + Lead Developer ICode&Ideas SRL (http://www.icode.ro) + hardwiredteks@gmail.com, hardwired@icode.ro + +---------------------------------------------------------------- +---------------------------------------------------------------- +INTRODUCTION + + The Need For Plug-in - I need it for the one of my installers. + + Briefly: Use it when you need to find\kill a process when +installing\uninstalling some application. Also, use it when you +need to test the presence of a device driver. + + +SUPPORT + + Supported platforms are: WinNT,Win2K,WinXP and Win2003 Server. + + +DESCRIPTION + + Processes::FindProcess + + Searches the currently running processes for the given + process name. + + return: 1 - the process was found + 0 - the process was not found + + Processes::KillProcess + + Searches the currently running processes for the given + process name. If the process is found then the it gets + killed. + + return: 1 - the process was found and killed + 0 - the process was not found or the process + cannot be killed (insuficient rights) + + Processes::FindDevice + + Searches the installed devices drivers for the given + device base name. + (important: I said BASE NAME not FILENAME) + + return: 1 - the device driver was found + 0 - the device driver was not found + + +USAGE + + First of all, does not matter where you use it. Ofcourse, the +routines must be called inside of a Section/Function scope. + + Processes::FindProcess "process_name.exe" + Pop $R0 + + StrCmp $R0 "1" make_my_day noooooo + + make_my_day: + ... + + noooooo: + ... + + + Processes::KillProcess "process_name.exe" + Pop $R0 + + StrCmp $R0 "1" dead_meat why_wont_you_die + + dead_meat: + ... + + why_wont_you_die: + ... + + + Processes::FindDevice "device_base_name" + Pop $R0 + + StrCmp $R0 "1" blabla more_blabla + + blabla: + ... + + more_blabla: + ... + + +THANKS + + Sunil Kamath for inspiring me. I wanted to use its FindProcDLL +but my requirements made it imposible. + + Nullsoft for creating this very powerfull installer. One big, +free and full-featured (hmmm... and guiless for the moment) mean +install machine!:) + + ME for being such a great coder... + ... HAHAHAHAHAHAHA! + +ONE MORE THING + + If you use the plugin or it's source-code, I would apreciate +if my name is mentioned. + +---------------------------------------------------------------- +---------------------------------------------------------------- diff --git a/admin/win/nsi/nsis_processes/src/processes.vcproj b/admin/win/nsi/nsis_processes/src/processes.vcproj new file mode 100755 index 000000000..245cbc99f --- /dev/null +++ b/admin/win/nsi/nsis_processes/src/processes.vcproj @@ -0,0 +1,222 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/admin/win/nsi/nsis_processes/src/resource.h b/admin/win/nsi/nsis_processes/src/resource.h new file mode 100755 index 000000000..506377e21 --- /dev/null +++ b/admin/win/nsi/nsis_processes/src/resource.h @@ -0,0 +1,15 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by processes.rc +// + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 101 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1001 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/admin/win/nsi/page_header.bmp b/admin/win/nsi/page_header.bmp old mode 100755 new mode 100644 diff --git a/admin/win/nsi/revision.txt b/admin/win/nsi/revision.txt index efee1f88b..0fd0714a5 100644 --- a/admin/win/nsi/revision.txt +++ b/admin/win/nsi/revision.txt @@ -1 +1 @@ -78 \ No newline at end of file +103 \ No newline at end of file diff --git a/admin/win/nsi/tomahawk.ini b/admin/win/nsi/tomahawk.ini old mode 100755 new mode 100644 diff --git a/admin/win/nsi/tomahawk.nsi b/admin/win/nsi/tomahawk.nsi old mode 100755 new mode 100644 index a9b6e8081..11c2469ba --- a/admin/win/nsi/tomahawk.nsi +++ b/admin/win/nsi/tomahawk.nsi @@ -4,7 +4,7 @@ ; Some installer script options (comment-out options not required) ;----------------------------------------------------------------------------- ;!define OPTION_LICENSE_AGREEMENT -;!define OPTION_UAC_PLUGIN_ENHANCED +!define OPTION_UAC_PLUGIN_ENHANCED !define OPTION_SECTION_SC_START_MENU !define OPTION_SECTION_SC_DESKTOP !define OPTION_SECTION_SC_QUICK_LAUNCH @@ -15,7 +15,7 @@ ;----------------------------------------------------------------------------- ; Some paths. ;----------------------------------------------------------------------------- -!define MING_PATH "/usr/i686-pc-mingw32/sys-root/mingw" +!define MING_PATH "/usr/i686-w64-mingw32/sys-root/mingw" !define MING_BIN "${MING_PATH}/bin" !define MING_DLL_PATH "${MING_BIN}" !define MING_LIB "${MING_PATH}/lib" @@ -23,6 +23,7 @@ !define BUILD_PATH "${ROOT_PATH}\build" !define QT_DLL_PATH "${MING_BIN}" !define SQLITE_DLL_PATH "${MING_LIB}/qt4/plugins/sqldrivers" +!define IMAGEFORMATS_DLL_PATH "${MING_LIB}/qt4/plugins/imageformats" ;----------------------------------------------------------------------------- ; Increment installer revision number as part of this script. @@ -34,7 +35,7 @@ !define VER_MAJOR "0" !define VER_MINOR "0" -!define VER_BUILD "0" +!define VER_BUILD "3" !define VERSION "${VER_MAJOR}.${VER_MINOR}.${VER_BUILD}" @@ -57,17 +58,10 @@ InstType Full InstType Minimal CRCCheck On SetCompressor /SOLID lzma +RequestExecutionLevel user ;Now using the UAC plugin. ReserveFile tomahawk.ini ReserveFile "${NSISDIR}\Plugins\InstallOptions.dll" -;The UAC plugin provides an elevated user. -;Otherwise request admin level here. -!ifdef OPTION_UAC_PLUGIN_ENHANCED - RequestExecutionLevel user -!else - RequestExecutionLevel admin -!endif - ;----------------------------------------------------------------------------- ; Include some required header files. ;----------------------------------------------------------------------------- @@ -78,9 +72,7 @@ ReserveFile "${NSISDIR}\Plugins\InstallOptions.dll" !include Memento.nsh ;Remember user selections. !include WinVer.nsh ;Windows version detection. !include WordFunc.nsh ;Used by VersionCompare macro function. -!ifdef OPTION_UAC_PLUGIN_ENHANCED - !include UAC.nsh ;Used by the UAC elevation to install as user or admin. -!endif +!include UAC.nsh ;Used by the UAC elevation to install as user or admin. ;----------------------------------------------------------------------------- ; Memento selections stored in registry. @@ -145,18 +137,46 @@ UninstPage custom un.UnPageUserAppData un.UnPageUserAppDataLeave ############################################################################## Function LaunchTomahawk - !ifdef OPTION_UAC_PLUGIN_ENHANCED - ${UAC.CallFunctionAsUser} LaunchTomahawkAsUser - !else - Exec "$INSTDIR\tomahawk.exe" - !endif + ${UAC.CallFunctionAsUser} LaunchTomahawkAsUser FunctionEnd -!ifdef OPTION_UAC_PLUGIN_ENHANCED Function LaunchTomahawkAsUser Exec "$INSTDIR\tomahawk.exe" FunctionEnd -!endif + +############################################################################## +# # +# PROCESS HANDLING FUNCTIONS AND MACROS # +# # +############################################################################## + +!macro CheckForProcess processName gotoWhenFound gotoWhenNotFound + Processes::FindProcess ${processName} + StrCmp $R0 "0" ${gotoWhenNotFound} ${gotoWhenFound} +!macroend + +!macro ConfirmEndProcess processName + MessageBox MB_YESNO|MB_ICONEXCLAMATION \ + "Found ${processName} process(s) which need to be stopped.$\nDo you want the installer to stop these for you?" \ + IDYES process_${processName}_kill IDNO process_${processName}_ended + process_${processName}_kill: + DetailPrint "Killing ${processName} processes." + Processes::KillProcess ${processName} + Sleep 1500 + StrCmp $R0 "1" process_${processName}_ended + DetailPrint "Process to kill not found!" + process_${processName}_ended: +!macroend + +!macro CheckAndConfirmEndProcess processName + !insertmacro CheckForProcess ${processName} 0 no_process_${processName}_to_end + !insertmacro ConfirmEndProcess ${processName} + no_process_${processName}_to_end: +!macroend + +Function EnsureTomahawkShutdown + !insertmacro CheckAndConfirmEndProcess "tomahawk.exe" +FunctionEnd ############################################################################## # # @@ -225,12 +245,10 @@ Function PageLeaveReinstall Delete $R1 RMDir $INSTDIR no_remove_uninstaller: - StrCmp $R0 "2" +2 0 + StrCmp $R0 "2" 0 +3 + UAC::Unload + Quit BringToFront - !ifdef OPTION_UAC_PLUGIN_ENHANCED - UAC::Unload - Quit - !endif reinst_done: FunctionEnd @@ -249,14 +267,32 @@ Section "Tomahawk Player" SEC_TOMAHAWK_PLAYER SetDetailsPrint listonly SetOutPath "$INSTDIR" - ;Main executable. - File "${BUILD_PATH}\tomahawk.exe" + !ifdef INSTALL_PATH + ;Main executable. + File "${INSTALL_PATH}\bin\tomahawk.exe" - ;Plugins - File "${BUILD_PATH}\src\libtomahawk\libtomahawklib.dll" - File "${BUILD_PATH}\libsip_jabber.dll" - File "${BUILD_PATH}\libsip_twitter.dll" - File "${BUILD_PATH}\libsip_zeroconf.dll" + File "${INSTALL_PATH}\lib\librtaudio.dll" + File "${INSTALL_PATH}\lib\libqxtweb-standalone.dll" + File "${INSTALL_PATH}\lib\libtomahawk_jdns.dll" + File "${INSTALL_PATH}\lib\libtomahawk_qtweetlib.dll" + File "${INSTALL_PATH}\lib\libtomahawklib.dll" + File "${INSTALL_PATH}\lib\libtomahawk_sipjabber.dll" + File "${INSTALL_PATH}\lib\libtomahawk_siptwitter.dll" + File "${INSTALL_PATH}\lib\libtomahawk_sipzeroconf.dll" + !endif + !ifndef INSTALL_PATH + ;Main executable. + File "${BUILD_PATH}\tomahawk.exe" + + File "${BUILD_PATH}\thirdparty\rtaudio\librtaudio.dll" + File "${BUILD_PATH}\thirdparty\qxt\qxtweb-standalone\libqxtweb-standalone.dll" + File "${BUILD_PATH}\thirdparty\jdns\libtomahawk_jdns.dll" + File "${BUILD_PATH}\thirdparty\qtweetlib\libtomahawk_qtweetlib.dll" + File "${BUILD_PATH}\src\libtomahawk\libtomahawklib.dll" + File "${BUILD_PATH}\libtomahawk_sipjabber.dll" + File "${BUILD_PATH}\libtomahawk_siptwitter.dll" + File "${BUILD_PATH}\libtomahawk_sipzeroconf.dll" + !endif ;License & release notes. File "${ROOT_PATH}\LICENSE.txt" @@ -274,6 +310,12 @@ Section "Tomahawk Player" SEC_TOMAHAWK_PLAYER SetOutPath "$INSTDIR\sqldrivers" File "${SQLITE_DLL_PATH}\qsqlite4.dll" SetOutPath "$INSTDIR" + + ;Image plugins + SetOutPath "$INSTDIR\imageformats" + File "${IMAGEFORMATS_DLL_PATH}\qgif4.dll" + File "${IMAGEFORMATS_DLL_PATH}\qjpeg4.dll" + SetOutPath "$INSTDIR" ;Cygwin/c++ stuff ;File "${MING_DLL_PATH}\cygmad-0.dll" @@ -283,8 +325,7 @@ Section "Tomahawk Player" SEC_TOMAHAWK_PLAYER File "${MING_DLL_PATH}\libstdc++-6.dll" ;Audio stuff - File "${BUILD_PATH}\thirdparty\rtaudio\librtaudio.dll" - ;File "${MING_DLL_PATH}\libmad.dll" + File "${MING_DLL_PATH}\libmad-0.dll" File "${MING_DLL_PATH}\libogg-0.dll" File "${MING_DLL_PATH}\libvorbisfile-3.dll" File "${MING_DLL_PATH}\libvorbis-0.dll" @@ -296,6 +337,7 @@ Section "Tomahawk Player" SEC_TOMAHAWK_PLAYER File "${MING_DLL_PATH}\libtag.dll" File "${MING_DLL_PATH}\libgloox-8.dll" File "${MING_DLL_PATH}\libpng15-15.dll" + File "${MING_DLL_PATH}\libjpeg-8.dll" File "${MING_DLL_PATH}\zlib1.dll" File "${MING_DLL_PATH}\libechonest.dll" @@ -304,9 +346,7 @@ Section "Tomahawk Player" SEC_TOMAHAWK_PLAYER File "${MING_LIB}\libclucene-core.dll" File "${MING_LIB}\libclucene-shared.dll" - File "${BUILD_PATH}\thirdparty\qxt\qxtweb-standalone\libqxtweb-standalone.dll" - File "${BUILD_PATH}\thirdparty\jdns\libtomahawk_jdns.dll" - File "${BUILD_PATH}\thirdparty\qtweetlib\libtomahawk_qtweetlib.dll" + File "${MING_BIN}\libqtsparkle.dll" SectionEnd SectionGroup "Shortcuts" @@ -323,7 +363,7 @@ SectionGroup "Shortcuts" CreateShortCut "$SMPROGRAMS\Tomahawk\LICENSE.lnk" "$INSTDIR\LICENSE.txt" CreateShortCut "$SMPROGRAMS\Tomahawk\Tomahawk.lnk" "$INSTDIR\tomahawk.exe" CreateShortCut "$SMPROGRAMS\Tomahawk\Release notes.lnk" "$INSTDIR\NOTES.txt" - CreateShortCut "$SMPROGRAMS\Tomahawk\Uninstall.lnk" "$INSTDIR\Uninstall.exe" + CreateShortCut "$SMPROGRAMS\Tomahawk\Uninstall.lnk" "$INSTDIR\uninstall.exe" SetShellVarContext current ${MementoSectionEnd} !endif @@ -367,7 +407,7 @@ Section -post SetDetailsPrint textonly DetailPrint "Writing Uninstaller" SetDetailsPrint listonly - WriteUninstaller $INSTDIR\Uninstall.exe + WriteUninstaller $INSTDIR\uninstall.exe ;Registry keys required for installer version handling and uninstaller. SetDetailsPrint textonly @@ -395,6 +435,12 @@ Section -post WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Tomahawk" "NoModify" "1" WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Tomahawk" "NoRepair" "1" + ; Register tomahawk:// protocol handler + WriteRegStr HKCR "tomahawk" "" "URL: Tomahawk Protocol" + WriteRegStr HKCR "tomahawk\DefaultIcon" "" $INSTDIR\tomahawk.exe,1 + WriteRegStr HKCR "tomahawk\shell" "" "open" + WriteRegStr HKCR "tomahawk\shell\open\command" "" '"$INSTDIR\tomahawk.exe" "%1"' + SetDetailsPrint textonly DetailPrint "Finsihed." SectionEnd @@ -454,6 +500,8 @@ Section Uninstall DeleteRegValue HKLM "Software\Tomahawk" "" DeleteRegKey HKLM "Software\Tomahawk" + DeleteRegKey HKCR "tomahawk" + ;Start menu shortcuts. !ifdef OPTION_SECTION_SC_START_MENU SetShellVarContext all @@ -503,27 +551,25 @@ Function .onInit ${MementoSectionRestore} - !ifdef OPTION_UAC_PLUGIN_ENHANCED - UAC_Elevate: - UAC::RunElevated - StrCmp 1223 $0 UAC_ElevationAborted ; UAC dialog aborted by user? - StrCmp 0 $0 0 UAC_Err ; Error? - StrCmp 1 $1 0 UAC_Success ;Are we the real deal or just the wrapper? - Quit + UAC_Elevate: + UAC::RunElevated + StrCmp 1223 $0 UAC_ElevationAborted ; UAC dialog aborted by user? + StrCmp 0 $0 0 UAC_Err ; Error? + StrCmp 1 $1 0 UAC_Success ;Are we the real deal or just the wrapper? + Quit - UAC_Err: - MessageBox MB_ICONSTOP "Unable to elevate, error $0" - Abort + UAC_Err: + MessageBox MB_ICONSTOP "Unable to elevate, error $0" + Abort - UAC_ElevationAborted: - Abort + UAC_ElevationAborted: + Abort - UAC_Success: - StrCmp 1 $3 +4 ;Admin? - StrCmp 3 $1 0 UAC_ElevationAborted ;Try again? - MessageBox MB_ICONSTOP "This installer requires admin access, try again" - goto UAC_Elevate - !endif + UAC_Success: + StrCmp 1 $3 +4 ;Admin? + StrCmp 3 $1 0 UAC_ElevationAborted ;Try again? + MessageBox MB_ICONSTOP "This installer requires admin access, try again" + goto UAC_Elevate ;Prevent multiple instances. System::Call 'kernel32::CreateMutexA(i 0, i 0, t "tomahawkInstaller") i .r1 ?e' @@ -531,19 +577,25 @@ Function .onInit StrCmp $R0 0 +3 MessageBox MB_OK|MB_ICONEXCLAMATION "The installer is already running." Abort + + ;Use available InstallLocation when possible. This is useful in the uninstaller + ;via re-install, which would otherwise use a default location - a bug. + ReadRegStr $R0 HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Tomahawk" "InstallLocation" + StrCmp $R0 "" SkipSetInstDir + StrCpy $INSTDIR $R0 + SkipSetInstDir: + + ;Shutdown Tomahawk in case Add/Remove re-installer option used. + Call EnsureTomahawkShutdown FunctionEnd Function .onInstSuccess ${MementoSectionSave} - !ifdef OPTION_UAC_PLUGIN_ENHANCED - UAC::Unload ;Must call unload! - !endif + UAC::Unload ;Must call unload! FunctionEnd Function .onInstFailed - !ifdef OPTION_UAC_PLUGIN_ENHANCED - UAC::Unload ;Must call unload! - !endif + UAC::Unload ;Must call unload! FunctionEnd ############################################################################## @@ -553,27 +605,25 @@ FunctionEnd ############################################################################## Function un.onInit - !ifdef OPTION_UAC_PLUGIN_ENHANCED - UAC_Elevate: - UAC::RunElevated - StrCmp 1223 $0 UAC_ElevationAborted ; UAC dialog aborted by user? - StrCmp 0 $0 0 UAC_Err ; Error? - StrCmp 1 $1 0 UAC_Success ;Are we the real deal or just the wrapper? - Quit + UAC_Elevate: + UAC::RunElevated + StrCmp 1223 $0 UAC_ElevationAborted ; UAC dialog aborted by user? + StrCmp 0 $0 0 UAC_Err ; Error? + StrCmp 1 $1 0 UAC_Success ;Are we the real deal or just the wrapper? + Quit - UAC_Err: - MessageBox MB_ICONSTOP "Unable to elevate, error $0" - Abort + UAC_Err: + MessageBox MB_ICONSTOP "Unable to elevate, error $0" + Abort - UAC_ElevationAborted: - Abort + UAC_ElevationAborted: + Abort - UAC_Success: - StrCmp 1 $3 +4 ;Admin? - StrCmp 3 $1 0 UAC_ElevationAborted ;Try again? - MessageBox MB_ICONSTOP "This uninstaller requires admin access, try again" - goto UAC_Elevate - !endif + UAC_Success: + StrCmp 1 $3 +4 ;Admin? + StrCmp 3 $1 0 UAC_ElevationAborted ;Try again? + MessageBox MB_ICONSTOP "This uninstaller requires admin access, try again" + goto UAC_Elevate ;Prevent multiple instances. System::Call 'kernel32::CreateMutexA(i 0, i 0, t "tomahawkUninstaller") i .r1 ?e' @@ -584,13 +634,9 @@ Function un.onInit FunctionEnd Function un.onUnInstSuccess - !ifdef OPTION_UAC_PLUGIN_ENHANCED - UAC::Unload ;Must call unload! - !endif + UAC::Unload ;Must call unload! FunctionEnd Function un.onUnInstFailed - !ifdef OPTION_UAC_PLUGIN_ENHANCED - UAC::Unload ;Must call unload! - !endif + UAC::Unload ;Must call unload! FunctionEnd diff --git a/admin/win/nsi/welcome.bmp b/admin/win/nsi/welcome.bmp old mode 100755 new mode 100644 diff --git a/admin/win/sparklewin-beta.rss b/admin/win/sparklewin-beta.rss index 52e9d0302..335303fdd 100644 --- a/admin/win/sparklewin-beta.rss +++ b/admin/win/sparklewin-beta.rss @@ -3,15 +3,23 @@ Tomahawk Player http://www.gettomahawk.com - Tomahawk Player + Tomahawk Player Beta en - Version 0.0.1 (Tomahawk Player - It Lives!) + Version 0.0.1 (Tomahawk Player Beta - It Lives?) - https://github.com/tomahawk-player/tomahawk/blob/master/ChangeLog + https://github.com/tomahawk-player/tomahawk/raw/0.0.1/ChangeLog - Fri, 04 Mar 2011 16:05:15 -0500 - + Fri, 25 Mar 2011 00:00:01 +0100 + + + + Version 0.0.2 (Tomahawk Player Beta - It Lives?) + + https://github.com/tomahawk-player/tomahawk/raw/0.0.2/ChangeLog + + Mon, 28 Mar 2011 05:00:02 +0100 + diff --git a/admin/win/sparklewin.rss b/admin/win/sparklewin.rss index 52e9d0302..9986a74ad 100644 --- a/admin/win/sparklewin.rss +++ b/admin/win/sparklewin.rss @@ -8,10 +8,18 @@ Version 0.0.1 (Tomahawk Player - It Lives!) - https://github.com/tomahawk-player/tomahawk/blob/master/ChangeLog + https://github.com/tomahawk-player/tomahawk/raw/0.0.1/ChangeLog - Fri, 04 Mar 2011 16:05:15 -0500 - + Fri, 25 Mar 2011 00:00:01 +0100 + + + + Version 0.0.2 (Tomahawk Player - It Lives!) + + https://github.com/tomahawk-player/tomahawk/raw/0.0.2/ChangeLog + + Mon, 28 Mar 2011 05:00:02 +0100 + diff --git a/cmake_uninstall.cmake.in b/cmake_uninstall.cmake.in new file mode 100644 index 000000000..df95fb9d8 --- /dev/null +++ b/cmake_uninstall.cmake.in @@ -0,0 +1,21 @@ +IF(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt") + MESSAGE(FATAL_ERROR "Cannot find install manifest: \"@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt\"") +ENDIF(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt") + +FILE(READ "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt" files) +STRING(REGEX REPLACE "\n" ";" files "${files}") +FOREACH(file ${files}) + MESSAGE(STATUS "Uninstalling \"$ENV{DESTDIR}${file}\"") + IF(EXISTS "$ENV{DESTDIR}${file}") + EXEC_PROGRAM( + "@CMAKE_COMMAND@" ARGS "-E remove \"$ENV{DESTDIR}${file}\"" + OUTPUT_VARIABLE rm_out + RETURN_VALUE rm_retval + ) + IF(NOT "${rm_retval}" STREQUAL 0) + MESSAGE(FATAL_ERROR "Problem when removing \"$ENV{DESTDIR}${file}\"") + ENDIF(NOT "${rm_retval}" STREQUAL 0) + ELSE(EXISTS "$ENV{DESTDIR}${file}") + MESSAGE(STATUS "File \"$ENV{DESTDIR}${file}\" does not exist.") + ENDIF(EXISTS "$ENV{DESTDIR}${file}") +ENDFOREACH(file) diff --git a/data/icons/tomahawk-icon-128x128.png b/data/icons/tomahawk-icon-128x128.png index 6c6b0974d..bbee6eb80 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 09a9e6f03..cd5709e6b 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 a74428897..227abc838 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 1b1b2ce00..396e3f8fa 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 c0e914aea..e42d30e85 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 d30c2e893..d8b278ce5 100644 Binary files a/data/icons/tomahawk-icon-64x64.png and b/data/icons/tomahawk-icon-64x64.png differ diff --git a/data/images/back.png b/data/images/back.png index 88db1dc23..3c9be3ebe 100644 Binary files a/data/images/back.png and b/data/images/back.png differ diff --git a/data/images/configure.png b/data/images/configure.png new file mode 100644 index 000000000..5ce478b1a Binary files /dev/null and b/data/images/configure.png differ diff --git a/data/images/forward.png b/data/images/forward.png index 3e0c42476..4b9ede5c5 100644 Binary files a/data/images/forward.png and b/data/images/forward.png differ diff --git a/data/images/home.png b/data/images/home.png index e36f187f6..5f86690c9 100644 Binary files a/data/images/home.png and b/data/images/home.png differ diff --git a/data/images/loading-animation.gif b/data/images/loading-animation.gif index 0be11d9d2..7fc905988 100644 Binary files a/data/images/loading-animation.gif and b/data/images/loading-animation.gif differ diff --git a/data/images/music-icon.png b/data/images/music-icon.png new file mode 100644 index 000000000..67e75a0b0 Binary files /dev/null and b/data/images/music-icon.png differ diff --git a/data/images/playlist-icon.png b/data/images/playlist-icon.png index 99ac953b1..658b5bc51 100644 Binary files a/data/images/playlist-icon.png and b/data/images/playlist-icon.png differ diff --git a/data/images/user-avatar.png b/data/images/user-avatar.png index 5be7b700f..0cbd4d6ce 100644 Binary files a/data/images/user-avatar.png and b/data/images/user-avatar.png differ diff --git a/include/tomahawk/infosystem.h b/include/tomahawk/infosystem.h deleted file mode 100644 index 983305c47..000000000 --- a/include/tomahawk/infosystem.h +++ /dev/null @@ -1,148 +0,0 @@ -#ifndef TOMAHAWK_INFOSYSTEM_H -#define TOMAHAWK_INFOSYSTEM_H - -#include -#include -#include -#include -#include -#include -#include - - -namespace Tomahawk { - -namespace InfoSystem { - -enum InfoType { - InfoTrackID, - InfoTrackArtist, - InfoTrackAlbum, - InfoTrackGenre, - InfoTrackComposer, - InfoTrackDate, - InfoTrackNumber, - InfoTrackDiscNumber, - InfoTrackBitRate, - InfoTrackLength, - InfoTrackSampleRate, - InfoTrackFileSize, - InfoTrackBPM, - InfoTrackReplayGain, - InfoTrackReplayPeakGain, - InfoTrackLyrics, - InfoTrackLocation, - InfoTrackProfile, - InfoTrackEnergy, - InfoTrackDanceability, - InfoTrackTempo, - InfoTrackLoudness, - - InfoArtistID, - InfoArtistName, - InfoArtistBiography, - InfoArtistBlog, - InfoArtistFamiliarity, - InfoArtistHotttness, - InfoArtistImages, - InfoArtistNews, - InfoArtistProfile, - InfoArtistReviews, - InfoArtistSongs, - InfoArtistSimilars, - InfoArtistTerms, - InfoArtistLinks, - InfoArtistVideos, - - InfoAlbumID, - InfoAlbumName, - InfoAlbumArtist, - InfoAlbumDate, - InfoAlbumGenre, - InfoAlbumComposer, - InfoMiscTopHotttness, - InfoMiscTopTerms, - - InfoNoInfo -}; - -typedef QMap< InfoType, QVariant > InfoMap; -typedef QMap< QString, QMap< QString, QString > > InfoGenericMap; -typedef QHash InfoCustomDataHash; -typedef QHash MusixMatchHash; - -class InfoPlugin : public QObject -{ - Q_OBJECT - -public: - InfoPlugin(QObject *parent) - :QObject(parent) - { - qDebug() << Q_FUNC_INFO; - } - ~InfoPlugin() - { - qDebug() << Q_FUNC_INFO; - } - - virtual void getInfo(const QString &caller, const InfoType type, const QVariant &data, Tomahawk::InfoSystem::InfoCustomDataHash customData) = 0; - -signals: - void info(QString caller, Tomahawk::InfoSystem::InfoType type, QVariant input, QVariant output, Tomahawk::InfoSystem::InfoCustomDataHash customData); - void finished(QString, Tomahawk::InfoSystem::InfoType); - -protected: - InfoType m_type; -}; - -typedef QWeakPointer< InfoPlugin > InfoPluginPtr; - -class InfoSystem : public QObject -{ - Q_OBJECT - -public: - - - InfoSystem(QObject *parent); - ~InfoSystem() - { - qDebug() << Q_FUNC_INFO; - } - - void registerInfoTypes(const InfoPluginPtr &plugin, const QSet< InfoType > &types); - - void getInfo(const QString &caller, const InfoType type, const QVariant &data, InfoCustomDataHash customData); - void getInfo(const QString &caller, const InfoMap &input, InfoCustomDataHash customData); - -signals: - void info(QString caller, Tomahawk::InfoSystem::InfoType, QVariant input, QVariant output, Tomahawk::InfoSystem::InfoCustomDataHash customData); - void finished(QString target); - -public slots: - void infoSlot(QString target, Tomahawk::InfoSystem::InfoType type, QVariant input, QVariant output, Tomahawk::InfoSystem::InfoCustomDataHash customData); - void finishedSlot(QString target,Tomahawk::InfoSystem::InfoType type); - -private: - - QLinkedList< InfoPluginPtr > determineOrderedMatches(const InfoType type) const; - - QMap< InfoType, QLinkedList > m_infoMap; - - // For now, statically instantiate plugins; this is just somewhere to keep them - QLinkedList m_plugins; - - QHash< QString, QHash< Tomahawk::InfoSystem::InfoType, int > > m_dataTracker; - -}; - -} - -} - -Q_DECLARE_METATYPE(Tomahawk::InfoSystem::InfoGenericMap) -Q_DECLARE_METATYPE(Tomahawk::InfoSystem::InfoCustomDataHash); -Q_DECLARE_METATYPE(Tomahawk::InfoSystem::MusixMatchHash) - -#endif // TOMAHAWK_INFOSYSTEM_H diff --git a/include/tomahawk/plugin_includes.h b/include/tomahawk/plugin_includes.h index 77c472446..f8844e05f 100644 --- a/include/tomahawk/plugin_includes.h +++ b/include/tomahawk/plugin_includes.h @@ -1,3 +1,21 @@ +/* === 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 PLUGIN_INCLUDES_H #define PLUGIN_INCLUDES_H diff --git a/include/tomahawk/tomahawkapp.h b/include/tomahawk/tomahawkapp.h index 36235edd7..2d55f3a1b 100644 --- a/include/tomahawk/tomahawkapp.h +++ b/include/tomahawk/tomahawkapp.h @@ -1,3 +1,21 @@ +/* === 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 TOMAHAWKAPP_H #define TOMAHAWKAPP_H @@ -16,15 +34,17 @@ #include "QxtHttpServerConnector" #include "QxtHttpSessionManager" -#include "tomahawk/tomahawkplugin.h" #include "typedefs.h" #include "playlist.h" #include "resolver.h" +#include "network/servent.h" #include "utils/tomahawkutils.h" +#include "kdsingleapplicationguard/kdsingleapplicationguard.h" class AudioEngine; class Database; +class ScanManager; class SipHandler; class TomahawkSettings; class XMPPBot; @@ -59,10 +79,10 @@ public: TomahawkApp( int& argc, char *argv[] ); virtual ~TomahawkApp(); + void init(); static TomahawkApp* instance(); SipHandler* sipHandler() { return m_sipHandler; } - Tomahawk::InfoSystem::InfoSystem* infoSystem() { return m_infoSystem; } XMPPBot* xmppBot() { return m_xmppBot; } #ifndef TOMAHAWK_HEADLESS @@ -70,16 +90,22 @@ public: TomahawkWindow* mainWindow() const { return m_mainwindow; } #endif - void addScriptResolver( const QString& scriptPath ); - void removeScriptResolver( const QString& scriptPath ); + void enableScriptResolver( const QString& scriptPath ); + void disableScriptResolver( const QString& scriptPath ); + Tomahawk::ExternalResolver* resolverForPath( const QString& scriptPath ); // PlatformInterface virtual void activate(); virtual bool loadUrl( const QString& url ); + // because QApplication::arguments() is expensive + bool scrubFriendlyName() const { return m_scrubFriendlyName; } + +public slots: + void instanceStarted( KDSingleApplicationGuard::Instance ); + private slots: void setupSIP(); - void messageReceived( const QString& ); private: void initLocalCollection(); @@ -91,15 +117,17 @@ private: void startHTTP(); QList m_collections; - QList m_plugins; - QList m_scriptResolvers; + QHash m_scriptResolvers; Database* m_database; + ScanManager *m_scanManager; AudioEngine* m_audioEngine; SipHandler* m_sipHandler; Servent* m_servent; + Tomahawk::InfoSystem::InfoSystem* m_infoSystem; XMPPBot* m_xmppBot; Tomahawk::ShortcutHandler* m_shortcutHandler; + bool m_scrubFriendlyName; #ifdef LIBLASTFM_FOUND Scrobbler* m_scrobbler; @@ -107,14 +135,13 @@ private: #ifndef TOMAHAWK_HEADLESS TomahawkWindow* m_mainwindow; -#endif +#endif bool m_headless; - Tomahawk::InfoSystem::InfoSystem* m_infoSystem; - QxtHttpServerConnector m_connector; QxtHttpSessionManager m_session; }; #endif // TOMAHAWKAPP_H + diff --git a/include/tomahawk/tomahawkplugin.h b/include/tomahawk/tomahawkplugin.h deleted file mode 100644 index 9f5f81d0e..000000000 --- a/include/tomahawk/tomahawkplugin.h +++ /dev/null @@ -1,31 +0,0 @@ -#ifndef TOMAHAWK_PLUGIN_H -#define TOMAHAWK_PLUGIN_H - -#include -#include - -#include "pluginapi.h" - -class TomahawkPlugin -{ -public: - TomahawkPlugin(){}; - TomahawkPlugin(Tomahawk::PluginAPI * api) - : m_api(api) {}; - - virtual TomahawkPlugin * factory(Tomahawk::PluginAPI * api) = 0; - - virtual QString name() const = 0; - virtual QString description() const = 0; - -protected: - Tomahawk::PluginAPI * api() const { return m_api; }; - -private: - Tomahawk::PluginAPI * m_api; - -}; - -Q_DECLARE_INTERFACE(TomahawkPlugin, "org.tomahawk.TomahawkPlugin/1.0") - -#endif diff --git a/lang/tomahawk_de.ts b/lang/tomahawk_de.ts new file mode 100644 index 000000000..1a028aea7 --- /dev/null +++ b/lang/tomahawk_de.ts @@ -0,0 +1,1484 @@ + + + + + AlbumModel + + + Album + Album + + + + + All albums from %1 + Alle Alben von %1 + + + + AudioControls + + + Form + + + + + Prev + + + + + Play + + + + + Pause + + + + + Next + + + + + TextLabel + + + + + Artist + + + + + Album + + + + + Owner + + + + + Time + + + + + Time Left + + + + + Shuffle + + + + + Repeat + + + + + Low + + + + + High + + + + + ClearButton + + + Clear + Leeren + + + + CollectionFlatModel + + + Your Collection + Deine Sammlung + + + + Collection of %1 + Deine Sammlung von %1 + + + + CollectionModel + + + Name + Name + + + + Tracks + Stücke + + + + Duration + Spieldauer + + + + Origin + Quelle + + + + CollectionView + + + &Play + &Abspielen + + + + Add to &Queue + Zur &Warteschlange hinzufügen + + + + This collection is empty. + Diese Sammlung ist leer. + + + + InfoBar + + + InfoBar + + + + + + + TextLabel + + + + + JabberPlugin + + + Add Friend... + Freund hinzufügen… + + + + + Add Friend + Freund hinzufügen + + + + + Enter Jabber ID: + Jabber-ID eingeben: + + + + Jabber_p + + + Authorize User + Benutzer authorisieren + + + + Do you want to grant <b>%1</b> access to your Collection? + Willst du <b>%1</b> wirklich den Zugriff auf deine Sammlung erlauben? + + + + NewPlaylistWidget + + + Enter a title for the new playlist: + Gib einen Titel für die neue Playliste ein: + + + + Tomahawk offers a variety of ways to help you create playlists and find music you enjoy! + Tomahawk bietet verschiedene Wege, Playlisten zu erstellen und Musik zu finden, die du magst! + + + + Just enter a genre or tag name and Tomahawk will suggest a few songs to get you started with your new playlist: + Gib einfach ein Genre oder einen Tagnamen ein und Tomahawk wird dir einige Lieder vorschlagen, um dir zu helfen, eine neue Playliste zu erstellen: + + + + &Create Playlist + Playliste &erstellen + + + + Create a new playlist + Erstelle eine neue Playliste + + + + PlaylistDelegate + + + %1 tracks + %1 Stücke + + + + PlaylistManager + + + All available tracks + Alle verfügbaren Stücke + + + + All available albums + Alle verfügbaren Alben + + + + PlaylistModel + + + A playlist by %1 + Eine Playliste von %1 + + + + you + dir + + + + PlaylistView + + + &Play + &Abspielen + + + + Add to &Queue + In &Warteschlange einreihen + + + + &Delete Items + Elemente &entfernen + + + + &Delete Item + Element &entfernen + + + + This playlist is currently empty. Add some tracks to it and enjoy the music! + Die Playliste ist derzeit leer. Füge einige Stücke hinzu und genieße die Musik! + + + + ProxyDialog + + + Proxy Settings + Proxy-Einstellungen + + + + Host + Rechnername + + + + Port + Port + + + + User + Benutzer + + + + Password + Passwort + + + + Type + Typ + + + + QueueView + + + + Click to show queue + Klicke hier, um die Warteschlange anzuzeigen + + + + Click to hide queue + Klicke hier, um die Warteschlange auszublenden + + + + SearchLineEdit + + + Search + Suchen + + + + SettingsDialog + + + Music Player Settings + Übersetzung eher dürftig + Einstellungen für das Musikabspielprogramm + + + + Jabber + Jabber + + + + Jabber ID: + Jabber-ID: + + + + + Password: + Passwort: + + + + Advanced Jabber Settings + Erweiterte Einstellungen für Jabber + + + + Server: + Server: + + + + Port: + Port: + + + + Network + Netzwerk + + + + Advanced Network Settings + Erweiterte Netzwerkeinstellungen + + + + If you're having difficulty connecting to peers, try setting this to your external IP address/host name and a port number (default 50210). Make sure to forward that port to this machine! + Wenn du Schwierigkeiten hast, zu anderen Leuten zu verbinden, versuche diene externe IP-Addresse/Rechnernamen und eine Portnummer (Standard 50210) hier einzutragen. Stelle sicher, den Port entsprechend an diesen Rechner weiterzuleiten! + + + + Static Host Name: + Statischer Rechnername: + + + + Static Port: + Statischer Port: + + + + Always use static host name/port? (Overrides UPnP discovery/port forwarding) + Statischen Rechnernamen/Port immer benutzen? (Überschreibt UPnP-discovery/Portweiterleitung) + + + + Proxy Settings... + Proxy-Einstellungen… + + + + Playdar HTTP API + Playdar HTTP API + + + + Connect automatically when Tomahawk starts + Automatisch beim Start von Tomahawk verbinden + + + + Use UPnP to establish port forward + Benutze UPnP um die Portweiterleitung zu konfigurieren + + + + Local Music + Lokale Musik + + + + Path to scan for music files: + Pfad zu den Musikdateien: + + + + ... + … + + + + Last.fm + Last.fm + + + + Scrobble tracks to Last.fm + Gespielte Stücke an Last.fm übertragen + + + + Last.fm Login + Last.fm Anmeldung + + + + Username: + Benutzername: + + + + Test Login + Anmeldung Testen + + + + Script Resolvers + + + + + Loaded script resolvers: + + + + + Select Music Folder + Musikordner auswählen + + + + + Failed + Fehlgeschlagen + + + + Success + Erfolgreich + + + + Could not contact server + Konnte den Server nicht erreichen + + + + Load script resolver file + + + + + SourceDelegate + + + Offline + Nicht Verbunden + + + + All available tracks + Alle verfügbaren Stücke + + + + Online + Verbunden + + + + SourceInfoWidget + + + Recent Albums + Aktuelle Alben + + + + Latest Additions to their Collection + Zuletzt zur Sammlung hinzugefügte Stücke + + + + Recently played Tracks + Aktuell gespiele Stücke + + + + Info about %1 + Information über %1 + + + + Your Collection + Deine Sammlung + + + + SourceTreeItem + + + Super Collection + Komplettsammlung + + + + SourceTreeItemWidget + + + Form + + + + + + + + TextLabel + + + + + Off + + + + + Info + + + + + Super Collection + Komplettsammlung + + + + All available tracks + Alle verfügbaren Stücke + + + + Idle + + + + + %L1 tracks + %L1 Stücke + + + + Checking + Teste + + + + Fetching + Hole + + + + Parsing + Parse + + + + Saving + Speichere + + + + Synced + Synchronisiert + + + + Scanning (%L1 tracks) + Durchsuche (%L1 Stücke) + + + + Offline + Nicht Verbunden + + + + SourceTreeView + + + &Load Playlist + &Lade Playliste + + + + &Rename Playlist + Playliste &umbenennen + + + + &Delete Playlist + Playliste &löschen + + + + Tomahawk::DynamicControlList + + + Click to collapse + Klicken zum Zusammenfalten + + + + Tomahawk::DynamicModel + + + + Could not find a playable track. + +Please change the filters or try again. + Konnte kein spielbares Stück finden. + +Bitte ändere den Filter oder versuche es erneut. + + + + Tomahawk::DynamicSetupWidget + + + Type: + Typ: + + + + Generate + Erzeugen + + + + Tomahawk::DynamicView + + + Add some filters above to seed this station! + Füge einige Filter hinzu, um diese Station zu initialisieren! + + + + Press Generate to get started! + Drücke Erzeugen, um zu beginnen! + + + + Add some filters above, and press Generate to get started! + Füge oben einige Filter hinzu und drücke Erzeugen um zu beginnen! + + + + Tomahawk::EchonestControl + + + + + + is + ist + + + + + + + + + Less + Kleiner + + + + + + + + + More + Größer + + + + 0 BPM + + + + + 500 BPM + + + + + 0 secs + 0 s + + + + 3600 secs + 3600 s + + + + -100 dB + + + + + 100 dB + + + + + Major + Dur + + + + Minor + Moll + + + + C + C + + + + C Sharp + Cis + + + + D + D + + + + E Flat + Es + + + + E + E + + + + F + F + + + + F Sharp + Fis + + + + G + G + + + + A Flat + As + + + + A + A + + + + B Flat + stimmt das? + B + + + + B + H + + + + Ascending + Aufsteigend + + + + Descending + Absteigend + + + + Tempo + Tempo + + + + Duration + Dauer + + + + Loudness + Lautstärke + + + + Artist Familiarity + + + + + Artist Hotttnesss + + + + + Song Hotttnesss + + + + + Latitude + Breitengrad + + + + Longitude + Längengrad + + + + Mode + Modus + + + + Key + Schlüssel + + + + Energy + Energie + + + + Danceability + Tanzbarkeit + + + + Tomahawk::EchonestSteerer + + + Steer this station: + Steuere diese Station: + + + + Takes effect on track change + Wird nach dem Wechsel eines Stückes aktiv + + + + 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 + + + + + By Description + Von der Beschreibung + + + + Enter a description + Gib eine Beschreibung ein + + + + Reset all steering commands + Setze alle Steuerkommandos zurück + + + + Tomahawk::Source + + + + Scanning (%L1 tracks) + Scanne (%L1 Stücke) + + + + Checking + Teste + + + + Fetching + Hole + + + + Parsing + Parse + + + + Saving + Speichere + + + + TomahawkTrayIcon + + + Play + Abspielen + + + + Pause + Pause + + + + Stop + Anhalten + + + + Previous Track + Vorheriges Stück + + + + Next Track + Nächstes Stück + + + + Quit + Verlassen + + + + Currently not playing. + Derzeit wird nichts gespielt. + + + + TomahawkWindow + + + Tomahawk + Tomahawk + + + + &Settings + &Einstellungen + + + + &Music Player + &Abspielprogramm + + + + &Playlist + &Playliste + + + + &Network + &Netzwerk + + + + &Help + &Hilfe + + + + &Quit + &Verlassen + + + + Ctrl+Q + Strg+Q + + + + + Go &online + &Verbindung herstellen + + + + Add &Friend... + Freund &hinzufügen… + + + + Re&scan Collection... + Sammlung neu&laden… + + + + &Configure Tomahawk... + Tomahawk &einrichten… + + + + Load &XSPF... + &XSPF-Datei laden… + + + + Create &New Playlist... + Neue &Playliste erstellen… + + + + About &Tomahawk... + Über &Tomahawk… + + + + Create New &Automatic Playlist + Neue, &automatische Playliste erstellen + + + + Create New &Station + Neue &Station erstellen + + + + Show Offline Sources + Nicht-Verfügbare Quellen anzeigen + + + + Hide Offline Sources + Nicht-Verfügbare Quellen ausblenden + + + + + Check for updates... + Teste auf updates… + + + + Back + Zurück + + + + Forward + Forwärts + + + + Home + + + + + + + Connect To Peer + Zu Gegenstelle 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: + + + + Go &offline + Verbindung &trennen + + + + Authentication Error + Authentifizierungsfehler + + + + by + von + + + + <h2><b>Tomahawk %1</h2>Copyright 2010, 2011<br/>Christian Muehlhaeuser &lt;muesli@tomahawk-player.org&gt;<br/><br/>Thanks to: Leo Franchi, Jeff Mitchell, Dominik Schmidt, Jason Herskowitz, Alejandro Wainzinger, Harald Sitter and Steve Robertson + + + + + TopBar + + + Form + + + + + 0 Sources + + + + + 0 Tracks + + + + + 0 Artists + + + + + 0 Shown + + + + + Tracks + Stücke + + + + Artists + Künstler + + + + Sources + Quellen + + + + Shown + Angezeigt + + + + TrackModel + + + Artist + Künstler + + + + Track + Titel + + + + Album + Album + + + + Duration + Spieldauer + + + + Bitrate + Bitrate + + + + Age + Alter + + + + Year + Jahr + + + + Size + Größe + + + + Origin + Quelle + + + + TrackView + + + Sorry, your filter '%1' did not match any results. + Entschuldige, dein Filter '%1' erzeugte keine Ergebnisse. + + + + TransferView + + + Peer + Gegenstelle + + + + Rate + Rate + + + + Track + Stück + + + + TwitterConfigWidget + + + Authenticating with Twitter allows you to discover and play music from your Twitter friends running Tomahawk. + + + + + This feature works best when you have set a static host name in the "Network" settings tab under Advanced Settings, but may work even if you do not. Tomahawk uses Direct Messages and this will only work when both Twitter users have followed each other. + + + + + Status: No saved credentials + + + + + Authenticate with Twitter + + + + + Here's how it works: just press one of the buttons below to tweet "Got Tomahawk?" and some necessary information. Then be (very) patient. Twitter is an asynchronous protocol so it can take a bit! + +If connections to peers seem to have been lost, just press the appropriate button again to re-post a tweet for resynchronization. + + + + + Select the kind of tweet you would like, then press the button to post it: + + + + + Global Tweet + + + + + @Mention + + + + + Direct Message + + + + + e.g. @tomahawkplayer + + + + + Tweet! + + + + + WelcomeWidget + + + Recently played playlists: + Aktuell gespielte Playlisten: + + + + Recently played tracks: + Aktuell gespielte Stücke: + + + + You have not played any playlists yet. + Du hast bisher keine Playlisten abgespielt. + + + + Welcome to Tomahawk + Willkommen bei Tomahawk + + + + XSPFLoader + + + New Playlist + Neue 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. + + + + XSPF Error + XSPF-Fehler + + + + This is not a valid XSPF playlist. + Dies ist keine valide XSPF-Playliste. + + + diff --git a/lang/tomahawk_i18n.qrc b/lang/tomahawk_i18n.qrc new file mode 100644 index 000000000..a309b5d51 --- /dev/null +++ b/lang/tomahawk_i18n.qrc @@ -0,0 +1,5 @@ + + +tomahawk_de.qm + + diff --git a/lang/translations.cmake b/lang/translations.cmake new file mode 100644 index 000000000..a5b92f2f4 --- /dev/null +++ b/lang/translations.cmake @@ -0,0 +1,25 @@ +FILE (GLOB TS_FILES ${CMAKE_SOURCE_DIR}/lang/*.ts) +QT4_ADD_TRANSLATION(QM_FILES ${TS_FILES}) + +## HACK HACK HACK - around rcc limitations to allow out of source-tree building +SET( trans_file tomahawk_i18n ) +SET( trans_srcfile ${CMAKE_SOURCE_DIR}/lang/${trans_file}.qrc) +SET( trans_infile ${CMAKE_CURRENT_BINARY_DIR}/${trans_file}.qrc) +SET( trans_outfile ${CMAKE_CURRENT_BINARY_DIR}/qrc_${trans_file}.cxx) + +# Copy the QRC file to the output directory +ADD_CUSTOM_COMMAND( + OUTPUT ${trans_infile} + COMMAND ${CMAKE_COMMAND} -E copy ${trans_srcfile} ${trans_infile} + MAIN_DEPENDENCY ${trans_srcfile} +) + +# Run the resource compiler (rcc_options should already be set) +ADD_CUSTOM_COMMAND( + OUTPUT ${trans_outfile} + COMMAND ${QT_RCC_EXECUTABLE} + ARGS ${rcc_options} -name ${trans_file} -o ${trans_outfile} ${trans_infile} + MAIN_DEPENDENCY ${trans_infile} + DEPENDS ${QM_FILES} +) + diff --git a/resources.qrc b/resources.qrc index 231eb32b2..9996e5cb0 100644 --- a/resources.qrc +++ b/resources.qrc @@ -73,6 +73,8 @@ ./data/images/home.png ./data/images/back.png ./data/images/forward.png +./data/images/music-icon.png +./data/images/configure.png ./data/topbar-radiobuttons.css ./data/icons/tomahawk-icon-16x16.png ./data/icons/tomahawk-icon-32x32.png diff --git a/src/CMakeLists.linux.txt b/src/CMakeLists.linux.txt index f6d98c0e8..ba02fc34c 100644 --- a/src/CMakeLists.linux.txt +++ b/src/CMakeLists.linux.txt @@ -10,5 +10,3 @@ ELSE() gnutls ) ENDIF() - -#include( "CPack.txt" ) diff --git a/src/CMakeLists.osx.txt b/src/CMakeLists.osx.txt index 3520e96a1..4f7b5970e 100644 --- a/src/CMakeLists.osx.txt +++ b/src/CMakeLists.osx.txt @@ -33,15 +33,18 @@ if (APPLE) # Use two different sparkle update tracks for debug and release # We have to change the URL in the Info.plist file :-/ - IF( NOT CMAKE_BUILD_TYPE STREQUAL "Release" ) - FILE(READ ${CMAKE_SOURCE_DIR}/admin/mac/Info.plist plist) - STRING( REPLACE "http://download.tomahawk-player.org/sparkle" # match this - "http://download.tomahawk-player.org/sparkle-debug" #replace with debug url + FILE(READ ${CMAKE_SOURCE_DIR}/admin/mac/Info.plist plist) + STRING( REPLACE "TOMAHAWK_VERSION" + ${VERSION} edited_plist # save in this variable "${plist}" # from the contents of this var ) - FILE( WRITE ${CMAKE_BINARY_DIR}/Info.plist "${edited_plist}" ) - ELSE() # Just copy the release one - FILE( COPY ${CMAKE_SOURCE_DIR}/admin/mac/Info.plist DESTINATION ${CMAKE_BINARY_DIR} ) + IF( NOT CMAKE_BUILD_TYPE STREQUAL "Release" ) + STRING( REPLACE "http://download.tomahawk-player.org/sparkle" # match this + "http://download.tomahawk-player.org/sparkle-debug" #replace with debug url + edited_plist # save in this variable + "${edited_plist}" # from the contents of this var + ) ENDIF() + FILE( WRITE ${CMAKE_BINARY_DIR}/Info.plist "${edited_plist}" ) endif (APPLE) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 50340d04c..666d5dfb6 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -35,10 +35,6 @@ ENDIF() SET( tomahawkSources ${tomahawkSources} sip/SipHandler.cpp - infosystem/infosystem.cpp - infosystem/infoplugins/echonestplugin.cpp - infosystem/infoplugins/musixmatchplugin.cpp - web/api_v1.cpp resolvers/scriptresolver.cpp @@ -68,18 +64,16 @@ SET( tomahawkSourcesGui ${tomahawkSourcesGui} tomahawktrayicon.cpp audiocontrols.cpp settingsdialog.cpp + resolverconfigdelegate.cpp + resolversmodel.cpp tomahawkwindow.cpp ) SET( tomahawkHeaders ${tomahawkHeaders} "${TOMAHAWK_INC_DIR}/tomahawk/tomahawkapp.h" - "${TOMAHAWK_INC_DIR}/tomahawk/infosystem.h" sip/SipHandler.h - infosystem/infoplugins/echonestplugin.h - infosystem/infoplugins/musixmatchplugin.h - web/api_v1.h resolvers/scriptresolver.h @@ -108,6 +102,9 @@ SET( tomahawkHeadersGui ${tomahawkHeadersGui} tomahawktrayicon.h audiocontrols.h settingsdialog.h + resolverconfigdelegate.h + resolversmodel.h + resolverconfigwrapper.h tomahawkwindow.h ) @@ -121,9 +118,10 @@ SET( tomahawkUI ${tomahawkUI} ) INCLUDE_DIRECTORIES( - . + . ${TOMAHAWK_INC_DIR} ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_BINARY_DIR}/thirdparty/liblastfm2/src audio database @@ -133,14 +131,14 @@ INCLUDE_DIRECTORIES( topbar utils libtomahawk - libtomahawk/utils mac - ${THIRDPARTY_DIR}/qxt/qxtweb-standalone/qxtweb/ + ${THIRDPARTY_DIR}/qxt/qxtweb-standalone/qxtweb ${THIRDPARTY_DIR}/qtweetlib/qtweetlib/src ${THIRDPARTY_DIR}/qtweetlib/tomahawk-custom ${TAGLIB_INCLUDES} + ${QJSON_INCLUDE_DIR} ${LIBECHONEST_INCLUDE_DIR} ${LIBECHONEST_INCLUDE_DIR}/.. ) @@ -165,6 +163,7 @@ IF( APPLE ) ENDIF( APPLE ) IF(GLOOX_FOUND) + INCLUDE_DIRECTORIES( ${GLOOX_INCLUDE_DIR} ) SET( tomahawkHeaders ${tomahawkHeaders} xmppbot/xmppbot.h ) SET( tomahawkSources ${tomahawkSources} xmppbot/xmppbot.cpp ) ENDIF(GLOOX_FOUND) @@ -177,7 +176,9 @@ qt4_wrap_cpp( tomahawkMoc ${tomahawkHeaders} ) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/config.h.in ${CMAKE_CURRENT_BINARY_DIR}/config.h) -SET( final_src ${final_src} ${tomahawkMoc} ${tomahawkSources} ${tomahawkHeaders} ) +include( ${CMAKE_SOURCE_DIR}/lang/translations.cmake ) + +SET( final_src ${final_src} ${tomahawkMoc} ${tomahawkSources} ${tomahawkHeaders} ${trans_outfile}) IF( "${gui}" STREQUAL "no" ) ELSE() @@ -195,14 +196,14 @@ IF( APPLE ) ) ENDIF( APPLE ) IF( WIN32 ) - ADD_EXECUTABLE( tomahawk ${final_src} ) + ADD_EXECUTABLE( tomahawk WIN32 ${final_src} ) ENDIF( WIN32 ) MESSAGE( STATUS "OS_SPECIFIC_LINK_LIBRARIES: ${OS_SPECIFIC_LINK_LIBRARIES}" ) SET(LINK_LIBRARIES "") IF(LIBLASTFM_FOUND) - SET(LINK_LIBRARIES ${LINK_LIBRARIES} ${LIBLASTFM_LIBRARY} ) + SET(LINK_LIBRARIES ${LINK_LIBRARIES} tomahawk_lastfm2 ) ENDIF(LIBLASTFM_FOUND) IF(GLOOX_FOUND) SET(LINK_LIBRARIES ${LINK_LIBRARIES} ${GLOOX_LIBRARIES} ) @@ -232,4 +233,16 @@ IF( APPLE ) ENDIF(HAVE_SPARKLE) ENDIF( APPLE ) -INCLUDE( "CPack.txt" ) +INSTALL( TARGETS tomahawk BUNDLE DESTINATION . RUNTIME DESTINATION bin ) + +IF(KDE4_FOUND) #install protocol file + FILE(READ ${CMAKE_SOURCE_DIR}/admin/unix/tomahawk.protocol protocol) + STRING( REPLACE "/path/to/binary" # match this + "${CMAKE_INSTALL_PREFIX}/bin/tomahawk" # this is linux (kde) so pretty safe I think + edited_protocol # save in this variable + "${protocol}" # from the contents of this var + ) + FILE( WRITE ${CMAKE_BINARY_DIR}/tomahawk.protocol "${edited_protocol}" ) + INSTALL( FILES ${CMAKE_BINARY_DIR}/tomahawk.protocol DESTINATION ${SERVICES_INSTALL_DIR} ) +ENDIF() +#INCLUDE( "CPack.txt" ) diff --git a/src/CPack.txt b/src/CPack.txt index 46174a970..37c2e1cc6 100644 --- a/src/CPack.txt +++ b/src/CPack.txt @@ -58,8 +58,8 @@ SET(CPACK_DEBIAN_PACKAGE_DEPENDS "libqtgui4 (>=4:4.7.0-0ubuntu1), libtag1c2a (>= INSTALL( TARGETS tomahawk DESTINATION bin RUNTIME DESTINATION bin -# LIBRARY DESTINATION lib -# ARCHIVE DESTINATION lib +# LIBRARY DESTINATION lib${LIB_SUFFIX} +# ARCHIVE DESTINATION lib${LIB_SUFFIX} ) INSTALL( diff --git a/src/audiocontrols.cpp b/src/audiocontrols.cpp index b716cd3fb..32fd58ca7 100644 --- a/src/audiocontrols.cpp +++ b/src/audiocontrols.cpp @@ -1,9 +1,26 @@ +/* === 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 "audiocontrols.h" #include "ui_audiocontrols.h" #include -#include "tomahawk/tomahawkapp.h" #include "audio/audioengine.h" #include "playlist/playlistmanager.h" #include "utils/imagebutton.h" @@ -11,7 +28,7 @@ #include "album.h" -#define LASTFM_DEFAULT_COVER "http://cdn.last.fm/flatness/catalogue/noimage" +static QString s_acInfoIdentifier = QString( "AUDIOCONTROLS" ); AudioControls::AudioControls( QWidget* parent ) @@ -27,6 +44,10 @@ AudioControls::AudioControls( QWidget* parent ) QFont font( ui->artistTrackLabel->font() ); font.setPixelSize( 12 ); +#ifdef Q_WS_MAC + font.setPointSize( font.pointSize() - 2 ); +#endif + ui->artistTrackLabel->setFont( font ); ui->artistTrackLabel->setElideMode( Qt::ElideMiddle ); ui->artistTrackLabel->setType( QueryLabel::ArtistAndTrack ); @@ -121,10 +142,10 @@ AudioControls::AudioControls( QWidget* parent ) connect( ui->volumeLowButton, SIGNAL( clicked() ), AudioEngine::instance(), SLOT( lowerVolume() ) ); connect( ui->volumeHighButton, SIGNAL( clicked() ), AudioEngine::instance(), SLOT( raiseVolume() ) ); - + connect( ui->playPauseButton, SIGNAL( clicked() ), this, SIGNAL( playPressed() ) ); connect( ui->pauseButton, SIGNAL( clicked() ), this, SIGNAL( pausePressed() ) ); - + connect( ui->repeatButton, SIGNAL( clicked() ), SLOT( onRepeatClicked() ) ); connect( ui->shuffleButton, SIGNAL( clicked() ), SLOT( onShuffleClicked() ) ); @@ -144,6 +165,12 @@ AudioControls::AudioControls( QWidget* parent ) m_defaultCover = QPixmap( RESPATH "images/no-album-art-placeholder.png" ) .scaled( ui->coverImage->size(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation ); + connect( Tomahawk::InfoSystem::InfoSystem::instance(), + SIGNAL( info( QString, Tomahawk::InfoSystem::InfoType, QVariant, QVariant, Tomahawk::InfoSystem::InfoCustomData ) ), + SLOT( infoSystemInfo( QString, Tomahawk::InfoSystem::InfoType, QVariant, QVariant, Tomahawk::InfoSystem::InfoCustomData ) ) ); + + connect( Tomahawk::InfoSystem::InfoSystem::instance(), SIGNAL( finished( QString ) ), SLOT( infoSystemFinished( QString ) ) ); + onPlaybackStopped(); // initial state } @@ -179,40 +206,6 @@ AudioControls::onVolumeChanged( int volume ) } -void -AudioControls::onCoverArtDownloaded() -{ - if ( m_currentTrack.isNull() ) - return; - - QNetworkReply* reply = qobject_cast( sender() ); - QUrl redir = reply->attribute( QNetworkRequest::RedirectionTargetAttribute ).toUrl(); - if ( redir.isEmpty() ) - { - const QByteArray ba = reply->readAll(); - if ( ba.length() ) - { - QPixmap pm; - pm.loadFromData( ba ); - - if ( pm.isNull() || reply->url().toString().startsWith( LASTFM_DEFAULT_COVER ) ) - ui->coverImage->setPixmap( m_defaultCover ); - else - ui->coverImage->setPixmap( pm.scaled( ui->coverImage->size(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation ) ); - } - } - else - { - // Follow HTTP redirect - QNetworkRequest req( redir ); - QNetworkReply* reply = TomahawkUtils::nam()->get( req ); - connect( reply, SIGNAL( finished() ), SLOT( onCoverArtDownloaded() ) ); - } - - reply->deleteLater(); -} - - void AudioControls::onPlaybackStarted( const Tomahawk::result_ptr& result ) { @@ -220,10 +213,61 @@ AudioControls::onPlaybackStarted( const Tomahawk::result_ptr& result ) onPlaybackLoading( result ); - QString imgurl = "http://ws.audioscrobbler.com/2.0/?method=album.imageredirect&artist=%1&album=%2&size=medium&api_key=7a90f6672a04b809ee309af169f34b8b"; - QNetworkRequest req( imgurl.arg( result->artist()->name() ).arg( result->album()->name() ) ); - QNetworkReply* reply = TomahawkUtils::nam()->get( req ); - connect( reply, SIGNAL( finished() ), SLOT( onCoverArtDownloaded() ) ); + Tomahawk::InfoSystem::InfoCustomData trackInfo; + trackInfo["artist"] = QVariant::fromValue< QString >( result->artist()->name() ); + trackInfo["album"] = QVariant::fromValue< QString >( result->album()->name() ); + + Tomahawk::InfoSystem::InfoSystem::instance()->getInfo( + s_acInfoIdentifier, Tomahawk::InfoSystem::InfoAlbumCoverArt, + QVariant::fromValue< Tomahawk::InfoSystem::InfoCustomData >( trackInfo ), Tomahawk::InfoSystem::InfoCustomData() ); +} + + +void +AudioControls::infoSystemInfo( QString caller, Tomahawk::InfoSystem::InfoType type, QVariant input, QVariant output, Tomahawk::InfoSystem::InfoCustomData customData ) +{ + Q_UNUSED( input ); + Q_UNUSED( customData ); + + qDebug() << Q_FUNC_INFO << caller << type << s_acInfoIdentifier << Tomahawk::InfoSystem::InfoAlbumCoverArt; + if ( caller != s_acInfoIdentifier || type != Tomahawk::InfoSystem::InfoAlbumCoverArt ) + { + qDebug() << "Info of wrong type or not with our identifier"; + return; + } + + if ( m_currentTrack.isNull() ) + { + qDebug() << "Current track is null when trying to apply fetched cover art"; + return; + } + + if ( !output.canConvert< Tomahawk::InfoSystem::InfoCustomData >() ) + { + qDebug() << "Cannot convert fetched art from a QByteArray"; + return; + } + + Tomahawk::InfoSystem::InfoCustomData returnedData = output.value< Tomahawk::InfoSystem::InfoCustomData >(); + const QByteArray ba = returnedData["imgbytes"].toByteArray(); + if ( ba.length() ) + { + QPixmap pm; + pm.loadFromData( ba ); + + if ( pm.isNull() ) + ui->coverImage->setPixmap( m_defaultCover ); + else + ui->coverImage->setPixmap( pm.scaled( ui->coverImage->size(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation ) ); + } +} + + +void +AudioControls::infoSystemFinished( QString target ) +{ + Q_UNUSED( target ); + qDebug() << Q_FUNC_INFO; } @@ -280,7 +324,7 @@ AudioControls::onPlaybackResumed() ui->pauseButton->setVisible( true ); ui->pauseButton->setEnabled( true ); ui->playPauseButton->setVisible( false ); - ui->playPauseButton->setEnabled( false ); + ui->playPauseButton->setEnabled( false ); } diff --git a/src/audiocontrols.h b/src/audiocontrols.h index 93bd27853..82f2ee623 100644 --- a/src/audiocontrols.h +++ b/src/audiocontrols.h @@ -1,3 +1,21 @@ +/* === 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 AUDIOCONTROLS_H #define AUDIOCONTROLS_H @@ -5,6 +23,7 @@ #include "result.h" #include "playlistinterface.h" +#include "infosystem/infosystem.h" namespace Ui { @@ -22,7 +41,7 @@ public: signals: void playPressed(); void pausePressed(); - + public slots: void onRepeatModeChanged( PlaylistInterface::RepeatMode mode ); void onShuffleModeChanged( bool enabled ); @@ -47,7 +66,8 @@ private slots: void onAlbumClicked(); void onTrackClicked(); - void onCoverArtDownloaded(); + void infoSystemInfo( QString caller, Tomahawk::InfoSystem::InfoType type, QVariant input, QVariant output, Tomahawk::InfoSystem::InfoCustomData customData ); + void infoSystemFinished( QString target ); private: Ui::AudioControls *ui; diff --git a/src/headlesscheck.h b/src/headlesscheck.h index fda2a07a9..f82fcea33 100644 --- a/src/headlesscheck.h +++ b/src/headlesscheck.h @@ -1,16 +1,34 @@ +/* === 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 HEADLESSCHECK #define HEADLESSCHECK #ifdef ENABLE_HEADLESS -#define TOMAHAWK_APPLICATION QtSingleCoreApplication +#define TOMAHAWK_APPLICATION QCoreApplication #define TOMAHAWK_HEADLESS -#include "qtsingleapp/qtsingleapplication.h" +#include > #else -#define TOMAHAWK_APPLICATION QtSingleApplication -#include "qtsingleapp/qtsingleapplication.h" +#define TOMAHAWK_APPLICATION QApplication +#include #include "tomahawkwindow.h" #endif diff --git a/src/infosystem/infoplugins/echonestplugin.h b/src/infosystem/infoplugins/echonestplugin.h deleted file mode 100644 index 89d4a15ce..000000000 --- a/src/infosystem/infoplugins/echonestplugin.h +++ /dev/null @@ -1,57 +0,0 @@ -#ifndef ECHONESTPLUGIN_H -#define ECHONESTPLUGIN_H - -#include "tomahawk/infosystem.h" - -#include - -class QNetworkReply; -namespace Echonest { -class Artist; -} - -namespace Tomahawk -{ - -namespace InfoSystem -{ - -class EchoNestPlugin : public InfoPlugin -{ - Q_OBJECT - -public: - EchoNestPlugin(QObject *parent); - virtual ~EchoNestPlugin(); - - void getInfo( const QString &caller, const InfoType type, const QVariant &data, InfoCustomDataHash customData ); - -private: - void getSongProfile( const QString &caller, const QVariant &data, InfoCustomDataHash &customData, const QString &item = QString() ); - void getArtistBiography ( const QString &caller, const QVariant &data, InfoCustomDataHash &customData ); - void getArtistFamiliarity( const QString &caller, const QVariant &data, InfoCustomDataHash &customData ); - void getArtistHotttnesss( const QString &caller, const QVariant &data, InfoCustomDataHash &customData ); - void getArtistTerms( const QString &caller, const QVariant &data, InfoCustomDataHash &customData ); - void getMiscTopTerms( const QString &caller, const QVariant &data, InfoCustomDataHash &customData ); - - bool isValidArtistData( const QString &caller, const QVariant& data, InfoCustomDataHash& customData ); - bool isValidTrackData( const QString &caller, const QVariant& data, InfoCustomDataHash& customData ); - Echonest::Artist artistFromReply( QNetworkReply* ); - -private slots: - void getArtistBiographySlot(); - void getArtistFamiliaritySlot(); - void getArtistHotttnesssSlot(); - void getArtistTermsSlot(); - void getMiscTopSlot(); - -private: - QHash< QNetworkReply*, InfoCustomDataHash > m_replyMap; - QHash< QNetworkReply*, QString > m_callerMap; -}; - -} - -} - -#endif // ECHONESTPLUGIN_H diff --git a/src/infosystem/infoplugins/musixmatchplugin.h b/src/infosystem/infoplugins/musixmatchplugin.h deleted file mode 100644 index 63301fa87..000000000 --- a/src/infosystem/infoplugins/musixmatchplugin.h +++ /dev/null @@ -1,38 +0,0 @@ -#ifndef MUSIXMATCHPLUGIN_H -#define MUSIXMATCHPLUGIN_H -#include "tomahawk/infosystem.h" - -class QNetworkReply; - -namespace Tomahawk -{ - -namespace InfoSystem -{ - -class MusixMatchPlugin : public InfoPlugin -{ - Q_OBJECT - -public: - MusixMatchPlugin(QObject *parent); - virtual ~MusixMatchPlugin(); - - void getInfo(const QString &caller, const InfoType type, const QVariant &data, InfoCustomDataHash customData); - -private: - bool isValidTrackData( const QString &caller, const QVariant& data, InfoCustomDataHash &customData ); - -public slots: - void trackSearchSlot(); - void trackLyricsSlot(); - -private: - QString m_apiKey; -}; - -} - -} - -#endif // MUSIXMATCHPLUGIN_H diff --git a/src/infosystem/infosystem.cpp b/src/infosystem/infosystem.cpp deleted file mode 100644 index 66b107d5e..000000000 --- a/src/infosystem/infosystem.cpp +++ /dev/null @@ -1,96 +0,0 @@ -#include "tomahawk/infosystem.h" -#include "infoplugins/echonestplugin.h" -#include "infoplugins/musixmatchplugin.h" - -using namespace Tomahawk::InfoSystem; - -InfoSystem::InfoSystem(QObject *parent) - : QObject( parent ) -{ - qDebug() << Q_FUNC_INFO; - qRegisterMetaType > >("Tomahawk::InfoSystem::InfoGenericMap"); - qRegisterMetaType >("Tomahawk::InfoSystem::InfoCustomDataHash"); - qRegisterMetaType >("Tomahawk::InfoSystem::MusixMatchHash"); - InfoPluginPtr enptr(new EchoNestPlugin(this)); - m_plugins.append(enptr); - InfoPluginPtr mmptr(new MusixMatchPlugin(this)); - m_plugins.append(mmptr); -} - -void InfoSystem::registerInfoTypes(const InfoPluginPtr &plugin, const QSet< InfoType >& types) -{ - qDebug() << Q_FUNC_INFO; - Q_FOREACH(InfoType type, types) - m_infoMap[type].append(plugin); -} - -QLinkedList< InfoPluginPtr > InfoSystem::determineOrderedMatches(const InfoType type) const -{ - //Dummy function for now that returns the various items in the QSet; at some point this will - //probably need to support ordering based on the data source - QLinkedList< InfoPluginPtr > providers; - Q_FOREACH(InfoPluginPtr ptr, m_infoMap[type]) - providers << ptr; - return providers; -} - -void InfoSystem::getInfo(const QString &caller, const InfoType type, const QVariant& data, InfoCustomDataHash customData) -{ - qDebug() << Q_FUNC_INFO; - QLinkedList< InfoPluginPtr > providers = determineOrderedMatches(type); - if (providers.isEmpty()) - { - emit info(QString(), Tomahawk::InfoSystem::InfoNoInfo, QVariant(), QVariant(), customData); - return; - } - - InfoPluginPtr ptr = providers.first(); - if (!ptr) - { - emit info(QString(), Tomahawk::InfoSystem::InfoNoInfo, QVariant(), QVariant(), customData); - return; - } - - m_dataTracker[caller][type] = m_dataTracker[caller][type] + 1; - qDebug() << "current count in dataTracker for type" << type << "is" << m_dataTracker[caller][type]; - connect(ptr.data(), SIGNAL(info(QString, Tomahawk::InfoSystem::InfoType, QVariant, QVariant, Tomahawk::InfoSystem::InfoCustomDataHash)), - this, SLOT(infoSlot(QString, Tomahawk::InfoSystem::InfoType, QVariant, QVariant, Tomahawk::InfoSystem::InfoCustomDataHash)), Qt::UniqueConnection); - connect(ptr.data(), SIGNAL(finished(QString, Tomahawk::InfoSystem::InfoType)), - this, SLOT(finishedSlot(QString, Tomahawk::InfoSystem::InfoType)), Qt::UniqueConnection); - ptr.data()->getInfo(caller, type, data, customData); -} - -void InfoSystem::getInfo(const QString &caller, const InfoMap &input, InfoCustomDataHash customData) -{ - Q_FOREACH( InfoType type, input.keys() ) - getInfo(caller, type, input[type], customData); -} - -void InfoSystem::infoSlot(QString target, Tomahawk::InfoSystem::InfoType type, QVariant input, QVariant output, Tomahawk::InfoSystem::InfoCustomDataHash customData) -{ - qDebug() << Q_FUNC_INFO; - qDebug() << "current count in dataTracker is " << m_dataTracker[target][type]; - if (m_dataTracker[target][type] == 0) - { - qDebug() << "Caller was not waiting for that type of data!"; - return; - } - emit info(target, type, input, output, customData); -} - -void InfoSystem::finishedSlot(QString target, Tomahawk::InfoSystem::InfoType type) -{ - qDebug() << Q_FUNC_INFO; - m_dataTracker[target][type] = m_dataTracker[target][type] - 1; - qDebug() << "current count in dataTracker is " << m_dataTracker[target][type]; - Q_FOREACH(Tomahawk::InfoSystem::InfoType testtype, m_dataTracker[target].keys()) - { - if (m_dataTracker[target][testtype] != 0) - { - qDebug() << "found outstanding request of type" << testtype; - return; - } - } - qDebug() << "emitting finished with target" << target; - emit finished(target); -} \ No newline at end of file diff --git a/src/junk/remoteioconnection.cpp b/src/junk/remoteioconnection.cpp deleted file mode 100644 index 9bc7cc655..000000000 --- a/src/junk/remoteioconnection.cpp +++ /dev/null @@ -1,86 +0,0 @@ -#include "remoteioconnection.h" -#include - -RemoteIOConnection::RemoteIOConnection(Servent * s, FileTransferSession * fts) - : Connection(s), m_fts(fts) -{ - qDebug() << "CTOR " << id() ; -} - -RemoteIOConnection::~RemoteIOConnection() -{ - qDebug() << "DTOR " << id() ; -} - -QString RemoteIOConnection::id() const -{ - return QString("RemoteIOConnection[%1]").arg(m_fts->fid()); -} - -void RemoteIOConnection::shutdown(bool wait) -{ - Connection::shutdown(wait); - /*if(!wait) - { - Connection::shutdown(wait); - return; - } - qDebug() << id() << " shutdown requested - waiting until we've received all data TODO"; - */ -} - -void RemoteIOConnection::setup() -{ - if(m_fts->type() == FileTransferSession::RECEIVING) - { - qDebug() << "RemoteIOConnection in RX mode"; - return; - } - - qDebug() << "RemoteIOConnection in TX mode, fid:" << m_fts->fid(); - - QString url("/tmp/test.mp3"); - qDebug() << "TODO map fid to file://, hardcoded for now"; - - qDebug() << "Opening for transmission:" << url; - m_readdev = QSharedPointer(new QFile(url)); - m_readdev->open(QIODevice::ReadOnly); - if(!m_readdev->isOpen()) - { - qDebug() << "WARNING file is not readable: " << url; - shutdown(); - } - // send chunks within our event loop, since we're not in our own thread - sendSome(); -} - -void RemoteIOConnection::handleMsg(QByteArray msg) -{ - Q_ASSERT(m_fts->type() == FileTransferSession::RECEIVING); - m_fts->iodevice()->addData(msg); - if(msg.length()==0) qDebug() << "Got 0len msg. end?"; -} - - -Connection * RemoteIOConnection::clone() -{ - Q_ASSERT(false); return 0; -}; - - -void RemoteIOConnection::sendSome() -{ - Q_ASSERT(m_fts->type() == FileTransferSession::SENDING); - if(m_readdev->atEnd()) - { - qDebug() << "Sent all. DONE"; - shutdown(true); - return; - } - QByteArray ba = m_readdev->read(4096); - //qDebug() << "Sending " << ba.length() << " bytes of audiofile"; - sendMsg(ba); - QTimer::singleShot(0, this, SLOT(sendSome())); -} - - diff --git a/src/junk/remoteioconnection.h b/src/junk/remoteioconnection.h deleted file mode 100644 index 5874f8a4f..000000000 --- a/src/junk/remoteioconnection.h +++ /dev/null @@ -1,38 +0,0 @@ -#ifndef REMOTEIOCONNECTION_H -#define REMOTEIOCONNECTION_H - -#include -#include -#include -#include -#include - -#include "controlconnection.h" -#include "filetransfersession.h" - -class RemoteIOConnection : public Connection -{ - Q_OBJECT -public: - RemoteIOConnection(Servent * s, FileTransferSession * fts); - ~RemoteIOConnection(); - QString id() const; - - - void shutdown(bool wait = false); - void setup(); - void handleMsg(QByteArray msg); - Connection * clone(); - -signals: - -private slots: - void sendSome(); - -private: - - FileTransferSession * m_fts; - QSharedPointer m_readdev; -}; - -#endif // REMOTEIOCONNECTION_H diff --git a/src/junk/remoteiodevice.cpp b/src/junk/remoteiodevice.cpp deleted file mode 100644 index 92cefbfe9..000000000 --- a/src/junk/remoteiodevice.cpp +++ /dev/null @@ -1,101 +0,0 @@ -#include "remoteiodevice.h" - -RemoteIODevice::RemoteIODevice(RemoteIOConnection * c) - : m_eof(false), m_totalAdded(0), m_rioconn(c) -{ - qDebug() << "CTOR RemoteIODevice"; -} - -RemoteIODevice::~RemoteIODevice() -{ - qDebug() << "DTOR RemoteIODevice"; - m_rioconn->shutdown(); -} - -void RemoteIODevice::close() -{ - qDebug() << "RemoteIODevice::close"; - QIODevice::close(); - deleteLater(); -} - -bool RemoteIODevice::open ( OpenMode mode ) -{ - return QIODevice::open(mode & QIODevice::ReadOnly); -} - -qint64 RemoteIODevice::bytesAvailable () const -{ - return m_buffer.length(); -} - -bool RemoteIODevice::isSequential () const -{ - return true; -}; - -bool RemoteIODevice::atEnd() const -{ - return m_eof && m_buffer.length() == 0; -}; - -void RemoteIODevice::addData(QByteArray msg) -{ - m_mut_recv.lock(); - if(msg.length()==0) - { - m_eof=true; - //qDebug() << "addData finished, entire file received. EOF."; - m_mut_recv.unlock(); - m_wait.wakeAll(); - return; - } - else - { - m_buffer.append(msg); - m_totalAdded += msg.length(); - //qDebug() << "RemoteIODevice has seen in total: " << m_totalAdded ; - m_mut_recv.unlock(); - m_wait.wakeAll(); - emit readyRead(); - return; - } -} - -qint64 RemoteIODevice::writeData ( const char * data, qint64 maxSize ) -{ - Q_ASSERT(false); - return 0; -} - -qint64 RemoteIODevice::readData ( char * data, qint64 maxSize ) -{ - //qDebug() << "RemIO::readData, bytes in buffer: " << m_buffer.length(); - m_mut_recv.lock(); - if(m_eof && m_buffer.length() == 0) - { - // eof - qDebug() << "readData called when EOF"; - m_mut_recv.unlock(); - return 0; - } - if(!m_buffer.length())// return 0; - { - //qDebug() << "WARNING readData when buffer is empty"; - m_mut_recv.unlock(); - return 0; - } - int len; - if(maxSize>=m_buffer.length()) // whole buffer - { - len = m_buffer.length(); - memcpy(data, m_buffer.constData(), len); - m_buffer.clear(); - } else { // partial - len = maxSize; - memcpy(data, m_buffer.constData(), len); - m_buffer.remove(0,len); - } - m_mut_recv.unlock(); - return len; -} diff --git a/src/junk/remoteiodevice.h b/src/junk/remoteiodevice.h deleted file mode 100644 index e8ee34fd4..000000000 --- a/src/junk/remoteiodevice.h +++ /dev/null @@ -1,43 +0,0 @@ -#ifndef REMOTEIODEVICE_H -#define REMOTEIODEVICE_H -#include -#include -#include -#include -#include -#include "remoteioconnection.h" - -class RemoteIOConnection; - -class RemoteIODevice : public QIODevice -{ - Q_OBJECT -public: - - RemoteIODevice(RemoteIOConnection * c); - ~RemoteIODevice(); - virtual void close(); - virtual bool open ( OpenMode mode ); - qint64 bytesAvailable () const; - virtual bool isSequential () const; - virtual bool atEnd() const; - -public slots: - - void addData(QByteArray msg); - -protected: - - virtual qint64 writeData ( const char * data, qint64 maxSize ); - virtual qint64 readData ( char * data, qint64 maxSize ); - -private: - QByteArray m_buffer; - QMutex m_mut_wait, m_mut_recv; - QWaitCondition m_wait; - bool m_eof; - int m_totalAdded; - - RemoteIOConnection * m_rioconn; -}; -#endif // REMOTEIODEVICE_H diff --git a/src/libtomahawk/CMakeLists.txt b/src/libtomahawk/CMakeLists.txt index 33d05344c..a5a15283d 100644 --- a/src/libtomahawk/CMakeLists.txt +++ b/src/libtomahawk/CMakeLists.txt @@ -5,6 +5,7 @@ SET( QT_USE_QTSQL TRUE ) SET( QT_USE_QTNETWORK TRUE ) SET( QT_USE_QTXML TRUE ) SET( QT_USE_PHONON TRUE ) +SET( QT_USE_QTUITOOLS TRUE ) include( ${QT_USE_FILE} ) @@ -17,11 +18,12 @@ set( libSources sourcelist.cpp pipeline.cpp + aclsystem.cpp artist.cpp album.cpp collection.cpp playlist.cpp - pluginapi.cpp + resolver.cpp query.cpp result.cpp source.cpp @@ -40,6 +42,7 @@ set( libSources database/databasecommand.cpp database/databasecommandloggable.cpp database/databasecommand_resolve.cpp + database/databasecommand_allartists.cpp database/databasecommand_allalbums.cpp database/databasecommand_alltracks.cpp database/databasecommand_addfiles.cpp @@ -71,12 +74,20 @@ set( libSources database/databasecommand_clientauthvalid.cpp database/database.cpp - playlist/collectionmodel.cpp + infosystem/infosystemcache.cpp + infosystem/infosystem.cpp + infosystem/infoplugins/echonestplugin.cpp + infosystem/infoplugins/lastfmplugin.cpp + infosystem/infoplugins/musixmatchplugin.cpp + + playlist/treemodel.cpp + playlist/treeproxymodel.cpp + playlist/treeheader.cpp + playlist/treeitemdelegate.cpp playlist/collectionproxymodel.cpp playlist/collectionflatmodel.cpp playlist/collectionview.cpp playlist/playlistmanager.cpp - playlist/plitem.cpp playlist/playlistmodel.cpp playlist/playlistproxymodel.cpp playlist/playlistview.cpp @@ -84,18 +95,21 @@ set( libSources playlist/queueproxymodel.cpp playlist/queueview.cpp playlist/trackmodel.cpp + playlist/trackmodelitem.cpp playlist/trackproxymodel.cpp playlist/trackview.cpp playlist/trackheader.cpp + playlist/treemodelitem.cpp playlist/albumitem.cpp playlist/albummodel.cpp playlist/albumproxymodel.cpp playlist/albumitemdelegate.cpp playlist/albumview.cpp + playlist/artistview.cpp playlist/topbar/topbar.cpp - playlist/topbar/clearbutton.cpp - playlist/topbar/searchlineedit.cpp + playlist/topbar/clearbutton.cpp + playlist/topbar/searchlineedit.cpp playlist/topbar/lineedit.cpp playlist/topbar/searchbutton.cpp @@ -121,7 +135,7 @@ set( libSources network/bufferiodevice.cpp network/msgprocessor.cpp - network/filetransferconnection.cpp + network/streamconnection.cpp network/dbsyncconnection.cpp network/remotecollection.cpp network/portfwdthread.cpp @@ -144,8 +158,10 @@ set( libSources widgets/overlaywidget.cpp widgets/infowidgets/sourceinfowidget.cpp - qtsingleapp/qtlocalpeer.cpp - qtsingleapp/qtsingleapplication.cpp + kdsingleapplicationguard/kdsingleapplicationguard.cpp + kdsingleapplicationguard/kdsharedmemorylocker.cpp + kdsingleapplicationguard/kdtoolsglobal.cpp + kdsingleapplicationguard/kdlockedsharedmemorypointer.cpp ) set( libHeaders @@ -154,8 +170,8 @@ set( libHeaders pipeline.h functimeout.h + aclsystem.h collection.h - pluginapi.h query.h resolver.h result.h @@ -179,6 +195,7 @@ set( libHeaders database/databasecommand.h database/databasecommandloggable.h database/databasecommand_resolve.h + database/databasecommand_allartists.h database/databasecommand_allalbums.h database/databasecommand_alltracks.h database/databasecommand_addfiles.h @@ -209,22 +226,30 @@ set( libHeaders database/databasecommand_addclientauth.h database/databasecommand_clientauthvalid.h + infosystem/infosystem.h + infosystem/infosystemcache.h + infosystem/infoplugins/echonestplugin.h + infosystem/infoplugins/lastfmplugin.h + infosystem/infoplugins/musixmatchplugin.h + network/bufferiodevice.h network/msgprocessor.h network/remotecollection.h - network/filetransferconnection.h + network/streamconnection.h network/dbsyncconnection.h network/servent.h network/connection.h network/controlconnection.h network/portfwdthread.h - playlist/collectionmodel.h + playlist/treemodel.h + playlist/treeproxymodel.h + playlist/treeheader.h + playlist/treeitemdelegate.h playlist/collectionproxymodel.h playlist/collectionflatmodel.h playlist/collectionview.h playlist/playlistmanager.h - playlist/plitem.h playlist/playlistmodel.h playlist/playlistproxymodel.h playlist/playlistview.h @@ -232,18 +257,21 @@ set( libHeaders playlist/queueproxymodel.h playlist/queueview.h playlist/trackmodel.h + playlist/trackmodelitem.h playlist/trackproxymodel.h playlist/trackview.h playlist/trackheader.h + playlist/treemodelitem.h playlist/albumitem.h playlist/albummodel.h playlist/albumproxymodel.h playlist/albumitemdelegate.h playlist/albumview.h + playlist/artistview.h playlist/topbar/topbar.h playlist/topbar/clearbutton.h - playlist/topbar/searchlineedit.h + playlist/topbar/searchlineedit.h playlist/topbar/lineedit.h playlist/topbar/lineedit_p.h playlist/topbar/searchbutton.h @@ -283,12 +311,14 @@ set( libHeaders widgets/overlaywidget.h widgets/infowidgets/sourceinfowidget.h - qtsingleapp/qtlocalpeer.h - qtsingleapp/qtsingleapplication.h + kdsingleapplicationguard/kdsingleapplicationguard.h + kdsingleapplicationguard/kdsharedmemorylocker.h + kdsingleapplicationguard/kdtoolsglobal.h + kdsingleapplicationguard/kdlockedsharedmemorypointer.h ) -set( libHeaders_NoMOC - playlist/dynamic/GeneratorInterface.h +set( libHeaders_NoMOC + playlist/dynamic/GeneratorInterface.h ) set( libUI ${libUI} widgets/newplaylistwidget.ui @@ -299,14 +329,16 @@ set( libUI ${libUI} ) include_directories( . ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_BINARY_DIR}/.. .. + ${CMAKE_CURRENT_SOURCE_DIR} ${QT_INCLUDE_DIR} + ${QJSON_INCLUDE_DIR} ${LIBECHONEST_INCLUDE_DIR} ${LIBECHONEST_INCLUDE_DIR}/.. ${CLUCENE_INCLUDE_DIR} ${CLUCENE_LIBRARY_DIR} + ${CMAKE_BINARY_DIR}/thirdparty/liblastfm2/src ../../include - ../network playlist ${THIRDPARTY_DIR}/libportfwd/include @@ -315,6 +347,7 @@ include_directories( . ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_BINARY_DIR}/. ${THIRDPARTY_DIR}/jdns/jdns ${THIRDPARTY_DIR}/jdns/jdnsshared ${THIRDPARTY_DIR}/qtweetlib/qtweetlib/src + ${CMAKE_BINARY_DIR}/thirdparty/liblastfm2/src ) @@ -322,6 +355,7 @@ IF( WIN32 ) SET( OS_SPECIFIC_LINK_LIBRARIES ${OS_SPECIFIC_LINK_LIBRARIES} # Thirdparty + ${QJSON_LDFLAGS} # System "iphlpapi.a" "ws2_32.dll" @@ -340,6 +374,7 @@ IF( APPLE ) SET( OS_SPECIFIC_LINK_LIBRARIES ${OS_SPECIFIC_LINK_LIBRARIES} # Thirdparty + ${QJSON_LIBRARIES} # System ${COREAUDIO_LIBRARY} ${COREFOUNDATION_LIBRARY} @@ -350,9 +385,14 @@ IF( UNIX AND NOT APPLE ) SET( OS_SPECIFIC_LINK_LIBRARIES ${OS_SPECIFIC_LINK_LIBRARIES} # Thirdparty + ${QJSON_LDFLAGS} ) ENDIF( UNIX AND NOT APPLE ) +IF(LIBLASTFM_FOUND) + SET(LINK_LIBRARIES ${LINK_LIBRARIES} tomahawk_lastfm2 ) +ENDIF(LIBLASTFM_FOUND) + qt4_wrap_ui( libUI_H ${libUI} ) qt4_wrap_cpp( libMoc ${libHeaders} ) @@ -370,12 +410,12 @@ target_link_libraries( tomahawklib # External deps ${TAGLIB_LIBRARIES} - ${QJSON_LIBRARIES} ${CLUCENE_LIBRARIES} ${LIBECHONEST_LIBRARY} ${QT_LIBRARIES} ${OS_SPECIFIC_LINK_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} + ${LINK_LIBRARIES} ) -install( TARGETS tomahawklib DESTINATION lib ) +INSTALL( TARGETS tomahawklib DESTINATION lib${LIB_SUFFIX} ) diff --git a/src/libtomahawk/aclsystem.cpp b/src/libtomahawk/aclsystem.cpp new file mode 100644 index 000000000..15c75a407 --- /dev/null +++ b/src/libtomahawk/aclsystem.cpp @@ -0,0 +1,150 @@ +/* === 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 "aclsystem.h" + +#include +#include +#include + +#include + +ACLSystem* ACLSystem::s_instance = 0; + +ACLSystem* +ACLSystem::instance() +{ + if( !s_instance ) + new ACLSystem(); + return s_instance; +} + + +ACLSystem::ACLSystem( QObject* parent ) + : QObject( parent ), + m_saveTimer( this ) +{ + s_instance = this; + //qRegisterMetaType< QHash< QString, QHash< QString, ACL > > >("ACLSystem::ACLCacheHash"); + + QStringList savedEntries = TomahawkSettings::instance()->aclEntries(); + if( !savedEntries.empty() && savedEntries.size() % 3 == 0 ) + { + int index = 0; + while( index < savedEntries.length() ) + { + if( !m_cache.contains( savedEntries.at( index ) ) ) + m_cache[savedEntries.at( index ) ] = QHash< QString, ACL >(); + m_cache[savedEntries.at( index )][savedEntries.at( index + 1 )] = (ACL)(savedEntries.at( index + 2 ).toInt() ); + index += 3; + } + } + + m_saveTimer.setSingleShot( false ); + m_saveTimer.setInterval( 60000 ); + connect( &m_saveTimer, SIGNAL( timeout() ), this, SLOT( saveTimerFired() ) ); + m_saveTimer.start(); +} + +ACLSystem::~ACLSystem() +{ + m_saveTimer.stop(); + saveTimerFired(); +} + +ACLSystem::ACL +ACLSystem::isAuthorizedUser( const QString& dbid ) +{ + qDebug() << Q_FUNC_INFO; + QMutexLocker locker( &m_cacheMutex ); + qDebug() << "Current cache keys = " << m_cache.keys(); + qDebug() << "Looking up dbid"; + if( !m_cache.contains( dbid ) ) + return ACLSystem::NotFound; + else + { + QHash< QString, ACL > peerHash = m_cache[dbid]; + if( peerHash.contains( "global" ) ) + return peerHash["global"]; + return ACLSystem::NotFound; + } +} + +void +ACLSystem::authorizeUser( const QString& dbid, ACLSystem::ACL globalType ) +{ + qDebug() << Q_FUNC_INFO; + if( globalType == ACLSystem::NotFound ) + return; + + QMutexLocker locker( &m_cacheMutex ); + + QHash< QString, ACL > peerHash; + if( m_cache.contains( dbid ) ) + peerHash = m_cache[dbid]; + peerHash["global"] = globalType; + m_cache[dbid] = peerHash; +} + +ACLSystem::ACL +ACLSystem::isAuthorizedPath( const QString& dbid, const QString& path ) +{ + QMutexLocker locker( &m_cacheMutex ); + + if( !m_cache.contains( dbid ) ) + return ACLSystem::NotFound; + + QHash< QString, ACL > peerHash = m_cache[dbid]; + if( !peerHash.contains( path ) ) + { + if( peerHash.contains( "global" ) ) + return peerHash["global"]; + else + return ACLSystem::Deny; + } + return peerHash[path]; +} + +void +ACLSystem::authorizePath( const QString& dbid, const QString& path, ACLSystem::ACL type ) +{ + TomahawkSettings *s = TomahawkSettings::instance(); + if( !s->scannerPaths().contains( path ) ) + { + qDebug() << "path selected is not in our scanner path!"; + return; + } + QMutexLocker locker( &m_cacheMutex ); + QHash< QString, ACLSystem::ACL > peerHash; + if ( m_cache.contains( dbid ) ) + peerHash = m_cache[dbid]; + peerHash[path] = type; + m_cache[dbid] = peerHash; +} + +void +ACLSystem::saveTimerFired() +{ + QStringList saveCache; + foreach( QString dbid, m_cache.keys() ) + { + foreach( QString path, m_cache[dbid].keys() ) + saveCache << dbid << path << QString::number( (int)(m_cache[dbid][path]) ); + } + TomahawkSettings::instance()->setAclEntries( saveCache ); +} \ No newline at end of file diff --git a/src/libtomahawk/aclsystem.h b/src/libtomahawk/aclsystem.h new file mode 100644 index 000000000..8792cfd3a --- /dev/null +++ b/src/libtomahawk/aclsystem.h @@ -0,0 +1,64 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2010-2011, Christian Muehlhaeuser + * + * Tomahawk is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Tomahawk is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Tomahawk. If not, see . + */ + +#ifndef TOMAHAWK_ACLSYSTEM_H +#define TOMAHAWK_ACLSYSTEM_H + +#include +#include +#include +#include +#include + +#include "dllmacro.h" + +class DLLEXPORT ACLSystem : public QObject +{ + Q_OBJECT + +public: + + static ACLSystem* instance(); + + enum ACL { + Allow = 0, + Deny = 1, + NotFound = 2 + }; + + ACLSystem( QObject *parent = 0 ); + ~ACLSystem(); + + ACL isAuthorizedUser( const QString &dbid ); + void authorizeUser( const QString &dbid, ACL globalType ); + + ACL isAuthorizedPath( const QString &dbid, const QString &path ); + void authorizePath( const QString &dbid, const QString &path, ACL type ); + +private slots: + void saveTimerFired(); + +private: + QHash< QString, QHash< QString, ACL > > m_cache; + QTimer m_saveTimer; + QMutex m_cacheMutex; + + static ACLSystem* s_instance; +}; + +#endif // TOMAHAWK_ACLSYSTEM_H diff --git a/src/libtomahawk/album.cpp b/src/libtomahawk/album.cpp index c4c382b42..425183174 100644 --- a/src/libtomahawk/album.cpp +++ b/src/libtomahawk/album.cpp @@ -1,3 +1,21 @@ +/* === 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 "album.h" #include @@ -75,7 +93,7 @@ Album::tracks() cmd->setAlbum( this ); cmd->setSortOrder( DatabaseCommand_AllTracks::AlbumPosition ); - connect( cmd, SIGNAL( tracks( QList ) ), + connect( cmd, SIGNAL( tracks( QList, QVariant ) ), SLOT( onTracksAdded( QList ) ) ); Database::instance()->enqueue( QSharedPointer( cmd ) ); diff --git a/src/libtomahawk/album.h b/src/libtomahawk/album.h index 35f4b48e2..a1497f490 100644 --- a/src/libtomahawk/album.h +++ b/src/libtomahawk/album.h @@ -1,3 +1,21 @@ +/* === 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 TOMAHAWKALBUM_H #define TOMAHAWKALBUM_H @@ -40,7 +58,7 @@ public: virtual void setRepeatMode( PlaylistInterface::RepeatMode ) {} virtual void setShuffled( bool ) {} - virtual void setFilter( const QString& pattern ) {} + virtual void setFilter( const QString& /*pattern*/ ) {} signals: void repeatModeChanged( PlaylistInterface::RepeatMode mode ); diff --git a/src/libtomahawk/artist.cpp b/src/libtomahawk/artist.cpp index ec84884e6..0d24772a4 100644 --- a/src/libtomahawk/artist.cpp +++ b/src/libtomahawk/artist.cpp @@ -1,3 +1,21 @@ +/* === 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 "artist.h" #include @@ -77,7 +95,7 @@ Artist::tracks() cmd->setArtist( this ); cmd->setSortOrder( DatabaseCommand_AllTracks::Album ); - connect( cmd, SIGNAL( tracks( QList ) ), + connect( cmd, SIGNAL( tracks( QList, QVariant ) ), SLOT( onTracksAdded( QList ) ) ); Database::instance()->enqueue( QSharedPointer( cmd ) ); diff --git a/src/libtomahawk/artist.h b/src/libtomahawk/artist.h index 455b09918..200e29c48 100644 --- a/src/libtomahawk/artist.h +++ b/src/libtomahawk/artist.h @@ -1,3 +1,21 @@ +/* === 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 TOMAHAWKARTIST_H #define TOMAHAWKARTIST_H @@ -40,7 +58,7 @@ public: virtual void setRepeatMode( PlaylistInterface::RepeatMode ) {} virtual void setShuffled( bool ) {} - virtual void setFilter( const QString& pattern ) {} + virtual void setFilter( const QString& /*pattern*/ ) {} signals: void repeatModeChanged( PlaylistInterface::RepeatMode mode ); diff --git a/src/libtomahawk/audio/audioengine.cpp b/src/libtomahawk/audio/audioengine.cpp index d073cd135..adf249059 100644 --- a/src/libtomahawk/audio/audioengine.cpp +++ b/src/libtomahawk/audio/audioengine.cpp @@ -1,3 +1,21 @@ +/* === 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 "audioengine.h" #include @@ -162,6 +180,13 @@ AudioEngine::loadTrack( const Tomahawk::result_ptr& result ) { setCurrentTrack( result ); io = Servent::instance()->getIODeviceForUrl( m_currentTrack ); + if ( m_currentTrack->url().startsWith( "http://" ) ) + { + m_readReady = false; + connect( io.data(), SIGNAL( downloadProgress( qint64, qint64 ) ), SLOT( onDownloadProgress( qint64, qint64 ) ) ); + } + else + m_readReady = true; if ( !io || io.isNull() ) { diff --git a/src/libtomahawk/audio/audioengine.h b/src/libtomahawk/audio/audioengine.h index 0c0ef67ff..8897e4137 100644 --- a/src/libtomahawk/audio/audioengine.h +++ b/src/libtomahawk/audio/audioengine.h @@ -1,3 +1,21 @@ +/* === 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 AUDIOENGINE_H #define AUDIOENGINE_H @@ -98,6 +116,7 @@ private: Phonon::MediaObject* m_mediaObject; Phonon::AudioOutput* m_audioOutput; + bool m_readReady; unsigned int m_timeElapsed; bool m_expectStop; diff --git a/src/libtomahawk/audio/dummytranscode.cpp b/src/libtomahawk/audio/dummytranscode.cpp new file mode 100644 index 000000000..92b5efc6e --- /dev/null +++ b/src/libtomahawk/audio/dummytranscode.cpp @@ -0,0 +1,64 @@ +/* === 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 "dummytranscode.h" + +#include + +DummyTranscode::DummyTranscode() + : m_init( false ) +{ + qDebug() << Q_FUNC_INFO; +} + + +DummyTranscode::~DummyTranscode() +{ + qDebug() << Q_FUNC_INFO; +} + + +void +DummyTranscode::processData( const QByteArray &buffer, bool finish ) +{ + Q_UNUSED( finish ); + m_buffer.append( buffer ); +// qDebug() << "DUMMYTRANSCODING:" << buffer.size(); + + if( !m_init && m_buffer.size() >= 16364 ) { + m_init = true; + emit streamInitialized( 44100, 2 ); + } +} + + +void +DummyTranscode::onSeek( int seconds ) +{ + Q_UNUSED( seconds ); + m_buffer.clear(); +} + + +void +DummyTranscode::clearBuffers() +{ + m_buffer.clear(); + m_init = false; +} + diff --git a/src/libtomahawk/audio/dummytranscode.h b/src/libtomahawk/audio/dummytranscode.h new file mode 100644 index 000000000..d38774ffd --- /dev/null +++ b/src/libtomahawk/audio/dummytranscode.h @@ -0,0 +1,63 @@ +/* + + 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 . +*/ + + +#ifndef DUMMYTRANSCODE_H +#define DUMMYTRANSCODE_H + +#include "audio/transcodeinterface.h" +#include "dllmacro.h" + +#define _BUFFER_PREFERRED 32768 + +class DLLEXPORT DummyTranscode : public TranscodeInterface +{ + Q_OBJECT + +public: + DummyTranscode(); + virtual ~DummyTranscode(); + + const QStringList supportedTypes() const { QStringList l; l << "audio/basic"; return l; } + + int needData() { return true; } // always eats data + bool haveData() { return !m_buffer.isEmpty(); } + + unsigned int preferredDataSize() { return _BUFFER_PREFERRED; } + + QByteArray data() { QByteArray b = m_buffer; m_buffer.clear(); return b; } + + virtual void setBufferCapacity( int bytes ) { m_bufferCapacity = bytes; } + int bufferSize() { return m_buffer.size(); } + +public slots: + virtual void clearBuffers(); + virtual void onSeek( int seconds ); + virtual void processData( const QByteArray& data, bool finish ); + +signals: + void streamInitialized( long sampleRate, int channels ); + void timeChanged( int seconds ); + +private: + QByteArray m_buffer; + int m_bufferCapacity; + bool m_init; +}; + +#endif // DUMMYTRANSCODE_H diff --git a/src/libtomahawk/collection.cpp b/src/libtomahawk/collection.cpp index 85e7262e4..e02acd260 100644 --- a/src/libtomahawk/collection.cpp +++ b/src/libtomahawk/collection.cpp @@ -1,3 +1,21 @@ +/* === 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 "collection.h" #include @@ -92,10 +110,10 @@ Collection::deleteDynamicPlaylist( const Tomahawk::dynplaylist_ptr& p ) QList todelete; todelete << p; m_dynplaylists.removeAll( p ); - + qDebug() << Q_FUNC_INFO << "Collection name" << name() - << "from source id" << source()->id() - << "numplaylists:" << m_playlists.length(); + << "from source id" << source()->id() + << "numplaylists:" << m_playlists.length(); emit dynamicPlaylistsDeleted( todelete ); } @@ -158,7 +176,6 @@ Collection::setTracks( const QList& tracks ) { qDebug() << Q_FUNC_INFO << tracks.count() << name(); - m_isLoaded = true; m_tracks << tracks; emit tracksAdded( tracks ); } diff --git a/src/libtomahawk/collection.h b/src/libtomahawk/collection.h index 76ab19897..ca2aa66fd 100644 --- a/src/libtomahawk/collection.h +++ b/src/libtomahawk/collection.h @@ -1,3 +1,21 @@ +/* === 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 . + */ + /* The collection - acts as container for someones music library load() -> async populate by calling addArtists etc, @@ -33,6 +51,7 @@ public: Collection( const source_ptr& source, const QString& name, QObject* parent = 0 ); virtual ~Collection(); + virtual void setLoaded() { m_isLoaded = true; } virtual bool isLoaded() const { return m_isLoaded; } virtual QString name() const; @@ -72,9 +91,10 @@ public slots: void setPlaylists( const QList& plists ); void setDynamicPlaylists( const QList< Tomahawk::dynplaylist_ptr >& dynplists ); - void setTracks( const QList& tracks ); + void setTracks( const QList& tracks ); void delTracks( const QStringList& files ); + void resetTrackCache() { m_tracks.clear(); m_isLoaded = false; } protected: QString m_name; diff --git a/src/libtomahawk/database/database.cpp b/src/libtomahawk/database/database.cpp index 23ae975b4..4436f6cfa 100644 --- a/src/libtomahawk/database/database.cpp +++ b/src/libtomahawk/database/database.cpp @@ -1,3 +1,21 @@ +/* === 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.h" #define WORKER_THREADS 5 @@ -14,11 +32,16 @@ Database::instance() 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 ) ) { s_instance = this; + connect( m_impl, SIGNAL( indexReady() ), SIGNAL( indexReady() ) ); + connect( m_impl, SIGNAL( indexReady() ), SIGNAL( ready() ) ); + connect( m_impl, SIGNAL( indexReady() ), SLOT( setIsReadyTrue() ) ); + m_workerRW->start(); } diff --git a/src/libtomahawk/database/database.h b/src/libtomahawk/database/database.h index 07e55be78..df02ce2a0 100644 --- a/src/libtomahawk/database/database.h +++ b/src/libtomahawk/database/database.h @@ -1,3 +1,21 @@ +/* === 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 DATABASE_H #define DATABASE_H @@ -23,6 +41,7 @@ class DLLEXPORT Database : public QObject { Q_OBJECT + public: static Database* instance(); @@ -30,19 +49,27 @@ public: ~Database(); QString dbid() const; - const bool indexReady() const { return m_indexReady; } + bool indexReady() const { return m_indexReady; } void loadIndex(); + + bool isReady() const { return m_ready; } signals: void indexReady(); // search index + void ready(); + void newJobRO( QSharedPointer ); void newJobRW( QSharedPointer ); public slots: void enqueue( QSharedPointer lc ); +private slots: + void setIsReadyTrue() { m_ready = true; } + private: + bool m_ready; DatabaseImpl* m_impl; DatabaseWorker* m_workerRW; QHash< QString, DatabaseWorker* > m_workers; diff --git a/src/libtomahawk/database/databasecollection.cpp b/src/libtomahawk/database/databasecollection.cpp index 6383098b1..23e36584d 100644 --- a/src/libtomahawk/database/databasecollection.cpp +++ b/src/libtomahawk/database/databasecollection.cpp @@ -1,3 +1,21 @@ +/* === 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 "databasecollection.h" #include "database/database.h" @@ -12,7 +30,6 @@ using namespace Tomahawk; DatabaseCollection::DatabaseCollection( const source_ptr& src, QObject* parent ) : Collection( src, QString( "dbcollection:%1" ).arg( src->userName() ), parent ) - , m_loadedTracks( false ) { } @@ -35,10 +52,10 @@ DatabaseCollection::loadDynamicPlaylists() { qDebug() << Q_FUNC_INFO; DatabaseCommand_LoadAllDynamicPlaylists* cmd = new DatabaseCommand_LoadAllDynamicPlaylists( source() ); - + connect( cmd, SIGNAL( playlistLoaded( Tomahawk::source_ptr, QVariantList ) ), SLOT( dynamicPlaylistCreated( const Tomahawk::source_ptr&, const QVariantList& ) ) ); - + Database::instance()->enqueue( QSharedPointer( cmd ) ); } @@ -48,10 +65,10 @@ DatabaseCollection::loadTracks() { qDebug() << Q_FUNC_INFO << source()->userName(); - m_loadedTracks = true; + setLoaded(); DatabaseCommand_AllTracks* cmd = new DatabaseCommand_AllTracks( source()->collection() ); - connect( cmd, SIGNAL( tracks( QList ) ), + connect( cmd, SIGNAL( tracks( QList, QVariant ) ), SLOT( setTracks( QList ) ) ); Database::instance()->enqueue( QSharedPointer( cmd ) ); @@ -95,12 +112,12 @@ DatabaseCollection::playlists() QList< dynplaylist_ptr > DatabaseCollection::dynamicPlaylists() { qDebug() << Q_FUNC_INFO; - + if ( Collection::dynamicPlaylists().isEmpty() ) { loadDynamicPlaylists(); } - + return Collection::dynamicPlaylists(); } @@ -110,7 +127,7 @@ DatabaseCollection::tracks() { qDebug() << Q_FUNC_INFO; - if ( !m_loadedTracks ) + if ( !isLoaded() ) { loadTracks(); } diff --git a/src/libtomahawk/database/databasecollection.h b/src/libtomahawk/database/databasecollection.h index 5c2ce4832..5b15913c0 100644 --- a/src/libtomahawk/database/databasecollection.h +++ b/src/libtomahawk/database/databasecollection.h @@ -1,3 +1,21 @@ +/* === 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 DATABASECOLLECTION_H #define DATABASECOLLECTION_H @@ -34,9 +52,6 @@ public slots: private slots: void dynamicPlaylistCreated( const Tomahawk::source_ptr& source, const QVariantList& data ); - -private: - bool m_loadedTracks; }; #endif // DATABASECOLLECTION_H diff --git a/src/libtomahawk/database/databasecommand.cpp b/src/libtomahawk/database/databasecommand.cpp index 82e0c5cad..41824c87c 100644 --- a/src/libtomahawk/database/databasecommand.cpp +++ b/src/libtomahawk/database/databasecommand.cpp @@ -1,3 +1,21 @@ +/* === 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 "databasecommand.h" #include diff --git a/src/libtomahawk/database/databasecommand.h b/src/libtomahawk/database/databasecommand.h index b8a59fc7f..3ce9b6873 100644 --- a/src/libtomahawk/database/databasecommand.h +++ b/src/libtomahawk/database/databasecommand.h @@ -1,3 +1,21 @@ +/* === 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 DATABASECOMMAND_H #define DATABASECOMMAND_H @@ -30,6 +48,7 @@ public: explicit DatabaseCommand( const Tomahawk::source_ptr& src, QObject* parent = 0 ); DatabaseCommand( const DatabaseCommand &other ) + : QObject( other.parent() ) { } @@ -41,7 +60,7 @@ public: // if i make this pure virtual, i get compile errors in qmetatype.h. // we need Q_DECLARE_METATYPE to use in queued sig/slot connections. - virtual void exec( DatabaseImpl* lib ) { Q_ASSERT( false ); } + virtual void exec( DatabaseImpl* /*lib*/ ) { Q_ASSERT( false ); } void _exec( DatabaseImpl* lib ); @@ -57,6 +76,9 @@ public: virtual bool singletonCmd() const { return false; } virtual bool localOnly() const { return false; } + virtual QVariant data() const { return m_data; } + virtual void setData( const QVariant& data ) { m_data = data; } + QString guid() const { if( m_guid.isEmpty() ) @@ -79,6 +101,8 @@ private: State m_state; Tomahawk::source_ptr m_source; mutable QString m_guid; + + QVariant m_data; }; Q_DECLARE_METATYPE( DatabaseCommand ) diff --git a/src/libtomahawk/database/databasecommand_addclientauth.cpp b/src/libtomahawk/database/databasecommand_addclientauth.cpp index d8e705952..a1aacc48f 100644 --- a/src/libtomahawk/database/databasecommand_addclientauth.cpp +++ b/src/libtomahawk/database/databasecommand_addclientauth.cpp @@ -1,18 +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 2 of the License, or (at your option) any later * - * version. * - * * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY * - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * - * PARTICULAR PURPOSE. See the GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License along with * - * this program. If not, see . * - ****************************************************************************************/ +/* === 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 "databasecommand_addclientauth.h" diff --git a/src/libtomahawk/database/databasecommand_addclientauth.h b/src/libtomahawk/database/databasecommand_addclientauth.h index fccec5947..b562ef65a 100644 --- a/src/libtomahawk/database/databasecommand_addclientauth.h +++ b/src/libtomahawk/database/databasecommand_addclientauth.h @@ -1,18 +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 2 of the License, or (at your option) any later * - * version. * - * * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY * - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * - * PARTICULAR PURPOSE. See the GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License along with * - * this program. If not, see . * - ****************************************************************************************/ +/* === 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 DATABASECOMMAND_ADDCLIENTAUTH_H #define DATABASECOMMAND_ADDCLIENTAUTH_H diff --git a/src/libtomahawk/database/databasecommand_addfiles.cpp b/src/libtomahawk/database/databasecommand_addfiles.cpp index 10c1ee429..e28164290 100644 --- a/src/libtomahawk/database/databasecommand_addfiles.cpp +++ b/src/libtomahawk/database/databasecommand_addfiles.cpp @@ -1,3 +1,21 @@ +/* === 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 "databasecommand_addfiles.h" #include @@ -45,11 +63,11 @@ DatabaseCommand_AddFiles::postCommitHook() // collection browser will update/fade in etc. Collection* coll = source()->collection().data(); - connect( this, SIGNAL( notify( QList, Tomahawk::collection_ptr ) ), - coll, SLOT( setTracks( QList, Tomahawk::collection_ptr ) ), + connect( this, SIGNAL( notify( QList ) ), + coll, SLOT( setTracks( QList ) ), Qt::QueuedConnection ); - emit notify( m_queries, source()->collection() ); + emit notify( m_queries ); // also re-calc the collection stats, to updates the "X tracks" in the sidebar etc: DatabaseCommand_CollectionStats* cmd = new DatabaseCommand_CollectionStats( source() ); diff --git a/src/libtomahawk/database/databasecommand_addfiles.h b/src/libtomahawk/database/databasecommand_addfiles.h index 8e8b5e11c..5d82ec9ac 100644 --- a/src/libtomahawk/database/databasecommand_addfiles.h +++ b/src/libtomahawk/database/databasecommand_addfiles.h @@ -1,3 +1,21 @@ +/* === 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 DATABASECOMMAND_ADDFILES_H #define DATABASECOMMAND_ADDFILES_H @@ -37,7 +55,7 @@ public: signals: void done( const QList&, const Tomahawk::collection_ptr& ); - void notify( const QList&, const Tomahawk::collection_ptr& ); + void notify( const QList& ); private: QVariantList m_files; diff --git a/src/libtomahawk/database/databasecommand_addsource.cpp b/src/libtomahawk/database/databasecommand_addsource.cpp index 765e715d3..5af922f5e 100644 --- a/src/libtomahawk/database/databasecommand_addsource.cpp +++ b/src/libtomahawk/database/databasecommand_addsource.cpp @@ -1,3 +1,21 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2010-2011, Christian Muehlhaeuser + * + * Tomahawk is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Tomahawk is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Tomahawk. If not, see . + */ + #include #include "databasecommand_addsource.h" #include "databaseimpl.h" diff --git a/src/libtomahawk/database/databasecommand_addsource.h b/src/libtomahawk/database/databasecommand_addsource.h index aa657aae8..375467ea1 100644 --- a/src/libtomahawk/database/databasecommand_addsource.h +++ b/src/libtomahawk/database/databasecommand_addsource.h @@ -1,3 +1,21 @@ +/* === 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 DATABASECOMMAND_ADDSOURCE_H #define DATABASECOMMAND_ADDSOURCE_H diff --git a/src/libtomahawk/database/databasecommand_allalbums.cpp b/src/libtomahawk/database/databasecommand_allalbums.cpp index 56aba5672..55b2b7139 100644 --- a/src/libtomahawk/database/databasecommand_allalbums.cpp +++ b/src/libtomahawk/database/databasecommand_allalbums.cpp @@ -1,17 +1,34 @@ +/* === 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 "databasecommand_allalbums.h" #include #include "databaseimpl.h" -void -DatabaseCommand_AllAlbums::exec( DatabaseImpl* dbi ) -{ - Q_ASSERT( !m_collection->source().isNull() ); +void +DatabaseCommand_AllAlbums::execForArtist( DatabaseImpl* dbi ) +{ TomahawkSqlQuery query = dbi->newquery(); QList al; - QString m_orderToken; + QString orderToken, sourceToken; switch ( m_sortOrder ) { @@ -19,22 +36,78 @@ DatabaseCommand_AllAlbums::exec( DatabaseImpl* dbi ) break; case ModificationTime: - m_orderToken = "file.mtime"; + orderToken = "file.mtime"; + } + + if ( !m_collection.isNull() ) + sourceToken = QString( "AND file.source %1 " ).arg( m_collection->source()->isLocal() ? "IS NULL" : QString( "= %1" ).arg( m_collection->source()->id() ) ); + + QString sql = QString( + "SELECT DISTINCT album.id, album.name " + "FROM file, file_join " + "LEFT OUTER JOIN album " + "ON file_join.album = album.id " + "WHERE file.id = file_join.file " + "AND file_join.artist = %1 " + "%2 " + "%3 %4 %5" + ).arg( m_artist->id() ) + .arg( sourceToken ) + .arg( m_sortOrder > 0 ? QString( "ORDER BY %1" ).arg( orderToken ) : QString() ) + .arg( m_sortDescending ? "DESC" : QString() ) + .arg( m_amount > 0 ? QString( "LIMIT 0, %1" ).arg( m_amount ) : QString() ); + + query.prepare( sql ); + query.exec(); + + while( query.next() ) + { + unsigned int albumId = query.value( 0 ).toUInt(); + QString albumName = query.value( 1 ).toString(); + if ( query.value( 0 ).isNull() ) + { + albumName = tr( "Unknown" ); + } + + Tomahawk::album_ptr album = Tomahawk::Album::get( albumId, albumName, m_artist ); + al << album; + } + + if ( al.count() ) + emit albums( al, data() ); + emit done(); +} + + +void +DatabaseCommand_AllAlbums::execForCollection( DatabaseImpl* dbi ) +{ + TomahawkSqlQuery query = dbi->newquery(); + QList al; + QString orderToken; + + switch ( m_sortOrder ) + { + case 0: + break; + + case ModificationTime: + orderToken = "file.mtime"; } QString sql = QString( - "SELECT DISTINCT album.id, album.name, album.artist, artist.name " - "FROM album, file, file_join " - "LEFT OUTER JOIN artist " - "ON album.artist = artist.id " - "WHERE file.id = file_join.file " - "AND file_join.album = album.id " - "AND file.source %1 " - "%2 %3 %4" - ).arg( m_collection->source()->isLocal() ? "IS NULL" : QString( "= %1" ).arg( m_collection->source()->id() ) ) - .arg( m_sortOrder > 0 ? QString( "ORDER BY %1" ).arg( m_orderToken ) : QString() ) - .arg( m_sortDescending ? "DESC" : QString() ) - .arg( m_amount > 0 ? QString( "LIMIT 0, %1" ).arg( m_amount ) : QString() ); + "SELECT DISTINCT album.id, album.name, album.artist, artist.name " + "FROM album, file, file_join " + "LEFT OUTER JOIN artist " + "ON album.artist = artist.id " + "WHERE file.id = file_join.file " + "AND file_join.album = album.id " + "AND file.source %1 " + "%2 %3 %4" + ).arg( m_collection->source()->isLocal() ? "IS NULL" : QString( "= %1" ).arg( m_collection->source()->id() ) ) + .arg( m_sortOrder > 0 ? QString( "ORDER BY %1" ).arg( orderToken ) : QString() ) + .arg( m_sortDescending ? "DESC" : QString() ) + .arg( m_amount > 0 ? QString( "LIMIT 0, %1" ).arg( m_amount ) : QString() ); query.prepare( sql ); query.exec(); @@ -48,6 +121,20 @@ DatabaseCommand_AllAlbums::exec( DatabaseImpl* dbi ) } if ( al.count() ) - emit albums( al, m_collection ); - emit done( m_collection ); + emit albums( al, data() ); + emit done(); +} + + +void +DatabaseCommand_AllAlbums::exec( DatabaseImpl* dbi ) +{ + if ( !m_artist.isNull() ) + { + execForArtist( dbi ); + } + else + { + execForCollection( dbi ); + } } diff --git a/src/libtomahawk/database/databasecommand_allalbums.h b/src/libtomahawk/database/databasecommand_allalbums.h index 37b6d0db2..8520e0d07 100644 --- a/src/libtomahawk/database/databasecommand_allalbums.h +++ b/src/libtomahawk/database/databasecommand_allalbums.h @@ -1,3 +1,21 @@ +/* === 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 DATABASECOMMAND_ALLALBUMS_H #define DATABASECOMMAND_ALLALBUMS_H @@ -20,9 +38,10 @@ public: ModificationTime = 1 }; - explicit DatabaseCommand_AllAlbums( const Tomahawk::collection_ptr& collection, QObject* parent = 0 ) + explicit DatabaseCommand_AllAlbums( const Tomahawk::collection_ptr& collection, const Tomahawk::artist_ptr& artist = Tomahawk::artist_ptr(), QObject* parent = 0 ) : DatabaseCommand( parent ) , m_collection( collection ) + , m_artist( artist ) , m_amount( 0 ) , m_sortOrder( DatabaseCommand_AllAlbums::None ) , m_sortDescending( false ) @@ -33,16 +52,21 @@ public: virtual bool doesMutates() const { return false; } virtual QString commandname() const { return "allalbums"; } + void execForCollection( DatabaseImpl* ); + void execForArtist( DatabaseImpl* ); + void setLimit( unsigned int amount ) { m_amount = amount; } void setSortOrder( DatabaseCommand_AllAlbums::SortOrder order ) { m_sortOrder = order; } void setSortDescending( bool descending ) { m_sortDescending = descending; } signals: - void albums( const QList&, const Tomahawk::collection_ptr& ); - void done( const Tomahawk::collection_ptr& ); + void albums( const QList&, const QVariant& data ); + void done(); private: Tomahawk::collection_ptr m_collection; + Tomahawk::artist_ptr m_artist; + unsigned int m_amount; DatabaseCommand_AllAlbums::SortOrder m_sortOrder; bool m_sortDescending; diff --git a/src/libtomahawk/database/databasecommand_allartists.cpp b/src/libtomahawk/database/databasecommand_allartists.cpp new file mode 100644 index 000000000..41b7e3079 --- /dev/null +++ b/src/libtomahawk/database/databasecommand_allartists.cpp @@ -0,0 +1,68 @@ +/* === 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 "databasecommand_allartists.h" + +#include + +#include "databaseimpl.h" + +void +DatabaseCommand_AllArtists::exec( DatabaseImpl* dbi ) +{ + TomahawkSqlQuery query = dbi->newquery(); + QList al; + QString orderToken, sourceToken; + + switch ( m_sortOrder ) + { + case 0: + break; + + case ModificationTime: + orderToken = "file.mtime"; + } + + if ( !m_collection.isNull() ) + sourceToken = QString( "AND file.source %1 " ).arg( m_collection->source()->isLocal() ? "IS NULL" : QString( "= %1" ).arg( m_collection->source()->id() ) ); + + QString sql = QString( + "SELECT DISTINCT artist.id, artist.name " + "FROM artist, file, file_join " + "WHERE file.id = file_join.file " + "AND file_join.artist = artist.id " + "%1 %2 %3 %4" + ).arg( sourceToken ) + .arg( m_sortOrder > 0 ? QString( "ORDER BY %1" ).arg( orderToken ) : QString() ) + .arg( m_sortDescending ? "DESC" : QString() ) + .arg( m_amount > 0 ? QString( "LIMIT 0, %1" ).arg( m_amount ) : QString() ); + + query.prepare( sql ); + query.exec(); + + while( query.next() ) + { + Tomahawk::artist_ptr artist = Tomahawk::Artist::get( query.value( 0 ).toUInt(), query.value( 1 ).toString() ); + + al << artist; + } + + if ( al.count() ) + emit artists( al ); + emit done(); +} diff --git a/src/libtomahawk/database/databasecommand_allartists.h b/src/libtomahawk/database/databasecommand_allartists.h new file mode 100644 index 000000000..6d13a107c --- /dev/null +++ b/src/libtomahawk/database/databasecommand_allartists.h @@ -0,0 +1,69 @@ +/* === 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 DATABASECOMMAND_ALLARTISTS_H +#define DATABASECOMMAND_ALLARTISTS_H + +#include +#include + +#include "databasecommand.h" +#include "album.h" +#include "collection.h" +#include "typedefs.h" + +#include "dllmacro.h" + +class DLLEXPORT DatabaseCommand_AllArtists : public DatabaseCommand +{ +Q_OBJECT +public: + enum SortOrder { + None = 0, + ModificationTime = 1 + }; + + explicit DatabaseCommand_AllArtists( const Tomahawk::collection_ptr& collection = Tomahawk::collection_ptr(), QObject* parent = 0 ) + : DatabaseCommand( parent ) + , m_collection( collection ) + , m_amount( 0 ) + , m_sortOrder( DatabaseCommand_AllArtists::None ) + , m_sortDescending( false ) + {} + + virtual void exec( DatabaseImpl* ); + + virtual bool doesMutates() const { return false; } + virtual QString commandname() const { return "allartists"; } + + void setLimit( unsigned int amount ) { m_amount = amount; } + void setSortOrder( DatabaseCommand_AllArtists::SortOrder order ) { m_sortOrder = order; } + void setSortDescending( bool descending ) { m_sortDescending = descending; } + +signals: + void artists( const QList& ); + void done(); + +private: + Tomahawk::collection_ptr m_collection; + unsigned int m_amount; + DatabaseCommand_AllArtists::SortOrder m_sortOrder; + bool m_sortDescending; +}; + +#endif // DATABASECOMMAND_ALLARTISTS_H diff --git a/src/libtomahawk/database/databasecommand_alltracks.cpp b/src/libtomahawk/database/databasecommand_alltracks.cpp index cb89505d3..01117751c 100644 --- a/src/libtomahawk/database/databasecommand_alltracks.cpp +++ b/src/libtomahawk/database/databasecommand_alltracks.cpp @@ -1,3 +1,21 @@ +/* === 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 "databasecommand_alltracks.h" #include @@ -33,10 +51,21 @@ DatabaseCommand_AllTracks::exec( DatabaseImpl* dbi ) break; } - if ( !m_collection.isNull() ) sourceToken = QString( "AND file.source %1" ).arg( m_collection->source()->isLocal() ? "IS NULL" : QString( "= %1" ).arg( m_collection->source()->id() ) ); + QString albumToken; + if ( m_album ) + { + if ( m_album->id() == 0 ) + { + m_artist = m_album->artist().data(); + albumToken = QString( "AND album.id IS NULL" ); + } + else + albumToken = QString( "AND album.id = %1" ).arg( m_album->id() ); + } + QString sql = QString( "SELECT file.id, artist.name, album.name, track.name, file.size, " "file.duration, file.bitrate, file.url, file.source, file.mtime, file.mimetype, file_join.albumpos, artist.id, album.id, track.id " @@ -51,7 +80,7 @@ DatabaseCommand_AllTracks::exec( DatabaseImpl* dbi ) "%4 %5 %6" ).arg( sourceToken ) .arg( !m_artist ? QString() : QString( "AND artist.id = %1" ).arg( m_artist->id() ) ) - .arg( !m_album ? QString() : QString( "AND album.id = %1" ).arg( m_album->id() ) ) + .arg( !m_album ? QString() : albumToken ) .arg( m_sortOrder > 0 ? QString( "ORDER BY %1" ).arg( m_orderToken ) : QString() ) .arg( m_sortDescending ? "DESC" : QString() ) .arg( m_amount > 0 ? QString( "LIMIT 0, %1" ).arg( m_amount ) : QString() ); @@ -59,7 +88,6 @@ DatabaseCommand_AllTracks::exec( DatabaseImpl* dbi ) query.prepare( sql ); query.exec(); - int i = 0; while( query.next() ) { Tomahawk::result_ptr result = Tomahawk::result_ptr( new Tomahawk::Result() ); @@ -120,12 +148,13 @@ DatabaseCommand_AllTracks::exec( DatabaseImpl* dbi ) QList results; results << result; qry->addResults( results ); + qry->setResolveFinished( true ); ql << qry; } qDebug() << Q_FUNC_INFO << ql.length(); - emit tracks( ql ); + emit tracks( ql, data() ); emit done( m_collection ); } diff --git a/src/libtomahawk/database/databasecommand_alltracks.h b/src/libtomahawk/database/databasecommand_alltracks.h index 5eea1c55c..335c11585 100644 --- a/src/libtomahawk/database/databasecommand_alltracks.h +++ b/src/libtomahawk/database/databasecommand_alltracks.h @@ -1,3 +1,21 @@ +/* === 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 DATABASECOMMAND_ALLTRACKS_H #define DATABASECOMMAND_ALLTRACKS_H @@ -45,7 +63,7 @@ public: void setSortDescending( bool descending ) { m_sortDescending = descending; } signals: - void tracks( const QList& ); + void tracks( const QList&, const QVariant& data ); void done( const Tomahawk::collection_ptr& ); private: diff --git a/src/libtomahawk/database/databasecommand_clientauthvalid.cpp b/src/libtomahawk/database/databasecommand_clientauthvalid.cpp index e751748bf..be0e4d495 100644 --- a/src/libtomahawk/database/databasecommand_clientauthvalid.cpp +++ b/src/libtomahawk/database/databasecommand_clientauthvalid.cpp @@ -1,18 +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 2 of the License, or (at your option) any later * - * version. * - * * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY * - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * - * PARTICULAR PURPOSE. See the GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License along with * - * this program. If not, see . * - ****************************************************************************************/ +/* === 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 "databasecommand_clientauthvalid.h" diff --git a/src/libtomahawk/database/databasecommand_clientauthvalid.h b/src/libtomahawk/database/databasecommand_clientauthvalid.h index efb3c7bb1..94191200e 100644 --- a/src/libtomahawk/database/databasecommand_clientauthvalid.h +++ b/src/libtomahawk/database/databasecommand_clientauthvalid.h @@ -1,18 +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 2 of the License, or (at your option) any later * - * version. * - * * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY * - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * - * PARTICULAR PURPOSE. See the GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License along with * - * this program. If not, see . * - ****************************************************************************************/ +/* === 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 DATABASECOMMAND_CLIENTAUTHVALID_H #define DATABASECOMMAND_CLIENTAUTHVALID_H diff --git a/src/libtomahawk/database/databasecommand_collectionstats.cpp b/src/libtomahawk/database/databasecommand_collectionstats.cpp index 7236981ff..081b24ade 100644 --- a/src/libtomahawk/database/databasecommand_collectionstats.cpp +++ b/src/libtomahawk/database/databasecommand_collectionstats.cpp @@ -1,3 +1,21 @@ +/* === 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 "databasecommand_collectionstats.h" #include "databaseimpl.h" diff --git a/src/libtomahawk/database/databasecommand_collectionstats.h b/src/libtomahawk/database/databasecommand_collectionstats.h index 1c14a3eb4..58b55aefc 100644 --- a/src/libtomahawk/database/databasecommand_collectionstats.h +++ b/src/libtomahawk/database/databasecommand_collectionstats.h @@ -1,3 +1,21 @@ +/* === 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 DATABASECOMMAND_COLLECTIONSTATS_H #define DATABASECOMMAND_COLLECTIONSTATS_H diff --git a/src/libtomahawk/database/databasecommand_createdynamicplaylist.cpp b/src/libtomahawk/database/databasecommand_createdynamicplaylist.cpp index b906e3077..f432f3a25 100644 --- a/src/libtomahawk/database/databasecommand_createdynamicplaylist.cpp +++ b/src/libtomahawk/database/databasecommand_createdynamicplaylist.cpp @@ -1,3 +1,21 @@ +/* === 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 "databasecommand_createdynamicplaylist.h" #include @@ -8,7 +26,6 @@ #include "dynamic/GeneratorInterface.h" #include "network/servent.h" -#include "tomahawk/tomahawkapp.h" #include "playlist/playlistmanager.h" using namespace Tomahawk; diff --git a/src/libtomahawk/database/databasecommand_createdynamicplaylist.h b/src/libtomahawk/database/databasecommand_createdynamicplaylist.h index cdf180155..2c2183b61 100644 --- a/src/libtomahawk/database/databasecommand_createdynamicplaylist.h +++ b/src/libtomahawk/database/databasecommand_createdynamicplaylist.h @@ -1,3 +1,21 @@ +/* === 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 DATABASECOMMAND_CREATEDYNAMICPLAYLIST_H #define DATABASECOMMAND_CREATEDYNAMICPLAYLIST_H diff --git a/src/libtomahawk/database/databasecommand_createplaylist.cpp b/src/libtomahawk/database/databasecommand_createplaylist.cpp index 7d703b668..b5bd2ccc2 100644 --- a/src/libtomahawk/database/databasecommand_createplaylist.cpp +++ b/src/libtomahawk/database/databasecommand_createplaylist.cpp @@ -1,9 +1,26 @@ +/* === 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 "databasecommand_createplaylist.h" #include #include "network/servent.h" -#include "tomahawk/tomahawkapp.h" #include "playlist/playlistmanager.h" using namespace Tomahawk; diff --git a/src/libtomahawk/database/databasecommand_createplaylist.h b/src/libtomahawk/database/databasecommand_createplaylist.h index 40b2c3de0..ec3aa14a3 100644 --- a/src/libtomahawk/database/databasecommand_createplaylist.h +++ b/src/libtomahawk/database/databasecommand_createplaylist.h @@ -1,3 +1,21 @@ +/* === 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 DATABASECOMMAND_CREATEPLAYLIST_H #define DATABASECOMMAND_CREATEPLAYLIST_H diff --git a/src/libtomahawk/database/databasecommand_deletedynamicplaylist.cpp b/src/libtomahawk/database/databasecommand_deletedynamicplaylist.cpp index e59e55ff9..34c5defbd 100644 --- a/src/libtomahawk/database/databasecommand_deletedynamicplaylist.cpp +++ b/src/libtomahawk/database/databasecommand_deletedynamicplaylist.cpp @@ -1,18 +1,21 @@ -/**************************************************************************************** - * Copyright (c) 2010 Leo Franchi * - * * - * This program is free software; you can redistribute it and/or modify it under * - * the terms of the GNU General Public License as published by the Free Software * - * Foundation; either version 2 of the License, or (at your option) any later * - * version. * - * * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY * - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * - * PARTICULAR PURPOSE. See the GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License along with * - * this program. If not, see . * - ****************************************************************************************/ +/* === 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 "databasecommand_deletedynamicplaylist.h" #include diff --git a/src/libtomahawk/database/databasecommand_deletedynamicplaylist.h b/src/libtomahawk/database/databasecommand_deletedynamicplaylist.h index b9fcf905f..be0ba5df1 100644 --- a/src/libtomahawk/database/databasecommand_deletedynamicplaylist.h +++ b/src/libtomahawk/database/databasecommand_deletedynamicplaylist.h @@ -1,18 +1,20 @@ -/**************************************************************************************** - * Copyright (c) 2010 Leo Franchi * - * * - * This program is free software; you can redistribute it and/or modify it under * - * the terms of the GNU General Public License as published by the Free Software * - * Foundation; either version 2 of the License, or (at your option) any later * - * version. * - * * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY * - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * - * PARTICULAR PURPOSE. See the GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License along with * - * this program. If not, see . * - ****************************************************************************************/ +/* === 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 DATABASECOMMAND_DELETEDYNAMICPLAYLIST_H #define DATABASECOMMAND_DELETEDYNAMICPLAYLIST_H diff --git a/src/libtomahawk/database/databasecommand_deletefiles.cpp b/src/libtomahawk/database/databasecommand_deletefiles.cpp index 436bffcfc..f3675369f 100644 --- a/src/libtomahawk/database/databasecommand_deletefiles.cpp +++ b/src/libtomahawk/database/databasecommand_deletefiles.cpp @@ -1,3 +1,21 @@ +/* === 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 "databasecommand_deletefiles.h" #include @@ -23,10 +41,10 @@ DatabaseCommand_DeleteFiles::postCommitHook() // collection browser will update/fade in etc. Collection* coll = source()->collection().data(); - connect( this, SIGNAL( notify( QStringList, Tomahawk::collection_ptr ) ), - coll, SLOT( delTracks( QStringList, Tomahawk::collection_ptr ) ), Qt::QueuedConnection ); + connect( this, SIGNAL( notify( QStringList ) ), + coll, SLOT( delTracks( QStringList ) ), Qt::QueuedConnection ); - emit notify( m_files, source()->collection() ); + emit notify( m_files ); // also re-calc the collection stats, to updates the "X tracks" in the sidebar etc: DatabaseCommand_CollectionStats* cmd = new DatabaseCommand_CollectionStats( source() ); @@ -48,6 +66,7 @@ DatabaseCommand_DeleteFiles::exec( DatabaseImpl* dbi ) int deleted = 0; QVariant srcid = source()->isLocal() ? QVariant( QVariant::Int ) : source()->id(); TomahawkSqlQuery delquery = dbi->newquery(); + QString lastPath; if ( !m_dir.path().isEmpty() && source()->isLocal() ) { @@ -59,15 +78,18 @@ DatabaseCommand_DeleteFiles::exec( DatabaseImpl* dbi ) delquery.prepare( QString( "DELETE FROM file WHERE source %1 AND id = ?" ) .arg( source()->isLocal() ? "IS NULL" : QString( "= %1" ).arg( source()->id() ) ) ); - dirquery.bindValue( 0, "file://" + m_dir.absolutePath() + "/%" ); + dirquery.bindValue( 0, "file://" + m_dir.canonicalPath() + "/%" ); dirquery.exec(); while ( dirquery.next() ) { QFileInfo fi( dirquery.value( 1 ).toString().mid( 7 ) ); // remove file:// - if ( fi.absolutePath() != m_dir.absolutePath() ) + if ( fi.canonicalPath() != m_dir.canonicalPath() ) { - qDebug() << "Skipping subdir:" << fi.absolutePath(); + if ( lastPath != fi.canonicalPath() ) + qDebug() << "Skipping subdir:" << fi.canonicalPath(); + + lastPath = fi.canonicalPath(); continue; } @@ -111,11 +133,11 @@ DatabaseCommand_DeleteFiles::exec( DatabaseImpl* dbi ) << delquery.boundValues(); continue; } - + deleted++; } } - + qDebug() << "Deleted" << deleted << m_ids << m_files; emit done( m_files, source()->collection() ); diff --git a/src/libtomahawk/database/databasecommand_deletefiles.h b/src/libtomahawk/database/databasecommand_deletefiles.h index d10fb98c7..53d16f314 100644 --- a/src/libtomahawk/database/databasecommand_deletefiles.h +++ b/src/libtomahawk/database/databasecommand_deletefiles.h @@ -1,3 +1,21 @@ +/* === 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 DATABASECOMMAND_DELETEFILES_H #define DATABASECOMMAND_DELETEFILES_H @@ -27,6 +45,12 @@ public: setSource( source ); } + explicit DatabaseCommand_DeleteFiles( const QVariantList& ids, const Tomahawk::source_ptr& source, QObject* parent = 0 ) + : DatabaseCommandLoggable( parent ), m_ids( ids ) + { + setSource( source ); + } + virtual QString commandname() const { return "deletefiles"; } virtual void exec( DatabaseImpl* ); @@ -41,7 +65,7 @@ public: signals: void done( const QStringList&, const Tomahawk::collection_ptr& ); - void notify( const QStringList&, const Tomahawk::collection_ptr& ); + void notify( const QStringList& ); private: QDir m_dir; diff --git a/src/libtomahawk/database/databasecommand_deleteplaylist.cpp b/src/libtomahawk/database/databasecommand_deleteplaylist.cpp index 424c90df8..d87699594 100644 --- a/src/libtomahawk/database/databasecommand_deleteplaylist.cpp +++ b/src/libtomahawk/database/databasecommand_deleteplaylist.cpp @@ -1,3 +1,21 @@ +/* === 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 "databasecommand_deleteplaylist.h" #include diff --git a/src/libtomahawk/database/databasecommand_deleteplaylist.h b/src/libtomahawk/database/databasecommand_deleteplaylist.h index 5e6917f07..995e0efcf 100644 --- a/src/libtomahawk/database/databasecommand_deleteplaylist.h +++ b/src/libtomahawk/database/databasecommand_deleteplaylist.h @@ -1,3 +1,21 @@ +/* === 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 DATABASECOMMAND_DELETEPLAYLIST_H #define DATABASECOMMAND_DELETEPLAYLIST_H diff --git a/src/libtomahawk/database/databasecommand_dirmtimes.cpp b/src/libtomahawk/database/databasecommand_dirmtimes.cpp index 42c7c7084..c0ef4ffc5 100644 --- a/src/libtomahawk/database/databasecommand_dirmtimes.cpp +++ b/src/libtomahawk/database/databasecommand_dirmtimes.cpp @@ -1,3 +1,21 @@ +/* === 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 "databasecommand_dirmtimes.h" #include @@ -20,21 +38,35 @@ DatabaseCommand_DirMtimes::execSelect( DatabaseImpl* dbi ) { QMap mtimes; TomahawkSqlQuery query = dbi->newquery(); - if( m_prefix.isEmpty() ) + if( m_prefix.isEmpty() && m_prefixes.isEmpty() ) query.exec( "SELECT name, mtime FROM dirs_scanned" ); + else if( m_prefixes.isEmpty() ) + execSelectPath( dbi, m_prefix, mtimes ); else { - query.prepare( QString( "SELECT name, mtime " - "FROM dirs_scanned " - "WHERE name LIKE '%1%'" ).arg( m_prefix.replace( '\'',"''" ) ) ); - query.exec(); + if( !m_prefix.isEmpty() ) + execSelectPath( dbi, m_prefix, mtimes ); + foreach( QString path, m_prefixes ) + execSelectPath( dbi, path, mtimes ); } + emit done( mtimes ); +} + +void +DatabaseCommand_DirMtimes::execSelectPath( DatabaseImpl *dbi, const QDir& path, QMap &mtimes ) +{ + TomahawkSqlQuery query = dbi->newquery(); + query.prepare( QString( "SELECT name, mtime " + "FROM dirs_scanned " + "WHERE name LIKE :prefix" ) ); + + query.bindValue( ":prefix", path.canonicalPath() + "%" ); + query.exec(); + while( query.next() ) { mtimes.insert( query.value( 0 ).toString(), query.value( 1 ).toUInt() ); } - - emit done( mtimes ); } diff --git a/src/libtomahawk/database/databasecommand_dirmtimes.h b/src/libtomahawk/database/databasecommand_dirmtimes.h index 9071599e0..9677a2317 100644 --- a/src/libtomahawk/database/databasecommand_dirmtimes.h +++ b/src/libtomahawk/database/databasecommand_dirmtimes.h @@ -1,3 +1,21 @@ +/* === 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 DATABASECOMMAND_DIRMTIMES_H #define DATABASECOMMAND_DIRMTIMES_H @@ -16,9 +34,13 @@ class DLLEXPORT DatabaseCommand_DirMtimes : public DatabaseCommand Q_OBJECT public: - explicit DatabaseCommand_DirMtimes( const QString& prefix = "", QObject* parent = 0 ) + explicit DatabaseCommand_DirMtimes( const QString& prefix = QString(), QObject* parent = 0 ) : DatabaseCommand( parent ), m_prefix( prefix ), m_update( false ) {} + + explicit DatabaseCommand_DirMtimes( const QStringList& prefixes = QStringList(), QObject* parent = 0 ) + : DatabaseCommand( parent ), m_prefixes( prefixes ), m_update( false ) + {} explicit DatabaseCommand_DirMtimes( QMap tosave, QObject* parent = 0 ) : DatabaseCommand( parent ), m_update( true ), m_tosave( tosave ) @@ -34,9 +56,12 @@ signals: public slots: private: + void execSelectPath( DatabaseImpl *dbi, const QDir& path, QMap &mtimes ); + void execSelect( DatabaseImpl* dbi ); void execUpdate( DatabaseImpl* dbi ); QString m_prefix; + QStringList m_prefixes; bool m_update; QMap m_tosave; }; diff --git a/src/libtomahawk/database/databasecommand_importplaylist.cpp b/src/libtomahawk/database/databasecommand_importplaylist.cpp index b0e84c07d..2d5fdddc2 100644 --- a/src/libtomahawk/database/databasecommand_importplaylist.cpp +++ b/src/libtomahawk/database/databasecommand_importplaylist.cpp @@ -1,3 +1,21 @@ +/* === 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 "databasecommand_importplaylist.h" #include diff --git a/src/libtomahawk/database/databasecommand_importplaylist.h b/src/libtomahawk/database/databasecommand_importplaylist.h index 62e3a5d3f..90629f1ba 100644 --- a/src/libtomahawk/database/databasecommand_importplaylist.h +++ b/src/libtomahawk/database/databasecommand_importplaylist.h @@ -1,3 +1,21 @@ +/* === 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 DATABASECOMMAND_IMPORTPLAYLIST_H #define DATABASECOMMAND_IMPORTPLAYLIST_H diff --git a/src/libtomahawk/database/databasecommand_loadalldynamicplaylists.cpp b/src/libtomahawk/database/databasecommand_loadalldynamicplaylists.cpp index b7c0659a5..333b596fd 100644 --- a/src/libtomahawk/database/databasecommand_loadalldynamicplaylists.cpp +++ b/src/libtomahawk/database/databasecommand_loadalldynamicplaylists.cpp @@ -1,18 +1,21 @@ -/**************************************************************************************** - * Copyright (c) 2010 Leo Franchi * - * * - * This program is free software; you can redistribute it and/or modify it under * - * the terms of the GNU General Public License as published by the Free Software * - * Foundation; either version 2 of the License, or (at your option) any later * - * version. * - * * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY * - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * - * PARTICULAR PURPOSE. See the GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License along with * - * this program. If not, see . * - ****************************************************************************************/ +/* === 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 "databasecommand_loadalldynamicplaylists.h" #include diff --git a/src/libtomahawk/database/databasecommand_loadalldynamicplaylists.h b/src/libtomahawk/database/databasecommand_loadalldynamicplaylists.h index 759735dc1..9b2834c3f 100644 --- a/src/libtomahawk/database/databasecommand_loadalldynamicplaylists.h +++ b/src/libtomahawk/database/databasecommand_loadalldynamicplaylists.h @@ -1,18 +1,21 @@ -/**************************************************************************************** - * Copyright (c) 2010 Leo Franchi * - * * - * This program is free software; you can redistribute it and/or modify it under * - * the terms of the GNU General Public License as published by the Free Software * - * Foundation; either version 2 of the License, or (at your option) any later * - * version. * - * * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY * - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * - * PARTICULAR PURPOSE. See the GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License along with * - * this program. If not, see . * - ****************************************************************************************/ +/* === 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 DATABASECOMMAND_LOADALLDYNAMICPLAYLISTS_H #define DATABASECOMMAND_LOADALLDYNAMICPLAYLISTS_H diff --git a/src/libtomahawk/database/databasecommand_loadallplaylists.cpp b/src/libtomahawk/database/databasecommand_loadallplaylists.cpp index d742c6c91..db7865767 100644 --- a/src/libtomahawk/database/databasecommand_loadallplaylists.cpp +++ b/src/libtomahawk/database/databasecommand_loadallplaylists.cpp @@ -1,3 +1,21 @@ +/* === 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 "databasecommand_loadallplaylists.h" #include diff --git a/src/libtomahawk/database/databasecommand_loadallplaylists.h b/src/libtomahawk/database/databasecommand_loadallplaylists.h index 53ee5e3c0..4a2c6f66d 100644 --- a/src/libtomahawk/database/databasecommand_loadallplaylists.h +++ b/src/libtomahawk/database/databasecommand_loadallplaylists.h @@ -1,3 +1,21 @@ +/* === 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 DATABASECOMMAND_LOADALLPLAYLIST_H #define DATABASECOMMAND_LOADALLPLAYLIST_H diff --git a/src/libtomahawk/database/databasecommand_loadallsources.cpp b/src/libtomahawk/database/databasecommand_loadallsources.cpp index 58e6744d2..79e09387c 100644 --- a/src/libtomahawk/database/databasecommand_loadallsources.cpp +++ b/src/libtomahawk/database/databasecommand_loadallsources.cpp @@ -1,3 +1,21 @@ +/* === 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 "databasecommand_loadallsources.h" #include diff --git a/src/libtomahawk/database/databasecommand_loadallsources.h b/src/libtomahawk/database/databasecommand_loadallsources.h index fbd99597d..46ef0ec2c 100644 --- a/src/libtomahawk/database/databasecommand_loadallsources.h +++ b/src/libtomahawk/database/databasecommand_loadallsources.h @@ -1,3 +1,21 @@ +/* === 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 DATABASECOMMAND_LOADALLSOURCES_H #define DATABASECOMMAND_LOADALLSOURCES_H diff --git a/src/libtomahawk/database/databasecommand_loaddynamicplaylist.cpp b/src/libtomahawk/database/databasecommand_loaddynamicplaylist.cpp index eb0436e7f..f8b449e61 100644 --- a/src/libtomahawk/database/databasecommand_loaddynamicplaylist.cpp +++ b/src/libtomahawk/database/databasecommand_loaddynamicplaylist.cpp @@ -1,3 +1,21 @@ +/* === 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 "databasecommand_loaddynamicplaylist.h" #include @@ -34,9 +52,9 @@ DatabaseCommand_LoadDynamicPlaylist::exec( DatabaseImpl* dbi ) QList< QVariantMap > controls; QString playlist_guid; qDebug() << "Loading controls..." << revisionGuid(); - qDebug() << "SELECT playlist_revision.playlist, controls, plmode, pltype " - "FROM dynamic_playlist_revision, playlist_revision " - "WHERE dynamic_playlist_revision.guid = "<< revisionGuid() << " AND playlist_revision.guid = dynamic_playlist_revision.guid"; +// qDebug() << "SELECT playlist_revision.playlist, controls, plmode, pltype " +// "FROM dynamic_playlist_revision, playlist_revision " +// "WHERE dynamic_playlist_revision.guid = "<< revisionGuid() << " AND playlist_revision.guid = dynamic_playlist_revision.guid"; if( controlsQuery.first() ) { playlist_guid = controlsQuery.value( 0 ).toString(); diff --git a/src/libtomahawk/database/databasecommand_loaddynamicplaylist.h b/src/libtomahawk/database/databasecommand_loaddynamicplaylist.h index 8b7205f73..e797e1cec 100644 --- a/src/libtomahawk/database/databasecommand_loaddynamicplaylist.h +++ b/src/libtomahawk/database/databasecommand_loaddynamicplaylist.h @@ -1,3 +1,21 @@ +/* === 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 DATABASECOMMAND_LOADDYNAMICPLAYLIST_H #define DATABASECOMMAND_LOADDYNAMICPLAYLIST_H diff --git a/src/libtomahawk/database/databasecommand_loadfile.cpp b/src/libtomahawk/database/databasecommand_loadfile.cpp index b54c601b2..8890b3ee5 100644 --- a/src/libtomahawk/database/databasecommand_loadfile.cpp +++ b/src/libtomahawk/database/databasecommand_loadfile.cpp @@ -1,3 +1,21 @@ +/* === 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 "databasecommand_loadfile.h" #include "databaseimpl.h" diff --git a/src/libtomahawk/database/databasecommand_loadfile.h b/src/libtomahawk/database/databasecommand_loadfile.h index 1bc04b6bc..7d40c281c 100644 --- a/src/libtomahawk/database/databasecommand_loadfile.h +++ b/src/libtomahawk/database/databasecommand_loadfile.h @@ -1,3 +1,21 @@ +/* === 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 DATABASECOMMAND_LOADFILE_H #define DATABASECOMMAND_LOADFILE_H diff --git a/src/libtomahawk/database/databasecommand_loadops.cpp b/src/libtomahawk/database/databasecommand_loadops.cpp index 11a9516e0..afc131da4 100644 --- a/src/libtomahawk/database/databasecommand_loadops.cpp +++ b/src/libtomahawk/database/databasecommand_loadops.cpp @@ -1,3 +1,21 @@ +/* === 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 "databasecommand_loadops.h" diff --git a/src/libtomahawk/database/databasecommand_loadops.h b/src/libtomahawk/database/databasecommand_loadops.h index e846ea95c..06c8cb56f 100644 --- a/src/libtomahawk/database/databasecommand_loadops.h +++ b/src/libtomahawk/database/databasecommand_loadops.h @@ -1,3 +1,21 @@ +/* === 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 DATABASECOMMAND_LOADOPS_H #define DATABASECOMMAND_LOADOPS_H @@ -14,7 +32,9 @@ Q_OBJECT public: explicit DatabaseCommand_loadOps( const Tomahawk::source_ptr& src, QString since, QObject* parent = 0 ) : DatabaseCommand( src ), m_since( since ) - {} + { + Q_UNUSED( parent ); + } virtual void exec( DatabaseImpl* db ); virtual bool doesMutates() const { return false; } diff --git a/src/libtomahawk/database/databasecommand_loadplaylistentries.cpp b/src/libtomahawk/database/databasecommand_loadplaylistentries.cpp index 0e47ee26a..f29dc8a40 100644 --- a/src/libtomahawk/database/databasecommand_loadplaylistentries.cpp +++ b/src/libtomahawk/database/databasecommand_loadplaylistentries.cpp @@ -1,3 +1,21 @@ +/* === 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 "databasecommand_loadplaylistentries.h" #include @@ -27,7 +45,7 @@ DatabaseCommand_LoadPlaylistEntries::generateEntries( DatabaseImpl* dbi ) query_entries.bindValue( ":guid", m_revguid ); query_entries.exec(); - qDebug() << "trying to load entries:" << m_revguid; +// qDebug() << "trying to load entries:" << m_revguid; QString prevrev; QJson::Parser parser; bool ok; @@ -37,7 +55,6 @@ DatabaseCommand_LoadPlaylistEntries::generateEntries( DatabaseImpl* dbi ) QVariant v = parser.parse( query_entries.value(0).toByteArray(), &ok ); Q_ASSERT( ok && v.type() == QVariant::List ); //TODO m_guids = v.toStringList(); - // qDebug() << "Entries:" << guids; QString inclause = QString("('%1')").arg(m_guids.join("', '")); @@ -97,5 +114,5 @@ DatabaseCommand_LoadPlaylistEntries::generateEntries( DatabaseImpl* dbi ) m_islatest = query_entries_old.value( 1 ).toBool(); } - qDebug() << Q_FUNC_INFO << "entrymap:" << m_entrymap; +// qDebug() << Q_FUNC_INFO << "entrymap:" << m_entrymap; } diff --git a/src/libtomahawk/database/databasecommand_loadplaylistentries.h b/src/libtomahawk/database/databasecommand_loadplaylistentries.h index 5f672749e..255c9a017 100644 --- a/src/libtomahawk/database/databasecommand_loadplaylistentries.h +++ b/src/libtomahawk/database/databasecommand_loadplaylistentries.h @@ -1,3 +1,21 @@ +/* === 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 DATABASECOMMAND_LOADPLAYLIST_H #define DATABASECOMMAND_LOADPLAYLIST_H @@ -15,7 +33,7 @@ Q_OBJECT public: explicit DatabaseCommand_LoadPlaylistEntries( QString revision_guid, QObject* parent = 0 ) - : DatabaseCommand( parent ), m_revguid( revision_guid ), m_islatest( true ) + : DatabaseCommand( parent ), m_islatest( true ), m_revguid( revision_guid ) {} virtual void exec( DatabaseImpl* ); diff --git a/src/libtomahawk/database/databasecommand_logplayback.cpp b/src/libtomahawk/database/databasecommand_logplayback.cpp index e08f071d5..6443b2216 100644 --- a/src/libtomahawk/database/databasecommand_logplayback.cpp +++ b/src/libtomahawk/database/databasecommand_logplayback.cpp @@ -1,3 +1,21 @@ +/* === 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 "databasecommand_logplayback.h" #include @@ -26,7 +44,8 @@ DatabaseCommand_LogPlayback::postCommitHook() connect( this, SIGNAL( trackPlayed( Tomahawk::query_ptr ) ), source().data(), SLOT( onPlaybackFinished( Tomahawk::query_ptr ) ), Qt::QueuedConnection ); - Tomahawk::query_ptr q = Tomahawk::Query::get( m_artist, m_track, QString(), uuid() ); + // do not auto resolve this track + Tomahawk::query_ptr q = Tomahawk::Query::get( m_artist, m_track, QString() ); if ( m_action == Finished ) { diff --git a/src/libtomahawk/database/databasecommand_logplayback.h b/src/libtomahawk/database/databasecommand_logplayback.h index f7f654780..ab1f7b8ed 100644 --- a/src/libtomahawk/database/databasecommand_logplayback.h +++ b/src/libtomahawk/database/databasecommand_logplayback.h @@ -1,3 +1,21 @@ +/* === 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 DATABASECOMMAND_LOGPLAYBACK_H #define DATABASECOMMAND_LOGPLAYBACK_H diff --git a/src/libtomahawk/database/databasecommand_modifyplaylist.cpp b/src/libtomahawk/database/databasecommand_modifyplaylist.cpp index 91a2ae9ce..1d60da687 100644 --- a/src/libtomahawk/database/databasecommand_modifyplaylist.cpp +++ b/src/libtomahawk/database/databasecommand_modifyplaylist.cpp @@ -1,3 +1,21 @@ +/* === 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 "databasecommand_modifyplaylist.h" using namespace Tomahawk; @@ -14,4 +32,5 @@ DatabaseCommand_ModifyPlaylist::DatabaseCommand_ModifyPlaylist( Playlist* playli void DatabaseCommand_ModifyPlaylist::exec( DatabaseImpl* lib ) { + Q_UNUSED( lib ); } diff --git a/src/libtomahawk/database/databasecommand_modifyplaylist.h b/src/libtomahawk/database/databasecommand_modifyplaylist.h index e967bdb29..fd2f6e7d9 100644 --- a/src/libtomahawk/database/databasecommand_modifyplaylist.h +++ b/src/libtomahawk/database/databasecommand_modifyplaylist.h @@ -1,3 +1,21 @@ +/* === 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 DATABASECOMMAND_MODIFYPLAYLIST_H #define DATABASECOMMAND_MODIFYPLAYLIST_H diff --git a/src/libtomahawk/database/databasecommand_playbackhistory.cpp b/src/libtomahawk/database/databasecommand_playbackhistory.cpp index d3356b841..853bc2a03 100644 --- a/src/libtomahawk/database/databasecommand_playbackhistory.cpp +++ b/src/libtomahawk/database/databasecommand_playbackhistory.cpp @@ -1,3 +1,21 @@ +/* === 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 "databasecommand_playbackhistory.h" #include diff --git a/src/libtomahawk/database/databasecommand_playbackhistory.h b/src/libtomahawk/database/databasecommand_playbackhistory.h index da57e07df..662e66ff2 100644 --- a/src/libtomahawk/database/databasecommand_playbackhistory.h +++ b/src/libtomahawk/database/databasecommand_playbackhistory.h @@ -1,3 +1,21 @@ +/* === 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 DATABASECOMMAND_PLAYBACKHISTORY_H #define DATABASECOMMAND_PLAYBACKHISTORY_H diff --git a/src/libtomahawk/database/databasecommand_renameplaylist.cpp b/src/libtomahawk/database/databasecommand_renameplaylist.cpp index 7363d823e..4187ae27e 100644 --- a/src/libtomahawk/database/databasecommand_renameplaylist.cpp +++ b/src/libtomahawk/database/databasecommand_renameplaylist.cpp @@ -1,3 +1,21 @@ +/* === 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 "databasecommand_renameplaylist.h" #include diff --git a/src/libtomahawk/database/databasecommand_renameplaylist.h b/src/libtomahawk/database/databasecommand_renameplaylist.h index c6692dcff..9b0015b70 100644 --- a/src/libtomahawk/database/databasecommand_renameplaylist.h +++ b/src/libtomahawk/database/databasecommand_renameplaylist.h @@ -1,3 +1,21 @@ +/* === 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 DATABASECOMMAND_RENAMEPLAYLIST_H #define DATABASECOMMAND_RENAMEPLAYLIST_H diff --git a/src/libtomahawk/database/databasecommand_resolve.cpp b/src/libtomahawk/database/databasecommand_resolve.cpp index 74c2742e4..f6f2b1c42 100644 --- a/src/libtomahawk/database/databasecommand_resolve.cpp +++ b/src/libtomahawk/database/databasecommand_resolve.cpp @@ -1,3 +1,21 @@ +/* === 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 "databasecommand_resolve.h" #include "album.h" diff --git a/src/libtomahawk/database/databasecommand_resolve.h b/src/libtomahawk/database/databasecommand_resolve.h index d09ad70af..8c194451e 100644 --- a/src/libtomahawk/database/databasecommand_resolve.h +++ b/src/libtomahawk/database/databasecommand_resolve.h @@ -1,3 +1,21 @@ +/* === 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 DATABASECOMMAND_RESOLVE_H #define DATABASECOMMAND_RESOLVE_H diff --git a/src/libtomahawk/database/databasecommand_setdynamicplaylistrevision.cpp b/src/libtomahawk/database/databasecommand_setdynamicplaylistrevision.cpp index b8a203109..39494f3fd 100644 --- a/src/libtomahawk/database/databasecommand_setdynamicplaylistrevision.cpp +++ b/src/libtomahawk/database/databasecommand_setdynamicplaylistrevision.cpp @@ -1,3 +1,21 @@ +/* === 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 "databasecommand_setdynamicplaylistrevision.h" #include diff --git a/src/libtomahawk/database/databasecommand_setdynamicplaylistrevision.h b/src/libtomahawk/database/databasecommand_setdynamicplaylistrevision.h index fb381ba2f..238aa163f 100644 --- a/src/libtomahawk/database/databasecommand_setdynamicplaylistrevision.h +++ b/src/libtomahawk/database/databasecommand_setdynamicplaylistrevision.h @@ -1,3 +1,21 @@ +/* === 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 DATABASECOMMAND_SETDYNAMICPLAYLISTREVISION_H #define DATABASECOMMAND_SETDYNAMICPLAYLISTREVISION_H diff --git a/src/libtomahawk/database/databasecommand_setplaylistrevision.cpp b/src/libtomahawk/database/databasecommand_setplaylistrevision.cpp index 4f2ee99ed..62eec9d6e 100644 --- a/src/libtomahawk/database/databasecommand_setplaylistrevision.cpp +++ b/src/libtomahawk/database/databasecommand_setplaylistrevision.cpp @@ -1,3 +1,21 @@ +/* === 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 "databasecommand_setplaylistrevision.h" #include diff --git a/src/libtomahawk/database/databasecommand_setplaylistrevision.h b/src/libtomahawk/database/databasecommand_setplaylistrevision.h index 292ee0aa3..e71c1f7e1 100644 --- a/src/libtomahawk/database/databasecommand_setplaylistrevision.h +++ b/src/libtomahawk/database/databasecommand_setplaylistrevision.h @@ -1,3 +1,21 @@ +/* === 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 DATABASECOMMAND_SETPLAYLISTREVISION_H #define DATABASECOMMAND_SETPLAYLISTREVISION_H diff --git a/src/libtomahawk/database/databasecommand_sourceoffline.cpp b/src/libtomahawk/database/databasecommand_sourceoffline.cpp index e31e7861e..e09186ddc 100644 --- a/src/libtomahawk/database/databasecommand_sourceoffline.cpp +++ b/src/libtomahawk/database/databasecommand_sourceoffline.cpp @@ -1,3 +1,21 @@ +/* === 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 "databasecommand_sourceoffline.h" diff --git a/src/libtomahawk/database/databasecommand_sourceoffline.h b/src/libtomahawk/database/databasecommand_sourceoffline.h index 02ccc1571..7f50578e1 100644 --- a/src/libtomahawk/database/databasecommand_sourceoffline.h +++ b/src/libtomahawk/database/databasecommand_sourceoffline.h @@ -1,3 +1,21 @@ +/* === 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 DATABASECOMMAND_SOURCEOFFLINE_H #define DATABASECOMMAND_SOURCEOFFLINE_H diff --git a/src/libtomahawk/database/databasecommand_updatesearchindex.cpp b/src/libtomahawk/database/databasecommand_updatesearchindex.cpp index 3e461dd2c..8362c9749 100644 --- a/src/libtomahawk/database/databasecommand_updatesearchindex.cpp +++ b/src/libtomahawk/database/databasecommand_updatesearchindex.cpp @@ -1,3 +1,21 @@ +/* === 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 "databasecommand_updatesearchindex.h" diff --git a/src/libtomahawk/database/databasecommand_updatesearchindex.h b/src/libtomahawk/database/databasecommand_updatesearchindex.h index d8d74ed02..8a0fc95a8 100644 --- a/src/libtomahawk/database/databasecommand_updatesearchindex.h +++ b/src/libtomahawk/database/databasecommand_updatesearchindex.h @@ -1,3 +1,21 @@ +/* === 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 DATABASECOMMAND_UPDATESEARCHINDEX_H #define DATABASECOMMAND_UPDATESEARCHINDEX_H diff --git a/src/libtomahawk/database/databasecommandloggable.cpp b/src/libtomahawk/database/databasecommandloggable.cpp index 404490944..9c193b454 100644 --- a/src/libtomahawk/database/databasecommandloggable.cpp +++ b/src/libtomahawk/database/databasecommandloggable.cpp @@ -1,3 +1,21 @@ +/* === 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 "databasecommandloggable.h" diff --git a/src/libtomahawk/database/databasecommandloggable.h b/src/libtomahawk/database/databasecommandloggable.h index 20cfb032e..be400621b 100644 --- a/src/libtomahawk/database/databasecommandloggable.h +++ b/src/libtomahawk/database/databasecommandloggable.h @@ -1,3 +1,21 @@ +/* === 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 DATABASECOMMANDLOGGABLE_H #define DATABASECOMMANDLOGGABLE_H diff --git a/src/libtomahawk/database/databaseimpl.cpp b/src/libtomahawk/database/databaseimpl.cpp index 30002466b..85615f00e 100644 --- a/src/libtomahawk/database/databaseimpl.cpp +++ b/src/libtomahawk/database/databaseimpl.cpp @@ -1,3 +1,21 @@ +/* === 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 "databaseimpl.h" #include @@ -28,8 +46,6 @@ DatabaseImpl::DatabaseImpl( const QString& dbname, Database* parent ) , m_lastalbid( 0 ) , m_lasttrkid( 0 ) { - connect( this, SIGNAL( indexReady() ), parent, SIGNAL( indexReady() ) ); - db = QSqlDatabase::addDatabase( "QSQLITE", "tomahawk" ); db.setDatabaseName( dbname ); if ( !db.open() ) @@ -353,6 +369,7 @@ DatabaseImpl::albumId( int artistid, const QString& name_orig, bool& isnew ) QList< int > DatabaseImpl::searchTable( const QString& table, const QString& name, uint limit ) { + Q_UNUSED( limit ); QList< int > results; if( table != "artist" && table != "track" && table != "album" ) return results; diff --git a/src/libtomahawk/database/databaseimpl.h b/src/libtomahawk/database/databaseimpl.h index b40ee4cf6..e2965f553 100644 --- a/src/libtomahawk/database/databaseimpl.h +++ b/src/libtomahawk/database/databaseimpl.h @@ -1,3 +1,21 @@ +/* === 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 DATABASEIMPL_H #define DATABASEIMPL_H @@ -66,6 +84,8 @@ signals: public slots: private: + bool m_ready; + bool updateSchema( int currentver ); QSqlDatabase db; diff --git a/src/libtomahawk/database/databaseresolver.cpp b/src/libtomahawk/database/databaseresolver.cpp index 799803176..e5da896af 100644 --- a/src/libtomahawk/database/databaseresolver.cpp +++ b/src/libtomahawk/database/databaseresolver.cpp @@ -1,3 +1,21 @@ +/* === 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 "databaseresolver.h" #include "network/servent.h" diff --git a/src/libtomahawk/database/databaseresolver.h b/src/libtomahawk/database/databaseresolver.h index d2877c0c5..11abb80a0 100644 --- a/src/libtomahawk/database/databaseresolver.h +++ b/src/libtomahawk/database/databaseresolver.h @@ -1,3 +1,21 @@ +/* === 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 DATABASERESOLVER_H #define DATABASERESOLVER_H diff --git a/src/libtomahawk/database/databaseworker.cpp b/src/libtomahawk/database/databaseworker.cpp index e8efedf91..e0d1f7be7 100644 --- a/src/libtomahawk/database/databaseworker.cpp +++ b/src/libtomahawk/database/databaseworker.cpp @@ -1,3 +1,21 @@ +/* === 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 "databaseworker.h" #include @@ -14,6 +32,9 @@ DatabaseWorker::DatabaseWorker( DatabaseImpl* lib, Database* db, bool mutates ) , m_abort( false ) , m_outstanding( 0 ) { + Q_UNUSED( db ); + Q_UNUSED( mutates ); + moveToThread( this ); qDebug() << "CTOR DatabaseWorker" << this->thread(); diff --git a/src/libtomahawk/database/databaseworker.h b/src/libtomahawk/database/databaseworker.h index 87073e9df..ea45cec0f 100644 --- a/src/libtomahawk/database/databaseworker.h +++ b/src/libtomahawk/database/databaseworker.h @@ -1,3 +1,21 @@ +/* === 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 DATABASEWORKER_H #define DATABASEWORKER_H diff --git a/src/libtomahawk/database/fuzzyindex.cpp b/src/libtomahawk/database/fuzzyindex.cpp index 710848b45..25719317c 100644 --- a/src/libtomahawk/database/fuzzyindex.cpp +++ b/src/libtomahawk/database/fuzzyindex.cpp @@ -1,3 +1,21 @@ +/* === 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 "fuzzyindex.h" #include "databaseimpl.h" diff --git a/src/libtomahawk/database/fuzzyindex.h b/src/libtomahawk/database/fuzzyindex.h index bc294705c..3e93fbb23 100644 --- a/src/libtomahawk/database/fuzzyindex.h +++ b/src/libtomahawk/database/fuzzyindex.h @@ -1,3 +1,21 @@ +/* === 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 FUZZYINDEX_H #define FUZZYINDEX_H diff --git a/src/libtomahawk/database/op.h b/src/libtomahawk/database/op.h index 775a0a958..9c58e9c56 100644 --- a/src/libtomahawk/database/op.h +++ b/src/libtomahawk/database/op.h @@ -1,3 +1,21 @@ +/* === 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 OP_H #define OP_H #include diff --git a/src/libtomahawk/database/tomahawksqlquery.h b/src/libtomahawk/database/tomahawksqlquery.h index 5b21a03aa..76f65605c 100644 --- a/src/libtomahawk/database/tomahawksqlquery.h +++ b/src/libtomahawk/database/tomahawksqlquery.h @@ -1,3 +1,21 @@ +/* === 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 TOMAHAWKSQLQUERY_H #define TOMAHAWKSQLQUERY_H // subclass QSqlQuery so that it prints the error msg if a query fails diff --git a/src/libtomahawk/dllmacro.h b/src/libtomahawk/dllmacro.h index b4b199505..55a2b6ab9 100644 --- a/src/libtomahawk/dllmacro.h +++ b/src/libtomahawk/dllmacro.h @@ -1,14 +1,32 @@ +/* === 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 DLLMACRO_H #define DLLMACRO_H -#ifdef WIN32 - #ifdef DLLEXPORT_PRO - #define DLLEXPORT __declspec(dllexport) - #else - #define DLLEXPORT __declspec(dllimport) - #endif -#else - #define DLLEXPORT +#include + +#ifndef DLLEXPORT +# if defined (DLLEXPORT_PRO) +# define DLLEXPORT Q_DECL_EXPORT +# else +# define DLLEXPORT Q_DECL_IMPORT +# endif #endif #endif diff --git a/src/libtomahawk/functimeout.h b/src/libtomahawk/functimeout.h index 39bad1950..bbb632f59 100644 --- a/src/libtomahawk/functimeout.h +++ b/src/libtomahawk/functimeout.h @@ -1,9 +1,28 @@ +/* === 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 FUNCTIMEOUT_H #define FUNCTIMEOUT_H #include #include #include +#include #include "boost/function.hpp" #include "boost/bind.hpp" @@ -25,11 +44,12 @@ class DLLEXPORT FuncTimeout : public QObject Q_OBJECT public: - FuncTimeout( int ms, boost::function func ) + FuncTimeout( int ms, boost::function func, QObject* besafe ) : m_func( func ) + , m_watch( QWeakPointer< QObject >( besafe ) ) { //qDebug() << Q_FUNC_INFO; - QTimer::singleShot( ms, this, SLOT(exec() ) ); + QTimer::singleShot( ms, this, SLOT( exec() ) ); }; ~FuncTimeout() @@ -40,12 +60,14 @@ public: public slots: void exec() { - m_func(); + if( !m_watch.isNull() ) + m_func(); this->deleteLater(); }; private: boost::function m_func; + QWeakPointer< QObject > m_watch; }; }; // ns diff --git a/src/infosystem/infoplugins/echonestplugin.cpp b/src/libtomahawk/infosystem/infoplugins/echonestplugin.cpp similarity index 87% rename from src/infosystem/infoplugins/echonestplugin.cpp rename to src/libtomahawk/infosystem/infoplugins/echonestplugin.cpp index cc9f08a9d..73dd3b7b2 100644 --- a/src/infosystem/infoplugins/echonestplugin.cpp +++ b/src/libtomahawk/infosystem/infoplugins/echonestplugin.cpp @@ -1,5 +1,21 @@ -#include "tomahawk/infosystem.h" -#include "tomahawk/tomahawkapp.h" +/* === 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 "echonestplugin.h" #include #include @@ -23,7 +39,7 @@ EchoNestPlugin::~EchoNestPlugin() qDebug() << Q_FUNC_INFO; } -void EchoNestPlugin::getInfo(const QString &caller, const InfoType type, const QVariant& data, InfoCustomDataHash customData) +void EchoNestPlugin::getInfo(const QString &caller, const InfoType type, const QVariant& data, InfoCustomData customData) { switch (type) { @@ -47,26 +63,27 @@ void EchoNestPlugin::getInfo(const QString &caller, const InfoType type, const Q } } -void EchoNestPlugin::getSongProfile(const QString &caller, const QVariant& data, InfoCustomDataHash &customData, const QString &item) +void EchoNestPlugin::getSongProfile(const QString &caller, const QVariant& data, InfoCustomData &customData, const QString &item) { //WARNING: Totally not implemented yet + Q_UNUSED( item ); if( !isValidTrackData( caller, data, customData ) ) return; - + // Track track( data.toString() ); // Artist artist( customData.data()->property("artistName").toString() ); -// reply->setProperty("artist", QVariant::fromValue(artist)); +// reply->setProperty("artist", QVariant::fromValue(artist)); // reply->setProperty( "data", data ); // m_replyMap[reply] = customData; // connect(reply, SIGNAL(finished()), SLOT(getArtistBiographySlot())); } -void EchoNestPlugin::getArtistBiography(const QString &caller, const QVariant& data, InfoCustomDataHash &customData) +void EchoNestPlugin::getArtistBiography(const QString &caller, const QVariant& data, InfoCustomData &customData) { if( !isValidArtistData( caller, data, customData ) ) return; - + Echonest::Artist artist( data.toString() ); QNetworkReply *reply = artist.fetchBiographies(); reply->setProperty("artist", QVariant::fromValue(artist)); @@ -76,11 +93,11 @@ void EchoNestPlugin::getArtistBiography(const QString &caller, const QVariant& d connect(reply, SIGNAL(finished()), SLOT(getArtistBiographySlot())); } -void EchoNestPlugin::getArtistFamiliarity(const QString &caller, const QVariant& data, InfoCustomDataHash &customData) +void EchoNestPlugin::getArtistFamiliarity(const QString &caller, const QVariant& data, InfoCustomData &customData) { if( !isValidArtistData( caller, data, customData ) ) return; - + qDebug() << "Fetching artist familiarity!" << data; Echonest::Artist artist( data.toString() ); QNetworkReply* reply = artist.fetchFamiliarity(); @@ -88,14 +105,14 @@ void EchoNestPlugin::getArtistFamiliarity(const QString &caller, const QVariant& reply->setProperty( "data", data ); m_replyMap[reply] = customData; m_callerMap[reply] = caller; - connect(reply, SIGNAL(finished()), SLOT(getArtistFamiliaritySlot())); + connect(reply, SIGNAL(finished()), SLOT(getArtistFamiliaritySlot())); } -void EchoNestPlugin::getArtistHotttnesss(const QString &caller, const QVariant& data, InfoCustomDataHash &customData) +void EchoNestPlugin::getArtistHotttnesss(const QString &caller, const QVariant& data, InfoCustomData &customData) { if( !isValidArtistData( caller, data, customData ) ) return; - + Echonest::Artist artist( data.toString() ); QNetworkReply* reply = artist.fetchHotttnesss(); reply->setProperty( "artist", QVariant::fromValue(artist)); @@ -105,11 +122,11 @@ void EchoNestPlugin::getArtistHotttnesss(const QString &caller, const QVariant& connect(reply, SIGNAL(finished()), SLOT(getArtistHotttnesssSlot())); } -void EchoNestPlugin::getArtistTerms(const QString &caller, const QVariant& data, InfoCustomDataHash &customData) +void EchoNestPlugin::getArtistTerms(const QString &caller, const QVariant& data, InfoCustomData &customData) { if( !isValidArtistData( caller, data, customData ) ) return; - + Echonest::Artist artist( data.toString() ); QNetworkReply* reply = artist.fetchTerms( Echonest::Artist::Weight ); reply->setProperty( "artist", QVariant::fromValue(artist)); @@ -119,8 +136,9 @@ void EchoNestPlugin::getArtistTerms(const QString &caller, const QVariant& data, connect(reply, SIGNAL(finished()), SLOT(getArtistTermsSlot())); } -void EchoNestPlugin::getMiscTopTerms(const QString &caller, const QVariant& data, InfoCustomDataHash& customData) +void EchoNestPlugin::getMiscTopTerms(const QString &caller, const QVariant& data, InfoCustomData& customData) { + Q_UNUSED( data ); QNetworkReply* reply = Echonest::Artist::topTerms( 20 ); m_replyMap[reply] = customData; m_callerMap[reply] = caller; @@ -142,10 +160,9 @@ void EchoNestPlugin::getArtistBiographySlot() biographyMap[biography.site()]["attribution"] = biography.license().attribution; biographyMap[biography.site()]["licensetype"] = biography.license().type; biographyMap[biography.site()]["attribution"] = biography.license().url.toString(); - + } emit info( m_callerMap[reply], Tomahawk::InfoSystem::InfoArtistBiography, reply->property( "data" ), QVariant::fromValue(biographyMap), m_replyMap[reply] ); - emit finished( m_callerMap[reply], Tomahawk::InfoSystem::InfoArtistBiography); m_replyMap.remove(reply); m_callerMap.remove(reply); reply->deleteLater(); @@ -157,7 +174,6 @@ void EchoNestPlugin::getArtistFamiliaritySlot() Echonest::Artist artist = artistFromReply( reply ); qreal familiarity = artist.familiarity(); emit info( m_callerMap[reply], Tomahawk::InfoSystem::InfoArtistFamiliarity, reply->property( "data" ), familiarity, m_replyMap[reply] ); - emit finished( m_callerMap[reply], Tomahawk::InfoSystem::InfoArtistFamiliarity); m_replyMap.remove(reply); m_callerMap.remove(reply); reply->deleteLater(); @@ -169,7 +185,6 @@ void EchoNestPlugin::getArtistHotttnesssSlot() Echonest::Artist artist = artistFromReply( reply ); qreal hotttnesss = artist.hotttnesss(); emit info( m_callerMap[reply], Tomahawk::InfoSystem::InfoArtistHotttness, reply->property( "data" ), hotttnesss, m_replyMap[reply] ); - emit finished( m_callerMap[reply], Tomahawk::InfoSystem::InfoArtistHotttness); m_replyMap.remove(reply); m_callerMap.remove(reply); reply->deleteLater(); @@ -188,7 +203,6 @@ void EchoNestPlugin::getArtistTermsSlot() termsMap[ term.name() ] = termMap; } emit info( m_callerMap[reply], Tomahawk::InfoSystem::InfoArtistTerms, reply->property( "data" ), QVariant::fromValue(termsMap), m_replyMap[reply] ); - emit finished( m_callerMap[reply], Tomahawk::InfoSystem::InfoArtistTerms); m_replyMap.remove(reply); m_callerMap.remove(reply); reply->deleteLater(); @@ -206,13 +220,12 @@ void EchoNestPlugin::getMiscTopSlot() termsMap[ term.name().toLower() ] = termMap; } emit info( m_callerMap[reply], Tomahawk::InfoSystem::InfoMiscTopTerms, QVariant(), QVariant::fromValue(termsMap), m_replyMap[reply] ); - emit finished( m_callerMap[reply], Tomahawk::InfoSystem::InfoMiscTopTerms); m_replyMap.remove(reply); m_callerMap.remove(reply); reply->deleteLater(); } -bool EchoNestPlugin::isValidArtistData(const QString &caller, const QVariant& data, InfoCustomDataHash &customData) +bool EchoNestPlugin::isValidArtistData(const QString &caller, const QVariant& data, InfoCustomData &customData) { if (data.isNull() || !data.isValid() || !data.canConvert()) { @@ -228,7 +241,7 @@ bool EchoNestPlugin::isValidArtistData(const QString &caller, const QVariant& da return true; } -bool EchoNestPlugin::isValidTrackData(const QString &caller, const QVariant& data, InfoCustomDataHash &customData) +bool EchoNestPlugin::isValidTrackData(const QString &caller, const QVariant& data, InfoCustomData &customData) { if (data.isNull() || !data.isValid() || !data.canConvert()) { @@ -241,7 +254,7 @@ bool EchoNestPlugin::isValidTrackData(const QString &caller, const QVariant& dat emit info(caller, Tomahawk::InfoSystem::InfoNoInfo, QVariant(), QVariant(), customData); return false; } - if (!customData.contains("artistName") || + if (!customData.contains("artistName") || customData["artistName"].toString().isEmpty()) return false; return true; diff --git a/src/libtomahawk/infosystem/infoplugins/echonestplugin.h b/src/libtomahawk/infosystem/infoplugins/echonestplugin.h new file mode 100644 index 000000000..25276c373 --- /dev/null +++ b/src/libtomahawk/infosystem/infoplugins/echonestplugin.h @@ -0,0 +1,75 @@ +/* === 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 ECHONESTPLUGIN_H +#define ECHONESTPLUGIN_H + +#include "infosystem/infosystem.h" + +#include + +class QNetworkReply; +namespace Echonest { +class Artist; +} + +namespace Tomahawk +{ + +namespace InfoSystem +{ + +class EchoNestPlugin : public InfoPlugin +{ + Q_OBJECT + +public: + EchoNestPlugin(QObject *parent); + virtual ~EchoNestPlugin(); + + void getInfo( const QString &caller, const InfoType type, const QVariant &data, InfoCustomData customData ); + +private: + void getSongProfile( const QString &caller, const QVariant &data, InfoCustomData &customData, const QString &item = QString() ); + void getArtistBiography ( const QString &caller, const QVariant &data, InfoCustomData &customData ); + void getArtistFamiliarity( const QString &caller, const QVariant &data, InfoCustomData &customData ); + void getArtistHotttnesss( const QString &caller, const QVariant &data, InfoCustomData &customData ); + void getArtistTerms( const QString &caller, const QVariant &data, InfoCustomData &customData ); + void getMiscTopTerms( const QString &caller, const QVariant &data, InfoCustomData &customData ); + + bool isValidArtistData( const QString &caller, const QVariant& data, InfoCustomData& customData ); + bool isValidTrackData( const QString &caller, const QVariant& data, InfoCustomData& customData ); + Echonest::Artist artistFromReply( QNetworkReply* ); + +private slots: + void getArtistBiographySlot(); + void getArtistFamiliaritySlot(); + void getArtistHotttnesssSlot(); + void getArtistTermsSlot(); + void getMiscTopSlot(); + +private: + QHash< QNetworkReply*, InfoCustomData > m_replyMap; + QHash< QNetworkReply*, QString > m_callerMap; +}; + +} + +} + +#endif // ECHONESTPLUGIN_H diff --git a/src/libtomahawk/infosystem/infoplugins/lastfmplugin.cpp b/src/libtomahawk/infosystem/infoplugins/lastfmplugin.cpp new file mode 100644 index 000000000..407031f1e --- /dev/null +++ b/src/libtomahawk/infosystem/infoplugins/lastfmplugin.cpp @@ -0,0 +1,464 @@ +/* === 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 "lastfmplugin.h" + +#include +#include +#include + +#include "album.h" +#include "typedefs.h" +#include "audio/audioengine.h" +#include "tomahawksettings.h" +#include "utils/tomahawkutils.h" + +#include +#include + +using namespace Tomahawk::InfoSystem; + +static QString +md5( const QByteArray& src ) +{ + QByteArray const digest = QCryptographicHash::hash( src, QCryptographicHash::Md5 ); + return QString::fromLatin1( digest.toHex() ).rightJustified( 32, '0' ); +} + + +LastFmPlugin::LastFmPlugin( QObject* parent ) + : InfoPlugin(parent) + , m_scrobbler( 0 ) + , m_authJob( 0 ) +{ + QSet< InfoType > supportedTypes; + supportedTypes << InfoMiscSubmitScrobble << InfoMiscSubmitNowPlaying << InfoAlbumCoverArt << InfoArtistImages; + qobject_cast< InfoSystem* >(parent)->registerInfoTypes(this, supportedTypes); + +/* + Your API Key is 7194b85b6d1f424fe1668173a78c0c4a + Your secret is ba80f1df6d27ae63e9cb1d33ccf2052f +*/ + + // Flush session key cache + TomahawkSettings::instance()->setLastFmSessionKey( QByteArray() ); + + lastfm::ws::ApiKey = "7194b85b6d1f424fe1668173a78c0c4a"; + lastfm::ws::SharedSecret = "ba80f1df6d27ae63e9cb1d33ccf2052f"; + lastfm::ws::Username = TomahawkSettings::instance()->lastFmUsername(); + + m_pw = TomahawkSettings::instance()->lastFmPassword(); + + if( TomahawkSettings::instance()->scrobblingEnabled() && !lastfm::ws::Username.isEmpty() ) + { + createScrobbler(); + } + + //HACK work around a bug in liblastfm---it doesn't create its config dir, so when it + // tries to write the track cache, it fails silently. until we have a fixed version, do this + // code taken from Amarok (src/services/lastfm/ScrobblerAdapter.cpp) +#ifdef Q_WS_X11 + QString lpath = QDir::home().filePath( ".local/share/Last.fm" ); + QDir ldir = QDir( lpath ); + if( !ldir.exists() ) + { + ldir.mkpath( lpath ); + } +#endif + + m_badUrls << QUrl( "http://cdn.last.fm/flatness/catalogue/noimage" ); + + connect( TomahawkSettings::instance(), SIGNAL( changed() ), + SLOT( settingsChanged() ), Qt::QueuedConnection ); +} + + +LastFmPlugin::~LastFmPlugin() +{ + delete m_scrobbler; +} + + +void +LastFmPlugin::dataError( const QString &caller, const InfoType type, const QVariant& data, Tomahawk::InfoSystem::InfoCustomData &customData ) +{ + emit info( caller, type, data, QVariant(), customData ); + return; +} + + +void +LastFmPlugin::getInfo( const QString &caller, const InfoType type, const QVariant& data, Tomahawk::InfoSystem::InfoCustomData customData ) +{ + qDebug() << Q_FUNC_INFO; + + switch ( type ) + { + case InfoMiscSubmitNowPlaying: + nowPlaying( caller, type, data, customData ); + break; + + case InfoMiscSubmitScrobble: + scrobble( caller, type, data, customData ); + break; + + case InfoArtistImages: + fetchArtistImages( caller, type, data, customData ); + break; + + case InfoAlbumCoverArt: + fetchCoverArt( caller, type, data, customData ); + break; + + default: + dataError( caller, type, data, customData ); + } +} + + +void +LastFmPlugin::nowPlaying( const QString &caller, const InfoType type, const QVariant& data, Tomahawk::InfoSystem::InfoCustomData &customData ) +{ + if ( !data.canConvert< Tomahawk::InfoSystem::InfoCustomData >() || !m_scrobbler ) + { + dataError( caller, type, data, customData ); + return; + } + InfoCustomData hash = data.value< Tomahawk::InfoSystem::InfoCustomData >(); + if ( !hash.contains( "title" ) || !hash.contains( "artist" ) || !hash.contains( "album" ) || !hash.contains( "duration" ) ) + { + dataError( caller, type, data, customData ); + return; + } + + m_track = lastfm::MutableTrack(); + m_track.stamp(); + + m_track.setTitle( hash["title"].toString() ); + m_track.setArtist( hash["artist"].toString() ); + m_track.setAlbum( hash["album"].toString() ); + m_track.setDuration( hash["duration"].toUInt() ); + m_track.setSource( lastfm::Track::Player ); + + m_scrobbler->nowPlaying( m_track ); + emit info( caller, type, data, QVariant(), customData ); +} + + +void +LastFmPlugin::scrobble( const QString &caller, const InfoType type, const QVariant& data, Tomahawk::InfoSystem::InfoCustomData &customData ) +{ + Q_ASSERT( QThread::currentThread() == thread() ); + + if ( !m_scrobbler || m_track.isNull() ) + { + dataError( caller, type, data, customData ); + return; + } + + qDebug() << Q_FUNC_INFO << m_track.toString(); + m_scrobbler->cache( m_track ); + m_scrobbler->submit(); + + emit info( caller, type, data, QVariant(), customData ); +} + + +void +LastFmPlugin::fetchCoverArt( const QString &caller, const InfoType type, const QVariant& data, Tomahawk::InfoSystem::InfoCustomData &customData ) +{ + qDebug() << Q_FUNC_INFO; + if ( !data.canConvert< Tomahawk::InfoSystem::InfoCustomData >() ) + { + dataError( caller, type, data, customData ); + return; + } + InfoCustomData hash = data.value< Tomahawk::InfoSystem::InfoCustomData >(); + if ( !hash.contains( "artist" ) || !hash.contains( "album" ) ) + { + dataError( caller, type, data, customData ); + return; + } + + Tomahawk::InfoSystem::InfoCacheCriteria criteria; + criteria["artist"] = hash["artist"].toString(); + criteria["album"] = hash["album"].toString(); + + emit getCachedInfo( criteria, 2419200000, caller, type, data, customData ); +} + + +void +LastFmPlugin::fetchArtistImages( const QString &caller, const InfoType type, const QVariant& data, Tomahawk::InfoSystem::InfoCustomData &customData ) +{ + qDebug() << Q_FUNC_INFO; + if ( !data.canConvert< Tomahawk::InfoSystem::InfoCustomData >() ) + { + dataError( caller, type, data, customData ); + return; + } + InfoCustomData hash = data.value< Tomahawk::InfoSystem::InfoCustomData >(); + if ( !hash.contains( "artist" ) ) + { + dataError( caller, type, data, customData ); + return; + } + + Tomahawk::InfoSystem::InfoCacheCriteria criteria; + criteria["artist"] = hash["artist"].toString(); + + emit getCachedInfo( criteria, 2419200000, caller, type, data, customData ); +} + + +void +LastFmPlugin::notInCacheSlot( QHash criteria, QString caller, Tomahawk::InfoSystem::InfoType type, QVariant input, Tomahawk::InfoSystem::InfoCustomData customData ) +{ + qDebug() << Q_FUNC_INFO; + + switch ( type ) + { + case InfoAlbumCoverArt: + { + QString artistName = criteria["artist"]; + QString albumName = criteria["album"]; + + QString imgurl = "http://ws.audioscrobbler.com/2.0/?method=album.imageredirect&artist=%1&album=%2&autocorrect=1&size=medium&api_key=7a90f6672a04b809ee309af169f34b8b"; + QNetworkRequest req( imgurl.arg( artistName ).arg( albumName ) ); + QNetworkReply* reply = TomahawkUtils::nam()->get( req ); + reply->setProperty( "customData", QVariant::fromValue( customData ) ); + reply->setProperty( "origData", input ); + reply->setProperty( "caller", caller ); + reply->setProperty( "type", (uint)(type) ); + + connect( reply, SIGNAL( finished() ), SLOT( coverArtReturned() ) ); + return; + } + + case InfoArtistImages: + { + QString artistName = criteria["artist"]; + + QString imgurl = "http://ws.audioscrobbler.com/2.0/?method=artist.imageredirect&artist=%1&autocorrect=1&size=medium&api_key=7a90f6672a04b809ee309af169f34b8b"; + QNetworkRequest req( imgurl.arg( artistName ) ); + QNetworkReply* reply = TomahawkUtils::nam()->get( req ); + reply->setProperty( "customData", QVariant::fromValue( customData ) ); + reply->setProperty( "origData", input ); + reply->setProperty( "caller", caller ); + reply->setProperty( "type", (uint)(type) ); + + connect( reply, SIGNAL( finished() ), SLOT( artistImagesReturned() ) ); + return; + } + + default: + qDebug() << "Couldn't figure out what to do with this type of request after cache miss"; + } +} + + +void +LastFmPlugin::coverArtReturned() +{ + qDebug() << Q_FUNC_INFO; + QNetworkReply* reply = qobject_cast( sender() ); + QUrl redir = reply->attribute( QNetworkRequest::RedirectionTargetAttribute ).toUrl(); + if ( redir.isEmpty() ) + { + QByteArray ba = reply->readAll(); + foreach ( const QUrl& url, m_badUrls ) + { + if ( reply->url().toString().startsWith( url.toString() ) ) + ba = QByteArray(); + } + + InfoCustomData returnedData; + returnedData["imgbytes"] = ba; + returnedData["url"] = reply->url().toString(); + + InfoCustomData customData = reply->property( "customData" ).value< Tomahawk::InfoSystem::InfoCustomData >(); + InfoType type = (Tomahawk::InfoSystem::InfoType)(reply->property( "type" ).toUInt()); + emit info( + reply->property( "caller" ).toString(), + type, + reply->property( "origData" ), + returnedData, + customData + ); + + InfoCustomData origData = reply->property( "origData" ).value< Tomahawk::InfoSystem::InfoCustomData >(); + Tomahawk::InfoSystem::InfoCacheCriteria criteria; + criteria["artist"] = origData["artist"].toString(); + criteria["album"] = origData["album"].toString(); + emit updateCache( criteria, 2419200000, type, returnedData ); + } + else + { + // Follow HTTP redirect + QNetworkRequest req( redir ); + QNetworkReply* newReply = TomahawkUtils::nam()->get( req ); + newReply->setProperty( "origData", reply->property( "origData" ) ); + newReply->setProperty( "customData", reply->property( "customData" ) ); + newReply->setProperty( "caller", reply->property( "caller" ) ); + newReply->setProperty( "type", reply->property( "type" ) ); + connect( newReply, SIGNAL( finished() ), SLOT( coverArtReturned() ) ); + } + + reply->deleteLater(); +} + + +void +LastFmPlugin::artistImagesReturned() +{ + qDebug() << Q_FUNC_INFO; + QNetworkReply* reply = qobject_cast( sender() ); + QUrl redir = reply->attribute( QNetworkRequest::RedirectionTargetAttribute ).toUrl(); + if ( redir.isEmpty() ) + { + QByteArray ba = reply->readAll(); + foreach ( const QUrl& url, m_badUrls ) + { + if ( reply->url().toString().startsWith( url.toString() ) ) + ba = QByteArray(); + } + + InfoCustomData returnedData; + returnedData["imgbytes"] = ba; + returnedData["url"] = reply->url().toString(); + + InfoCustomData customData = reply->property( "customData" ).value< Tomahawk::InfoSystem::InfoCustomData >(); + InfoType type = (Tomahawk::InfoSystem::InfoType)(reply->property( "type" ).toUInt()); + emit info( + reply->property( "caller" ).toString(), + type, + reply->property( "origData" ), + returnedData, + customData + ); + + InfoCustomData origData = reply->property( "origData" ).value< Tomahawk::InfoSystem::InfoCustomData >(); + Tomahawk::InfoSystem::InfoCacheCriteria criteria; + criteria["artist"] = origData["artist"].toString(); + emit updateCache( criteria, 2419200000, type, returnedData ); + } + else + { + // Follow HTTP redirect + QNetworkRequest req( redir ); + QNetworkReply* newReply = TomahawkUtils::nam()->get( req ); + newReply->setProperty( "origData", reply->property( "origData" ) ); + newReply->setProperty( "customData", reply->property( "customData" ) ); + newReply->setProperty( "caller", reply->property( "caller" ) ); + newReply->setProperty( "type", reply->property( "type" ) ); + connect( newReply, SIGNAL( finished() ), SLOT( artistImagesReturned() ) ); + } + + reply->deleteLater(); +} + + +void +LastFmPlugin::settingsChanged() +{ + if( !m_scrobbler && TomahawkSettings::instance()->scrobblingEnabled() ) + { // can simply create the scrobbler + lastfm::ws::Username = TomahawkSettings::instance()->lastFmUsername(); + m_pw = TomahawkSettings::instance()->lastFmPassword(); + + createScrobbler(); + } + else if( m_scrobbler && !TomahawkSettings::instance()->scrobblingEnabled() ) + { + delete m_scrobbler; + m_scrobbler = 0; + } + else if( TomahawkSettings::instance()->lastFmUsername() != lastfm::ws::Username || + TomahawkSettings::instance()->lastFmPassword() != m_pw ) + { + lastfm::ws::Username = TomahawkSettings::instance()->lastFmUsername(); + // credentials have changed, have to re-create scrobbler for them to take effect + if( m_scrobbler ) + delete m_scrobbler; + + createScrobbler(); + } +} + + +void +LastFmPlugin::onAuthenticated() +{ + if( !m_authJob ) + { + qDebug() << Q_FUNC_INFO << "Help! No longer got a last.fm auth job!"; + return; + } + + if( m_authJob->error() == QNetworkReply::NoError ) + { + lastfm::XmlQuery lfm = lastfm::XmlQuery( m_authJob->readAll() ); + + if( lfm.children( "error" ).size() > 0 ) + { + qDebug() << "Error from authenticating with Last.fm service:" << lfm.text(); + TomahawkSettings::instance()->setLastFmSessionKey( QByteArray() ); + + } + else + { + lastfm::ws::SessionKey = lfm[ "session" ][ "key" ].text(); + + TomahawkSettings::instance()->setLastFmSessionKey( lastfm::ws::SessionKey.toLatin1() ); + + if( TomahawkSettings::instance()->scrobblingEnabled() ) + m_scrobbler = new lastfm::Audioscrobbler( "thk" ); + } + } + else + { + qDebug() << "Got error in Last.fm authentication job:" << m_authJob->errorString(); + } + + m_authJob->deleteLater(); +} + + +void +LastFmPlugin::createScrobbler() +{ + if( TomahawkSettings::instance()->lastFmSessionKey().isEmpty() ) // no session key, so get one + { + QString authToken = md5( ( lastfm::ws::Username.toLower() + md5( m_pw.toUtf8() ) ).toUtf8() ); + + QMap query; + query[ "method" ] = "auth.getMobileSession"; + query[ "username" ] = lastfm::ws::Username; + query[ "authToken" ] = authToken; + m_authJob = lastfm::ws::post( query ); + + connect( m_authJob, SIGNAL( finished() ), SLOT( onAuthenticated() ) ); + } + else + { + lastfm::ws::SessionKey = TomahawkSettings::instance()->lastFmSessionKey(); + + m_scrobbler = new lastfm::Audioscrobbler( "thk" ); + m_scrobbler->moveToThread( thread() ); + } +} diff --git a/src/libtomahawk/infosystem/infoplugins/lastfmplugin.h b/src/libtomahawk/infosystem/infoplugins/lastfmplugin.h new file mode 100644 index 000000000..1b97b0e0c --- /dev/null +++ b/src/libtomahawk/infosystem/infoplugins/lastfmplugin.h @@ -0,0 +1,80 @@ +/* === 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 LASTFMPLUGIN_H +#define LASTFMPLUGIN_H +#include "infosystem/infosystem.h" +#include "result.h" + +#include +#include +#include + +#include + +class QNetworkReply; + +namespace Tomahawk +{ + +namespace InfoSystem +{ + +class LastFmPlugin : public InfoPlugin +{ + Q_OBJECT + +public: + LastFmPlugin( QObject *parent ); + virtual ~LastFmPlugin(); + + void getInfo( const QString &caller, const InfoType type, const QVariant &data, InfoCustomData customData ); + +public slots: + void settingsChanged(); + + void onAuthenticated(); + void coverArtReturned(); + void artistImagesReturned(); + + virtual void notInCacheSlot( Tomahawk::InfoSystem::InfoCacheCriteria criteria, QString caller, Tomahawk::InfoSystem::InfoType type, QVariant input, Tomahawk::InfoSystem::InfoCustomData customData ); + +private: + void fetchCoverArt( const QString &caller, const InfoType type, const QVariant& data, Tomahawk::InfoSystem::InfoCustomData &customData ); + void fetchArtistImages( const QString &caller, const InfoType type, const QVariant& data, Tomahawk::InfoSystem::InfoCustomData &customData ); + + void createScrobbler(); + void scrobble( const QString &caller, const InfoType type, const QVariant& data, InfoCustomData &customData ); + void nowPlaying( const QString &caller, const InfoType type, const QVariant& data, InfoCustomData &customData ); + + void dataError( const QString &caller, const InfoType type, const QVariant& data, InfoCustomData &customData ); + + lastfm::MutableTrack m_track; + lastfm::Audioscrobbler* m_scrobbler; + QString m_pw; + + QList< QUrl > m_badUrls; + + QNetworkReply* m_authJob; +}; + +} + +} + +#endif // LASTFMPLUGIN_H diff --git a/src/infosystem/infoplugins/musixmatchplugin.cpp b/src/libtomahawk/infosystem/infoplugins/musixmatchplugin.cpp similarity index 75% rename from src/infosystem/infoplugins/musixmatchplugin.cpp rename to src/libtomahawk/infosystem/infoplugins/musixmatchplugin.cpp index 8cae2c6fe..914a29e27 100644 --- a/src/infosystem/infoplugins/musixmatchplugin.cpp +++ b/src/libtomahawk/infosystem/infoplugins/musixmatchplugin.cpp @@ -1,7 +1,25 @@ -#include "tomahawk/infosystem.h" -#include "tomahawk/tomahawkapp.h" +/* === 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 "musixmatchplugin.h" +#include "utils/tomahawkutils.h" + #include #include @@ -24,18 +42,17 @@ MusixMatchPlugin::~MusixMatchPlugin() qDebug() << Q_FUNC_INFO; } -void MusixMatchPlugin::getInfo(const QString &caller, const InfoType type, const QVariant& data, Tomahawk::InfoSystem::InfoCustomDataHash customData) +void MusixMatchPlugin::getInfo(const QString &caller, const InfoType type, const QVariant& data, Tomahawk::InfoSystem::InfoCustomData customData) { qDebug() << Q_FUNC_INFO; - if( !isValidTrackData(caller, data, customData) || !data.canConvert()) + if( !isValidTrackData(caller, data, customData) || !data.canConvert()) return; - Tomahawk::InfoSystem::MusixMatchHash hash = data.value(); - QString artist = hash["artistName"]; - QString track = hash["trackName"]; + Tomahawk::InfoSystem::InfoCustomData hash = data.value(); + QString artist = hash["artistName"].toString(); + QString track = hash["trackName"].toString(); if( artist.isEmpty() || track.isEmpty() ) { emit info(caller, Tomahawk::InfoSystem::InfoTrackLyrics, data, QVariant(), customData); - emit finished(caller, Tomahawk::InfoSystem::InfoTrackLyrics); return; } qDebug() << "artist is " << artist << ", track is " << track; @@ -45,35 +62,32 @@ void MusixMatchPlugin::getInfo(const QString &caller, const InfoType type, const url.addQueryItem("q_artist", artist); url.addQueryItem("q_track", track); QNetworkReply* reply = TomahawkUtils::nam()->get(QNetworkRequest(url)); - reply->setProperty("customData", QVariant::fromValue(customData)); + reply->setProperty("customData", QVariant::fromValue(customData)); reply->setProperty("origData", data); reply->setProperty("caller", caller); - + connect(reply, SIGNAL(finished()), SLOT(trackSearchSlot())); } -bool MusixMatchPlugin::isValidTrackData(const QString &caller, const QVariant& data, Tomahawk::InfoSystem::InfoCustomDataHash &customData) +bool MusixMatchPlugin::isValidTrackData(const QString &caller, const QVariant& data, Tomahawk::InfoSystem::InfoCustomData &customData) { qDebug() << Q_FUNC_INFO; - if (data.isNull() || !data.isValid() || !data.canConvert()) + if (data.isNull() || !data.isValid() || !data.canConvert()) { emit info(caller, Tomahawk::InfoSystem::InfoTrackLyrics, data, QVariant(), customData); - emit finished(caller, Tomahawk::InfoSystem::InfoTrackLyrics); qDebug() << "MusixMatchPlugin::isValidTrackData: Data null, invalid, or can't convert"; return false; } - MusixMatchHash hash = data.value(); - if (hash["trackName"].isEmpty() ) + InfoCustomData hash = data.value(); + if (hash["trackName"].toString().isEmpty() ) { emit info(caller, Tomahawk::InfoSystem::InfoTrackLyrics, data, QVariant(), customData); - emit finished(caller, Tomahawk::InfoSystem::InfoTrackLyrics); qDebug() << "MusixMatchPlugin::isValidTrackData: Track name is empty"; return false; } - if (hash["artistName"].isEmpty() ) + if (hash["artistName"].toString().isEmpty() ) { emit info(caller, Tomahawk::InfoSystem::InfoTrackLyrics, data, QVariant(), customData); - emit finished(caller, Tomahawk::InfoSystem::InfoTrackLyrics); qDebug() << "MusixMatchPlugin::isValidTrackData: No artist name found"; return false; } @@ -86,7 +100,7 @@ void MusixMatchPlugin::trackSearchSlot() QNetworkReply* oldReply = qobject_cast( sender() ); if (!oldReply) { - emit info(QString(), Tomahawk::InfoSystem::InfoTrackLyrics, QVariant(), QVariant(), Tomahawk::InfoSystem::InfoCustomDataHash()); + emit info(QString(), Tomahawk::InfoSystem::InfoTrackLyrics, QVariant(), QVariant(), Tomahawk::InfoSystem::InfoCustomData()); return; } QDomDocument doc; @@ -95,8 +109,7 @@ void MusixMatchPlugin::trackSearchSlot() QDomNodeList domNodeList = doc.elementsByTagName("track_id"); if (domNodeList.isEmpty()) { - emit info(oldReply->property("caller").toString(), Tomahawk::InfoSystem::InfoTrackLyrics, oldReply->property("origData"), QVariant(), oldReply->property("customData").value()); - emit finished(oldReply->property("caller").toString(), Tomahawk::InfoSystem::InfoTrackLyrics); + emit info(oldReply->property("caller").toString(), Tomahawk::InfoSystem::InfoTrackLyrics, oldReply->property("origData"), QVariant(), oldReply->property("customData").value()); return; } QString track_id = domNodeList.at(0).toElement().text(); @@ -117,7 +130,7 @@ void MusixMatchPlugin::trackLyricsSlot() QNetworkReply* reply = qobject_cast( sender() ); if (!reply) { - emit info(QString(), Tomahawk::InfoSystem::InfoTrackLyrics, QVariant(), QVariant(), Tomahawk::InfoSystem::InfoCustomDataHash()); + emit info(QString(), Tomahawk::InfoSystem::InfoTrackLyrics, QVariant(), QVariant(), Tomahawk::InfoSystem::InfoCustomData()); return; } QDomDocument doc; @@ -125,12 +138,10 @@ void MusixMatchPlugin::trackLyricsSlot() QDomNodeList domNodeList = doc.elementsByTagName("lyrics_body"); if (domNodeList.isEmpty()) { - emit info(reply->property("caller").toString(), Tomahawk::InfoSystem::InfoTrackLyrics, reply->property("origData"), QVariant(), reply->property("customData").value()); - emit finished(reply->property("caller").toString(), Tomahawk::InfoSystem::InfoTrackLyrics); + emit info(reply->property("caller").toString(), Tomahawk::InfoSystem::InfoTrackLyrics, reply->property("origData"), QVariant(), reply->property("customData").value()); return; } QString lyrics = domNodeList.at(0).toElement().text(); qDebug() << "Emitting lyrics: " << lyrics; - emit info(reply->property("caller").toString(), Tomahawk::InfoSystem::InfoTrackLyrics, reply->property("origData"), QVariant(lyrics), reply->property("customData").value()); - emit finished(reply->property("caller").toString(), Tomahawk::InfoSystem::InfoTrackLyrics); + emit info(reply->property("caller").toString(), Tomahawk::InfoSystem::InfoTrackLyrics, reply->property("origData"), QVariant(lyrics), reply->property("customData").value()); } diff --git a/src/libtomahawk/infosystem/infoplugins/musixmatchplugin.h b/src/libtomahawk/infosystem/infoplugins/musixmatchplugin.h new file mode 100644 index 000000000..3fcb1ded7 --- /dev/null +++ b/src/libtomahawk/infosystem/infoplugins/musixmatchplugin.h @@ -0,0 +1,57 @@ +/* === 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 MUSIXMATCHPLUGIN_H +#define MUSIXMATCHPLUGIN_H + +#include "infosystem/infosystem.h" + +class QNetworkReply; + +namespace Tomahawk +{ + +namespace InfoSystem +{ + +class MusixMatchPlugin : public InfoPlugin +{ + Q_OBJECT + +public: + MusixMatchPlugin(QObject *parent); + virtual ~MusixMatchPlugin(); + + void getInfo(const QString &caller, const InfoType type, const QVariant &data, InfoCustomData customData); + +private: + bool isValidTrackData( const QString &caller, const QVariant& data, InfoCustomData &customData ); + +public slots: + void trackSearchSlot(); + void trackLyricsSlot(); + +private: + QString m_apiKey; +}; + +} + +} + +#endif // MUSIXMATCHPLUGIN_H diff --git a/src/libtomahawk/infosystem/infosystem.cpp b/src/libtomahawk/infosystem/infosystem.cpp new file mode 100644 index 000000000..6c8357ed6 --- /dev/null +++ b/src/libtomahawk/infosystem/infosystem.cpp @@ -0,0 +1,212 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2010-2011, Christian Muehlhaeuser + * + * Tomahawk is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Tomahawk is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Tomahawk. If not, see . + */ + +#include + +#include "infosystem.h" +#include "utils/tomahawkutils.h" +#include "infosystemcache.h" +#include "infoplugins/echonestplugin.h" +#include "infoplugins/musixmatchplugin.h" +#include "infoplugins/lastfmplugin.h" + +namespace Tomahawk +{ + +namespace InfoSystem +{ + +InfoPlugin::InfoPlugin(QObject *parent) + :QObject( parent ) + { + qDebug() << Q_FUNC_INFO; + InfoSystem *system = qobject_cast< InfoSystem* >( parent ); + if( system ) + { + QObject::connect( + this, + SIGNAL( getCachedInfo( Tomahawk::InfoSystem::InfoCacheCriteria, qint64, QString, Tomahawk::InfoSystem::InfoType, QVariant, Tomahawk::InfoSystem::InfoCustomData ) ), + system->getCache(), + SLOT( getCachedInfoSlot( Tomahawk::InfoSystem::InfoCacheCriteria, qint64, QString, Tomahawk::InfoSystem::InfoType, QVariant, Tomahawk::InfoSystem::InfoCustomData ) ) + ); + QObject::connect( + system->getCache(), + SIGNAL( notInCache( Tomahawk::InfoSystem::InfoCacheCriteria, QString, Tomahawk::InfoSystem::InfoType, QVariant, Tomahawk::InfoSystem::InfoCustomData ) ), + this, + SLOT( notInCacheSlot( Tomahawk::InfoSystem::InfoCacheCriteria, QString, Tomahawk::InfoSystem::InfoType, QVariant, Tomahawk::InfoSystem::InfoCustomData ) ) + ); + QObject::connect( + this, + SIGNAL( updateCache( Tomahawk::InfoSystem::InfoCacheCriteria, qint64, Tomahawk::InfoSystem::InfoType, QVariant ) ), + system->getCache(), + SLOT( updateCacheSlot( Tomahawk::InfoSystem::InfoCacheCriteria, qint64, Tomahawk::InfoSystem::InfoType, QVariant ) ) + ); + } + } + + +InfoSystem* InfoSystem::s_instance = 0; + +InfoSystem* +InfoSystem::instance() +{ + return s_instance; +} + +InfoSystem::InfoSystem(QObject *parent) + : QObject(parent) +{ + s_instance = this; + + qDebug() << Q_FUNC_INFO; + qRegisterMetaType< QMap< QString, QMap< QString, QString > > >( "Tomahawk::InfoSystem::InfoGenericMap" ); + qRegisterMetaType< QHash< QString, QVariant > >( "Tomahawk::InfoSystem::InfoCustomData" ); + qRegisterMetaType< QHash< QString, QString > >( "Tomahawk::InfoSystem::InfoCacheCriteria" ); + qRegisterMetaType< Tomahawk::InfoSystem::InfoType >( "Tomahawk::InfoSystem::InfoType" ); + + m_infoSystemCacheThreadController = new QThread( this ); + m_cache = new InfoSystemCache(); + m_cache->moveToThread( m_infoSystemCacheThreadController ); + m_infoSystemCacheThreadController->start( QThread::IdlePriority ); + + InfoPluginPtr enptr( new EchoNestPlugin( this ) ); + m_plugins.append( enptr ); + InfoPluginPtr mmptr( new MusixMatchPlugin( this ) ); + m_plugins.append( mmptr ); + InfoPluginPtr lfmptr( new LastFmPlugin( this ) ); + m_plugins.append( lfmptr ); + + Q_FOREACH( InfoPluginPtr plugin, m_plugins ) + { + connect( + plugin.data(), + SIGNAL( info( QString, Tomahawk::InfoSystem::InfoType, QVariant, QVariant, Tomahawk::InfoSystem::InfoCustomData ) ), + this, + SLOT( infoSlot( QString, Tomahawk::InfoSystem::InfoType, QVariant, QVariant, Tomahawk::InfoSystem::InfoCustomData ) ), + Qt::UniqueConnection + ); + } + connect( m_cache, SIGNAL( info( QString, Tomahawk::InfoSystem::InfoType, QVariant, QVariant, Tomahawk::InfoSystem::InfoCustomData ) ), + this, SLOT( infoSlot( QString, Tomahawk::InfoSystem::InfoType, QVariant, QVariant, Tomahawk::InfoSystem::InfoCustomData ) ), Qt::UniqueConnection ); +} + +InfoSystem::~InfoSystem() +{ + qDebug() << Q_FUNC_INFO; + Q_FOREACH( InfoPluginPtr plugin, m_plugins ) + { + if( plugin ) + delete plugin.data(); + } + + if( m_infoSystemCacheThreadController ) + { + m_infoSystemCacheThreadController->quit(); + + while( !m_infoSystemCacheThreadController->isFinished() ) + { + QCoreApplication::processEvents( QEventLoop::AllEvents, 200 ); + TomahawkUtils::Sleep::msleep( 100 ); + } + + if( m_cache ) + { + delete m_cache; + m_cache = 0; + } + + delete m_infoSystemCacheThreadController; + m_infoSystemCacheThreadController = 0; + } +} + +void InfoSystem::registerInfoTypes(const InfoPluginPtr &plugin, const QSet< InfoType >& types) +{ + qDebug() << Q_FUNC_INFO; + Q_FOREACH(InfoType type, types) + m_infoMap[type].append(plugin); +} + +QLinkedList< InfoPluginPtr > InfoSystem::determineOrderedMatches(const InfoType type) const +{ + //Dummy function for now that returns the various items in the QSet; at some point this will + //probably need to support ordering based on the data source + QLinkedList< InfoPluginPtr > providers; + Q_FOREACH(InfoPluginPtr ptr, m_infoMap[type]) + providers << ptr; + return providers; +} + +void InfoSystem::getInfo(const QString &caller, const InfoType type, const QVariant& data, InfoCustomData customData) +{ + qDebug() << Q_FUNC_INFO; + QLinkedList< InfoPluginPtr > providers = determineOrderedMatches(type); + if (providers.isEmpty()) + { + emit info(QString(), InfoNoInfo, QVariant(), QVariant(), customData); + emit finished(caller); + return; + } + + InfoPluginPtr ptr = providers.first(); + if (!ptr) + { + emit info(QString(), InfoNoInfo, QVariant(), QVariant(), customData); + emit finished(caller); + return; + } + + m_dataTracker[caller][type] = m_dataTracker[caller][type] + 1; + qDebug() << "current count in dataTracker for type" << type << "is" << m_dataTracker[caller][type]; + ptr.data()->getInfo(caller, type, data, customData); +} + +void InfoSystem::getInfo(const QString &caller, const InfoMap &input, InfoCustomData customData) +{ + Q_FOREACH( InfoType type, input.keys() ) + getInfo(caller, type, input[type], customData); +} + +void InfoSystem::infoSlot(QString target, InfoType type, QVariant input, QVariant output, InfoCustomData customData) +{ + qDebug() << Q_FUNC_INFO; + qDebug() << "current count in dataTracker is " << m_dataTracker[target][type]; + if (m_dataTracker[target][type] == 0) + { + qDebug() << "Caller was not waiting for that type of data!"; + return; + } + emit info(target, type, input, output, customData); + + m_dataTracker[target][type] = m_dataTracker[target][type] - 1; + qDebug() << "current count in dataTracker is " << m_dataTracker[target][type]; + Q_FOREACH(InfoType testtype, m_dataTracker[target].keys()) + { + if (m_dataTracker[target][testtype] != 0) + { + qDebug() << "found outstanding request of type" << testtype; + return; + } + } + qDebug() << "emitting finished with target" << target; + emit finished(target); +} + +} //namespace InfoSystem + +} //namespace Tomahawk \ No newline at end of file diff --git a/src/libtomahawk/infosystem/infosystem.h b/src/libtomahawk/infosystem/infosystem.h new file mode 100644 index 000000000..75e674f49 --- /dev/null +++ b/src/libtomahawk/infosystem/infosystem.h @@ -0,0 +1,205 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2010-2011, Christian Muehlhaeuser + * + * Tomahawk is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Tomahawk is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Tomahawk. If not, see . + */ + +#ifndef TOMAHAWK_INFOSYSTEM_H +#define TOMAHAWK_INFOSYSTEM_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dllmacro.h" + +namespace Tomahawk { + +namespace InfoSystem { + +class InfoSystemCache; + +enum InfoType { + InfoTrackID = 0, + InfoTrackArtist, + InfoTrackAlbum, + InfoTrackGenre, + InfoTrackComposer, + InfoTrackDate, + InfoTrackNumber, + InfoTrackDiscNumber, + InfoTrackBitRate, + InfoTrackLength, + InfoTrackSampleRate, + InfoTrackFileSize, + InfoTrackBPM, + InfoTrackReplayGain, + InfoTrackReplayPeakGain, + InfoTrackLyrics, + InfoTrackLocation, + InfoTrackProfile, + InfoTrackEnergy, + InfoTrackDanceability, + InfoTrackTempo, + InfoTrackLoudness, + + InfoArtistID, + InfoArtistName, + InfoArtistBiography, + InfoArtistBlog, + InfoArtistFamiliarity, + InfoArtistHotttness, + InfoArtistImages, + InfoArtistNews, + InfoArtistProfile, + InfoArtistReviews, + InfoArtistSongs, + InfoArtistSimilars, + InfoArtistTerms, + InfoArtistLinks, + InfoArtistVideos, + + InfoAlbumID, + InfoAlbumName, + InfoAlbumArtist, + InfoAlbumDate, + InfoAlbumGenre, + InfoAlbumComposer, + InfoAlbumCoverArt, + + InfoMiscTopHotttness, + InfoMiscTopTerms, + + InfoMiscSubmitNowPlaying, + InfoMiscSubmitScrobble, + + InfoNoInfo +}; + +typedef QMap< InfoType, QVariant > InfoMap; +typedef QMap< QString, QMap< QString, QString > > InfoGenericMap; +typedef QHash< QString, QVariant > InfoCustomData; +typedef QHash< QString, QString > InfoCacheCriteria; + +class DLLEXPORT InfoPlugin : public QObject +{ + Q_OBJECT + +public: + InfoPlugin( QObject *parent ); + + virtual ~InfoPlugin() + { + qDebug() << Q_FUNC_INFO; + } + + virtual void getInfo( const QString &caller, const InfoType type, const QVariant &data, InfoCustomData customData ) = 0; + +signals: + void getCachedInfo( Tomahawk::InfoSystem::InfoCacheCriteria criteria, qint64 newMaxAge, QString caller, Tomahawk::InfoSystem::InfoType type, QVariant input, Tomahawk::InfoSystem::InfoCustomData customData ); + void updateCache( Tomahawk::InfoSystem::InfoCacheCriteria criteria, qint64, Tomahawk::InfoSystem::InfoType type, QVariant output ); + void info( QString caller, Tomahawk::InfoSystem::InfoType type, QVariant input, QVariant output, Tomahawk::InfoSystem::InfoCustomData customData ); + void finished( QString, Tomahawk::InfoSystem::InfoType ); + +public slots: + //FIXME: Make pure virtual when everything supports it + virtual void notInCacheSlot( Tomahawk::InfoSystem::InfoCacheCriteria criteria, QString caller, Tomahawk::InfoSystem::InfoType type, QVariant input, Tomahawk::InfoSystem::InfoCustomData customData ) + { + Q_UNUSED( criteria ); + Q_UNUSED( caller ); + Q_UNUSED( type ); + Q_UNUSED( input ); + Q_UNUSED( customData ); + } + +protected: + InfoType m_type; +}; + +typedef QWeakPointer< InfoPlugin > InfoPluginPtr; + +class DLLEXPORT InfoSystem : public QObject +{ + Q_OBJECT + +public: + static InfoSystem* instance(); + + InfoSystem( QObject *parent ); + ~InfoSystem(); + + void registerInfoTypes( const InfoPluginPtr &plugin, const QSet< InfoType > &types ); + + void getInfo( const QString &caller, const InfoType type, const QVariant &data, InfoCustomData customData ); + void getInfo( const QString &caller, const InfoMap &input, InfoCustomData customData ); + + InfoSystemCache* getCache() { return m_cache; } + +signals: + void info( QString caller, Tomahawk::InfoSystem::InfoType, QVariant input, QVariant output, Tomahawk::InfoSystem::InfoCustomData customData ); + void finished( QString target ); + +public slots: + void infoSlot( QString target, Tomahawk::InfoSystem::InfoType type, QVariant input, QVariant output, Tomahawk::InfoSystem::InfoCustomData customData ); + +private: + QLinkedList< InfoPluginPtr > determineOrderedMatches( const InfoType type ) const; + + QMap< InfoType, QLinkedList< InfoPluginPtr > > m_infoMap; + + // For now, statically instantiate plugins; this is just somewhere to keep them + QLinkedList< InfoPluginPtr > m_plugins; + + QHash< QString, QHash< InfoType, int > > m_dataTracker; + + InfoSystemCache* m_cache; + QThread* m_infoSystemCacheThreadController; + + static InfoSystem* s_instance; +}; + +} + +} + +inline uint qHash( Tomahawk::InfoSystem::InfoCacheCriteria hash ) +{ + QCryptographicHash md5( QCryptographicHash::Md5 ); + foreach( QString key, hash.keys() ) + md5.addData( key.toUtf8() ); + foreach( QString value, hash.values() ) + md5.addData( value.toUtf8() ); + + QString hexData = md5.result(); + + uint returnval = 0; + + foreach( uint val, hexData.toUcs4() ) + returnval += val; + + return returnval; +} + +Q_DECLARE_METATYPE( Tomahawk::InfoSystem::InfoGenericMap ); +Q_DECLARE_METATYPE( Tomahawk::InfoSystem::InfoCustomData ); +Q_DECLARE_METATYPE( Tomahawk::InfoSystem::InfoCacheCriteria ); + +#endif // TOMAHAWK_INFOSYSTEM_H diff --git a/src/libtomahawk/infosystem/infosystemcache.cpp b/src/libtomahawk/infosystem/infosystemcache.cpp new file mode 100644 index 000000000..9ecdc7435 --- /dev/null +++ b/src/libtomahawk/infosystem/infosystemcache.cpp @@ -0,0 +1,204 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2010-2011, Christian Muehlhaeuser + * + * Tomahawk is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Tomahawk is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Tomahawk. If not, see . + */ + +#include +#include +#include +#include + +#include "infosystemcache.h" + + +namespace Tomahawk +{ + +namespace InfoSystem +{ + + +InfoSystemCache::InfoSystemCache( QObject* parent ) + : QObject(parent) +{ + qDebug() << Q_FUNC_INFO; + QString cacheBaseDir = QDesktopServices::storageLocation( QDesktopServices::CacheLocation ); + for( int i = 0; i <= InfoNoInfo; i++ ) + { + InfoType type = (InfoType)(i); + QString cacheDir = cacheBaseDir + "/InfoSystemCache/" + QString::number( i ); + QString cacheFile = cacheDir + '/' + QString::number( i ); + QDir dir( cacheDir ); + if( dir.exists() && QFile::exists( cacheFile ) ) + loadCache( type, cacheFile ); + } +} + + +InfoSystemCache::~InfoSystemCache() +{ + qDebug() << Q_FUNC_INFO; + qDebug() << "Saving infosystemcache to disk"; + QString cacheBaseDir = QDesktopServices::storageLocation( QDesktopServices::CacheLocation ); + for ( int i = 0; i <= InfoNoInfo; i++ ) + { + InfoType type = (InfoType)(i); + if ( m_dirtySet.contains( type ) && m_dataCache.contains( type ) ) + { + QString cacheDir = cacheBaseDir + "/InfoSystemCache/" + QString::number( i ); + saveCache( type, cacheDir ); + } + } +} + + +void +InfoSystemCache::getCachedInfoSlot( Tomahawk::InfoSystem::InfoCacheCriteria criteria, qint64 newMaxAge, QString caller, Tomahawk::InfoSystem::InfoType type, QVariant input, Tomahawk::InfoSystem::InfoCustomData customData ) +{ + qDebug() << Q_FUNC_INFO; + if ( !m_dataCache.contains( type ) || !m_dataCache[type].contains( criteria ) ) + { + emit notInCache( criteria, caller, type, input, customData ); + return; + } + + QHash< InfoCacheCriteria, QDateTime > typemaxtimecache = m_maxTimeCache[type]; + + if ( typemaxtimecache[criteria].toMSecsSinceEpoch() < QDateTime::currentMSecsSinceEpoch() ) + { + QHash< InfoCacheCriteria, QVariant > typedatacache = m_dataCache[type]; + QHash< InfoCacheCriteria, QDateTime > typeinserttimecache = m_insertTimeCache[type]; + typemaxtimecache.remove( criteria ); + m_maxTimeCache[type] = typemaxtimecache; + typedatacache.remove( criteria ); + m_dataCache[type] = typedatacache; + typeinserttimecache.remove( criteria ); + m_insertTimeCache[type] = typeinserttimecache; + m_dirtySet.insert( type ); + emit notInCache( criteria, caller, type, input, customData ); + return; + } + + if ( newMaxAge > 0 ) + { + QHash< InfoCacheCriteria, QDateTime > typemaxtimecache = m_maxTimeCache[type]; + typemaxtimecache[criteria] = QDateTime::fromMSecsSinceEpoch( QDateTime::currentMSecsSinceEpoch() + newMaxAge ); + m_maxTimeCache[type] = typemaxtimecache; + m_dirtySet.insert( type ); + } + + emit info( caller, type, input, m_dataCache[type][criteria], customData ); +} + + +void +InfoSystemCache::updateCacheSlot( Tomahawk::InfoSystem::InfoCacheCriteria criteria, qint64 maxAge, Tomahawk::InfoSystem::InfoType type, QVariant output ) +{ + qDebug() << Q_FUNC_INFO; + QHash< InfoCacheCriteria, QVariant > typedatacache = m_dataCache[type]; + QHash< InfoCacheCriteria, QDateTime > typeinserttimecache = m_insertTimeCache[type]; + QHash< InfoCacheCriteria, QDateTime > typemaxtimecache = m_maxTimeCache[type]; + typedatacache[criteria] = output; + typeinserttimecache[criteria] = QDateTime::currentDateTimeUtc(); + typemaxtimecache[criteria] = QDateTime::fromMSecsSinceEpoch( QDateTime::currentMSecsSinceEpoch() + maxAge ); + m_dataCache[type] = typedatacache; + m_insertTimeCache[type] = typeinserttimecache; + m_maxTimeCache[type] = typemaxtimecache; + m_dirtySet.insert( type ); +} + + +void +InfoSystemCache::loadCache( InfoType type, const QString &cacheFile ) +{ + qDebug() << Q_FUNC_INFO; + QSettings cachedSettings( cacheFile, QSettings::IniFormat ); + + foreach ( QString group, cachedSettings.childGroups() ) + { + cachedSettings.beginGroup( group ); + if ( cachedSettings.value( "maxtime" ).toDateTime().toMSecsSinceEpoch() < QDateTime::currentMSecsSinceEpoch() ) + continue; + QHash< InfoCacheCriteria, QVariant > dataHash = m_dataCache[type]; + QHash< InfoCacheCriteria, QDateTime > insertDateHash = m_insertTimeCache[type]; + QHash< InfoCacheCriteria, QDateTime > maxDateHash = m_maxTimeCache[type]; + InfoCacheCriteria criteria; + int numCriteria = cachedSettings.beginReadArray( "criteria" ); + for ( int i = 0; i < numCriteria; i++ ) + { + cachedSettings.setArrayIndex( i ); + QStringList criteriaValues = cachedSettings.value( QString::number( i ) ).toStringList(); + for ( int j = 0; j < criteriaValues.length(); j += 2 ) + criteria[criteriaValues.at( j )] = criteriaValues.at( j + 1 ); + } + cachedSettings.endArray(); + dataHash[criteria] = cachedSettings.value( "data" ); + insertDateHash[criteria] = cachedSettings.value( "inserttime" ).toDateTime(); + maxDateHash[criteria] = cachedSettings.value( "maxtime" ).toDateTime(); + cachedSettings.endGroup(); + m_dataCache[type] = dataHash; + m_insertTimeCache[type] = insertDateHash; + m_maxTimeCache[type] = maxDateHash; + } +} + + +void +InfoSystemCache::saveCache( InfoType type, const QString &cacheDir ) +{ + qDebug() << Q_FUNC_INFO; + QDir dir( cacheDir ); + if( !dir.exists( cacheDir ) ) + { + qDebug() << "Creating cache directory " << cacheDir; + if( !dir.mkpath( cacheDir ) ) + { + qDebug() << "Failed to create cache dir! Bailing..."; + return; + } + } + + QSettings cachedSettings( QString( cacheDir + '/' + QString::number( (int)type ) ), QSettings::IniFormat ); + + int criteriaNumber = 0; + + foreach( InfoCacheCriteria criteria, m_dataCache[type].keys() ) + { + cachedSettings.beginGroup( "group_" + QString::number( criteriaNumber ) ); + cachedSettings.beginWriteArray( "criteria" ); + QStringList keys = criteria.keys(); + for( int i = 0; i < criteria.size(); i++ ) + { + cachedSettings.setArrayIndex( i ); + QStringList critVal; + critVal << keys.at( i ) << criteria[keys.at( i )]; + cachedSettings.setValue( QString::number( i ), critVal ); + } + cachedSettings.endArray(); + cachedSettings.setValue( "data", m_dataCache[type][criteria] ); + cachedSettings.setValue( "inserttime", m_insertTimeCache[type][criteria] ); + cachedSettings.setValue( "maxtime", m_maxTimeCache[type][criteria] ); + cachedSettings.endGroup(); + ++criteriaNumber; + } + + m_dirtySet.remove( type ); +} + + +} //namespace InfoSystem + +} //namespace Tomahawk \ No newline at end of file diff --git a/src/libtomahawk/infosystem/infosystemcache.h b/src/libtomahawk/infosystem/infosystemcache.h new file mode 100644 index 000000000..20307eab8 --- /dev/null +++ b/src/libtomahawk/infosystem/infosystemcache.h @@ -0,0 +1,65 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2010-2011, Christian Muehlhaeuser + * + * Tomahawk is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Tomahawk is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Tomahawk. If not, see . + */ + +#ifndef TOMAHAWK_INFOSYSTEMCACHE_H +#define TOMAHAWK_INFOSYSTEMCACHE_H + +#include +#include +#include + +#include "infosystem.h" + +namespace Tomahawk +{ + +namespace InfoSystem +{ + +class InfoSystemCache : public QObject +{ +Q_OBJECT + +public: + InfoSystemCache( QObject *parent = 0 ); + + virtual ~InfoSystemCache(); + +signals: + void notInCache( Tomahawk::InfoSystem::InfoCacheCriteria criteria, QString caller, Tomahawk::InfoSystem::InfoType type, QVariant input, Tomahawk::InfoSystem::InfoCustomData customData ); + void info( QString caller, Tomahawk::InfoSystem::InfoType type, QVariant input, QVariant output, Tomahawk::InfoSystem::InfoCustomData customData ); + +public slots: + void getCachedInfoSlot( Tomahawk::InfoSystem::InfoCacheCriteria criteria, qint64 newMaxAge, QString caller, Tomahawk::InfoSystem::InfoType type, QVariant input, Tomahawk::InfoSystem::InfoCustomData customData ); + void updateCacheSlot( Tomahawk::InfoSystem::InfoCacheCriteria criteria, qint64 maxAge, Tomahawk::InfoSystem::InfoType type, QVariant output ); + +private: + void loadCache( InfoType type, const QString &cacheFile ); + void saveCache( InfoType type, const QString &cacheDir ); + + QHash< InfoType, QHash< InfoCacheCriteria, QVariant > > m_dataCache; + QHash< InfoType, QHash< InfoCacheCriteria, QDateTime > > m_insertTimeCache; + QHash< InfoType, QHash< InfoCacheCriteria, QDateTime > > m_maxTimeCache; + QSet< InfoType > m_dirtySet; +}; + +} //namespace InfoSystem + +} //namespace Tomahawk + +#endif //TOMAHAWK_INFOSYSTEMCACHE_H \ No newline at end of file diff --git a/src/libtomahawk/kdsingleapplicationguard/kdlockedsharedmemorypointer.cpp b/src/libtomahawk/kdsingleapplicationguard/kdlockedsharedmemorypointer.cpp new file mode 100644 index 000000000..e1fe10a4d --- /dev/null +++ b/src/libtomahawk/kdsingleapplicationguard/kdlockedsharedmemorypointer.cpp @@ -0,0 +1,475 @@ +#include "kdlockedsharedmemorypointer.h" + +#if QT_VERSION >= 0x040400 || defined( DOXYGEN_RUN ) +#ifndef QT_NO_SHAREDMEMORY + +namespace kdtools +{ +} +using namespace kdtools; + +KDLockedSharedMemoryPointerBase::KDLockedSharedMemoryPointerBase( QSharedMemory * m ) + : locker( m ), + mem( m ) +{ + +} + +KDLockedSharedMemoryPointerBase::KDLockedSharedMemoryPointerBase( QSharedMemory & m ) + : locker( &m ), + mem( &m ) +{ + +} + +KDLockedSharedMemoryPointerBase::~KDLockedSharedMemoryPointerBase() {} + +void * KDLockedSharedMemoryPointerBase::get() { + return mem ? mem->data() : 0 ; +} + +const void * KDLockedSharedMemoryPointerBase::get() const { + return mem ? mem->data() : 0 ; +} + +size_t KDLockedSharedMemoryPointerBase::byteSize() const { + return mem->size(); +} + +/*! + \class KDLockedSharedMemoryPointer + \ingroup core raii smartptr + \brief Locking pointer for Qt shared memory segments + \since_c 2.1 + + (The exception safety of this class has not been evaluated yet.) + + KDLockedSharedMemoryPointer is a smart immutable pointer, which gives convenient and safe access to a QSharedMemory data segment. + The content of a KDLockedSharedMemoryPointer cannot be changed during it's lifetime. + + You can use this class like a normal pointer to the shared memory segment and be sure it's locked while accessing it. + \note You can only put simple types/structs/classes into it. structs and classes shall not contain any other pointers. See the + documentation of QSharedMemory for details. +*/ + +/*! + \fn KDLockedSharedMemoryPointer::KDLockedSharedMemoryPointer( QSharedMemory * mem ) + + Constructor. Constructs a KDLockedSharedMemory pointer which points to the data segment of \a mem. + The constructor locks \a mem. If the memory segment is already locked by another process, this constructor + blocks until the lock is released. + + \post data() == mem->data() and the memory segment has been locked +*/ + +/*! + \fn KDLockedSharedMemoryPointer::KDLockedSharedMemoryPointer( QSharedMemory & mem ) + + \overload + + \post data() == mem.data() and the memory segment has been locked +*/ + +/*! + \fn KDLockedSharedMemoryPointer::~KDLockedSharedMemoryPointer() + + Destructor. Unlocks the shared memory segment. + + \post The shared memory segment has been unlocked +*/ + +/*! + \fn T * KDLockedSharedMemoryPointer::get() + + \returns a pointer to the contained object. +*/ + +/*! + \fn const T * KDLockedSharedMemoryPointer::get() const + + \returns a const pointer to the contained object + \overload +*/ + +/*! + \fn T * KDLockedSharedMemoryPointer::data() + + Equivalent to get(), provided for consistency with Qt naming conventions. +*/ + +/*! + \fn const T * KDLockedSharedMemoryPointer::data() const + + \overload +*/ + +/*! + \fn T & KDLockedSharedMemoryPointer::operator*() + + Dereference operator. Returns \link get() *get()\endlink. +*/ + +/*! + \fn const T & KDLockedSharedMemoryPointer::operator*() const + + Dereference operator. Returns \link get() *get()\endlink. + \overload +*/ + +/*! + \fn T * KDLockedSharedMemoryPointer::operator->() + + Member-by-pointer operator. Returns get(). +*/ + +/*! + \fn const T * KDLockedSharedMemoryPointer::operator->() const + + Member-by-pointer operator. Returns get(). + \overload +*/ + +/*! + \class KDLockedSharedMemoryArray + \ingroup core raii smartptr + \brief Locking array pointer to Qt shared memory segments + \since_c 2.1 + + (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 + data segment. + The content of a KDLockedSharedMemoryArray cannot be changed during it's lifetime. + + You can use this class like a normal pointer to the shared memory segment and be sure it's locked while accessing it. + \note You can only put arrays of simple types/structs/classes into it. structs and classes shall not contain any other pointers. See the + documentation of QSharedMemory for details. + + \sa KDLockedSharedMemoryPointer +*/ + +/*! + \fn KDLockedSharedMemoryArray::KDLockedSharedMemoryArray( QSharedMemory* mem ) + Constructor. Constructs a KDLockedSharedMemoryArray which points to the data segment of \a mem. The constructor locks \a mem. If the memory + segment is already locked by another process, this constructor blocks until the lock is release. + + \post get() == mem->data() and the memory segment has been locked +*/ + +/*! + \fn KDLockedSharedMemoryArray::KDLockedSharedMemoryArray( QSharedMemory& mem ) + \overload + + \post get() == mem->data() and the memory segment has been locked +*/ + + +/*! + \typedef KDLockedSharedMemoryArray::size_type + Typedef for std::size_t. Provided for STL compatibility. +*/ + +/*! + \typedef KDLockedSharedMemoryArray::difference_type + Typedef for std::ptrdiff_t. Provided for STL compatibility. +*/ + +/*! + \typedef KDLockedSharedMemoryArray::iterator + Typedef for T*. Provided for STL compatibility. + \since_t 2.2 +*/ + +/*! + \typedef KDLockedSharedMemoryArray::const_iterator + Typedef for const T*. Provided for STL compatibility. + \since_t 2.2 +*/ + +/*! + \typedef KDLockedSharedMemoryArray::reverse_iterator + Typedef for std::reverse_iterator< \link KDLockedSharedMemoryArray::iterator iterator\endlink >. Provided for STL compatibility. + \since_t 2.2 +*/ + +/*! + \typedef KDLockedSharedMemoryArray::const_reverse_iterator + Typedef for std::reverse_iterator< \link KDLockedSharedMemoryArray::const_iterator const_iterator\endlink >. Provided for STL compatibility. + \since_t 2.2 +*/ + +/*! + \fn KDLockedSharedMemoryArray::iterator KDLockedSharedMemoryArray::begin() + Returns an \link KDLockedSharedMemoryArray::iterator iterator\endlink pointing to the first item of the array. + \since_f 2.2 +*/ + +/*! + \fn KDLockedSharedMemoryArray::const_iterator KDLockedSharedMemoryArray::begin() const + \overload + \since_f 2.2 +*/ + +/*! + \fn KDLockedSharedMemoryArray::iterator KDLockedSharedMemoryArray::end() + Returns an \link KDLockedSharedMemoryArray::iterator iterator\endlink pointing to the item after the last item of the array. + \since_f 2.2 +*/ + +/*! + \fn KDLockedSharedMemoryArray::const_iterator KDLockedSharedMemoryArray::end() const + \overload + \since_f 2.2 +*/ + +/*! + \fn KDLockedSharedMemoryArray::reverse_iterator KDLockedSharedMemoryArray::rbegin() + Returns an \link KDLockedSharedMemoryArray::reverse_iterator reverse_iterator\endlink pointing to the item after the last item of the array. + \since_f 2.2 +*/ + +/*! + \fn KDLockedSharedMemoryArray::const_reverse_iterator KDLockedSharedMemoryArray::rbegin() const + \overload + \since_f 2.2 +*/ + +/*! + \fn KDLockedSharedMemoryArray::reverse_iterator KDLockedSharedMemoryArray::rend() + Returns an \link KDLockedSharedMemoryArray::reverse_iterator reverse_iterator\endlink pointing to the first item of the array. + \since_f 2.2 +*/ + +/*! + \fn KDLockedSharedMemoryArray::const_reverse_iterator KDLockedSharedMemoryArray::rend() const + \overload + \since_f 2.2 +*/ + +/*! + \fn KDLockedSharedMemoryArray::size_type KDLockedSharedMemoryArray::size() const + 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 +*/ + +/*! + \fn T& KDLockedSharedMemoryArray::operator[]( difference_type n ) + Array access operator. Returns a reference to the item at index position \a n. +*/ + +/*! + \fn const T& KDLockedSharedMemoryArray::operator[]( difference_type n ) const + \overload +*/ + +/*! + \fn T& KDLockedSharedMemoryArray::front() + Returns a reference to the first item in the array. This is the same as operator[](0). +*/ + +/*! + \fn const T& KDLockedSharedMemoryArray::front() const + \overload +*/ + +/*! + \fn T& KDLockedSharedMemoryArray::back() + Returns a reference to the last item in the array. This is the same as operator[](size()-1). + \since_f 2.2 +*/ + +/*! + \fn const T& KDLockedSharedMemoryArray::back() const + \overload + \since_f 2.2 +*/ + + +#ifdef eKDTOOLSCORE_UNITTESTS + +#include + +#include +#include + +namespace +{ + struct TestStruct + { + TestStruct( uint nn = 0 ) + : n( nn ), + f( 0.0 ), + c( '\0' ), + b( false ) + { + } + uint n; + double f; + char c; + bool b; + }; + + bool operator==( const TestStruct& lhs, const TestStruct& rhs ) + { + return lhs.n == rhs.n && lhs.f == rhs.f && lhs.c == rhs.c && lhs.b == rhs.b; + } + + class TestThread : public QThread + { + public: + TestThread( const QString& key ) + : mem( key ) + { + mem.attach(); + } + + void run() + { + while( true ) + { + msleep( 100 ); + kdtools::KDLockedSharedMemoryPointer< TestStruct > p( &mem ); + if( !p->b ) + continue; + + p->n = 5; + p->f = 3.14; + p->c = 'A'; + p->b = false; + return; + } + } + + QSharedMemory mem; + }; + + bool isConst( TestStruct* ) + { + return false; + } + + bool isConst( const TestStruct* ) + { + return true; + } +} + + +KDAB_UNITTEST_SIMPLE( KDLockedSharedMemoryPointer, "kdcoretools" ) { + + const QString key = QUuid::createUuid(); + QSharedMemory mem( key ); + const bool created = mem.create( sizeof( TestStruct ) ); + assertTrue( created ); + if ( !created ) + return; // don't execute tests if shm coulnd't be created + + // On Windows, shared mem is only available in increments of page + // size (4k), so don't fail if the segment is larger: + const unsigned long mem_size = mem.size(); + assertGreaterOrEqual( mem_size, sizeof( TestStruct ) ); + + { + kdtools::KDLockedSharedMemoryPointer< TestStruct > p( &mem ); + assertTrue( p ); + *p = TestStruct(); + assertEqual( p->n, 0u ); + assertEqual( p->f, 0.0 ); + assertEqual( p->c, '\0' ); + assertFalse( p->b ); + } + + { + TestThread thread( key ); + assertEqual( thread.mem.key().toStdString(), key.toStdString() ); + assertEqual( static_cast< unsigned long >( thread.mem.size() ), mem_size ); + thread.start(); + + assertTrue( thread.isRunning() ); + thread.wait( 2000 ); + assertTrue( thread.isRunning() ); + + { + kdtools::KDLockedSharedMemoryPointer< TestStruct > p( &mem ); + p->b = true; + } + + thread.wait( 2000 ); + assertFalse( thread.isRunning() ); + } + + { + kdtools::KDLockedSharedMemoryPointer< TestStruct > p( &mem ); + assertEqual( p->n, 5u ); + assertEqual( p->f, 3.14 ); + assertEqual( p->c, 'A' ); + assertFalse( p->b ); + } + + { + kdtools::KDLockedSharedMemoryPointer< TestStruct > p( mem ); + assertEqual( mem.data(), p.get() ); + assertEqual( p.get(), p.operator->() ); + assertEqual( p.get(), &(*p) ); + assertEqual( p.get(), p.data() ); + assertFalse( isConst( p.get() ) ); + } + + { + const kdtools::KDLockedSharedMemoryPointer< TestStruct > p( &mem ); + assertEqual( mem.data(), p.get() ); + assertEqual( p.get(), p.operator->() ); + assertEqual( p.get(), &(*p) ); + assertEqual( p.get(), p.data() ); + assertTrue( isConst( p.get() ) ); + } + + { + QSharedMemory mem2( key + key ); + const bool created2 = mem2.create( 16 * sizeof( TestStruct ) ); + assertTrue( created2 ); + if ( !created2 ) + return; // don't execute tests if shm coulnd't be created + + kdtools::KDLockedSharedMemoryArray a( mem2 ); + assertTrue( a ); + assertEqual( a.get(), mem2.data() ); + assertEqual( &a[0], a.get() ); + + a[1] = a[0]; + assertTrue( a[0] == a[1] ); + + TestStruct ts; + ts.n = 5; + ts.f = 3.14; + a[0] = ts; + assertFalse( a[0] == a[1] ); + assertEqual( a.front().n, ts.n ); + assertEqual( a[0].f, ts.f ); + a[0].n = 10; + assertEqual( a.front().n, 10u ); + ts = a[0]; + assertEqual( ts.n, 10u ); + + std::vector< TestStruct > v; + for( uint i = 0; i < a.size(); ++i ) + v.push_back( TestStruct( i ) ); + + std::copy( v.begin(), v.end(), a.begin() ); + for( uint i = 0; i < a.size(); ++i ) + 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 ); + assertEqual( a.front().n, a.size() - 1 ); + assertEqual( a.back().n, 0u ); + } + +} +#endif // KDTOOLSCORE_UNITTESTS +#endif // QT_NO_SHAREDMEMORY +#endif // QT_VERSION >= 0x040400 || defined( DOXYGEN_RUN ) diff --git a/src/libtomahawk/kdsingleapplicationguard/kdlockedsharedmemorypointer.h b/src/libtomahawk/kdsingleapplicationguard/kdlockedsharedmemorypointer.h new file mode 100644 index 000000000..df0ea4998 --- /dev/null +++ b/src/libtomahawk/kdsingleapplicationguard/kdlockedsharedmemorypointer.h @@ -0,0 +1,115 @@ +#ifndef __KDTOOLS__CORE__KDLOCKEDSHAREDMEMORYPOINTER_H__ +#define __KDTOOLS__CORE__KDLOCKEDSHAREDMEMORYPOINTER_H__ + +#include + +#if QT_VERSION >= 0x040400 || defined( DOXYGEN_RUN ) +#ifndef QT_NO_SHAREDMEMORY + +#include "kdsharedmemorylocker.h" +#include + +#include + +#ifndef DOXYGEN_RUN +namespace kdtools { +#endif + +class KDLockedSharedMemoryPointerBase { +protected: + explicit KDLockedSharedMemoryPointerBase( QSharedMemory * mem ); + explicit KDLockedSharedMemoryPointerBase( QSharedMemory & mem ); + ~KDLockedSharedMemoryPointerBase(); + + // PENDING(marc) do we really want const propagation here? I + // usually declare all my RAII objects const... + void * get(); + const void * get() const; + + KDAB_IMPLEMENT_SAFE_BOOL_OPERATOR( get() ) + + size_t byteSize() const; + +private: + KDSharedMemoryLocker locker; + QSharedMemory * const mem; +}; + +template< typename T> +class MAKEINCLUDES_EXPORT KDLockedSharedMemoryPointer : KDLockedSharedMemoryPointerBase { + KDAB_DISABLE_COPY( KDLockedSharedMemoryPointer ); +public: + explicit KDLockedSharedMemoryPointer( QSharedMemory * m ) + : KDLockedSharedMemoryPointerBase( m ) {} + explicit KDLockedSharedMemoryPointer( QSharedMemory & m ) + : KDLockedSharedMemoryPointerBase( m ) {} + + T * get() { return static_cast( KDLockedSharedMemoryPointerBase::get() ); } + const T * get() const { return static_cast( KDLockedSharedMemoryPointerBase::get() ); } + + T * data() { return static_cast( get() ); } + const T * data() const { return static_cast( get() ); } + + T & operator*() { assert( get() ); return *get(); } + const T & operator*() const { assert( get() ); return *get(); } + + T * operator->() { return get(); } + const T * operator->() const { return get(); } + + KDAB_USING_SAFE_BOOL_OPERATOR( KDLockedSharedMemoryPointerBase ) +}; + +template +class MAKEINCLUDES_EXPORT KDLockedSharedMemoryArray : KDLockedSharedMemoryPointerBase { + KDAB_DISABLE_COPY( KDLockedSharedMemoryArray ); +public: + explicit KDLockedSharedMemoryArray( QSharedMemory * m ) + : KDLockedSharedMemoryPointerBase( m ) {} + explicit KDLockedSharedMemoryArray( QSharedMemory & m ) + : KDLockedSharedMemoryPointerBase( m ) {} + + typedef std::size_t size_type; + typedef std::ptrdiff_t difference_type; + typedef T* iterator; + typedef const T* const_iterator; + typedef std::reverse_iterator< const_iterator > const_reverse_iterator; + typedef std::reverse_iterator< iterator > reverse_iterator; + + iterator begin() { return get(); } + const_iterator begin() const { return get(); } + + iterator end() { return begin() + size(); } + const_iterator end() const { return begin() + size(); } + + reverse_iterator rbegin() { return reverse_iterator( end() ); } + const_reverse_iterator rbegin() const { return reverse_iterator( end() ); } + + reverse_iterator rend() { return reverse_iterator( begin() ); } + const_reverse_iterator rend() const { return const_reverse_iterator( begin() ); } + + size_type size() const { return byteSize() / sizeof( T ); } + + T * get() { return static_cast( KDLockedSharedMemoryPointerBase::get() ); } + const T * get() const { return static_cast( KDLockedSharedMemoryPointerBase::get() ); } + + T & operator[]( difference_type n ) { assert( get() ); return *(get()+n); } + const T & operator[]( difference_type n ) const { assert( get() ); return *(get()+n); } + + T & front() { assert( get() ); return *get(); } + const T & front() const { assert( get() ); return *get(); } + + T & back() { assert( get() ); return *( get() + size() - 1 ); } + const T & back() const { assert( get() ); return *( get() + size() - 1 ); } + + KDAB_USING_SAFE_BOOL_OPERATOR( KDLockedSharedMemoryPointerBase ) +}; + +#ifndef DOXYGEN_RUN +} +#endif + +#endif /* QT_NO_SHAREDMEMORY */ + +#endif /* QT_VERSION >= 0x040400 || defined( DOXYGEN_RUN ) */ + +#endif /* __KDTOOLS__CORE__KDLOCKEDSHAREDMEMORYPOINTER_H__ */ diff --git a/src/libtomahawk/kdsingleapplicationguard/kdsharedmemorylocker.cpp b/src/libtomahawk/kdsingleapplicationguard/kdsharedmemorylocker.cpp new file mode 100644 index 000000000..0c99b8fff --- /dev/null +++ b/src/libtomahawk/kdsingleapplicationguard/kdsharedmemorylocker.cpp @@ -0,0 +1,40 @@ +#include "kdsharedmemorylocker.h" + +#if QT_VERSION >= 0x040400 || defined( DOXYGEN_RUN ) + +#include + +using namespace kdtools; + +/*! + \class KDSharedMemoryLocker + \ingroup raii core + \brief Exception-safe and convenient wrapper around QSharedMemory::lock() +*/ + +/** + * Constructor. Locks the shared memory segment \a mem. + * If another process has locking the segment, this constructor blocks + * until the lock is released. The memory segments needs to be properly created or attached. + */ +KDSharedMemoryLocker::KDSharedMemoryLocker( QSharedMemory* mem ) + : mem( mem ) +{ + mem->lock(); +} + +/** + * Destructor. Unlocks the shared memory segment associated with this + * KDSharedMemoryLocker. + */ +KDSharedMemoryLocker::~KDSharedMemoryLocker() +{ + mem->unlock(); +} + +#ifdef KDAB_EVAL +#include KDAB_EVAL +static const EvalDialogChecker evalChecker( "KD Tools", false ); +#endif + +#endif diff --git a/src/libtomahawk/kdsingleapplicationguard/kdsharedmemorylocker.h b/src/libtomahawk/kdsingleapplicationguard/kdsharedmemorylocker.h new file mode 100644 index 000000000..7ae83e771 --- /dev/null +++ b/src/libtomahawk/kdsingleapplicationguard/kdsharedmemorylocker.h @@ -0,0 +1,36 @@ +#ifndef __KDTOOLS__CORE__KDSHAREDMEMORYLOCKER_H +#define __KDTOOLS__CORE__KDSHAREDMEMORYLOCKER_H + +#include "kdtoolsglobal.h" + +#if QT_VERSION < 0x040400 && !defined( DOXYGEN_RUN ) +#ifdef Q_CC_GNU +#warning "Can't use KDTools KDSharedMemoryLocker with Qt versions prior to 4.4" +#endif +#else + +class QSharedMemory; + +#ifndef DOXYGEN_RUN +namespace kdtools +{ +#endif + +class KDTOOLSCORE_EXPORT KDSharedMemoryLocker +{ + Q_DISABLE_COPY( KDSharedMemoryLocker ) +public: + KDSharedMemoryLocker( QSharedMemory* mem ); + ~KDSharedMemoryLocker(); + +private: + QSharedMemory* const mem; +}; + +#ifndef DOXYGEN_RUN +} +#endif + +#endif + +#endif diff --git a/src/libtomahawk/kdsingleapplicationguard/kdsingleapplicationguard.cpp b/src/libtomahawk/kdsingleapplicationguard/kdsingleapplicationguard.cpp new file mode 100644 index 000000000..6ccfe4e39 --- /dev/null +++ b/src/libtomahawk/kdsingleapplicationguard/kdsingleapplicationguard.cpp @@ -0,0 +1,627 @@ +#include "kdsingleapplicationguard.h" + +#ifndef KDSINGLEAPPLICATIONGUARD_NUMBER_OF_PROCESSES +#define KDSINGLEAPPLICATIONGUARD_NUMBER_OF_PROCESSES 128 +#endif + +#ifndef KDSINGLEAPPLICATIONGUARD_MAX_COMMAND_LINE +#define KDSINGLEAPPLICATIONGUARD_MAX_COMMAND_LINE 1024 +#endif + + + +KDSingleApplicationGuard::Instance::Instance( const QStringList& args, qint64 p ) +: arguments( args ), +pid( p ) +{ +} + +#if QT_VERSION < 0x040400 + +class KDSingleApplicationGuard::Private +{ +}; + +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; + +/*! + * \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 + */ + +/*! + * \fn void KDSingleApplicationGuard::instanceStarted() + * This signal is emitted by the primary instance when ever one other + * instance was started. + */ + +/*! + * \fn void KDSingleApplicationGuard::instanceExited() + * This signal is emitted by the primary instance when ever one other + * instance was 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. + */ + +enum Command +{ + NoCommand = 0x00, + ExitedInstance = 0x01, + NewInstance = 0x02, + FreeInstance = 0x04, + ShutDownCommand = 0x08, + KillCommand = 0x10, + BecomePrimaryCommand = 0x20 +}; + +Q_DECLARE_FLAGS( Commands, Command ) +Q_DECLARE_OPERATORS_FOR_FLAGS( Commands ) + +struct ProcessInfo +{ + explicit ProcessInfo( Command c = FreeInstance, const QStringList& arguments = QStringList(), qint64 p = -1 ) + : command( c ), + pid( p ) + { + 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 + } + } + + QStringList arguments() const + { + QStringList result; + + 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; +}; + +bool operator==( const ProcessInfo& lhs, const ProcessInfo& rhs ) +{ + return lhs.command == rhs.command && + ::memcmp( lhs.commandline, rhs.commandline, KDSINGLEAPPLICATIONGUARD_MAX_COMMAND_LINE ) == 0; +} + +bool operator!=( const ProcessInfo& lhs, const ProcessInfo& rhs ) +{ + return !operator==( lhs, rhs ); +} + +/*! + * This struct contains information about the managed process system. + * \internal + */ +struct InstanceRegister +{ + InstanceRegister( KDSingleApplicationGuard::Policy policy = KDSingleApplicationGuard::NoPolicy ) + : policy( policy ) + { + std::fill_n( info, KDSINGLEAPPLICATIONGUARD_NUMBER_OF_PROCESSES, ProcessInfo() ); + ::memcpy( magicCookie, "kdsingleapp", 12 ); + } + + /*! + * Returns wheter 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 ]; +}; + +bool operator==( const InstanceRegister& lhs, const InstanceRegister& rhs ) +{ + if( lhs.policy != rhs.policy ) + return false; + + for( int i = 0; i < KDSINGLEAPPLICATIONGUARD_NUMBER_OF_PROCESSES; ++i ) + if( lhs.info[ i ] != rhs.info[ i ] ) + return false; + + return true; +} + +/*! + * \internal + */ +class KDSingleApplicationGuard::Private +{ +public: + Private( KDSingleApplicationGuard* qq ) + : q( qq ), + id( -1 ) + { + if( primaryInstance == 0 ) + primaryInstance = q; + } + + ~Private() + { + if( primaryInstance == q ) + primaryInstance = 0; + } + + void 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; 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 :-( + } + } + + static KDSingleApplicationGuard* primaryInstance; + +private: + KDSingleApplicationGuard* const q; + +public: + Policy policy; + QSharedMemory mem; + int id; +}; + +KDSingleApplicationGuard* KDSingleApplicationGuard::Private::primaryInstance = 0; + +#ifndef Q_WS_WIN +void SIGINT_handler( int sig ) +{ + if( sig == SIGINT && KDSingleApplicationGuard::Private::primaryInstance != 0 ) + KDSingleApplicationGuard::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 ) ) +{ + 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( 250 ); +} + +/*! + * 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() +{ + if( d->id == -1 ) + return; + + d->shutdownInstance(); +} + +/*! + * \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(). + */ +bool KDSingleApplicationGuard::isPrimaryInstance() const +{ + return d->id == 0; +} + +/*! + * \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 +{ + return d->policy; +} + +void KDSingleApplicationGuard::setPolicy( Policy policy ) +{ + Q_ASSERT( isPrimaryInstance() ); + if( d->policy == policy ) + return; + + d->policy = policy; + emit policyChanged(); + KDLockedSharedMemoryPointer< InstanceRegister > instances( &d->mem ); + instances->policy = policy; +} + +/*! + * Returns a list of all currently running instances. + */ +QList< KDSingleApplicationGuard::Instance > KDSingleApplicationGuard::instances() const +{ + QList< Instance > result; + const KDLockedSharedMemoryPointer< InstanceRegister > instances( const_cast< QSharedMemory* >( &d->mem ) ); + for( int i = 0; i < KDSINGLEAPPLICATIONGUARD_NUMBER_OF_PROCESSES; ++i ) + { + const ProcessInfo& info = instances->info[ i ]; + if( ( info.command & ( FreeInstance | ExitedInstance ) ) == 0 ) + result.push_back( Instance( info.arguments(), 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(). + */ +void KDSingleApplicationGuard::shutdownOtherInstances() +{ + Q_ASSERT( isPrimaryInstance() ); + KDLockedSharedMemoryPointer< InstanceRegister > instances( &d->mem ); + for( int i = 1; i < KDSINGLEAPPLICATIONGUARD_NUMBER_OF_PROCESSES; ++i ) + { + if( ( instances->info[ i ].command & ( FreeInstance | ExitedInstance ) ) == 0 ) + instances->info[ i ].command = ShutDownCommand; + } +} + +/*! + * Kills all other instances. This can only be called from the + * the primary instance. + * Killing is done via exit(1) + */ +void KDSingleApplicationGuard::killOtherInstances() +{ + Q_ASSERT( isPrimaryInstance() ); + KDLockedSharedMemoryPointer< InstanceRegister > instances( &d->mem ); + for( int i = 1; i < KDSINGLEAPPLICATIONGUARD_NUMBER_OF_PROCESSES; ++i ) + { + if( ( instances->info[ i ].command & ( FreeInstance | ExitedInstance ) ) == 0 ) + instances->info[ i ].command = KillCommand; + } +} + +/*! + * \reimp + */ +void KDSingleApplicationGuard::timerEvent( QTimerEvent* event ) +{ + Q_UNUSED( event ) + + if( isPrimaryInstance() ) + { + // only the primary instance will get notified about new instances + QList< Instance > exitedInstances; + QList< Instance > startedInstances; + + { + KDLockedSharedMemoryPointer< InstanceRegister > instances( &d->mem ); + + for( int i = 1; i < KDSINGLEAPPLICATIONGUARD_NUMBER_OF_PROCESSES; ++i ) + { + ProcessInfo& info = instances->info[ i ]; + if( info.command & NewInstance ) + { + startedInstances.push_back( Instance( info.arguments(), info.pid ) ); + info.command &= ~NewInstance; // clear NewInstance flag + } + else if( info.command & ExitedInstance ) + { + exitedInstances.push_back( Instance( info.arguments(), 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 ); + } + else + { + // do we have a command? + bool killOurSelf = false; + bool shutDownOurSelf = false; + bool policyDidChange = false; + + { + KDLockedSharedMemoryPointer< InstanceRegister > instances( &d->mem ); + + policyDidChange = instances->policy != d->policy; + d->policy = instances->policy; + + if( instances->info[ d->id ].command & BecomePrimaryCommand ) + { + // 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(); + } + + 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( 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 + } + } + + if( killOurSelf ) // kill our self takes precedence + exit( 1 ); + else if( shutDownOurSelf ) + qApp->quit(); + else if( policyDidChange ) + emit policyChanged(); + } +} + +#ifdef KDTOOLSCORE_UNITTESTS + +#include + +#include + +#include +#include +#include + +Q_DECLARE_METATYPE( KDSingleApplicationGuard::Instance ); + +static void wait( int msec ) +{ + QTime t; + t.start(); + while( t.elapsed() < msec ) + { + qApp->processEvents( QEventLoop::WaitForMoreEvents, msec - t.elapsed() ); + } +} + +static std::ostream& operator<<( std::ostream& stream, const QStringList& list ) +{ + stream << "QStringList("; + for( QStringList::const_iterator it = list.begin(); it != list.end(); ++it ) + { + stream << " " << it->toLocal8Bit().data(); + if( it + 1 != list.end() ) + stream << ","; + } + stream << " )"; + return stream; +} + + +KDAB_UNITTEST_SIMPLE( KDSingleApplicationGuard, "kdcoretools" ) { + + // set it to an unique name + qApp->setApplicationName( QUuid::createUuid().toString() ); + + qRegisterMetaType< KDSingleApplicationGuard::Instance >(); + + KDSingleApplicationGuard* guard3 = 0; + QSignalSpy* spy3 = 0; + + { + KDSingleApplicationGuard guard1( qApp ); + assertEqual( guard1.policy(), KDSingleApplicationGuard::AutoKillOtherInstances ); + assertEqual( guard1.instances().count(), 1 ); + assertTrue( guard1.isPrimaryInstance() ); + + guard1.setPolicy( KDSingleApplicationGuard::NoPolicy ); + assertEqual( guard1.policy(), KDSingleApplicationGuard::NoPolicy ); + + QSignalSpy spy1( &guard1, SIGNAL( instanceStarted( KDSingleApplicationGuard::Instance ) ) ); + + KDSingleApplicationGuard guard2( qApp ); + assertEqual( guard1.instances().count(), 2 ); + assertEqual( guard2.instances().count(), 2 ); + assertEqual( guard2.policy(), KDSingleApplicationGuard::NoPolicy ); + assertFalse( guard2.isPrimaryInstance() ); + + wait( 1000 ); + + assertEqual( spy1.count(), 1 ); + guard3 = new KDSingleApplicationGuard( qApp ); + spy3 = new QSignalSpy( guard3, SIGNAL( becamePrimaryInstance() ) ); + assertFalse( guard3->isPrimaryInstance() ); + } + + wait( 1000 ); + assertEqual( spy3->count(), 1 ); + assertEqual( guard3->instances().count(), 1 ); + assertTrue( guard3->isPrimaryInstance() ); + + assertEqual( guard3->instances().first().arguments, qApp->arguments() ); + + QSignalSpy spyStarted( guard3, SIGNAL( instanceStarted( KDSingleApplicationGuard::Instance ) ) ); + QSignalSpy spyExited( guard3, SIGNAL( instanceExited( KDSingleApplicationGuard::Instance ) ) ); + + { + KDSingleApplicationGuard guard1( qApp ); + KDSingleApplicationGuard guard2( qApp ); + + wait( 1000 ); + + assertEqual( spyStarted.count(), 2 ); + } + + wait( 1000 ); + assertEqual( spyExited.count(), 2 ); + + delete spy3; + delete guard3; +} + +#endif // KDTOOLSCORE_UNITTESTS + +#endif // QT_VERSION < 0x040400 diff --git a/src/libtomahawk/kdsingleapplicationguard/kdsingleapplicationguard.h b/src/libtomahawk/kdsingleapplicationguard/kdsingleapplicationguard.h new file mode 100644 index 000000000..14706e4d0 --- /dev/null +++ b/src/libtomahawk/kdsingleapplicationguard/kdsingleapplicationguard.h @@ -0,0 +1,75 @@ +#ifndef __KDTOOLSCORE_KDSINGLEAPPLICATIONGUARD_H__ +#define __KDTOOLSCORE_KDSINGLEAPPLICATIONGUARD_H__ + +#include +#include + +#include "pimpl_ptr.h" +#include "dllmacro.h" + +class QCoreApplication; + +#ifndef Q_WS_WIN +void SIGINT_handler( int sig ); +#endif + +class DLLEXPORT KDSingleApplicationGuard : public QObject +{ + Q_OBJECT +#ifndef Q_WS_WIN + friend void ::SIGINT_handler( int ); +#endif + +public: + enum Policy + { + NoPolicy = 0, + 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 ); + ~KDSingleApplicationGuard(); + + bool isPrimaryInstance() const; + + Policy policy() const; + void setPolicy( Policy policy ); + + struct Instance + { + Instance( const QStringList& arguments = QStringList(), qint64 pid = -1 ); + + QStringList arguments; + qint64 pid; + }; + + QList< Instance > instances() const; + +Q_SIGNALS: + void instanceStarted( KDSingleApplicationGuard::Instance instance ); + void instanceExited( KDSingleApplicationGuard::Instance instance ); + void becamePrimaryInstance(); + void policyChanged(); + +public Q_SLOTS: + void shutdownOtherInstances(); + void killOtherInstances(); + +protected: + void timerEvent( QTimerEvent* event ); + +private: + 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 + +#endif diff --git a/src/libtomahawk/kdsingleapplicationguard/kdtoolsglobal.cpp b/src/libtomahawk/kdsingleapplicationguard/kdtoolsglobal.cpp new file mode 100644 index 000000000..5997fe64c --- /dev/null +++ b/src/libtomahawk/kdsingleapplicationguard/kdtoolsglobal.cpp @@ -0,0 +1,32 @@ +#include "kdtoolsglobal.h" + +#include + +#include + +namespace { + struct Version { + unsigned char v[3]; + }; + + static inline bool operator<( const Version & lhs, const Version & rhs ) { + return std::lexicographical_compare( lhs.v, lhs.v + 3, rhs.v, rhs.v + 3 ); + } + static inline bool operator==( const Version & lhs, const Version & rhs ) { + return std::equal( lhs.v, lhs.v + 3, rhs.v ); + } + KDTOOLS_MAKE_RELATION_OPERATORS( Version, static inline ) +} + +static Version kdParseQtVersion( const char * const version ) { + if ( !version || qstrlen( version ) < 5 || version[1] != '.' || version[3] != '.' || version[5] != 0 && version[5] != '.' && version[5] != '-' ) + return Version(); // parse error + const Version result = { { version[0] - '0', version[2] - '0', version[4] - '0' } }; + return result; +} + +bool _kdCheckQtVersion_impl( int major, int minor, int patchlevel ) { + static const Version actual = kdParseQtVersion( qVersion() ); // do this only once each run... + const Version requested = { { major, minor, patchlevel } }; + return actual >= requested; +} diff --git a/src/libtomahawk/kdsingleapplicationguard/kdtoolsglobal.h b/src/libtomahawk/kdsingleapplicationguard/kdtoolsglobal.h new file mode 100644 index 000000000..4e8d0d673 --- /dev/null +++ b/src/libtomahawk/kdsingleapplicationguard/kdtoolsglobal.h @@ -0,0 +1,113 @@ +#ifndef __KDTOOLS_KDTOOLSGLOBAL_H__ +#define __KDTOOLS_KDTOOLSGLOBAL_H__ + +#include + +#define KDAB_DISABLE_COPY( x ) private: x( const x & ); x & operator=( const x & ) + +#ifdef KDTOOLS_SHARED +# ifdef BUILD_SHARED_KDTOOLSCORE +# define KDTOOLSCORE_EXPORT Q_DECL_EXPORT +# else +# define KDTOOLSCORE_EXPORT Q_DECL_IMPORT +# endif +# ifdef BUILD_SHARED_KDTOOLSGUI +# define KDTOOLSGUI_EXPORT Q_DECL_EXPORT +# else +# define KDTOOLSGUI_EXPORT Q_DECL_IMPORT +# endif +# ifdef BUILD_SHARED_KDTOOLSXML +# define KDTOOLSXML_EXPORT Q_DECL_EXPORT +# else +# define KDTOOLSXML_EXPORT Q_DECL_IMPORT +# endif +# ifdef BUILD_SHARED_KDUPDATER +# define KDTOOLS_UPDATER_EXPORT Q_DECL_EXPORT +# else +# define KDTOOLS_UPDATER_EXPORT Q_DECL_IMPORT +# endif +#else // KDTOOLS_SHARED +# define KDTOOLSCORE_EXPORT +# define KDTOOLSGUI_EXPORT +# define KDTOOLSXML_EXPORT +# define KDTOOLS_UPDATER_EXPORT +#endif // KDTOOLS_SHARED + +#define MAKEINCLUDES_EXPORT + +#define DOXYGEN_PROPERTY( x ) +#ifdef DOXYGEN_RUN +# define KDAB_IMPLEMENT_SAFE_BOOL_OPERATOR( func ) operator unspecified_bool_type() const { return func; } +# define KDAB_USING_SAFE_BOOL_OPERATOR( Class ) operator unspecified_bool_type() const; +#else +# define KDAB_IMPLEMENT_SAFE_BOOL_OPERATOR( func ) \ + private: struct __safe_bool_dummy__ { void nonnull() {} }; \ + typedef void ( __safe_bool_dummy__::*unspecified_bool_type )(); \ + public: \ + operator unspecified_bool_type() const { \ + return ( func ) ? &__safe_bool_dummy__::nonnull : 0 ; \ + } +#define KDAB_USING_SAFE_BOOL_OPERATOR( Class ) \ + using Class::operator Class::unspecified_bool_type; +#endif + +#define KDTOOLS_MAKE_RELATION_OPERATORS( Class, linkage ) \ + linkage bool operator>( const Class & lhs, const Class & rhs ) { \ + return operator<( rhs, lhs ); \ + } \ + linkage bool operator!=( const Class & lhs, const Class & rhs ) { \ + return !operator==( lhs, rhs ); \ + } \ + linkage bool operator<=( const Class & lhs, const Class & rhs ) { \ + return !operator>( lhs, rhs ); \ + } \ + linkage bool operator>=( const Class & lhs, const Class & rhs ) { \ + return !operator<( lhs, rhs ); \ + } + +template +inline T & __kdtools__dereference_for_methodcall( T & o ) { + return o; +} + +template +inline T & __kdtools__dereference_for_methodcall( T * o ) { + return *o; +} + +#define KDAB_SET_OBJECT_NAME( x ) __kdtools__dereference_for_methodcall( x ).setObjectName( QLatin1String( #x ) ) + +KDTOOLSCORE_EXPORT bool _kdCheckQtVersion_impl( int major, int minor=0, int patchlevel=0 ); +static inline bool kdCheckQtVersion( unsigned int major, unsigned int minor=0, unsigned int patchlevel=0 ) { + return (major<<16|minor<<8|patchlevel) <= static_cast(QT_VERSION) + || _kdCheckQtVersion_impl( major, minor, patchlevel ); +} + +#define KDTOOLS_DECLARE_PRIVATE_BASE( Class ) \ +protected: \ + class Private; \ + Private * d_func() { return _d; } \ + const Private * d_func() const { return _d; } \ + Class( Private * _d_, bool b ) : _d( _d_ ) { init(b); } \ +private: \ + void init(bool); \ +private: \ + Private * _d + +#define KDTOOLS_DECLARE_PRIVATE_DERIVED( Class, Base ) \ +protected: \ + class Private; \ + Private * d_func() { \ + return reinterpret_cast( Base::d_func() ); \ + } \ + const Private * d_func() const { \ + return reinterpret_cast( Base::d_func() ); \ + } \ + Class( Private * _d_, bool b ) \ + : Base( reinterpret_cast(_d_), b ) { init(b); } \ +private: \ + void init(bool) + + +#endif /* __KDTOOLS_KDTOOLSGLOBAL_H__ */ + diff --git a/src/libtomahawk/kdsingleapplicationguard/license-gpl b/src/libtomahawk/kdsingleapplicationguard/license-gpl new file mode 100644 index 000000000..332ed973c --- /dev/null +++ b/src/libtomahawk/kdsingleapplicationguard/license-gpl @@ -0,0 +1,349 @@ + + The KD Tools Library is Copyright (C) 2001-2003 Klarälvdalens Datakonsult AB. + + You may use, distribute and copy the KD Tools Library under the terms of + GNU General Public License version 2, which is displayed below. + +------------------------------------------------------------------------- + + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 675 Mass Ave, Cambridge, MA 02139, USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + Appendix: How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) 19yy + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) 19yy name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. + +------------------------------------------------------------------------- diff --git a/src/libtomahawk/kdsingleapplicationguard/pimpl_ptr.cpp b/src/libtomahawk/kdsingleapplicationguard/pimpl_ptr.cpp new file mode 100644 index 000000000..3045ebc2a --- /dev/null +++ b/src/libtomahawk/kdsingleapplicationguard/pimpl_ptr.cpp @@ -0,0 +1,203 @@ +#include "pimpl_ptr.h" + +/*! + \class pimpl_ptr: + \ingroup core smartptr + \brief Owning pointer for private implementations + \since_c 2.1 + + (The exception safety of this class has not been evaluated yet.) + + pimpl_ptr is a smart immutable pointer, which owns the contained object. Unlike other smart pointers, + it creates a standard constructed object when instanciated via the + \link pimpl_ptr() standard constructor\endlink. + Additionally, pimpl_ptr respects constness of the pointer object and returns \c const \c T* for + a const pimpl_ptr object. + + The content of a pimpl_ptr cannot be changed during it's lifetime. + + \section general-use General Use + + The general use case of pimpl_ptr is the "Pimpl Idiom", i.e. hiding the private implementation of a class + from the user's compiler which see \c MyClass as + + \code + class MyClass + { + public: + MyClass(); + ~MyClass(); + + // public class API + int value() const; + + private: + class Private; // defined later + kdtools::pimpl_ptr< Private > d; + }; + \endcode + + but not the private parts of it. These can only be seen (and accessed) by the code knowing \c MyClass::Private: + + \code + class MyClass::Private + { + public: + int value; + }; + + MyClass::MyClass() + { + // d was automatically filled with new Private + d->value = 42; + } + + MyClass::~MyClass() + { + // the content of d gets deleted automatically + } + + int MyClass::value() const + { + // access the private part: + // since MyClass::value() is const, the returned pointee is const, too + return d->value; + } + \endcode + +*/ + +/*! + \fn pimpl_ptr::pimpl_ptr() + + Default constructor. Constructs a pimpl_tr that contains (owns) a standard constructed + instance of \c T. + + \post \c *this owns a new object. +*/ + +/*! + \fn pimpl_ptr::pimpl_ptr( T * t ) + + Constructor. Constructs a pimpl_ptr that contains (owns) \a t. + + \post get() == obj +*/ + +/*! + \fn pimpl_ptr::~pimpl_ptr() + + Destructor. + + \post The object previously owned by \c *this has been deleted. +*/ + +/*! + \fn const T * pimpl_ptr::get() const + + \returns a const pointer to the contained (owned) object. + \overload +*/ + +/*! + \fn T * pimpl_ptr::get() + + \returns a pointer to the contained (owned) object. +*/ + +/*! + \fn const T & pimpl_ptr::operator*() const + + Dereference operator. Returns \link get() *get()\endlink. + \overload +*/ + +/*! + \fn T & pimpl_ptr::operator*() + + Dereference operator. Returns \link get() *get()\endlink. +*/ + +/*! + \fn const T * pimpl_ptr::operator->() const + + Member-by-pointer operator. Returns get(). + \overload +*/ + +/*! + \fn T * pimpl_ptr::operator->() + + Member-by-pointer operator. Returns get(). +*/ + +#ifdef KDTOOLSCORE_UNITTESTS + +#include + +#include +#include + +namespace +{ + struct ConstTester + { + bool isConst() + { + return false; + } + + bool isConst() const + { + return true; + } + }; +} + +KDAB_UNITTEST_SIMPLE( pimpl_ptr, "kdcoretools" ) { + + { + kdtools::pimpl_ptr< QObject > p; + assertNotNull( p.get() ); + assertNull( p->parent() ); + } + + + { + QPointer< QObject > o; + { + kdtools::pimpl_ptr< QObject > qobject( new QObject ); + o = qobject.get(); + assertEqual( o, qobject.operator->() ); + assertEqual( o, &(qobject.operator*()) ); + } + assertNull( o ); + } + + { + const kdtools::pimpl_ptr< QObject > qobject( new QObject ); + const QObject* o = qobject.get(); + assertEqual( o, qobject.operator->() ); + assertEqual( o, &(qobject.operator*()) ); + } + + { + kdtools::pimpl_ptr< QObject > o1; + assertTrue( o1 ); + kdtools::pimpl_ptr< QObject > o2( 0 ); + assertFalse( o2 ); + } + + { + const kdtools::pimpl_ptr< ConstTester > o1; + kdtools::pimpl_ptr< ConstTester > o2; + assertTrue( o1->isConst() ); + assertFalse( o2->isConst() ); + assertTrue( (*o1).isConst() ); + assertFalse( (*o2).isConst() ); + assertTrue( o1.get()->isConst() ); + assertFalse( o2.get()->isConst() ); + } +} + +#endif // KDTOOLSCORE_UNITTESTS diff --git a/src/libtomahawk/kdsingleapplicationguard/pimpl_ptr.h b/src/libtomahawk/kdsingleapplicationguard/pimpl_ptr.h new file mode 100644 index 000000000..7b7f36839 --- /dev/null +++ b/src/libtomahawk/kdsingleapplicationguard/pimpl_ptr.h @@ -0,0 +1,44 @@ +#ifndef __KDTOOLSCORE__PIMPL_PTR_H__ +#define __KDTOOLSCORE__PIMPL_PTR_H__ + +#include "kdtoolsglobal.h" + +#ifndef DOXYGEN_RUN +namespace kdtools { +#endif + + template + class pimpl_ptr { + KDAB_DISABLE_COPY( pimpl_ptr ); + T * d; + public: + pimpl_ptr() : d( new T ) {} + explicit pimpl_ptr( T * t ) : d( t ) {} + ~pimpl_ptr() { delete d; d = 0; } + + T * get() { return d; } + const T * get() const { return d; } + + T * operator->() { return get(); } + const T * operator->() const { return get(); } + + T & operator*() { return *get(); } + const T & operator*() const { return *get(); } + + KDAB_IMPLEMENT_SAFE_BOOL_OPERATOR( get() ) + }; + + // these are not implemented, so's we can catch their use at + // link-time. Leaving them undeclared would open up a comparison + // via operator unspecified-bool-type(). + template + void operator==( const pimpl_ptr &, const pimpl_ptr & ); + template + void operator!=( const pimpl_ptr &, const pimpl_ptr & ); + +#ifndef DOXYGEN_RUN +} // namespace kdtools +#endif + +#endif /* __KDTOOLSCORE__PIMPL_PTR_H__ */ + diff --git a/src/libtomahawk/network/bufferiodevice.cpp b/src/libtomahawk/network/bufferiodevice.cpp index 073f7dda7..bceca0745 100644 --- a/src/libtomahawk/network/bufferiodevice.cpp +++ b/src/libtomahawk/network/bufferiodevice.cpp @@ -1,3 +1,21 @@ +/* === 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 "bufferiodevice.h" #include @@ -20,6 +38,7 @@ BufferIODevice::BufferIODevice( unsigned int size, QObject* parent ) bool BufferIODevice::open( OpenMode mode ) { + Q_UNUSED( mode ); QMutexLocker lock( &m_mut ); qDebug() << Q_FUNC_INFO; @@ -130,6 +149,8 @@ BufferIODevice::readData( char* data, qint64 maxSize ) qint64 BufferIODevice::writeData( const char* data, qint64 maxSize ) { + Q_UNUSED( data ); + Q_UNUSED( maxSize ); // call addData instead Q_ASSERT( false ); return 0; diff --git a/src/libtomahawk/network/bufferiodevice.h b/src/libtomahawk/network/bufferiodevice.h index 1474deb74..4e0244991 100644 --- a/src/libtomahawk/network/bufferiodevice.h +++ b/src/libtomahawk/network/bufferiodevice.h @@ -1,3 +1,21 @@ +/* === 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 BUFFERIODEVICE_H #define BUFFERIODEVICE_H diff --git a/src/libtomahawk/network/connection.cpp b/src/libtomahawk/network/connection.cpp index 42c78bd74..59342fba9 100644 --- a/src/libtomahawk/network/connection.cpp +++ b/src/libtomahawk/network/connection.cpp @@ -1,3 +1,21 @@ +/* === 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 "connection.h" #include @@ -44,16 +62,12 @@ Connection::Connection( Servent* parent ) Connection::~Connection() { - qDebug() << "DTOR connection (super)" << id() << thread(); + qDebug() << "DTOR connection (super)" << id() << thread() << m_sock.isNull(); if( !m_sock.isNull() ) { - qDebug() << "deleteLatering sock" << m_sock; +// qDebug() << "deleteLatering sock" << m_sock; m_sock->deleteLater(); } - else - { - qDebug() << "no valid sock to delete"; - } delete m_statstimer; } @@ -100,7 +114,7 @@ Connection::setFirstMessage( msg_ptr m ) void Connection::shutdown( bool waitUntilSentAll ) { - qDebug() << Q_FUNC_INFO << waitUntilSentAll; + qDebug() << Q_FUNC_INFO << waitUntilSentAll << id(); if ( m_do_shutdown ) { //qDebug() << id() << " already shutting down"; @@ -110,7 +124,7 @@ Connection::shutdown( bool waitUntilSentAll ) m_do_shutdown = true; if ( !waitUntilSentAll ) { - qDebug() << "Shutting down immediately " << id(); +// qDebug() << "Shutting down immediately " << id(); actualShutdown(); } else @@ -128,10 +142,9 @@ Connection::shutdown( bool waitUntilSentAll ) void Connection::actualShutdown() { - qDebug() << Q_FUNC_INFO; + qDebug() << Q_FUNC_INFO << m_actually_shutting_down << id(); if( m_actually_shutting_down ) { - qDebug() << "(already actually shutting down)"; return; } m_actually_shutting_down = true; @@ -141,7 +154,7 @@ Connection::actualShutdown() m_sock->disconnectFromHost(); } - qDebug() << "EMITTING finished()"; +// qDebug() << "EMITTING finished()"; emit finished(); } diff --git a/src/libtomahawk/network/connection.h b/src/libtomahawk/network/connection.h index 4bcefe214..95c94980f 100644 --- a/src/libtomahawk/network/connection.h +++ b/src/libtomahawk/network/connection.h @@ -1,3 +1,21 @@ +/* === 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 CONNECTION_H #define CONNECTION_H diff --git a/src/libtomahawk/network/controlconnection.cpp b/src/libtomahawk/network/controlconnection.cpp index 0973b59ed..24cc6af6d 100644 --- a/src/libtomahawk/network/controlconnection.cpp +++ b/src/libtomahawk/network/controlconnection.cpp @@ -1,6 +1,24 @@ +/* === 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 "controlconnection.h" -#include "filetransferconnection.h" +#include "streamconnection.h" #include "database/database.h" #include "database/databasecommand_collectionstats.h" #include "dbsyncconnection.h" @@ -57,6 +75,13 @@ ControlConnection::setup() { qDebug() << Q_FUNC_INFO << id() << name(); + if ( !m_source.isNull() ) + { + qDebug() << "This source seems to be online already."; + Q_ASSERT( false ); + return; + } + QString friendlyName; if ( Servent::isIPWhitelisted( m_sock->peerAddress() ) ) { @@ -67,7 +92,7 @@ ControlConnection::setup() } else friendlyName = name(); - + // setup source and remote collection for this peer m_source = SourceList::instance()->get( id(), friendlyName ); m_source->setControlConnection( this ); @@ -105,7 +130,7 @@ ControlConnection::registerSource() void ControlConnection::setupDbSyncConnection( bool ondemand ) { - qDebug() << Q_FUNC_INFO << ondemand << m_source->id() << ondemand << m_dbconnkey << m_dbsyncconn << m_registered; + qDebug() << Q_FUNC_INFO << ondemand << m_source->id() << m_dbconnkey << m_dbsyncconn << m_registered; if ( m_dbsyncconn || !m_registered ) return; @@ -165,7 +190,7 @@ ControlConnection::dbSyncConnection() if ( !m_dbsyncconn ) { setupDbSyncConnection( true ); - Q_ASSERT( m_dbsyncconn ); +// Q_ASSERT( m_dbsyncconn ); } return m_dbsyncconn; @@ -203,7 +228,8 @@ ControlConnection::handleMsg( msg_ptr msg ) { QString theirkey = m["key"].toString(); QString ourkey = m["offer"].toString(); - servent()->reverseOfferRequest( this, ourkey, theirkey ); + QString theirdbid = m["controlid"].toString(); + servent()->reverseOfferRequest( this, theirdbid, ourkey, theirkey ); } else if( m.value( "method" ).toString() == "dbsync-offer" ) { diff --git a/src/libtomahawk/network/controlconnection.h b/src/libtomahawk/network/controlconnection.h index 77261ffd3..d23c91f43 100644 --- a/src/libtomahawk/network/controlconnection.h +++ b/src/libtomahawk/network/controlconnection.h @@ -1,3 +1,21 @@ +/* === 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 . + */ + /* One ControlConnection always remains open to each peer. @@ -15,8 +33,6 @@ #include "dllmacro.h" -class FileTransferSession; - class DLLEXPORT ControlConnection : public Connection { Q_OBJECT diff --git a/src/libtomahawk/network/dbsyncconnection.cpp b/src/libtomahawk/network/dbsyncconnection.cpp index 334fe86d9..b3559088f 100644 --- a/src/libtomahawk/network/dbsyncconnection.cpp +++ b/src/libtomahawk/network/dbsyncconnection.cpp @@ -1,3 +1,21 @@ +/* === 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 . + */ + /* Database syncing using the oplog table. ======================================= @@ -57,7 +75,7 @@ DBSyncConnection::~DBSyncConnection() void DBSyncConnection::idleTimeout() { - qDebug() << Q_FUNC_INFO << "*************"; + qDebug() << Q_FUNC_INFO; shutdown( true ); } @@ -69,11 +87,6 @@ DBSyncConnection::changeState( State newstate ) m_state = newstate; qDebug() << "DBSYNC State changed from" << s << "to" << newstate; emit stateChanged( newstate, s, "" ); - - if ( newstate == SYNCED ) - { - qDebug() << "Synced :)"; - } } @@ -179,7 +192,7 @@ DBSyncConnection::handleMsg( msg_ptr msg ) msg->is( Msg::DBOP ) && msg->payload() == "ok" ) { - qDebug() << "No ops to apply, we are synced."; +// qDebug() << "No ops to apply, we are synced."; changeState( SYNCED ); // calc the collection stats, to updates the "X tracks" in the sidebar etc // this is done automatically if you run a dbcmd to add files. diff --git a/src/libtomahawk/network/dbsyncconnection.h b/src/libtomahawk/network/dbsyncconnection.h index fdb34fffa..f97c633d5 100644 --- a/src/libtomahawk/network/dbsyncconnection.h +++ b/src/libtomahawk/network/dbsyncconnection.h @@ -1,3 +1,21 @@ +/* === 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 DBSYNCCONNECTION_H #define DBSYNCCONNECTION_H diff --git a/src/libtomahawk/network/msg.h b/src/libtomahawk/network/msg.h index d91044ba4..d79fd0982 100644 --- a/src/libtomahawk/network/msg.h +++ b/src/libtomahawk/network/msg.h @@ -1,3 +1,21 @@ +/* === 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 . + */ + /* Msg is a wire msg used by p2p connections. Msgs have a 5-byte header: diff --git a/src/libtomahawk/network/msgprocessor.cpp b/src/libtomahawk/network/msgprocessor.cpp index c84107a22..85bdcd26c 100644 --- a/src/libtomahawk/network/msgprocessor.cpp +++ b/src/libtomahawk/network/msgprocessor.cpp @@ -1,3 +1,21 @@ +/* === 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 "msgprocessor.h" #include "network/servent.h" diff --git a/src/libtomahawk/network/msgprocessor.h b/src/libtomahawk/network/msgprocessor.h index 9ecf1e58d..b8dc7ef7d 100644 --- a/src/libtomahawk/network/msgprocessor.h +++ b/src/libtomahawk/network/msgprocessor.h @@ -1,3 +1,21 @@ +/* === 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 . + */ + /* MsgProcessor is a FIFO queue of msg_ptr, you .add() a msg_ptr, and it emits done(msg_ptr) for each msg, preserving the order. diff --git a/src/libtomahawk/network/portfwdthread.cpp b/src/libtomahawk/network/portfwdthread.cpp index 5b6a0a142..36cdd8088 100644 --- a/src/libtomahawk/network/portfwdthread.cpp +++ b/src/libtomahawk/network/portfwdthread.cpp @@ -1,3 +1,21 @@ +/* === 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 "portfwdthread.h" #include diff --git a/src/libtomahawk/network/portfwdthread.h b/src/libtomahawk/network/portfwdthread.h index d18c699e4..85eb98023 100644 --- a/src/libtomahawk/network/portfwdthread.h +++ b/src/libtomahawk/network/portfwdthread.h @@ -1,3 +1,21 @@ +/* === 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 PORTFWDTHREAD_H #define PORTFWDTHREAD_H diff --git a/src/libtomahawk/network/remotecollection.cpp b/src/libtomahawk/network/remotecollection.cpp index 757164456..e73966cac 100644 --- a/src/libtomahawk/network/remotecollection.cpp +++ b/src/libtomahawk/network/remotecollection.cpp @@ -1,3 +1,21 @@ +/* === 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 "remotecollection.h" using namespace Tomahawk; @@ -13,6 +31,7 @@ RemoteCollection::RemoteCollection( source_ptr source, QObject* parent ) // the database will make us emit the appropriate signals (tracksAdded etc.) void RemoteCollection::addTracks( const QList& newitems ) { + Q_UNUSED( newitems ); qDebug() << Q_FUNC_INFO; Q_ASSERT( false ); } @@ -20,6 +39,7 @@ void RemoteCollection::addTracks( const QList& newitems ) void RemoteCollection::removeTracks( const QDir& dir ) { + Q_UNUSED( dir ); qDebug() << Q_FUNC_INFO; Q_ASSERT( false ); } diff --git a/src/libtomahawk/network/remotecollection.h b/src/libtomahawk/network/remotecollection.h index 86d334f50..ef28be853 100644 --- a/src/libtomahawk/network/remotecollection.h +++ b/src/libtomahawk/network/remotecollection.h @@ -1,3 +1,21 @@ +/* === 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 REMOTECOLLECTION_H #define REMOTECOLLECTION_H diff --git a/src/libtomahawk/network/servent.cpp b/src/libtomahawk/network/servent.cpp index e85de3456..a1ddff6a2 100644 --- a/src/libtomahawk/network/servent.cpp +++ b/src/libtomahawk/network/servent.cpp @@ -1,3 +1,21 @@ +/* === 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 "servent.h" #include @@ -8,6 +26,8 @@ #include #include #include +#include +#include #include "result.h" #include "source.h" @@ -15,12 +35,13 @@ #include "connection.h" #include "controlconnection.h" #include "database/database.h" -#include "filetransferconnection.h" +#include "streamconnection.h" #include "sourcelist.h" #include "portfwdthread.h" #include "tomahawksettings.h" #include "utils/tomahawkutils.h" +#include using namespace Tomahawk; @@ -42,6 +63,8 @@ Servent::Servent( QObject* parent ) { s_instance = this; + new ACLSystem( this ); + setProxy( QNetworkProxy::NoProxy ); { @@ -74,23 +97,36 @@ bool Servent::startListening( QHostAddress ha, bool upnp, int port ) { m_port = port; - // try listening on one port higher as well, to aid debugging - // and let you run 2 instances easily - if( !listen( ha, m_port ) && !listen( ha, ++m_port ) ) + int defPort = TomahawkSettings::instance()->defaultPort(); + // Listen on both the selected port and, if not the same, the default port -- the latter sometimes necessary for zeroconf + // TODO: only listen on both when zeroconf sip is enabled + // TODO: use a real zeroconf system instead of a simple UDP broadcast? + if( !listen( ha, m_port ) ) { - qDebug() << "Failed to listen on port" << m_port; - qDebug() << "Error string is " << errorString(); - return false; + bool defPortAlso = false; + if( m_port != defPort ) + defPortAlso = listen( ha, defPort ); + if( !defPortAlso ) + { + qDebug() << "Failed to listen on both port " << m_port << " and port " << defPort; + qDebug() << "Error string is " << errorString(); + return false; + } + else + qDebug() << "Servent listening on port " << defPort << " servent thread:" << thread(); } else { - qDebug() << "Servent listening on port" << m_port << " servent thread:" << thread(); + bool defPortAlso = listen( ha, defPort ); + qDebug() << "Servent listening on port " << m_port << " servent thread:" << thread(); + if( defPortAlso ) + qDebug() << "Servent also listening on port " << defPort << " servent thread:" << thread(); } // --lanhack means to advertise your LAN IP over jabber as if it were externallyVisible qDebug() << "Address mode = " << (int)(TomahawkSettings::instance()->externalAddressMode()); qDebug() << "Static host/port preferred ? = " << ( TomahawkSettings::instance()->preferStaticHostPort() ? "true" : "false" ); - + if( TomahawkSettings::instance()->preferStaticHostPort() ) { qDebug() << "Forcing static preferred host and port"; @@ -100,8 +136,12 @@ Servent::startListening( QHostAddress ha, bool upnp, int port ) emit ready(); return true; } - - switch( TomahawkSettings::instance()->externalAddressMode() ) + + TomahawkSettings::ExternalAddressMode mode = TomahawkSettings::instance()->externalAddressMode(); + if ( mode == TomahawkSettings::Upnp && !upnp ) + mode = TomahawkSettings::Lan; + + switch( mode ) { case TomahawkSettings::Lan: foreach( QHostAddress ha, QNetworkInterface::allAddresses() ) @@ -283,7 +323,7 @@ Servent::readyRead() nodeid = m.value( "nodeid" ).toString(); controlid = m.value( "controlid" ).toString(); - qDebug() << m; + qDebug() << "Incoming connection details: " << m; if( !nodeid.isEmpty() ) // only control connections send nodeid { @@ -313,7 +353,7 @@ Servent::readyRead() if( conntype == "accept-offer" || "push-offer" ) { sock->_msg.clear(); - Connection* conn = claimOffer( cc, key, sock->peerAddress() ); + Connection* conn = claimOffer( cc, nodeid, key, sock->peerAddress() ); if( !conn ) { qDebug() << "claimOffer FAILED, key:" << key; @@ -331,7 +371,7 @@ Servent::readyRead() { qDebug() << "Invalid or unhandled conntype"; } - + // fallthru to cleanup: closeconnection: qDebug() << "Closing incoming connection, something was wrong."; @@ -463,6 +503,13 @@ Servent::connectToPeer( const QString& ha, int port, const QString &key, Connect qDebug() << "Servent::connectToPeer:" << ha << ":" << port << thread() << QThread::currentThread(); + if ( ( ha == m_externalAddress.toString() || ha == m_externalHostname ) && + ( port == m_externalPort ) ) + { + qDebug() << "ERROR: Tomahawk won't try to connect to" << ha << ":" << port << ": identified as ourselves."; + return; + } + if( key.length() && conn->firstMessage().isNull() ) { QVariantMap m; @@ -491,12 +538,12 @@ Servent::connectToPeer( const QString& ha, int port, const QString &key, Connect void -Servent::reverseOfferRequest( ControlConnection* orig_conn, const QString& key, const QString& theirkey ) +Servent::reverseOfferRequest( ControlConnection* orig_conn, const QString &theirdbid, const QString& key, const QString& theirkey ) { Q_ASSERT( this->thread() == QThread::currentThread() ); qDebug() << "Servent::reverseOfferRequest received for" << key; - Connection* new_conn = claimOffer( orig_conn, key ); + Connection* new_conn = claimOffer( orig_conn, theirdbid, key ); if ( !new_conn ) { qDebug() << "claimOffer failed, killing requesting connection out of spite"; @@ -516,11 +563,13 @@ Servent::reverseOfferRequest( ControlConnection* orig_conn, const QString& key, // return the appropriate connection for a given offer key, or NULL if invalid Connection* -Servent::claimOffer( ControlConnection* cc, const QString &key, const QHostAddress peer ) +Servent::claimOffer( ControlConnection* cc, const QString &nodeid, const QString &key, const QHostAddress peer ) { + qDebug() << Q_FUNC_INFO; + bool noauth = qApp->arguments().contains( "--noauth" ); - // magic key for file transfers: + // magic key for stream connections: if( key.startsWith( "FILE_REQUEST_KEY:" ) ) { // check if the source IP matches an existing, authenticated connection @@ -543,8 +592,8 @@ Servent::claimOffer( ControlConnection* cc, const QString &key, const QHostAddre } QString fid = key.right( key.length() - 17 ); - FileTransferConnection* ftc = new FileTransferConnection( this, cc, fid ); - return ftc; + StreamConnection* sc = new StreamConnection( this, cc, fid ); + return sc; } if( key == "whitelist" ) // LAN IP address, check source IP @@ -568,13 +617,25 @@ Servent::claimOffer( ControlConnection* cc, const QString &key, const QHostAddre QPointer conn = m_offers.value( key ); if( conn.isNull() ) { - // This can happen if it's a filetransferconnection, but the audioengine has + // This can happen if it's a streamconnection, but the audioengine has // already closed the iodevice, causing the connection to be deleted before // the peer connects and provides the first byte qDebug() << Q_FUNC_INFO << "invalid/expired offer:" << key; return NULL; } + if( !nodeid.isEmpty() ) + { + // If there isn't a nodeid it's not the first connection and will already have been stopped + if( !checkACL( conn, nodeid, true ) ) + { + qDebug() << "Connection not allowed due to ACL"; + return NULL; + } + } + + qDebug() << "ACL has allowed the connection"; + if( conn->onceOnly() ) { m_offers.remove( key ); @@ -599,6 +660,61 @@ Servent::claimOffer( ControlConnection* cc, const QString &key, const QHostAddre } } +bool +Servent::checkACL( const Connection* conn, const QString &nodeid, bool showDialog ) const +{ + qDebug() << "Checking ACLs"; + ACLSystem* aclSystem = ACLSystem::instance(); + ACLSystem::ACL peerStatus = aclSystem->isAuthorizedUser( nodeid ); + if( peerStatus == ACLSystem::Deny ) + return false; + +#ifndef TOMAHAWK_HEADLESS + //FIXME: Actually enable it when it makes sense + return true; + if( peerStatus == ACLSystem::NotFound ) + { + if( !showDialog ) + return false; + + qDebug() << "ACL for this node not found"; + QMessageBox msgBox; + msgBox.setIcon( QMessageBox::Question ); + msgBox.setText( tr( "Incoming Connection Attempt" ) ); + msgBox.setInformativeText( tr( "Another Tomahawk instance is attempting to connect to you. Select whether to allow or deny this connection.\n\nPeer name: %1\nPeer ID: %2\n\nRemember: Only allow peers to connect if you have the legal right for them to stream music from you.").arg( conn->name(), nodeid ) ); + QPushButton *denyButton = msgBox.addButton( tr( "Deny" ), QMessageBox::HelpRole ); + QPushButton *alwaysDenyButton = msgBox.addButton( tr( "Always Deny" ), QMessageBox::YesRole ); + QPushButton *allowButton = msgBox.addButton( tr( "Allow" ), QMessageBox::NoRole ); + QPushButton *alwaysAllowButton = msgBox.addButton( tr( "Always Allow" ), QMessageBox::ActionRole ); + + msgBox.setDefaultButton( denyButton ); + msgBox.setEscapeButton( denyButton ); + + msgBox.exec(); + + if( msgBox.clickedButton() == denyButton ) + return false; + else if( msgBox.clickedButton() == alwaysDenyButton ) + { + aclSystem->authorizeUser( nodeid, ACLSystem::Deny ); + return false; + } + else if( msgBox.clickedButton() == alwaysAllowButton ) + { + aclSystem->authorizeUser( nodeid, ACLSystem::Allow ); + return true; + } + else if( msgBox.clickedButton() == allowButton ) + return true; + + //How could we get here? + qDebug() << "Somehow no button matched"; + return false; + } +#endif + + return true; +} QSharedPointer Servent::remoteIODeviceFactory( const result_ptr& result ) @@ -614,37 +730,37 @@ Servent::remoteIODeviceFactory( const result_ptr& result ) return sp; ControlConnection* cc = s->controlConnection(); - FileTransferConnection* ftc = new FileTransferConnection( this, cc, fileId, result ); - createParallelConnection( cc, ftc, QString( "FILE_REQUEST_KEY:%1" ).arg( fileId ) ); - return ftc->iodevice(); + StreamConnection* sc = new StreamConnection( this, cc, fileId, result ); + createParallelConnection( cc, sc, QString( "FILE_REQUEST_KEY:%1" ).arg( fileId ) ); + return sc->iodevice(); } void -Servent::registerFileTransferConnection( FileTransferConnection* ftc ) +Servent::registerStreamConnection( StreamConnection* sc ) { - Q_ASSERT( !m_ftsessions.contains( ftc ) ); - qDebug() << "Registering FileTransfer" << m_ftsessions.length() + 1; + Q_ASSERT( !m_scsessions.contains( sc ) ); + qDebug() << "Registering Stream" << m_scsessions.length() + 1; QMutexLocker lock( &m_ftsession_mut ); - m_ftsessions.append( ftc ); + m_scsessions.append( sc ); printCurrentTransfers(); - emit fileTransferStarted( ftc ); + emit streamStarted( sc ); } void -Servent::onFileTransferFinished( FileTransferConnection* ftc ) +Servent::onStreamFinished( StreamConnection* sc ) { - Q_ASSERT( ftc ); - qDebug() << "FileTransfer Finished, unregistering" << ftc->id(); + Q_ASSERT( sc ); + qDebug() << "Stream Finished, unregistering" << sc->id(); QMutexLocker lock( &m_ftsession_mut ); - m_ftsessions.removeAll( ftc ); + m_scsessions.removeAll( sc ); printCurrentTransfers(); - emit fileTransferFinished( ftc ); + emit streamFinished( sc ); } @@ -653,8 +769,8 @@ void Servent::printCurrentTransfers() { int k = 1; - qDebug() << "~~~ Active file transfer connections:" << m_ftsessions.length(); - foreach( FileTransferConnection* i, m_ftsessions ) + qDebug() << "~~~ Active file transfer connections:" << m_scsessions.length(); + foreach( StreamConnection* i, m_scsessions ) { qDebug() << k << ") " << i->id(); } @@ -689,8 +805,11 @@ Servent::isIPWhitelisted( QHostAddress ip ) bool Servent::connectedToSession( const QString& session ) { + qDebug() << Q_FUNC_INFO; + qDebug() << "Checking against " << session; foreach( ControlConnection* cc, m_controlconnections ) { + qDebug() << "Checking session " << cc->id(); if( cc->id() == session ) return true; } @@ -726,7 +845,6 @@ Servent::registerIODeviceFactory( const QString &proto, boost::function Servent::getIODeviceForUrl( const Tomahawk::result_ptr& result ) { @@ -739,9 +857,9 @@ Servent::getIODeviceForUrl( const Tomahawk::result_ptr& result ) const QString proto = rx.cap( 1 ); //const QString urlpart = rx.cap( 2 ); + qDebug() << "Getting IODevice for URL:" << proto << m_iofactories.contains( proto ); if ( !m_iofactories.contains( proto ) ) return sp; - return m_iofactories.value( proto )( result ); } @@ -764,5 +882,5 @@ Servent::httpIODeviceFactory( const Tomahawk::result_ptr& result ) qDebug() << Q_FUNC_INFO << result->url(); QNetworkRequest req( result->url() ); QNetworkReply* reply = TomahawkUtils::nam()->get( req ); - return QSharedPointer( reply ); + return QSharedPointer( reply, &QObject::deleteLater ); } diff --git a/src/libtomahawk/network/servent.h b/src/libtomahawk/network/servent.h index d67fedd2a..4e9c37035 100644 --- a/src/libtomahawk/network/servent.h +++ b/src/libtomahawk/network/servent.h @@ -1,8 +1,24 @@ +/* === 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 SERVENT_H #define SERVENT_H -// port for servent to listen on -#define DEFAULT_LISTEN_PORT 50210 // time before new connection terminates if no auth received #define AUTH_TIMEOUT 180000 @@ -30,7 +46,7 @@ class Connection; class Connector; class ControlConnection; -class FileTransferConnection; +class StreamConnection; class ProxyConnection; class RemoteCollectionConnection; class PortFwdThread; @@ -73,7 +89,7 @@ public: explicit Servent( QObject* parent = 0 ); virtual ~Servent(); - bool startListening( QHostAddress ha, bool upnp = false, int port = DEFAULT_LISTEN_PORT ); + bool startListening( QHostAddress ha, bool upnp, int port ); int port() const { return m_port; } @@ -88,7 +104,7 @@ public: void connectToPeer( const QString& ha, int port, const QString &key, const QString& name = "", const QString& id = "" ); void connectToPeer( const QString& ha, int port, const QString &key, Connection* conn ); - void reverseOfferRequest( ControlConnection* orig_conn, const QString& key, const QString& theirkey ); + void reverseOfferRequest( ControlConnection* orig_conn, const QString &theirdbid, const QString& key, const QString& theirkey ); bool visibleExternally() const { return !m_externalHostname.isNull() || (m_externalPort > 0 && !m_externalAddress.isNull()); } QString externalAddress() const { return !m_externalHostname.isNull() ? m_externalHostname : m_externalAddress.toString(); } @@ -100,7 +116,7 @@ public: bool connectedToSession( const QString& session ); unsigned int numConnectedPeers() const { return m_controlconnections.length(); } - QList< FileTransferConnection* > fileTransfers() const { return m_ftsessions; } + QList< StreamConnection* > streams() const { return m_scsessions; } QSharedPointer getIODeviceForUrl( const Tomahawk::result_ptr& result ); void registerIODeviceFactory( const QString &proto, boost::function(Tomahawk::result_ptr)> fac ); @@ -108,8 +124,8 @@ public: QSharedPointer httpIODeviceFactory( const Tomahawk::result_ptr& result ); signals: - void fileTransferStarted( FileTransferConnection* ); - void fileTransferFinished( FileTransferConnection* ); + void streamStarted( StreamConnection* ); + void streamFinished( StreamConnection* ); void ready(); protected: @@ -121,8 +137,8 @@ public slots: void socketError( QAbstractSocket::SocketError ); void createParallelConnection( Connection* orig_conn, Connection* new_conn, const QString& key ); - void registerFileTransferConnection( FileTransferConnection* ); - void onFileTransferFinished( FileTransferConnection* ftc ); + void registerStreamConnection( StreamConnection* ); + void onStreamFinished( StreamConnection* sc ); void socketConnected(); void triggerDBSync(); @@ -130,11 +146,11 @@ public slots: private slots: void readyRead(); - Connection* claimOffer( ControlConnection* cc, const QString &key, const QHostAddress peer = QHostAddress::Any ); + Connection* claimOffer( ControlConnection* cc, const QString &nodeid, const QString &key, const QHostAddress peer = QHostAddress::Any ); private: void handoverSocket( Connection* conn, QTcpSocketExtra* sock ); - + bool checkACL( const Connection* conn, const QString &nodeid, bool showDialog ) const; void printCurrentTransfers(); QJson::Parser parser; @@ -145,7 +161,7 @@ private: QString m_externalHostname; // currently active file transfers: - QList< FileTransferConnection* > m_ftsessions; + QList< StreamConnection* > m_scsessions; QMutex m_ftsession_mut; QMap< QString,boost::function(Tomahawk::result_ptr)> > m_iofactories; diff --git a/src/libtomahawk/network/filetransferconnection.cpp b/src/libtomahawk/network/streamconnection.cpp similarity index 77% rename from src/libtomahawk/network/filetransferconnection.cpp rename to src/libtomahawk/network/streamconnection.cpp index 5a372f45c..e33ed6196 100644 --- a/src/libtomahawk/network/filetransferconnection.cpp +++ b/src/libtomahawk/network/streamconnection.cpp @@ -1,4 +1,22 @@ -#include "filetransferconnection.h" +/* === 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 "streamconnection.h" #include @@ -13,7 +31,7 @@ using namespace Tomahawk; -FileTransferConnection::FileTransferConnection( Servent* s, ControlConnection* cc, QString fid, const Tomahawk::result_ptr& result ) +StreamConnection::StreamConnection( Servent* s, ControlConnection* cc, QString fid, const Tomahawk::result_ptr& result ) : Connection( s ) , m_cc( cc ) , m_fid( fid ) @@ -28,10 +46,10 @@ FileTransferConnection::FileTransferConnection( Servent* s, ControlConnection* c qDebug() << Q_FUNC_INFO; BufferIODevice* bio = new BufferIODevice( result->size() ); - m_iodev = QSharedPointer( bio ); // device audio data gets written to + m_iodev = QSharedPointer( bio, &QObject::deleteLater ); // device audio data gets written to m_iodev->open( QIODevice::ReadWrite ); - Servent::instance()->registerFileTransferConnection( this ); + Servent::instance()->registerStreamConnection( this ); // if the audioengine closes the iodev (skip/stop/etc) then kill the connection // immediately to avoid unnecessary network transfer @@ -47,7 +65,7 @@ FileTransferConnection::FileTransferConnection( Servent* s, ControlConnection* c } -FileTransferConnection::FileTransferConnection( Servent* s, ControlConnection* cc, QString fid ) +StreamConnection::StreamConnection( Servent* s, ControlConnection* cc, QString fid ) : Connection( s ) , m_cc( cc ) , m_fid( fid ) @@ -57,13 +75,13 @@ FileTransferConnection::FileTransferConnection( Servent* s, ControlConnection* c , m_allok( false ) , m_transferRate( 0 ) { - Servent::instance()->registerFileTransferConnection( this ); + Servent::instance()->registerStreamConnection( this ); // auto delete when connection closes: connect( this, SIGNAL( finished() ), SLOT( deleteLater() ), Qt::QueuedConnection ); } -FileTransferConnection::~FileTransferConnection() +StreamConnection::~StreamConnection() { qDebug() << Q_FUNC_INFO << "TX/RX:" << bytesSent() << bytesReceived(); if( m_type == RECEIVING && !m_allok ) @@ -77,12 +95,12 @@ FileTransferConnection::~FileTransferConnection() ((BufferIODevice*)m_iodev.data())->inputComplete(); } - Servent::instance()->onFileTransferFinished( this ); + Servent::instance()->onStreamFinished( this ); } QString -FileTransferConnection::id() const +StreamConnection::id() const { return QString( "FTC[%1 %2]" ) .arg( m_type == SENDING ? "TX" : "RX" ) @@ -90,14 +108,14 @@ FileTransferConnection::id() const } -Tomahawk::source_ptr -FileTransferConnection::source() const +Tomahawk::source_ptr +StreamConnection::source() const { return m_source; } void -FileTransferConnection::showStats( qint64 tx, qint64 rx ) +StreamConnection::showStats( qint64 tx, qint64 rx ) { if( tx > 0 || rx > 0 ) { @@ -112,7 +130,7 @@ FileTransferConnection::showStats( qint64 tx, qint64 rx ) void -FileTransferConnection::setup() +StreamConnection::setup() { QList sources = SourceList::instance()->sources(); foreach( const source_ptr& src, sources ) @@ -145,7 +163,7 @@ FileTransferConnection::setup() void -FileTransferConnection::startSending( const Tomahawk::result_ptr& result ) +StreamConnection::startSending( const Tomahawk::result_ptr& result ) { if ( result.isNull() ) { @@ -173,7 +191,7 @@ FileTransferConnection::startSending( const Tomahawk::result_ptr& result ) void -FileTransferConnection::handleMsg( msg_ptr msg ) +StreamConnection::handleMsg( msg_ptr msg ) { Q_ASSERT( msg->is( Msg::RAW ) ); @@ -219,7 +237,7 @@ FileTransferConnection::handleMsg( msg_ptr msg ) Connection* -FileTransferConnection::clone() +StreamConnection::clone() { Q_ASSERT( false ); return 0; @@ -227,9 +245,9 @@ FileTransferConnection::clone() void -FileTransferConnection::sendSome() +StreamConnection::sendSome() { - Q_ASSERT( m_type == FileTransferConnection::SENDING ); + Q_ASSERT( m_type == StreamConnection::SENDING ); QByteArray ba = "data"; ba.append( m_readdev->read( BufferIODevice::blockSize() ) ); @@ -253,7 +271,7 @@ FileTransferConnection::sendSome() void -FileTransferConnection::onBlockRequest( int block ) +StreamConnection::onBlockRequest( int block ) { qDebug() << Q_FUNC_INFO << block; diff --git a/src/libtomahawk/network/filetransferconnection.h b/src/libtomahawk/network/streamconnection.h similarity index 53% rename from src/libtomahawk/network/filetransferconnection.h rename to src/libtomahawk/network/streamconnection.h index 660fc8507..86f0f1e71 100644 --- a/src/libtomahawk/network/filetransferconnection.h +++ b/src/libtomahawk/network/streamconnection.h @@ -1,6 +1,23 @@ +/* === 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 FILETRANSFERCONNECTION_H -#define FILETRANSFERCONNECTION_H +#ifndef STREAMCONNECTION_H +#define STREAMCONNECTION_H #include #include @@ -14,7 +31,7 @@ class ControlConnection; class BufferIODevice; -class DLLEXPORT FileTransferConnection : public Connection +class DLLEXPORT StreamConnection : public Connection { Q_OBJECT @@ -26,11 +43,11 @@ public: }; // RX: - explicit FileTransferConnection( Servent* s, ControlConnection* cc, QString fid, const Tomahawk::result_ptr& result ); + explicit StreamConnection( Servent* s, ControlConnection* cc, QString fid, const Tomahawk::result_ptr& result ); // TX: - explicit FileTransferConnection( Servent* s, ControlConnection* cc, QString fid ); + explicit StreamConnection( Servent* s, ControlConnection* cc, QString fid ); - virtual ~FileTransferConnection(); + virtual ~StreamConnection(); QString id() const; void setup(); @@ -76,4 +93,4 @@ private: qint64 m_transferRate; }; -#endif // FILETRANSFERCONNECTION_H +#endif // STREAMCONNECTION_H diff --git a/src/libtomahawk/pipeline.cpp b/src/libtomahawk/pipeline.cpp index fcb535723..c3bab44d6 100644 --- a/src/libtomahawk/pipeline.cpp +++ b/src/libtomahawk/pipeline.cpp @@ -1,3 +1,21 @@ +/* === 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 "pipeline.h" #include @@ -84,7 +102,7 @@ Pipeline::resolve( const QList& qlist, bool prioritized ) int i = 0; foreach( const query_ptr& q, qlist ) { - qDebug() << Q_FUNC_INFO << (qlonglong)q.data() << q->toString(); +// qDebug() << Q_FUNC_INFO << (qlonglong)q.data() << q->toString(); if ( !m_qids.contains( q->id() ) ) { m_qids.insert( q->id(), q ); @@ -116,7 +134,7 @@ Pipeline::resolve( const query_ptr& q, bool prioritized ) { if ( q.isNull() ) return; - + QList< query_ptr > qlist; qlist << q; resolve( qlist, prioritized ); @@ -133,7 +151,6 @@ Pipeline::resolve( QID qid, bool prioritized ) void Pipeline::reportResults( QID qid, const QList< result_ptr >& results ) { - int state = 0; { QMutexLocker lock( &m_mut ); @@ -150,40 +167,32 @@ Pipeline::reportResults( QID qid, const QList< result_ptr >& results ) Q_ASSERT( false ); return; } - - state = m_qidsState.value( qid ) - 1; - if ( state ) - { - qDebug() << Q_FUNC_INFO << "replacing" << qid << state; - m_qidsState.insert( qid, state ); - } - else - { - qDebug() << Q_FUNC_INFO << "removing" << qid << state; - m_qidsState.remove( qid ); - } - - if ( !results.isEmpty() ) - { - //qDebug() << Q_FUNC_INFO << qid; - //qDebug() << "solved query:" << (qlonglong)q.data() << q->toString(); - - const query_ptr& q = m_qids.value( qid ); - q->addResults( results ); - - foreach( const result_ptr& r, q->results() ) - { - m_rids.insert( r->id(), r ); - } - } } - - if ( state == 0 ) + + const query_ptr& q = m_qids.value( qid ); + if ( !results.isEmpty() ) + { + //qDebug() << Q_FUNC_INFO << qid; + //qDebug() << "solved query:" << (qlonglong)q.data() << q->toString(); + + q->addResults( results ); + + foreach( const result_ptr& r, q->results() ) + { + m_rids.insert( r->id(), r ); + } + + if ( q->solved() ) + q->onResolvingFinished(); + } + + if ( decQIDState( q ) == 0 ) { // All resolvers have reported back their results for this query now - const query_ptr& q = m_qids.value( qid ); - qDebug() << "Finished resolving:" << q->toString(); - q->onResolvingFinished(); + qDebug() << "Finished resolving:" << q->toString() << q->numResults(); + + if ( !q->solved() ) + q->onResolvingFinished(); shuntNext(); } @@ -202,10 +211,12 @@ Pipeline::shuntNext() if ( m_queries_pending.isEmpty() ) { - emit idle(); + if ( m_qidsState.isEmpty() ) + emit idle(); return; } +// qDebug() << Q_FUNC_INFO << m_qidsState.count(); // Check if we are ready to dispatch more queries if ( m_qidsState.count() >= CONCURRENT_QUERIES ) return; @@ -219,26 +230,35 @@ Pipeline::shuntNext() } if ( !q.isNull() ) + { + incQIDState( q ); shunt( q ); // bump into next stage of pipeline (highest weights are 100) + } } void Pipeline::shunt( const query_ptr& q ) { + qDebug() << Q_FUNC_INFO << q->solved() << q->toString() << q->id(); + unsigned int lastweight = 0; + unsigned int lasttimeout = 0; + if ( q->solved() ) { - qDebug() << "Query solved, pipeline aborted:" << q->toString() - << "numresults:" << q->results().length(); +// qDebug() << "Query solved, pipeline aborted:" << q->toString() +// << "numresults:" << q->results().length(); - shuntNext(); + QList< result_ptr > rl; + reportResults( q->id(), rl ); return; } - unsigned int lastweight = 0; - unsigned int lasttimeout = 0; + int thisResolver = 0; + int i = 0; foreach( Resolver* r, m_resolvers ) { + i++; if ( r->weight() >= q->lastPipelineWeight() ) continue; @@ -255,22 +275,9 @@ Pipeline::shunt( const query_ptr& q ) lasttimeout = r->timeout(); // resolvers aren't allowed to block in this call: - qDebug() << "Dispatching to resolver" << r->name(); - - { - QMutexLocker lock( &m_mut ); - int state = 0; - qDebug() << "Checking qidsstate:" << q->id(); - - if ( m_qidsState.contains( q->id() ) ) - { - state = m_qidsState.value( q->id() ); - } - -// qDebug() << Q_FUNC_INFO << "inserting to qidsstate:" << q->id() << state + 1; - m_qidsState.insert( q->id(), state + 1 ); - } + qDebug() << "Dispatching to resolver" << r->name() << q->toString(); + thisResolver = i; r->resolve( q ); } else @@ -280,13 +287,21 @@ Pipeline::shunt( const query_ptr& q ) if ( lastweight > 0 ) { q->setLastPipelineWeight( lastweight ); - //qDebug() << "Shunting in" << lasttimeout << "ms, q:" << q->toString(); - new FuncTimeout( lasttimeout, boost::bind( &Pipeline::shunt, this, q ) ); + + if ( thisResolver < m_resolvers.count() ) + { + incQIDState( q ); +// qDebug() << "Shunting in" << lasttimeout << "ms, q:" << q->toString(); + new FuncTimeout( lasttimeout, boost::bind( &Pipeline::shunt, this, q ), this ); + } } else { //qDebug() << "Reached end of pipeline for:" << q->toString(); // reached end of pipeline + QList< result_ptr > rl; + reportResults( q->id(), rl ); + return; } shuntNext(); @@ -296,8 +311,47 @@ Pipeline::shunt( const query_ptr& q ) bool Pipeline::resolverSorter( const Resolver* left, const Resolver* right ) { - if( left->weight() == right->weight() ) - return left->preference() > right->preference(); + if( left->weight() == right->weight() ) // TODO dispatch in parallel + return left; else return left->weight() > right->weight(); } + + +int +Pipeline::incQIDState( const Tomahawk::query_ptr& query ) +{ + QMutexLocker lock( &m_mut ); + + int state = 1; + if ( m_qidsState.contains( query->id() ) ) + { + state = m_qidsState.value( query->id() ) + 1; + } + +// qDebug() << Q_FUNC_INFO << "inserting to qidsstate:" << query->id() << state; + m_qidsState.insert( query->id(), state ); + + return state; +} + + +int +Pipeline::decQIDState( const Tomahawk::query_ptr& query ) +{ + QMutexLocker lock( &m_mut ); + + int state = m_qidsState.value( query->id() ) - 1; + if ( state ) + { +// qDebug() << Q_FUNC_INFO << "replacing" << query->id() << state; + m_qidsState.insert( query->id(), state ); + } + else + { +// qDebug() << Q_FUNC_INFO << "removing" << query->id() << state; + m_qidsState.remove( query->id() ); + } + + return state; +} diff --git a/src/libtomahawk/pipeline.h b/src/libtomahawk/pipeline.h index 7035535da..2c31bc28b 100644 --- a/src/libtomahawk/pipeline.h +++ b/src/libtomahawk/pipeline.h @@ -1,3 +1,21 @@ +/* === 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 PIPELINE_H #define PIPELINE_H @@ -61,6 +79,9 @@ private slots: void indexReady(); private: + int incQIDState( const Tomahawk::query_ptr& query ); + int decQIDState( const Tomahawk::query_ptr& query ); + QList< Resolver* > m_resolvers; QMap< QID, unsigned int > m_qidsState; diff --git a/src/libtomahawk/playlist.cpp b/src/libtomahawk/playlist.cpp index 13128baa8..bf65556da 100644 --- a/src/libtomahawk/playlist.cpp +++ b/src/libtomahawk/playlist.cpp @@ -1,3 +1,21 @@ +/* === 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 "playlist.h" #include @@ -94,7 +112,7 @@ Playlist::Playlist( const source_ptr& src, , m_lastmodified( lastmod ) , m_shared( shared ) { - qDebug() << Q_FUNC_INFO << "1"; +// qDebug() << Q_FUNC_INFO << "1"; init(); } @@ -211,6 +229,8 @@ Playlist::reportDeleted( const Tomahawk::playlist_ptr& self ) qDebug() << Q_FUNC_INFO; Q_ASSERT( self.data() == this ); m_source->collection()->deletePlaylist( self ); + + emit deleted( self ); } @@ -411,6 +431,7 @@ Playlist::resolve() void Playlist::onResultsFound( const QList& results ) { + Q_UNUSED( results ); m_locallyChanged = true; } diff --git a/src/libtomahawk/playlist.h b/src/libtomahawk/playlist.h index c3ae79175..5147a3d9b 100644 --- a/src/libtomahawk/playlist.h +++ b/src/libtomahawk/playlist.h @@ -1,3 +1,21 @@ +/* === 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 PLAYLIST_H #define PLAYLIST_H @@ -144,7 +162,7 @@ public: virtual int unfilteredTrackCount() const { return m_entries.count(); } virtual int trackCount() const { return m_entries.count(); } - virtual Tomahawk::result_ptr siblingItem( int itemsAway ) { return result_ptr(); } + virtual Tomahawk::result_ptr siblingItem( int /*itemsAway*/ ) { return result_ptr(); } virtual PlaylistInterface::RepeatMode repeatMode() const { return PlaylistInterface::NoRepeat; } virtual bool shuffled() const { return false; } @@ -152,7 +170,7 @@ public: virtual void setRepeatMode( PlaylistInterface::RepeatMode ) {} virtual void setShuffled( bool ) {} - virtual void setFilter( const QString& pattern ) {} + virtual void setFilter( const QString& /*pattern*/ ) {} signals: /// emitted when the playlist revision changes (whenever the playlist changes) @@ -164,6 +182,9 @@ signals: /// renamed etc. void changed(); + /// was deleted, eh? + void deleted( const Tomahawk::playlist_ptr& pl ); + void repeatModeChanged( PlaylistInterface::RepeatMode mode ); void shuffleModeChanged( bool enabled ); diff --git a/src/libtomahawk/playlist/albumitem.cpp b/src/libtomahawk/playlist/albumitem.cpp index c2bc9de5a..398703e68 100644 --- a/src/libtomahawk/playlist/albumitem.cpp +++ b/src/libtomahawk/playlist/albumitem.cpp @@ -1,3 +1,21 @@ +/* === 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" diff --git a/src/libtomahawk/playlist/albumitem.h b/src/libtomahawk/playlist/albumitem.h index 16dc81b70..ece235c3e 100644 --- a/src/libtomahawk/playlist/albumitem.h +++ b/src/libtomahawk/playlist/albumitem.h @@ -1,3 +1,21 @@ +/* === 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 diff --git a/src/libtomahawk/playlist/albumitemdelegate.cpp b/src/libtomahawk/playlist/albumitemdelegate.cpp index 631ac6bf1..a0b4ec2ae 100644 --- a/src/libtomahawk/playlist/albumitemdelegate.cpp +++ b/src/libtomahawk/playlist/albumitemdelegate.cpp @@ -1,3 +1,21 @@ +/* === 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 "albumitemdelegate.h" #include diff --git a/src/libtomahawk/playlist/albumitemdelegate.h b/src/libtomahawk/playlist/albumitemdelegate.h index 708eec70d..279f4a569 100644 --- a/src/libtomahawk/playlist/albumitemdelegate.h +++ b/src/libtomahawk/playlist/albumitemdelegate.h @@ -1,3 +1,21 @@ +/* === 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 ALBUMITEMDELEGATE_H #define ALBUMITEMDELEGATE_H diff --git a/src/libtomahawk/playlist/albummodel.cpp b/src/libtomahawk/playlist/albummodel.cpp index fbbad77bd..7e28d655d 100644 --- a/src/libtomahawk/playlist/albummodel.cpp +++ b/src/libtomahawk/playlist/albummodel.cpp @@ -1,3 +1,21 @@ +/* === 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 "albummodel.h" #include @@ -63,6 +81,7 @@ AlbumModel::rowCount( const QModelIndex& parent ) const int AlbumModel::columnCount( const QModelIndex& parent ) const { + Q_UNUSED( parent ); return 1; } @@ -221,8 +240,8 @@ AlbumModel::addCollection( const collection_ptr& collection ) DatabaseCommand_AllAlbums* cmd = new DatabaseCommand_AllAlbums( collection ); - connect( cmd, SIGNAL( albums( QList, Tomahawk::collection_ptr ) ), - SLOT( onAlbumsAdded( QList, Tomahawk::collection_ptr ) ) ); + connect( cmd, SIGNAL( albums( QList, QVariant ) ), + SLOT( onAlbumsAdded( QList ) ) ); Database::instance()->enqueue( QSharedPointer( cmd ) ); @@ -243,8 +262,8 @@ AlbumModel::addFilteredCollection( const collection_ptr& collection, unsigned in cmd->setSortOrder( order ); cmd->setSortDescending( true ); - connect( cmd, SIGNAL( albums( QList, Tomahawk::collection_ptr ) ), - SLOT( onAlbumsAdded( QList, Tomahawk::collection_ptr ) ) ); + connect( cmd, SIGNAL( albums( QList, QVariant ) ), + SLOT( onAlbumsAdded( QList ) ) ); Database::instance()->enqueue( QSharedPointer( cmd ) ); @@ -253,7 +272,7 @@ AlbumModel::addFilteredCollection( const collection_ptr& collection, unsigned in void -AlbumModel::onAlbumsAdded( const QList& albums, const Tomahawk::collection_ptr& collection ) +AlbumModel::onAlbumsAdded( const QList& albums ) { if ( !albums.count() ) return; diff --git a/src/libtomahawk/playlist/albummodel.h b/src/libtomahawk/playlist/albummodel.h index f25724ad4..ae8658e19 100644 --- a/src/libtomahawk/playlist/albummodel.h +++ b/src/libtomahawk/playlist/albummodel.h @@ -1,3 +1,21 @@ +/* === 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 ALBUMMODEL_H #define ALBUMMODEL_H @@ -51,7 +69,7 @@ public: 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* itemFromIndex( const QModelIndex& index ) const { if ( index.isValid() ) @@ -63,8 +81,8 @@ public: } public slots: - virtual void setRepeatMode( PlaylistInterface::RepeatMode mode ) {} - virtual void setShuffled( bool shuffled ) {} + virtual void setRepeatMode( PlaylistInterface::RepeatMode /*mode*/ ) {} + virtual void setShuffled( bool /*shuffled*/ ) {} signals: void repeatModeChanged( PlaylistInterface::RepeatMode mode ); @@ -75,7 +93,7 @@ signals: protected: private slots: - void onAlbumsAdded( const QList& albums, const Tomahawk::collection_ptr& collection ); + void onAlbumsAdded( const QList& albums ); void onCoverArtDownloaded(); void onDataChanged(); diff --git a/src/libtomahawk/playlist/albumproxymodel.cpp b/src/libtomahawk/playlist/albumproxymodel.cpp index 36c470fe7..95a764e15 100644 --- a/src/libtomahawk/playlist/albumproxymodel.cpp +++ b/src/libtomahawk/playlist/albumproxymodel.cpp @@ -1,10 +1,27 @@ +/* === 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 "albumproxymodel.h" #include #include #include "query.h" -#include "collectionmodel.h" AlbumProxyModel::AlbumProxyModel( QObject* parent ) @@ -20,12 +37,19 @@ AlbumProxyModel::AlbumProxyModel( QObject* parent ) setSortCaseSensitivity( Qt::CaseInsensitive ); setDynamicSortFilter( true ); - setSourceModel( 0 ); + setSourceAlbumModel( 0 ); } +void +AlbumProxyModel::setSourceModel( QAbstractItemModel* sourceModel ) +{ + Q_UNUSED( sourceModel ); + qDebug() << "Explicitly use setSourceAlbumModel instead"; + Q_ASSERT( false ); +} void -AlbumProxyModel::setSourceModel( AlbumModel* sourceModel ) +AlbumProxyModel::setSourceAlbumModel( AlbumModel* sourceModel ) { m_model = sourceModel; @@ -123,6 +147,7 @@ AlbumProxyModel::removeIndexes( const QList& indexes ) Tomahawk::result_ptr AlbumProxyModel::siblingItem( int itemsAway ) { + Q_UNUSED( itemsAway ); qDebug() << Q_FUNC_INFO; return Tomahawk::result_ptr( 0 ); } diff --git a/src/libtomahawk/playlist/albumproxymodel.h b/src/libtomahawk/playlist/albumproxymodel.h index 3e694e2a6..7427cc85f 100644 --- a/src/libtomahawk/playlist/albumproxymodel.h +++ b/src/libtomahawk/playlist/albumproxymodel.h @@ -1,3 +1,21 @@ +/* === 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 ALBUMPROXYMODEL_H #define ALBUMPROXYMODEL_H @@ -16,7 +34,8 @@ public: explicit AlbumProxyModel( QObject* parent = 0 ); virtual AlbumModel* sourceModel() const { return m_model; } - virtual void setSourceModel( AlbumModel* sourceModel ); + virtual void setSourceAlbumModel( AlbumModel* sourceModel ); + virtual void setSourceModel( QAbstractItemModel* sourceModel ); virtual QList tracks() { Q_ASSERT( FALSE ); QList queries; return queries; } diff --git a/src/libtomahawk/playlist/albumview.cpp b/src/libtomahawk/playlist/albumview.cpp index 83ecb28b9..351fd8ea2 100644 --- a/src/libtomahawk/playlist/albumview.cpp +++ b/src/libtomahawk/playlist/albumview.cpp @@ -1,3 +1,21 @@ +/* === 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 "albumview.h" #include @@ -54,13 +72,22 @@ AlbumView::setProxyModel( AlbumProxyModel* model ) void -AlbumView::setModel( AlbumModel* model ) +AlbumView::setModel( QAbstractItemModel* model ) +{ + Q_UNUSED( model ); + qDebug() << "Explicitly use setAlbumModel instead"; + Q_ASSERT( false ); +} + + +void +AlbumView::setAlbumModel( AlbumModel* model ) { m_model = model; if ( m_proxyModel ) { - m_proxyModel->setSourceModel( model ); + m_proxyModel->setSourceAlbumModel( m_model ); m_proxyModel->sort( 0 ); } @@ -124,6 +151,7 @@ AlbumView::onFilterChanged( const QString& ) void AlbumView::startDrag( Qt::DropActions supportedActions ) { + Q_UNUSED( supportedActions ); } @@ -131,5 +159,6 @@ AlbumView::startDrag( Qt::DropActions supportedActions ) QPixmap AlbumView::createDragPixmap( int itemCount ) const { + Q_UNUSED( itemCount ); return QPixmap(); } diff --git a/src/libtomahawk/playlist/albumview.h b/src/libtomahawk/playlist/albumview.h index d4616cd3c..194eab8d7 100644 --- a/src/libtomahawk/playlist/albumview.h +++ b/src/libtomahawk/playlist/albumview.h @@ -1,3 +1,21 @@ +/* === 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 ALBUMVIEW_H #define ALBUMVIEW_H @@ -24,7 +42,8 @@ public: AlbumProxyModel* proxyModel() const { return m_proxyModel; } // PlaylistItemDelegate* delegate() { return m_delegate; } - void setModel( AlbumModel* model ); + void setAlbumModel( AlbumModel* model ); + void setModel( QAbstractItemModel* model ); virtual QWidget* widget() { return this; } virtual PlaylistInterface* playlistInterface() const { return proxyModel(); } diff --git a/src/libtomahawk/playlist/artistview.cpp b/src/libtomahawk/playlist/artistview.cpp new file mode 100644 index 000000000..126f99ecd --- /dev/null +++ b/src/libtomahawk/playlist/artistview.cpp @@ -0,0 +1,234 @@ +/* === 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 "artistview.h" + +#include +#include +#include +#include +#include + +#include "audio/audioengine.h" + +#include "tomahawksettings.h" +#include "treeheader.h" +#include "treeitemdelegate.h" +#include "playlistmanager.h" + +static QString s_tmInfoIdentifier = QString( "TREEMODEL" ); + +#define SCROLL_TIMEOUT 280 + +using namespace Tomahawk; + + +ArtistView::ArtistView( QWidget* parent ) + : QTreeView( parent ) + , m_header( new TreeHeader( this ) ) + , m_model( 0 ) + , m_proxyModel( 0 ) +// , m_delegate( 0 ) +{ + setAlternatingRowColors( true ); + setDragEnabled( true ); + setDropIndicatorShown( false ); + setDragDropOverwriteMode( false ); + setUniformRowHeights( false ); + setVerticalScrollMode( QAbstractItemView::ScrollPerPixel ); + setRootIsDecorated( true ); + setAnimated( false ); + setAllColumnsShowFocus( true ); + setSelectionMode( QAbstractItemView::ExtendedSelection ); + setSelectionBehavior( QAbstractItemView::SelectRows ); + + setHeader( m_header ); + setProxyModel( new TreeProxyModel( this ) ); + + #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 + + m_timer.setInterval( SCROLL_TIMEOUT ); + + connect( verticalScrollBar(), SIGNAL( rangeChanged( int, int ) ), SLOT( onViewChanged() ) ); + connect( verticalScrollBar(), SIGNAL( valueChanged( int ) ), SLOT( onViewChanged() ) ); + connect( &m_timer, SIGNAL( timeout() ), SLOT( onScrollTimeout() ) ); + + connect( this, SIGNAL( doubleClicked( QModelIndex ) ), SLOT( onItemActivated( QModelIndex ) ) ); +} + + +ArtistView::~ArtistView() +{ + qDebug() << Q_FUNC_INFO; +} + + +void +ArtistView::setProxyModel( TreeProxyModel* model ) +{ + m_proxyModel = model; + setItemDelegate( new TreeItemDelegate( this, m_proxyModel ) ); + + QTreeView::setModel( m_proxyModel ); +} + + +void +ArtistView::setModel( TreeModel* model ) +{ + m_model = model; + + if ( m_proxyModel ) + { + m_proxyModel->setSourceModel( model ); + m_proxyModel->sort( 0 ); + } + + connect( m_proxyModel, SIGNAL( filterChanged( QString ) ), SLOT( onFilterChanged( QString ) ) ); + + setAcceptDrops( false ); +} + + +void +ArtistView::onItemActivated( const QModelIndex& index ) +{ + TreeModelItem* item = m_model->itemFromIndex( m_proxyModel->mapToSource( index ) ); + if ( item ) + { + if ( !item->artist().isNull() ) + PlaylistManager::instance()->show( item->artist() ); + else if ( !item->album().isNull() ) + PlaylistManager::instance()->show( item->album() ); + else if ( !item->result().isNull() ) + AudioEngine::instance()->playItem( 0, item->result() ); + } +} + + +void +ArtistView::dragEnterEvent( QDragEnterEvent* event ) +{ + qDebug() << Q_FUNC_INFO; + QTreeView::dragEnterEvent( event ); +} + + +void +ArtistView::dragMoveEvent( QDragMoveEvent* event ) +{ + QTreeView::dragMoveEvent( event ); +} + + +void +ArtistView::dropEvent( QDropEvent* event ) +{ + QTreeView::dropEvent( event ); +} + + +void +ArtistView::paintEvent( QPaintEvent* event ) +{ + QTreeView::paintEvent( event ); +} + + +void +ArtistView::resizeEvent( QResizeEvent* event ) +{ + QTreeView::resizeEvent( event ); + m_header->checkState(); +} + + +void +ArtistView::onFilterChanged( const QString& ) +{ + if ( selectedIndexes().count() ) + scrollTo( selectedIndexes().at( 0 ), QAbstractItemView::PositionAtCenter ); +} + + +void +ArtistView::startDrag( Qt::DropActions supportedActions ) +{ + Q_UNUSED( supportedActions ); +} + + +QPixmap +ArtistView::createDragPixmap( int itemCount ) const +{ + Q_UNUSED( itemCount ); + return QPixmap(); +} + + +void +ArtistView::onViewChanged() +{ + if ( m_timer.isActive() ) + m_timer.stop(); + + m_timer.start(); +} + + +void +ArtistView::onScrollTimeout() +{ + qDebug() << Q_FUNC_INFO; + if ( m_timer.isActive() ) + m_timer.stop(); + + QModelIndex left = indexAt( viewport()->rect().topLeft() ); + while ( left.isValid() && left.parent().isValid() ) + left = left.parent(); + + QModelIndex right = indexAt( viewport()->rect().bottomLeft() ); + while ( right.isValid() && right.parent().isValid() ) + right = right.parent(); + + int max = m_proxyModel->trackCount(); + if ( right.isValid() ) + max = right.row() + 1; + + for ( int i = left.row(); i < max; i++ ) + { + TreeModelItem* item = m_model->itemFromIndex( m_proxyModel->mapToSource( m_proxyModel->index( i, 0 ) ) ); + + Tomahawk::InfoSystem::InfoCustomData trackInfo; + trackInfo["artist"] = QVariant::fromValue< QString >( item->artist()->name() ); + trackInfo["pptr"] = QVariant::fromValue< qlonglong >( (qlonglong)item ); + + Tomahawk::InfoSystem::InfoSystem::instance()->getInfo( + s_tmInfoIdentifier, Tomahawk::InfoSystem::InfoArtistImages, + QVariant::fromValue< Tomahawk::InfoSystem::InfoCustomData >( trackInfo ), Tomahawk::InfoSystem::InfoCustomData() ); + } +} diff --git a/src/libtomahawk/playlist/artistview.h b/src/libtomahawk/playlist/artistview.h new file mode 100644 index 000000000..e02107cbb --- /dev/null +++ b/src/libtomahawk/playlist/artistview.h @@ -0,0 +1,90 @@ +/* === 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 ARTISTVIEW_H +#define ARTISTVIEW_H + +#include +#include + +#include "treemodel.h" +#include "treeproxymodel.h" +#include "viewpage.h" + +#include "dllmacro.h" + +class TreeHeader; + +class DLLEXPORT ArtistView : public QTreeView, public Tomahawk::ViewPage +{ +Q_OBJECT + +public: + explicit ArtistView( QWidget* parent = 0 ); + ~ArtistView(); + + void setProxyModel( TreeProxyModel* model ); + + TreeModel* model() const { return m_model; } + TreeProxyModel* proxyModel() const { return m_proxyModel; } +// PlaylistItemDelegate* delegate() { return m_delegate; } + + void setModel( TreeModel* model ); + + virtual QWidget* widget() { return this; } + virtual PlaylistInterface* playlistInterface() const { return proxyModel(); } + + virtual QString title() const { return m_model->title(); } + virtual QString description() const { return m_model->description(); } + + virtual bool showStatsBar() const { return false; } + virtual bool showModes() const { return true; } + + virtual bool jumpToCurrentTrack() { return false; } + + QString guid() const { return QString( "ArtistView" ); } + +public slots: + void onItemActivated( const QModelIndex& index ); + +protected: + virtual void startDrag( Qt::DropActions supportedActions ); + virtual void dragEnterEvent( QDragEnterEvent* event ); + virtual void dragMoveEvent( QDragMoveEvent* event ); + virtual void dropEvent( QDropEvent* event ); + virtual void resizeEvent( QResizeEvent* event ); + + void paintEvent( QPaintEvent* event ); + +private slots: + void onFilterChanged( const QString& filter ); + void onViewChanged(); + void onScrollTimeout(); + +private: + QPixmap createDragPixmap( int itemCount ) const; + + TreeHeader* m_header; + TreeModel* m_model; + TreeProxyModel* m_proxyModel; +// PlaylistItemDelegate* m_delegate; + + QTimer m_timer; +}; + +#endif // ARTISTVIEW_H diff --git a/src/libtomahawk/playlist/collectionflatmodel.cpp b/src/libtomahawk/playlist/collectionflatmodel.cpp index 8268df1fb..2090b6cba 100644 --- a/src/libtomahawk/playlist/collectionflatmodel.cpp +++ b/src/libtomahawk/playlist/collectionflatmodel.cpp @@ -1,3 +1,21 @@ +/* === 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 @@ -24,45 +42,23 @@ CollectionFlatModel::~CollectionFlatModel() } -int -CollectionFlatModel::columnCount( const QModelIndex& parent ) const -{ - return TrackModel::columnCount( parent ); -} - - -QVariant -CollectionFlatModel::data( const QModelIndex& index, int role ) const -{ - return TrackModel::data( index, role ); -} - - -QVariant -CollectionFlatModel::headerData( int section, Qt::Orientation orientation, int role ) const -{ - return TrackModel::headerData( section, orientation, role ); -} - -void +void CollectionFlatModel::addCollections( const QList< collection_ptr >& collections ) { qDebug() << Q_FUNC_INFO << "Adding collections!"; - foreach( const collection_ptr& col, collections ) + foreach( const collection_ptr& col, collections ) { - if( !col->isLoaded() ) - m_loadingCollections << col.data(); - addCollection( col ); } - - if( m_loadingCollections.isEmpty() ) - emit doneLoadingCollections(); + + // we are waiting for some to load + if( !m_loadingCollections.isEmpty() ) + emit loadingStarted(); } void -CollectionFlatModel::addCollection( const collection_ptr& collection ) +CollectionFlatModel::addCollection( const collection_ptr& collection, bool sendNotifications ) { qDebug() << Q_FUNC_INFO << collection->name() << collection->source()->id() @@ -73,15 +69,19 @@ CollectionFlatModel::addCollection( const collection_ptr& collection ) connect( collection.data(), SIGNAL( tracksRemoved( QList ) ), SLOT( onTracksRemoved( QList ) ) ); + if( sendNotifications ) + emit loadingStarted(); + if ( collection->isLoaded() ) + { onTracksAdded( collection->tracks() ); + } else { collection->tracks(); // data will arrive via signals - m_loadingCollections << collection.data(); } - + if ( collection->source()->isLocal() ) setTitle( tr( "Your Collection" ) ); else @@ -102,8 +102,8 @@ CollectionFlatModel::addFilteredCollection( const collection_ptr& collection, un cmd->setSortOrder( order ); cmd->setSortDescending( true ); - connect( cmd, SIGNAL( tracks( QList, Tomahawk::collection_ptr ) ), - SLOT( onTracksAdded( QList, Tomahawk::collection_ptr ) ), Qt::QueuedConnection ); + connect( cmd, SIGNAL( tracks( QList, QVariant ) ), + SLOT( onTracksAdded( QList ) ), Qt::QueuedConnection ); Database::instance()->enqueue( QSharedPointer( cmd ) ); } @@ -120,7 +120,7 @@ CollectionFlatModel::removeCollection( const collection_ptr& collection ) QTime timer; timer.start(); -// QList plitems = m_collectionIndex.values( collection ); +// QList plitems = m_collectionIndex.values( collection ); QList< QPair< int, int > > rows; QList< QPair< int, int > > sortrows; QPair< int, int > row; @@ -167,7 +167,7 @@ CollectionFlatModel::removeCollection( const collection_ptr& collection ) emit beginRemoveRows( QModelIndex(), row.first, row.second ); for ( int i = row.second; i >= row.first; i-- ) { - PlItem* item = itemFromIndex( index( i, 0, QModelIndex() ) ); + TrackModelItem* item = itemFromIndex( index( i, 0, QModelIndex() ) ); delete item; } emit endRemoveRows(); @@ -184,13 +184,12 @@ 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 + 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() ) ); - - if( m_loadingCollections.isEmpty() ) - emit doneLoadingCollections(); } - + bool kickOff = m_tracksToAdd.isEmpty(); m_tracksToAdd << tracks; @@ -198,26 +197,27 @@ CollectionFlatModel::onTracksAdded( const QList& tracks ) if ( m_tracksToAdd.count() && kickOff ) processTracksToAdd(); + else if ( m_tracksToAdd.isEmpty() && m_loadingCollections.isEmpty() ) + emit loadingFinished(); } void CollectionFlatModel::processTracksToAdd() { - int chunkSize = 500000; + int chunkSize = 5000; int maxc = qMin( chunkSize, m_tracksToAdd.count() ); int c = rowCount( QModelIndex() ); - //emit beginInsertRows( QModelIndex(), c, c + maxc - 1 ); - beginResetModel(); + emit beginInsertRows( QModelIndex(), c, c + maxc - 1 ); + //beginResetModel(); - PlItem* plitem; + TrackModelItem* plitem; QList< Tomahawk::query_ptr >::iterator iter = m_tracksToAdd.begin(); for( int i = 0; i < maxc; ++i ) { - - plitem = new PlItem( *iter, m_rootItem ); + plitem = new TrackModelItem( *iter, m_rootItem ); plitem->index = createIndex( m_rootItem->children.count() - 1, 0, plitem ); connect( plitem, SIGNAL( dataChanged() ), SLOT( onDataChanged() ) ); @@ -227,8 +227,11 @@ CollectionFlatModel::processTracksToAdd() m_tracksToAdd.erase( m_tracksToAdd.begin(), iter ); - endResetModel(); - //emit endInsertRows(); + if ( m_tracksToAdd.isEmpty() && m_loadingCollections.isEmpty() ) + emit loadingFinished(); + + //endResetModel(); + emit endInsertRows(); qDebug() << Q_FUNC_INFO << rowCount( QModelIndex() ); if ( m_tracksToAdd.count() ) @@ -242,7 +245,7 @@ CollectionFlatModel::onTracksRemoved( const QList& tracks ) QList t = tracks; for ( int i = rowCount( QModelIndex() ); i >= 0 && t.count(); i-- ) { - PlItem* item = itemFromIndex( index( i, 0, QModelIndex() ) ); + TrackModelItem* item = itemFromIndex( index( i, 0, QModelIndex() ) ); if ( !item ) continue; @@ -272,11 +275,11 @@ CollectionFlatModel::onTracksRemoved( const QList& tracks ) void CollectionFlatModel::onDataChanged() { - PlItem* p = (PlItem*)sender(); + TrackModelItem* p = (TrackModelItem*)sender(); // emit itemSizeChanged( p->index ); if ( p ) - emit dataChanged( p->index, p->index.sibling( p->index.row(), columnCount() - 1 ) ); + emit dataChanged( p->index, p->index.sibling( p->index.row(), columnCount( QModelIndex() ) - 1 ) ); } diff --git a/src/libtomahawk/playlist/collectionflatmodel.h b/src/libtomahawk/playlist/collectionflatmodel.h index 5e32ce030..65e1489a0 100644 --- a/src/libtomahawk/playlist/collectionflatmodel.h +++ b/src/libtomahawk/playlist/collectionflatmodel.h @@ -1,3 +1,21 @@ +/* === 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 COLLECTIONFLATMODEL_H #define COLLECTIONFLATMODEL_H @@ -5,7 +23,7 @@ #include #include -#include "plitem.h" +#include "trackmodelitem.h" #include "trackmodel.h" #include "collection.h" #include "query.h" @@ -27,21 +45,16 @@ public: explicit CollectionFlatModel( QObject* parent = 0 ); ~CollectionFlatModel(); - int columnCount( const QModelIndex& parent = QModelIndex() ) const; - virtual int trackCount() const { return rowCount( QModelIndex() ) + m_tracksToAdd.count(); } - QVariant data( const QModelIndex& index, int role ) const; - QVariant headerData( int section, Qt::Orientation orientation, int role ) const; - void addCollections( const QList< Tomahawk::collection_ptr >& collections ); - - void addCollection( const Tomahawk::collection_ptr& collection ); + + void addCollection( const Tomahawk::collection_ptr& collection, bool sendNotifications = true ); void removeCollection( const Tomahawk::collection_ptr& collection ); void addFilteredCollection( const Tomahawk::collection_ptr& collection, unsigned int amount, DatabaseCommand_AllTracks::SortOrder order ); - virtual void append( const Tomahawk::query_ptr& query ) {} + virtual void append( const Tomahawk::query_ptr& /*query*/ ) {} signals: void repeatModeChanged( PlaylistInterface::RepeatMode mode ); @@ -49,7 +62,6 @@ signals: void itemSizeChanged( const QModelIndex& index ); - void doneLoadingCollections(); private slots: void onDataChanged(); diff --git a/src/libtomahawk/playlist/collectionmodel.cpp b/src/libtomahawk/playlist/collectionmodel.cpp deleted file mode 100644 index 5aaf21d57..000000000 --- a/src/libtomahawk/playlist/collectionmodel.cpp +++ /dev/null @@ -1,269 +0,0 @@ -#include "collectionmodel.h" - -#include -#include -#include - -#include "sourcelist.h" -#include "utils/tomahawkutils.h" - -using namespace Tomahawk; - - -CollectionModel::CollectionModel( QObject* parent ) - : QAbstractItemModel( parent ) - , m_rootItem( 0 ) -{ - qDebug() << Q_FUNC_INFO; - - connect( SourceList::instance(), SIGNAL( sourceRemoved( Tomahawk::source_ptr ) ), SLOT( onSourceOffline( Tomahawk::source_ptr ) ) ); -} - - -CollectionModel::~CollectionModel() -{ -// delete m_rootItem; -} - - -QModelIndex -CollectionModel::index( int row, int column, const QModelIndex& parent ) const -{ - if ( !m_rootItem || row < 0 || column < 0 ) - return QModelIndex(); - - PlItem* parentItem = itemFromIndex( parent ); - PlItem* childItem = parentItem->children.value( row ); - if ( !childItem ) - return QModelIndex(); - - return createIndex( row, column, childItem ); -} - - -int -CollectionModel::rowCount( const QModelIndex& parent ) const -{ - if ( parent.column() > 0 ) - return 0; - - PlItem* parentItem = itemFromIndex( parent ); - if ( !parentItem ) - return 0; - - return parentItem->children.count(); -} - - -int -CollectionModel::columnCount( const QModelIndex& parent ) const -{ - return 4; -} - - -QModelIndex -CollectionModel::parent( const QModelIndex& child ) const -{ - PlItem* entry = itemFromIndex( child ); - if ( !entry ) - return QModelIndex(); - - PlItem* parentEntry = entry->parent; - if ( !parentEntry ) - return QModelIndex(); - - PlItem* grandparentEntry = parentEntry->parent; - if ( !grandparentEntry ) - return QModelIndex(); - - int row = grandparentEntry->children.indexOf( parentEntry ); - return createIndex( row, 0, parentEntry ); -} - - -QVariant -CollectionModel::data( const QModelIndex& index, int role ) const -{ - if ( role != Qt::DisplayRole ) - return QVariant(); - - PlItem* entry = itemFromIndex( index ); - if ( !entry ) - return QVariant(); - - const query_ptr& query = entry->query(); - if ( query.isNull() ) - { - if ( !index.column() ) - { - return entry->caption.isEmpty() ? "Unknown" : entry->caption; - } - - if ( index.column() == 1 ) - { - return entry->childCount; - } - - return QVariant( "" ); - } - - if ( !query->numResults() ) - { - switch( index.column() ) - { - case 0: - return query->track(); - break; - } - } - else - { - switch( index.column() ) - { - case 0: - return query->results().first()->track(); - break; - - case 1: - return QVariant(); - break; - - case 2: - return TomahawkUtils::timeToString( query->results().first()->duration() ); - break; - - case 3: - return query->results().first()->collection()->source()->friendlyName(); - break; - } - } - - return QVariant(); -} - - -QVariant -CollectionModel::headerData( int section, Qt::Orientation orientation, int role ) const -{ - QStringList headers; - headers << tr( "Name" ) << tr( "Tracks" ) << tr( "Duration" ) << tr( "Origin" ); - if ( orientation == Qt::Horizontal && role == Qt::DisplayRole && section >= 0 ) - { - return headers.at( section ); - } - - return QVariant(); -} - - -void -CollectionModel::addCollection( const collection_ptr& collection ) -{ - qDebug() << Q_FUNC_INFO << collection->name() - << collection->source()->id() - << collection->source()->userName(); - - emit loadingStarts(); - - connect( collection.data(), SIGNAL( tracksAdded( QList, Tomahawk::collection_ptr ) ), - SLOT( onTracksAdded( QList, Tomahawk::collection_ptr ) ) ); - connect( collection.data(), SIGNAL( tracksFinished( Tomahawk::collection_ptr ) ), - SLOT( onTracksAddingFinished( Tomahawk::collection_ptr ) ) ); -} - - -void -CollectionModel::removeCollection( const collection_ptr& collection ) -{ - disconnect( collection.data(), SIGNAL( tracksAdded( QList, Tomahawk::collection_ptr ) ), - this, SLOT( onTracksAdded( QList, Tomahawk::collection_ptr ) ) ); - disconnect( collection.data(), SIGNAL( tracksFinished( Tomahawk::collection_ptr ) ), - this, SLOT( onTracksAddingFinished( Tomahawk::collection_ptr ) ) ); - - QList plitems = m_collectionIndex.values( collection ); - - m_collectionIndex.remove( collection ); -} - - -void -CollectionModel::onTracksAdded( const QList& tracks, const collection_ptr& collection ) -{ -// int c = rowCount( QModelIndex() ); - - PlItem* plitem; - foreach( const Tomahawk::query_ptr& query, tracks ) - { - PlItem* parent = m_rootItem; - if ( parent->hash.contains( query->artist() ) ) - { - parent = parent->hash.value( query->artist() ); - } - else - { - parent = new PlItem( query->artist(), m_rootItem ); - m_rootItem->hash.insert( query->artist(), parent ); - } - - if ( parent->hash.contains( query->album() ) ) - { - parent->childCount++; - parent = parent->hash.value( query->album() ); - parent->childCount++; - } - else - { - PlItem* subitem = new PlItem( query->album(), parent ); - parent->hash.insert( query->album(), subitem ); - parent->childCount++; - subitem->childCount++; - parent = subitem; - } - - plitem = new PlItem( query, parent ); - m_collectionIndex.insertMulti( collection, plitem ); - } - - reset(); - - qDebug() << rowCount( QModelIndex() ); -} - - -void -CollectionModel::onTracksAddingFinished( const Tomahawk::collection_ptr& collection ) -{ - qDebug() << "Finished loading tracks" << collection->source()->friendlyName(); - - disconnect( collection.data(), SIGNAL( tracksAdded( QList, Tomahawk::collection_ptr ) ), - this, SLOT( onTracksAdded( QList, Tomahawk::collection_ptr ) ) ); - disconnect( collection.data(), SIGNAL( tracksFinished( Tomahawk::collection_ptr ) ), - this, SLOT( onTracksAddingFinished( Tomahawk::collection_ptr ) ) ); - - emit loadingFinished(); -} - - -void -CollectionModel::onSourceOffline( Tomahawk::source_ptr src ) -{ - qDebug() << Q_FUNC_INFO; - - if ( m_collectionIndex.contains( src->collection() ) ) - { - removeCollection( src->collection() ); - } -} - - -PlItem* -CollectionModel::itemFromIndex( const QModelIndex& index ) const -{ - if ( index.isValid() ) - return static_cast( index.internalPointer() ); - else - { - return m_rootItem; - } -} diff --git a/src/libtomahawk/playlist/collectionmodel.h b/src/libtomahawk/playlist/collectionmodel.h deleted file mode 100644 index 31f1ab779..000000000 --- a/src/libtomahawk/playlist/collectionmodel.h +++ /dev/null @@ -1,68 +0,0 @@ -#ifndef COLLECTIONMODEL_H -#define COLLECTIONMODEL_H - -#include -#include -#include - -#include "plitem.h" -#include "collection.h" -#include "query.h" -#include "typedefs.h" -#include "playlist.h" -#include "playlistinterface.h" - -#include "dllmacro.h" - -class QMetaData; - -class DLLEXPORT CollectionModel : public QAbstractItemModel -{ -Q_OBJECT - -public: - explicit CollectionModel( QObject* parent = 0 ); - ~CollectionModel(); - - QModelIndex index( int row, int column, const QModelIndex& parent ) const; - QModelIndex parent( const QModelIndex& child ) const; - - int trackCount() const { return rowCount( QModelIndex() ); } - - int rowCount( const QModelIndex& parent ) const; - int columnCount( const QModelIndex& parent ) const; - - QVariant data( const QModelIndex& index, int role ) const; - QVariant headerData( int section, Qt::Orientation orientation, int role ) const; - - void addCollection( const Tomahawk::collection_ptr& collection ); - void removeCollection( const Tomahawk::collection_ptr& collection ); - - virtual PlaylistInterface::RepeatMode repeatMode() const { return PlaylistInterface::NoRepeat; } - virtual bool shuffled() const { return false; } - - virtual void setRepeatMode( PlaylistInterface::RepeatMode mode ) {} - virtual void setShuffled( bool shuffled ) {} - - PlItem* itemFromIndex( const QModelIndex& index ) const; - -signals: - void repeatModeChanged( PlaylistInterface::RepeatMode mode ); - void shuffleModeChanged( bool enabled ); - - void loadingStarts(); - void loadingFinished(); - void trackCountChanged( unsigned int tracks ); - -private slots: - void onTracksAdded( const QList& tracks, const Tomahawk::collection_ptr& collection ); - void onTracksAddingFinished( const Tomahawk::collection_ptr& collection ); - - void onSourceOffline( Tomahawk::source_ptr src ); - -private: - PlItem* m_rootItem; - QMap< Tomahawk::collection_ptr, PlItem* > m_collectionIndex; -}; - -#endif // COLLECTIONMODEL_H diff --git a/src/libtomahawk/playlist/collectionproxymodel.cpp b/src/libtomahawk/playlist/collectionproxymodel.cpp index b477d2d17..4f602e5db 100644 --- a/src/libtomahawk/playlist/collectionproxymodel.cpp +++ b/src/libtomahawk/playlist/collectionproxymodel.cpp @@ -1,3 +1,21 @@ +/* === 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 "collectionproxymodel.h" #include @@ -5,7 +23,6 @@ #include "album.h" #include "query.h" -#include "collectionmodel.h" CollectionProxyModel::CollectionProxyModel( QObject* parent ) @@ -17,8 +34,8 @@ CollectionProxyModel::CollectionProxyModel( QObject* parent ) bool CollectionProxyModel::lessThan( const QModelIndex& left, const QModelIndex& right ) const { - PlItem* p1 = itemFromIndex( left ); - PlItem* p2 = itemFromIndex( right ); + TrackModelItem* p1 = itemFromIndex( left ); + TrackModelItem* p2 = itemFromIndex( right ); if ( !p1 ) return true; @@ -28,11 +45,6 @@ CollectionProxyModel::lessThan( const QModelIndex& left, const QModelIndex& righ const Tomahawk::query_ptr& q1 = p1->query(); const Tomahawk::query_ptr& q2 = p2->query(); - if ( q1.isNull() || q2.isNull() ) - { - return QString::localeAwareCompare( p1->caption, p2->caption ) < 0; - } - QString artist1 = q1->artist(); QString artist2 = q2->artist(); QString album1 = q1->album(); diff --git a/src/libtomahawk/playlist/collectionproxymodel.h b/src/libtomahawk/playlist/collectionproxymodel.h index af46be8c4..63c615f40 100644 --- a/src/libtomahawk/playlist/collectionproxymodel.h +++ b/src/libtomahawk/playlist/collectionproxymodel.h @@ -1,3 +1,21 @@ +/* === 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 COLLECTIONPROXYMODEL_H #define COLLECTIONPROXYMODEL_H diff --git a/src/libtomahawk/playlist/collectionview.cpp b/src/libtomahawk/playlist/collectionview.cpp index 09b557611..6d93742f6 100644 --- a/src/libtomahawk/playlist/collectionview.cpp +++ b/src/libtomahawk/playlist/collectionview.cpp @@ -1,3 +1,21 @@ +/* === 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 "collectionview.h" #include @@ -33,9 +51,18 @@ CollectionView::~CollectionView() void -CollectionView::setModel( TrackModel* model ) +CollectionView::setModel( QAbstractItemModel* model ) { - TrackView::setModel( model ); + Q_UNUSED( model ); + qDebug() << "Explicitly use setTrackModel instead"; + Q_ASSERT( false ); +} + + +void +CollectionView::setTrackModel( TrackModel* model ) +{ + TrackView::setTrackModel( model ); setGuid( "collectionview" ); connect( model, SIGNAL( trackCountChanged( unsigned int ) ), SLOT( onTrackCountChanged( unsigned int ) ) ); @@ -99,4 +126,5 @@ bool CollectionView::jumpToCurrentTrack() { scrollTo( proxyModel()->currentItem(), QAbstractItemView::PositionAtCenter ); + return true; } diff --git a/src/libtomahawk/playlist/collectionview.h b/src/libtomahawk/playlist/collectionview.h index cddd1e98c..1fe261646 100644 --- a/src/libtomahawk/playlist/collectionview.h +++ b/src/libtomahawk/playlist/collectionview.h @@ -1,3 +1,21 @@ +/* === 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 COLLECTIONVIEW_H #define COLLECTIONVIEW_H @@ -18,14 +36,16 @@ public: explicit CollectionView( QWidget* parent = 0 ); ~CollectionView(); - virtual void setModel( TrackModel* model ); + virtual void setTrackModel( TrackModel* model ); + virtual void setModel( QAbstractItemModel* model ); virtual QWidget* widget() { return this; } virtual PlaylistInterface* playlistInterface() const { return proxyModel(); } 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 jumpToCurrentTrack(); diff --git a/src/libtomahawk/playlist/dynamic/DynamicControl.cpp b/src/libtomahawk/playlist/dynamic/DynamicControl.cpp index 61f7cd315..ae81f8665 100644 --- a/src/libtomahawk/playlist/dynamic/DynamicControl.cpp +++ b/src/libtomahawk/playlist/dynamic/DynamicControl.cpp @@ -1,18 +1,20 @@ -/**************************************************************************************** - * Copyright (c) 2010 Leo Franchi * - * * - * This program is free software; you can redistribute it and/or modify it under * - * the terms of the GNU General Public License as published by the Free Software * - * Foundation; either version 2 of the License, or (at your option) any later * - * version. * - * * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY * - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * - * PARTICULAR PURPOSE. See the GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License along with * - * this program. If not, see . * - ****************************************************************************************/ +/* === 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 "DynamicControl.h" diff --git a/src/libtomahawk/playlist/dynamic/DynamicControl.h b/src/libtomahawk/playlist/dynamic/DynamicControl.h index 28aa126f7..c03d386fd 100644 --- a/src/libtomahawk/playlist/dynamic/DynamicControl.h +++ b/src/libtomahawk/playlist/dynamic/DynamicControl.h @@ -1,18 +1,20 @@ -/**************************************************************************************** - * Copyright (c) 2010 Leo Franchi * - * * - * This program is free software; you can redistribute it and/or modify it under * - * the terms of the GNU General Public License as published by the Free Software * - * Foundation; either version 2 of the License, or (at your option) any later * - * version. * - * * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY * - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * - * PARTICULAR PURPOSE. See the GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License along with * - * this program. If not, see . * - ****************************************************************************************/ +/* === 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 DYNAMIC_PLAYLIST_CONTROL #define DYNAMIC_PLAYLIST_CONTROL @@ -77,8 +79,8 @@ public: virtual QString summary() const { Q_ASSERT( false ); return QString(); } // used by JSON serialization - virtual void setMatch( const QString& match ) { Q_ASSERT( false ); } - virtual void setInput( const QString& input ) { Q_ASSERT( false ); } + virtual void setMatch( const QString& /*match*/ ) { Q_ASSERT( false ); } + virtual void setInput( const QString& /*input*/ ) { Q_ASSERT( false ); } /// All the potential type selectors for this control QStringList typeSelectors() const { return m_typeSelectors; } diff --git a/src/libtomahawk/playlist/dynamic/DynamicModel.cpp b/src/libtomahawk/playlist/dynamic/DynamicModel.cpp index c6e752186..689af3bca 100644 --- a/src/libtomahawk/playlist/dynamic/DynamicModel.cpp +++ b/src/libtomahawk/playlist/dynamic/DynamicModel.cpp @@ -1,18 +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 2 of the License, or (at your option) any later * - * version. * - * * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY * - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * - * PARTICULAR PURPOSE. See the GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License along with * - * this program. If not, see . * - ****************************************************************************************/ +/* === 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 "playlist/dynamic/DynamicModel.h" #include "GeneratorInterface.h" @@ -40,8 +42,10 @@ DynamicModel::~DynamicModel() } void -DynamicModel::loadPlaylist( const Tomahawk::dynplaylist_ptr& playlist ) +DynamicModel::loadPlaylist( const Tomahawk::dynplaylist_ptr& playlist, bool loadEntries ) { + Q_UNUSED( loadEntries ); + if( !m_playlist.isNull() ) { disconnect( m_playlist->generator().data(), SIGNAL( nextTrackGenerated( Tomahawk::query_ptr ) ), this, SLOT( newTrackGenerated( Tomahawk::query_ptr ) ) ); } @@ -52,6 +56,9 @@ DynamicModel::loadPlaylist( const Tomahawk::dynplaylist_ptr& playlist ) connect( m_playlist->generator().data(), SIGNAL( nextTrackGenerated( Tomahawk::query_ptr ) ), this, SLOT( newTrackGenerated( Tomahawk::query_ptr ) ) ); PlaylistModel::loadPlaylist( m_playlist, m_playlist->mode() == Static ); + + if( m_playlist->mode() == OnDemand ) + emit trackCountChanged( rowCount( QModelIndex() ) ); } QString @@ -76,7 +83,6 @@ DynamicModel::newTrackGenerated( const Tomahawk::query_ptr& query ) { if( m_onDemandRunning ) { connect( query.data(), SIGNAL( resolvingFinished( bool ) ), this, SLOT( trackResolveFinished( bool ) ) ); - connect( query.data(), SIGNAL( solvedStateChanged( bool ) ), this, SLOT( trackResolved( bool ) ) ); append( query ); } @@ -101,31 +107,14 @@ DynamicModel::changeStation() m_playlist->generator()->startOnDemand(); } - -void -DynamicModel::trackResolved( bool resolved ) -{ - if( !resolved ) - return; - - Query* q = qobject_cast(sender()); - qDebug() << "Got successful resolved track:" << q->track() << q->artist() << m_lastResolvedRow << m_currentAttempts; - - if( m_currentAttempts > 0 ) { - qDebug() << "EMITTING AN ASK FOR COLLAPSE:" << m_lastResolvedRow << m_currentAttempts; - emit collapseFromTo( m_lastResolvedRow, m_currentAttempts ); - } - m_currentAttempts = 0; - m_searchingForNext = false; - - emit checkForOverflow(); -} - void DynamicModel::trackResolveFinished( bool success ) { - if( !success ) { // if it was successful, we've already gotten a trackResolved() signal - Query* q = qobject_cast(sender()); + Q_UNUSED( success ); + + Query* q = qobject_cast(sender()); + + if( !q->playable() ) { qDebug() << "Got not resolved track:" << q->track() << q->artist() << m_lastResolvedRow << m_currentAttempts; m_currentAttempts++; @@ -138,6 +127,18 @@ DynamicModel::trackResolveFinished( bool success ) emit trackGenerationFailure( tr( "Could not find a playable track.\n\nPlease change the filters or try again." ) ); } } + else { + qDebug() << "Got successful resolved track:" << q->track() << q->artist() << m_lastResolvedRow << m_currentAttempts; + + if( m_currentAttempts > 0 ) { + qDebug() << "EMITTING AN ASK FOR COLLAPSE:" << m_lastResolvedRow << m_currentAttempts; + emit collapseFromTo( m_lastResolvedRow, m_currentAttempts ); + } + m_currentAttempts = 0; + m_searchingForNext = false; + + emit checkForOverflow(); + } } @@ -179,8 +180,8 @@ DynamicModel::filterUnresolved( const QList< query_ptr >& entries ) foreach( const query_ptr& q, entries ) { connect( q.data(), SIGNAL( resolvingFinished( bool ) ), this, SLOT( filteringTrackResolved( bool ) ) ); - Pipeline::instance()->resolve( q ); - } + } + Pipeline::instance()->resolve( entries, true ); } void @@ -216,7 +217,9 @@ DynamicModel::filteringTrackResolved( bool successful ) if( m_playlist->mode() == OnDemand ) { m_lastResolvedRow = rowCount( QModelIndex() ); } - } + } + if( m_toResolveList.isEmpty() && rowCount( QModelIndex() ) == 0 ) // we failed + emit trackGenerationFailure( tr( "Could not find a playable track.\n\nPlease change the filters or try again." ) ); } diff --git a/src/libtomahawk/playlist/dynamic/DynamicModel.h b/src/libtomahawk/playlist/dynamic/DynamicModel.h index 780042dee..23aa93e68 100644 --- a/src/libtomahawk/playlist/dynamic/DynamicModel.h +++ b/src/libtomahawk/playlist/dynamic/DynamicModel.h @@ -1,18 +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 2 of the License, or (at your option) any later * - * version. * - * * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY * - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * - * PARTICULAR PURPOSE. See the GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License along with * - * this program. If not, see . * - ****************************************************************************************/ +/* === 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 DYNAMIC_MODEL_H #define DYNAMIC_MODEL_H @@ -42,7 +44,7 @@ public: virtual QString description() const; - void loadPlaylist( const dynplaylist_ptr& playlist ); + void loadPlaylist( const dynplaylist_ptr& playlist, bool loadEntries = true ); virtual void removeIndex( const QModelIndex& index, bool moreToCome = false ); @@ -64,7 +66,6 @@ private slots: void newTrackGenerated( const Tomahawk::query_ptr& query ); void trackResolveFinished( bool ); - void trackResolved( bool ); void newTrackLoading(); void filteringTrackResolved( bool successful ); diff --git a/src/libtomahawk/playlist/dynamic/DynamicPlaylist.cpp b/src/libtomahawk/playlist/dynamic/DynamicPlaylist.cpp index 42a7d81f4..6186024ee 100644 --- a/src/libtomahawk/playlist/dynamic/DynamicPlaylist.cpp +++ b/src/libtomahawk/playlist/dynamic/DynamicPlaylist.cpp @@ -1,18 +1,20 @@ -/**************************************************************************************** - * Copyright (c) 2010 Leo Franchi * - * * - * This program is free software; you can redistribute it and/or modify it under * - * the terms of the GNU General Public License as published by the Free Software * - * Foundation; either version 2 of the License, or (at your option) any later * - * version. * - * * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY * - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * - * PARTICULAR PURPOSE. See the GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License along with * - * this program. If not, see . * - ****************************************************************************************/ +/* === 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 "DynamicPlaylist.h" @@ -251,8 +253,8 @@ DynamicPlaylist::reportCreated( const Tomahawk::dynplaylist_ptr& self ) Q_ASSERT( !author().isNull() ); Q_ASSERT( !author()->collection().isNull() ); // will emit Collection::playlistCreated(...) - qDebug() << "Creating dynplaylist belonging to:" << author().data() << author().isNull(); - qDebug() << "REPORTING DYNAMIC PLAYLIST CREATED:" << this << author()->friendlyName(); +// qDebug() << "Creating dynplaylist belonging to:" << author().data() << author().isNull(); +// qDebug() << "REPORTING DYNAMIC PLAYLIST CREATED:" << this << author()->friendlyName(); author()->collection()->addDynamicPlaylist( self ); } @@ -263,6 +265,8 @@ DynamicPlaylist::reportDeleted( const Tomahawk::dynplaylist_ptr& self ) Q_ASSERT( self.data() == this ); // will emit Collection::playlistDeleted(...) author()->collection()->deleteDynamicPlaylist( self ); + + emit deleted( self ); } void DynamicPlaylist::addEntries(const QList< query_ptr >& queries, const QString& oldrev) @@ -429,7 +433,7 @@ QList< dyncontrol_ptr > DynamicPlaylist::variantsToControl( const QList< QVarian QList realControls; foreach( QVariantMap controlV, controlsV ) { dyncontrol_ptr control = GeneratorFactory::createControl( controlV.value( "type" ).toString(), controlV.value( "selectedType" ).toString() ); - qDebug() << "CReating control with data:" << controlV; + qDebug() << "Creating control with data:" << controlV; QJson::QObjectHelper::qvariant2qobject( controlV, control.data() ); realControls << control; } diff --git a/src/libtomahawk/playlist/dynamic/DynamicPlaylist.h b/src/libtomahawk/playlist/dynamic/DynamicPlaylist.h index e8884cd0d..04cd2119e 100644 --- a/src/libtomahawk/playlist/dynamic/DynamicPlaylist.h +++ b/src/libtomahawk/playlist/dynamic/DynamicPlaylist.h @@ -1,18 +1,20 @@ -/**************************************************************************************** - * Copyright (c) 2010 Leo Franchi * - * * - * This program is free software; you can redistribute it and/or modify it under * - * the terms of the GNU General Public License as published by the Free Software * - * Foundation; either version 2 of the License, or (at your option) any later * - * version. * - * * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY * - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * - * PARTICULAR PURPOSE. See the GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License along with * - * this program. If not, see . * - ****************************************************************************************/ +/* === 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 DYNAMIC_PLAYLIST_H #define DYNAMIC_PLAYLIST_H @@ -103,7 +105,7 @@ public: // maybe friend QObjectHelper and make them private? explicit DynamicPlaylist( const source_ptr& author, const QString& type ); void setMode( int mode ); - void setType( const QString& type ) { /** TODO */; } + void setType( const QString& /*type*/ ) { /** TODO */; } void setGenerator( const geninterface_ptr& gen_ptr ); // @@ -111,6 +113,8 @@ signals: /// emitted when the playlist revision changes (whenever the playlist changes) void dynamicRevisionLoaded( Tomahawk::DynamicPlaylistRevision ); + void deleted( const Tomahawk::dynplaylist_ptr& pl ); + public slots: // want to update the playlist from the model? // generate a newrev using uuid() and call this: @@ -185,4 +189,4 @@ private: }; // namespace -#endif \ No newline at end of file +#endif diff --git a/src/libtomahawk/playlist/dynamic/DynamicView.cpp b/src/libtomahawk/playlist/dynamic/DynamicView.cpp index 0b48ca37f..ab1a67deb 100644 --- a/src/libtomahawk/playlist/dynamic/DynamicView.cpp +++ b/src/libtomahawk/playlist/dynamic/DynamicView.cpp @@ -1,18 +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 2 of the License, or (at your option) any later * - * version. * - * * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY * - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * - * PARTICULAR PURPOSE. See the GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License along with * - * this program. If not, see . * - ****************************************************************************************/ +/* === 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 "DynamicView.h" @@ -47,18 +49,18 @@ DynamicView::DynamicView( QWidget* parent ) 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 ); m_fadeOutAnim.setUpdateInterval( 5 ); - + QEasingCurve curve( QEasingCurve::OutBounce ); curve.setAmplitude( .25 ); m_slideAnim.setEasingCurve( curve ); m_slideAnim.setDirection( QTimeLine::Forward ); m_fadeOutAnim.setUpdateInterval( 5 ); - + connect( &m_fadeOutAnim, SIGNAL( frameChanged( int ) ), viewport(), SLOT( update() ) ); connect( &m_fadeOutAnim, SIGNAL( finished() ), this, SLOT( animFinished() ) ); } @@ -68,51 +70,51 @@ DynamicView::~DynamicView() } -void -DynamicView::setModel( DynamicModel* model) +void +DynamicView::setDynamicModel( DynamicModel* model) { m_model = model; - PlaylistView::setModel( model ); - - connect( model, SIGNAL( trackCountChanged( unsigned int ) ), SLOT( onTrackCountChanged( unsigned int ) ) ); - connect( model, SIGNAL( checkForOverflow() ), this, SLOT( checkForOverflow() ) ); + PlaylistView::setPlaylistModel( m_model ); + + connect( m_model, SIGNAL( trackCountChanged( unsigned int ) ), SLOT( onTrackCountChanged( unsigned int ) ) ); + connect( m_model, SIGNAL( checkForOverflow() ), this, SLOT( checkForOverflow() ) ); } void DynamicView::setOnDemand( bool onDemand ) { m_onDemand = onDemand; - + if( m_onDemand ) setVerticalScrollBarPolicy( Qt::ScrollBarAlwaysOff ); else setVerticalScrollBarPolicy( Qt::ScrollBarAsNeeded ); } -void +void DynamicView::setReadOnly( bool readOnly ) { m_readOnly = readOnly; } -void +void DynamicView::showMessageTimeout( const QString& title, const QString& body ) { m_title = title; m_body = body; - + overlay()->setText( QString( "%1:\n\n%2" ).arg( m_title, m_body ) ); overlay()->show( 10 ); } -void +void DynamicView::showMessage(const QString& message) { overlay()->setText( message ); overlay()->show(); } -void +void DynamicView::setDynamicWorking(bool working) { m_working = working; @@ -123,16 +125,16 @@ DynamicView::setDynamicWorking(bool working) } -void +void DynamicView::onTrackCountChanged( unsigned int tracks ) { if ( tracks == 0 && !m_working ) { if( m_onDemand ) { - if( m_readOnly ) - overlay()->setText( tr( "Press play to begin listening to this custom station!" ) ); + if( !m_readOnly ) + overlay()->setText( tr( "Add some filters above to seed this station!" ) ); else - overlay()->setText( tr( "Add some filters above, and press play to begin listening to this custom station!" ) ); + return; // when viewing a read-only station, don't show anything } else if( m_readOnly ) overlay()->setText( tr( "Press Generate to get started!" ) ); @@ -142,7 +144,6 @@ DynamicView::onTrackCountChanged( unsigned int tracks ) overlay()->show(); } else { - m_working = false; overlay()->hide(); } } @@ -172,7 +173,7 @@ DynamicView::checkForOverflow() } } -void +void DynamicView::collapseEntries( int startRow, int num, int numToKeep ) { qDebug() << "BEGINNING TO COLLAPSE FROM" << startRow << num << numToKeep; @@ -191,7 +192,7 @@ DynamicView::collapseEntries( int startRow, int num, int numToKeep ) } else { m_fadeOnly = false; } - + // we capture the image of the rows we're going to collapse // then we capture the image of the target row we're going to animate downwards // then we fade the first image out while sliding the second image up. @@ -203,7 +204,7 @@ DynamicView::collapseEntries( int startRow, int num, int numToKeep ) QRect fadingRectViewport = fadingRect; // all values that we use in paintEvent() have to be in viewport coords fadingRect.moveTo( viewport()->mapTo( this, fadingRect.topLeft() ) ); //fadingRect.setBottom( qMin( fadingRect.bottom(), viewport()->mapTo( this, viewport()->rect().bottomLeft() ).y() ) ); // limit what we capture to the viewport rect, if the last item is partially obscured - + m_fadingIndexes = QPixmap::grabWidget( this, fadingRect ); // but all values we use to grab the widgetr have to be in scrollarea coords :( m_fadingPointAnchor = QPoint( 0, fadingRectViewport.topLeft().y() ); @@ -211,7 +212,7 @@ DynamicView::collapseEntries( int startRow, int num, int numToKeep ) m_bg = backgroundBetween( m_fadingIndexes.rect(), startRow ); m_fadeOutAnim.start(); - + qDebug() << "Grabbed fading indexes from rect:" << fadingRect << m_fadingIndexes.size() << "ANCHORED:" << m_fadingPointAnchor; if( !m_fadeOnly ) { @@ -232,7 +233,7 @@ DynamicView::collapseEntries( int startRow, int num, int numToKeep ) QRect slidingRectViewport = slidingRect; // map internal view coord to external qscrollarea slidingRect.moveTo( viewport()->mapTo( this, slidingRect.topLeft() ) ); - + m_slidingIndex = QPixmap::grabWidget( this, slidingRect ); m_bottomAnchor = QPoint( 0, slidingRectViewport.topLeft().y() ); m_bottomOfAnim = QPoint( 0, slidingRectViewport.bottomLeft().y() ); @@ -245,7 +246,7 @@ DynamicView::collapseEntries( int startRow, int num, int numToKeep ) QTimer::singleShot( SLIDE_OFFSET, &m_slideAnim, SLOT( start() ) ); } - + // delete the actual indices QModelIndexList todel; for( int i = 0; i < num; i++ ) { @@ -293,11 +294,11 @@ DynamicView::animFinished() m_checkOnCollapse = false; } -void +void DynamicView::paintEvent( QPaintEvent* event ) { TrackView::paintEvent(event); - + QPainter p( viewport() ); if( m_fadeOutAnim.state() == QTimeLine::Running ) { // both run together p.save(); @@ -315,8 +316,8 @@ DynamicView::paintEvent( QPaintEvent* event ) // qDebug() << "FAST SETOPACITY:" << p.paintEngine()->hasFeature(QPaintEngine::ConstantOpacity); p.setOpacity( 1 - m_fadeOutAnim.currentValue() ); p.drawPixmap( m_fadingPointAnchor, m_fadingIndexes ); - p.restore(); - + p.restore(); + if( m_slideAnim.state() == QTimeLine::Running ) { // draw the collapsing entry p.drawPixmap( 0, m_slideAnim.currentFrame(), m_slidingIndex ); diff --git a/src/libtomahawk/playlist/dynamic/DynamicView.h b/src/libtomahawk/playlist/dynamic/DynamicView.h index 66ecabeee..39826fe97 100644 --- a/src/libtomahawk/playlist/dynamic/DynamicView.h +++ b/src/libtomahawk/playlist/dynamic/DynamicView.h @@ -1,18 +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 2 of the License, or (at your option) any later * - * version. * - * * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY * - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * - * PARTICULAR PURPOSE. See the GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License along with * - * this program. If not, see . * - ****************************************************************************************/ +/* === 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 DYNAMIC_VIEW_H #define DYNAMIC_VIEW_H @@ -38,7 +40,7 @@ public: explicit DynamicView( QWidget* parent = 0 ); virtual ~DynamicView(); - virtual void setModel( DynamicModel* model ); + virtual void setDynamicModel( DynamicModel* model ); void setOnDemand( bool onDemand ); void setReadOnly( bool readOnly ); diff --git a/src/libtomahawk/playlist/dynamic/GeneratorFactory.cpp b/src/libtomahawk/playlist/dynamic/GeneratorFactory.cpp index 8b33af936..dd4e44a96 100644 --- a/src/libtomahawk/playlist/dynamic/GeneratorFactory.cpp +++ b/src/libtomahawk/playlist/dynamic/GeneratorFactory.cpp @@ -1,3 +1,21 @@ +/* === 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 "dynamic/GeneratorFactory.h" #include "dynamic/GeneratorInterface.h" diff --git a/src/libtomahawk/playlist/dynamic/GeneratorFactory.h b/src/libtomahawk/playlist/dynamic/GeneratorFactory.h index 5e2120bfd..56a131cd1 100644 --- a/src/libtomahawk/playlist/dynamic/GeneratorFactory.h +++ b/src/libtomahawk/playlist/dynamic/GeneratorFactory.h @@ -1,3 +1,21 @@ +/* === 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 GENERATOR_FACTORY_H #define GENERATOR_FACTORY_H @@ -18,6 +36,8 @@ class DLLEXPORT GeneratorFactoryInterface { public: GeneratorFactoryInterface() {} + + virtual ~GeneratorFactoryInterface() {} virtual GeneratorInterface* create() = 0; /** diff --git a/src/libtomahawk/playlist/dynamic/GeneratorInterface.cpp b/src/libtomahawk/playlist/dynamic/GeneratorInterface.cpp index 6710c3af5..0ccf5888a 100644 --- a/src/libtomahawk/playlist/dynamic/GeneratorInterface.cpp +++ b/src/libtomahawk/playlist/dynamic/GeneratorInterface.cpp @@ -1,18 +1,20 @@ -/**************************************************************************************** - * Copyright (c) 2010 Leo Franchi * - * * - * This program is free software; you can redistribute it and/or modify it under * - * the terms of the GNU General Public License as published by the Free Software * - * Foundation; either version 2 of the License, or (at your option) any later * - * version. * - * * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY * - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * - * PARTICULAR PURPOSE. See the GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License along with * - * this program. If not, see . * - ****************************************************************************************/ +/* === 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 "dynamic/GeneratorInterface.h" @@ -69,6 +71,7 @@ void Tomahawk::GeneratorInterface::removeControl(const Tomahawk::dyncontrol_ptr& Tomahawk::dyncontrol_ptr Tomahawk::GeneratorInterface::createControl(const QString& type) { + Q_UNUSED( type ); Q_ASSERT( false ); return dyncontrol_ptr(); } diff --git a/src/libtomahawk/playlist/dynamic/GeneratorInterface.h b/src/libtomahawk/playlist/dynamic/GeneratorInterface.h index ac73d5bf2..238c14ff6 100644 --- a/src/libtomahawk/playlist/dynamic/GeneratorInterface.h +++ b/src/libtomahawk/playlist/dynamic/GeneratorInterface.h @@ -1,18 +1,20 @@ -/**************************************************************************************** - * Copyright (c) 2010-2011 Leo Franchi * - * * - * This program is free software; you can redistribute it and/or modify it under * - * the terms of the GNU General Public License as published by the Free Software * - * Foundation; either version 2 of the License, or (at your option) any later * - * version. * - * * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY * - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * - * PARTICULAR PURPOSE. See the GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License along with * - * this program. If not, see . * - ****************************************************************************************/ +/* === 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 GENERATOR_INTERFACE_H #define GENERATOR_INTERFACE_H @@ -68,7 +70,7 @@ public: * Connect to the generated() signal for the results. * */ - virtual void generate( int number = -1 ) {} + virtual void generate( int number = -1 ) { Q_UNUSED( number ); } /** * Starts an on demand session for this generator. Listen to the nextTrack() signal to get @@ -80,7 +82,7 @@ public: * Get the next on demand track. * \param rating Rating from 1-5, -1 for none */ - virtual void fetchNext( int rating = -1 ) {} + virtual void fetchNext( int rating = -1 ) { Q_UNUSED( rating ) } /** * Return a sentence that describes this generator's controls. TODO english only ATM diff --git a/src/libtomahawk/playlist/dynamic/echonest/EchonestControl.cpp b/src/libtomahawk/playlist/dynamic/echonest/EchonestControl.cpp index 787cf3bec..7a4c3fb47 100644 --- a/src/libtomahawk/playlist/dynamic/echonest/EchonestControl.cpp +++ b/src/libtomahawk/playlist/dynamic/echonest/EchonestControl.cpp @@ -1,18 +1,20 @@ -/**************************************************************************************** - * Copyright (c) 2010-2011 Leo Franchi * - * * - * This program is free software; you can redistribute it and/or modify it under * - * the terms of the GNU General Public License as published by the Free Software * - * Foundation; either version 2 of the License, or (at your option) any later * - * version. * - * * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY * - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * - * PARTICULAR PURPOSE. See the GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License along with * - * this program. If not, see . * - ****************************************************************************************/ +/* === 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 "dynamic/echonest/EchonestControl.h" diff --git a/src/libtomahawk/playlist/dynamic/echonest/EchonestControl.h b/src/libtomahawk/playlist/dynamic/echonest/EchonestControl.h index 357def005..405bed0c5 100644 --- a/src/libtomahawk/playlist/dynamic/echonest/EchonestControl.h +++ b/src/libtomahawk/playlist/dynamic/echonest/EchonestControl.h @@ -1,18 +1,20 @@ -/**************************************************************************************** - * Copyright (c) 2010 Leo Franchi * - * * - * This program is free software; you can redistribute it and/or modify it under * - * the terms of the GNU General Public License as published by the Free Software * - * Foundation; either version 2 of the License, or (at your option) any later * - * version. * - * * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY * - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * - * PARTICULAR PURPOSE. See the GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License along with * - * this program. If not, see . * - ****************************************************************************************/ +/* === 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 ECHONEST_CONTROL_H #define ECHONEST_CONTROL_H diff --git a/src/libtomahawk/playlist/dynamic/echonest/EchonestGenerator.cpp b/src/libtomahawk/playlist/dynamic/echonest/EchonestGenerator.cpp index 32d8448ab..6f07beb1d 100644 --- a/src/libtomahawk/playlist/dynamic/echonest/EchonestGenerator.cpp +++ b/src/libtomahawk/playlist/dynamic/echonest/EchonestGenerator.cpp @@ -1,18 +1,20 @@ -/**************************************************************************************** - * Copyright (c) 2010-2011 Leo Franchi * - * * - * This program is free software; you can redistribute it and/or modify it under * - * the terms of the GNU General Public License as published by the Free Software * - * Foundation; either version 2 of the License, or (at your option) any later * - * version. * - * * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY * - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * - * PARTICULAR PURPOSE. See the GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License along with * - * this program. If not, see . * - ****************************************************************************************/ +/* === 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 "dynamic/echonest/EchonestGenerator.h" #include "dynamic/echonest/EchonestControl.h" diff --git a/src/libtomahawk/playlist/dynamic/echonest/EchonestGenerator.h b/src/libtomahawk/playlist/dynamic/echonest/EchonestGenerator.h index dc5bd8420..30fbfba73 100644 --- a/src/libtomahawk/playlist/dynamic/echonest/EchonestGenerator.h +++ b/src/libtomahawk/playlist/dynamic/echonest/EchonestGenerator.h @@ -1,18 +1,20 @@ -/**************************************************************************************** - * Copyright (c) 2010-2011 Leo Franchi * - * * - * This program is free software; you can redistribute it and/or modify it under * - * the terms of the GNU General Public License as published by the Free Software * - * Foundation; either version 2 of the License, or (at your option) any later * - * version. * - * * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY * - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * - * PARTICULAR PURPOSE. See the GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License along with * - * this program. If not, see . * - ****************************************************************************************/ +/* === 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 ECHONEST_GENERATOR_H #define ECHONEST_GENERATOR_H diff --git a/src/libtomahawk/playlist/dynamic/echonest/EchonestSteerer.cpp b/src/libtomahawk/playlist/dynamic/echonest/EchonestSteerer.cpp index 6a02d4ccd..f8f36a34a 100644 --- a/src/libtomahawk/playlist/dynamic/echonest/EchonestSteerer.cpp +++ b/src/libtomahawk/playlist/dynamic/echonest/EchonestSteerer.cpp @@ -1,18 +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 2 of the License, or (at your option) any later * - * version. * - * * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY * - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * - * PARTICULAR PURPOSE. See the GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License along with * - * this program. If not, see . * - ****************************************************************************************/ +/* === 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 "dynamic/echonest/EchonestSteerer.h" @@ -23,7 +25,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/src/libtomahawk/playlist/dynamic/echonest/EchonestSteerer.h b/src/libtomahawk/playlist/dynamic/echonest/EchonestSteerer.h index c1a0400bd..614b12007 100644 --- a/src/libtomahawk/playlist/dynamic/echonest/EchonestSteerer.h +++ b/src/libtomahawk/playlist/dynamic/echonest/EchonestSteerer.h @@ -1,18 +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 2 of the License, or (at your option) any later * - * version. * - * * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY * - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * - * PARTICULAR PURPOSE. See the GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License along with * - * this program. If not, see . * - ****************************************************************************************/ +/* === 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 ECHONEST_STEERER_H #define ECHONEST_STEERER_H diff --git a/src/libtomahawk/playlist/dynamic/widgets/CollapsibleControls.cpp b/src/libtomahawk/playlist/dynamic/widgets/CollapsibleControls.cpp index c8d2585d3..65ac1564b 100644 --- a/src/libtomahawk/playlist/dynamic/widgets/CollapsibleControls.cpp +++ b/src/libtomahawk/playlist/dynamic/widgets/CollapsibleControls.cpp @@ -1,26 +1,29 @@ -/**************************************************************************************** - * Copyright (c) 2010-2011 Leo Franchi * - * * - * This program is free software; you can redistribute it and/or modify it under * - * the terms of the GNU General Public License as published by the Free Software * - * Foundation; either version 2 of the License, or (at your option) any later * - * version. * - * * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY * - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * - * PARTICULAR PURPOSE. See the GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License along with * - * this program. If not, see . * - ****************************************************************************************/ +/* === 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 "CollapsibleControls.h" -#include "tomahawk/tomahawkapp.h" #include "DynamicControlList.h" #include "DynamicControlWrapper.h" #include "dynamic/GeneratorInterface.h" #include "dynamic/DynamicControl.h" +#include "utils/tomahawkutils.h" +#include "utils/elidedlabel.h" #include #include @@ -83,7 +86,7 @@ CollapsibleControls::init() m_summaryLayout->setMargin( 0 ); m_summaryWidget->setContentsMargins( 3, 0, 0, 0 ); - m_summary = new QLabel( m_summaryWidget ); + m_summary = new ElidedLabel( m_summaryWidget ); QFont f = m_summary->font(); f.setPointSize( f.pointSize() + 1 ); f.setBold( true ); diff --git a/src/libtomahawk/playlist/dynamic/widgets/CollapsibleControls.h b/src/libtomahawk/playlist/dynamic/widgets/CollapsibleControls.h index 7b1121e30..a242769d0 100644 --- a/src/libtomahawk/playlist/dynamic/widgets/CollapsibleControls.h +++ b/src/libtomahawk/playlist/dynamic/widgets/CollapsibleControls.h @@ -1,18 +1,20 @@ -/**************************************************************************************** - * Copyright (c) 2010-2011 Leo Franchi * - * * - * This program is free software; you can redistribute it and/or modify it under * - * the terms of the GNU General Public License as published by the Free Software * - * Foundation; either version 2 of the License, or (at your option) any later * - * version. * - * * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY * - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * - * PARTICULAR PURPOSE. See the GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License along with * - * this program. If not, see . * - ****************************************************************************************/ +/* === 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 COLLAPSIBLE_CONTROLS_H #define COLLAPSIBLE_CONTROLS_H @@ -25,7 +27,7 @@ class QPaintEvent; class QHBoxLayout; class QTimeLine; class QToolButton; -class QLabel; +class ElidedLabel; class QStackedLayout; namespace Tomahawk { @@ -65,7 +67,7 @@ private: QWidget* m_summaryWidget; QHBoxLayout* m_summaryLayout; - QLabel* m_summary; + ElidedLabel* m_summary; QStackedLayout* m_expandL; QToolButton* m_summaryExpand; diff --git a/src/libtomahawk/playlist/dynamic/widgets/DynamicControlList.cpp b/src/libtomahawk/playlist/dynamic/widgets/DynamicControlList.cpp index d8bc65e93..5128562af 100644 --- a/src/libtomahawk/playlist/dynamic/widgets/DynamicControlList.cpp +++ b/src/libtomahawk/playlist/dynamic/widgets/DynamicControlList.cpp @@ -1,18 +1,20 @@ -/**************************************************************************************** - * Copyright (c) 2010 Leo Franchi * - * * - * This program is free software; you can redistribute it and/or modify it under * - * the terms of the GNU General Public License as published by the Free Software * - * Foundation; either version 2 of the License, or (at your option) any later * - * version. * - * * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY * - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * - * PARTICULAR PURPOSE. See the GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License along with * - * this program. If not, see . * - ****************************************************************************************/ +/* === 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 "DynamicControlList.h" @@ -23,11 +25,11 @@ #include #include #include +#include #include "DynamicControlWrapper.h" #include "dynamic/GeneratorInterface.h" -#include "tomahawk/tomahawkapp.h" -#include +#include "utils/tomahawkutils.h" using namespace Tomahawk; diff --git a/src/libtomahawk/playlist/dynamic/widgets/DynamicControlList.h b/src/libtomahawk/playlist/dynamic/widgets/DynamicControlList.h index 4b00f5e65..985a1ccba 100644 --- a/src/libtomahawk/playlist/dynamic/widgets/DynamicControlList.h +++ b/src/libtomahawk/playlist/dynamic/widgets/DynamicControlList.h @@ -1,18 +1,20 @@ -/**************************************************************************************** - * Copyright (c) 2010 Leo Franchi * - * * - * This program is free software; you can redistribute it and/or modify it under * - * the terms of the GNU General Public License as published by the Free Software * - * Foundation; either version 2 of the License, or (at your option) any later * - * version. * - * * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY * - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * - * PARTICULAR PURPOSE. See the GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License along with * - * this program. If not, see . * - ****************************************************************************************/ +/* === 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 DYNAMIC_CONTROL_LIST_H #define DYNAMIC_CONTROL_LIST_H diff --git a/src/libtomahawk/playlist/dynamic/widgets/DynamicControlWrapper.cpp b/src/libtomahawk/playlist/dynamic/widgets/DynamicControlWrapper.cpp index ad88959aa..c3ed4fed6 100644 --- a/src/libtomahawk/playlist/dynamic/widgets/DynamicControlWrapper.cpp +++ b/src/libtomahawk/playlist/dynamic/widgets/DynamicControlWrapper.cpp @@ -1,24 +1,27 @@ -/**************************************************************************************** - * Copyright (c) 2010-2011 Leo Franchi * - * * - * This program is free software; you can redistribute it and/or modify it under * - * the terms of the GNU General Public License as published by the Free Software * - * Foundation; either version 2 of the License, or (at your option) any later * - * version. * - * * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY * - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * - * PARTICULAR PURPOSE. See the GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License along with * - * this program. If not, see . * - ****************************************************************************************/ +/* === 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 "DynamicControlWrapper.h" -#include "tomahawk/tomahawkapp.h" #include "dynamic/DynamicControl.h" +#include "utils/tomahawkutils.h" +#include #include #include #include diff --git a/src/libtomahawk/playlist/dynamic/widgets/DynamicControlWrapper.h b/src/libtomahawk/playlist/dynamic/widgets/DynamicControlWrapper.h index c9c769c21..b0c3df109 100644 --- a/src/libtomahawk/playlist/dynamic/widgets/DynamicControlWrapper.h +++ b/src/libtomahawk/playlist/dynamic/widgets/DynamicControlWrapper.h @@ -1,18 +1,20 @@ -/**************************************************************************************** - * Copyright (c) 2010-2011 Leo Franchi * - * * - * This program is free software; you can redistribute it and/or modify it under * - * the terms of the GNU General Public License as published by the Free Software * - * Foundation; either version 2 of the License, or (at your option) any later * - * version. * - * * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY * - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * - * PARTICULAR PURPOSE. See the GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License along with * - * this program. If not, see . * - ****************************************************************************************/ +/* === 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 DYNAMIC_CONTROL_WRAPPER_H #define DYNAMIC_CONTROL_WRAPPER_H diff --git a/src/libtomahawk/playlist/dynamic/widgets/DynamicSetupWidget.cpp b/src/libtomahawk/playlist/dynamic/widgets/DynamicSetupWidget.cpp index a4fed6a6d..8eec49f0a 100644 --- a/src/libtomahawk/playlist/dynamic/widgets/DynamicSetupWidget.cpp +++ b/src/libtomahawk/playlist/dynamic/widgets/DynamicSetupWidget.cpp @@ -1,18 +1,20 @@ -/**************************************************************************************** - * Copyright (c) 2010-2011 Leo Franchi * - * * - * This program is free software; you can redistribute it and/or modify it under * - * the terms of the GNU General Public License as published by the Free Software * - * Foundation; either version 2 of the License, or (at your option) any later * - * version. * - * * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY * - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * - * PARTICULAR PURPOSE. See the GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License along with * - * this program. If not, see . * - ****************************************************************************************/ +/* === 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 "DynamicSetupWidget.h" @@ -102,7 +104,7 @@ DynamicSetupWidget::~DynamicSetupWidget() void DynamicSetupWidget::setPlaylist( const Tomahawk::dynplaylist_ptr& playlist ) { - + Q_UNUSED( playlist ); } void diff --git a/src/libtomahawk/playlist/dynamic/widgets/DynamicSetupWidget.h b/src/libtomahawk/playlist/dynamic/widgets/DynamicSetupWidget.h index 7bf7cf1c6..78568b19e 100644 --- a/src/libtomahawk/playlist/dynamic/widgets/DynamicSetupWidget.h +++ b/src/libtomahawk/playlist/dynamic/widgets/DynamicSetupWidget.h @@ -1,18 +1,20 @@ -/**************************************************************************************** - * Copyright (c) 2010-2011 Leo Franchi * - * * - * This program is free software; you can redistribute it and/or modify it under * - * the terms of the GNU General Public License as published by the Free Software * - * Foundation; either version 2 of the License, or (at your option) any later * - * version. * - * * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY * - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * - * PARTICULAR PURPOSE. See the GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License along with * - * this program. If not, see . * - ****************************************************************************************/ +/* === 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 DYNAMIC_SETUP_WIDGET_H #define DYNAMIC_SETUP_WIDGET_H diff --git a/src/libtomahawk/playlist/dynamic/widgets/DynamicWidget.cpp b/src/libtomahawk/playlist/dynamic/widgets/DynamicWidget.cpp index 5ec9d387d..60a4fc52d 100644 --- a/src/libtomahawk/playlist/dynamic/widgets/DynamicWidget.cpp +++ b/src/libtomahawk/playlist/dynamic/widgets/DynamicWidget.cpp @@ -1,18 +1,20 @@ -/**************************************************************************************** - * Copyright (c) 2010 Leo Franchi * - * * - * This program is free software; you can redistribute it and/or modify it under * - * the terms of the GNU General Public License as published by the Free Software * - * Foundation; either version 2 of the License, or (at your option) any later * - * version. * - * * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY * - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * - * PARTICULAR PURPOSE. See the GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License along with * - * this program. If not, see . * - ****************************************************************************************/ +/* === 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 "DynamicWidget.h" @@ -48,6 +50,7 @@ DynamicWidget::DynamicWidget( const Tomahawk::dynplaylist_ptr& playlist, QWidget , m_layout( new QVBoxLayout ) , m_resolveOnNextLoad( false ) , m_seqRevLaunched( 0 ) + , m_activePlaylist( false ) , m_setup( 0 ) , m_runningOnDemand( false ) , m_controlsChanged( false ) @@ -55,30 +58,30 @@ DynamicWidget::DynamicWidget( const Tomahawk::dynplaylist_ptr& playlist, QWidget , m_controls( 0 ) , m_view( 0 ) , m_model() -{ +{ m_controls = new CollapsibleControls( this ); m_layout->addWidget( m_controls ); setContentsMargins( 0, 0, 0, 1 ); // to align the bottom with the bottom of the sourcelist - + m_model = new DynamicModel( this ); m_view = new DynamicView( this ); - m_view->setModel( m_model ); + m_view->setDynamicModel( m_model ); m_view->setContentsMargins( 0, 0, 0, 0 ); m_layout->addWidget( m_view, 1 ); - + connect( m_model, SIGNAL( collapseFromTo( int, int ) ), m_view, SLOT( collapseEntries( int, int ) ) ); - connect( m_model, SIGNAL( trackGenerationFailure( QString ) ), this, SLOT( stationFailed( QString ) ) ); - - m_loading = new LoadingSpinner( m_view ); + connect( m_model, SIGNAL( trackGenerationFailure( QString ) ), this, SLOT( stationFailed( QString ) ) ); + + m_loading = new LoadingSpinner( m_view ); connect( m_model, SIGNAL( tracksAdded() ), m_loading, SLOT( fadeOut() ) ); - + m_setup = new DynamicSetupWidget( playlist, this ); m_setup->fadeIn(); - + connect( m_model, SIGNAL( tracksAdded() ), this, SLOT( tracksAdded() ) ); - + loadDynamicPlaylist( playlist ); - + m_layout->setContentsMargins( 0, 0, 0, 0 ); m_layout->setMargin( 0 ); m_layout->setSpacing( 0 ); @@ -93,14 +96,14 @@ DynamicWidget::DynamicWidget( const Tomahawk::dynplaylist_ptr& playlist, QWidget connect( m_controls, SIGNAL( controlsChanged() ), this, SLOT( controlsChanged() ), Qt::QueuedConnection ); connect( AudioEngine::instance(), SIGNAL( started( Tomahawk::result_ptr ) ), this, SLOT( trackStarted() ) ); - connect( AudioEngine::instance(), SIGNAL( playlistChanged( PlaylistInterface* ) ), this, SLOT( playlistStopped( PlaylistInterface* ) ) ); + connect( AudioEngine::instance(), SIGNAL( playlistChanged( PlaylistInterface* ) ), this, SLOT( playlistChanged( PlaylistInterface* ) ) ); } DynamicWidget::~DynamicWidget() { } -void +void DynamicWidget::loadDynamicPlaylist( const Tomahawk::dynplaylist_ptr& playlist ) { // special case: if we have launched multiple setRevision calls, and the number of controls is different, it means that we're getting an intermediate setRevision @@ -111,61 +114,64 @@ DynamicWidget::loadDynamicPlaylist( const Tomahawk::dynplaylist_ptr& playlist ) return; } m_seqRevLaunched = 0; - + // if we're being told to load the same dynamic playlist over again, only do it if the controls have a different number if( !m_playlist.isNull() && ( m_playlist.data() == playlist.data() ) // same playlist pointer && m_playlist->generator()->controls().size() == playlist->generator()->controls().size() ) { // we can skip our work. just let the dynamiccontrollist show the difference m_controls->setControls( m_playlist, m_playlist->author()->isLocal() ); - + m_playlist = playlist; - + if( !m_runningOnDemand ) { m_model->loadPlaylist( m_playlist ); } else if( !m_controlsChanged ) { // if the controls changed, we already dealt with that and don't want to change station yet m_model->changeStation(); } m_controlsChanged = false; - + return; } - + if( !m_playlist.isNull() ) { disconnect( m_playlist->generator().data(), SIGNAL( generated( QList ) ), this, SLOT( tracksGenerated( QList ) ) ); disconnect( m_playlist.data(), SIGNAL( dynamicRevisionLoaded( Tomahawk::DynamicPlaylistRevision) ), this, SLOT(onRevisionLoaded( Tomahawk::DynamicPlaylistRevision) ) ); disconnect( m_playlist->generator().data(), SIGNAL( error( QString, QString ) ), this, SLOT( generatorError( QString, QString ) ) ); + disconnect( m_playlist.data(), SIGNAL( deleted( Tomahawk::dynplaylist_ptr ) ), this, SLOT( onDeleted() ) ); } - - + + m_playlist = playlist; m_view->setOnDemand( m_playlist->mode() == OnDemand ); m_view->setReadOnly( !m_playlist->author()->isLocal() ); m_model->loadPlaylist( m_playlist ); m_controlsChanged = false; m_setup->setPlaylist( m_playlist ); - - + + if( !m_playlist->author()->isLocal() ) { // hide controls, as we show the description in the summary m_layout->removeWidget( m_controls ); } else if( m_layout->indexOf( m_controls ) == -1 ) { m_layout->insertWidget( 0, m_controls ); - } - + } + + if( m_playlist->mode() == OnDemand && !m_playlist->generator()->controls().isEmpty() ) + showPreview(); + if( !m_playlist.isNull() ) m_controls->setControls( m_playlist, m_playlist->author()->isLocal() ); - - if( m_playlist->mode() == OnDemand ) - showPreview(); - + connect( m_playlist->generator().data(), SIGNAL( generated( QList ) ), this, SLOT( tracksGenerated( QList ) ) ); connect( m_playlist.data(), SIGNAL( dynamicRevisionLoaded( Tomahawk::DynamicPlaylistRevision ) ), this, SLOT( onRevisionLoaded( Tomahawk::DynamicPlaylistRevision ) ) ); connect( m_playlist->generator().data(), SIGNAL( error( QString, QString ) ), this, SLOT( generatorError( QString, QString ) ) ); + connect( m_playlist.data(), SIGNAL( deleted( Tomahawk::dynplaylist_ptr ) ), this, SLOT( onDeleted() ) ); } -void +void DynamicWidget::onRevisionLoaded( const Tomahawk::DynamicPlaylistRevision& rev ) { + Q_UNUSED( rev ); qDebug() << "DynamicWidget::onRevisionLoaded"; loadDynamicPlaylist( m_playlist ); if( m_resolveOnNextLoad || !m_playlist->author()->isLocal() ) @@ -175,7 +181,7 @@ DynamicWidget::onRevisionLoaded( const Tomahawk::DynamicPlaylistRevision& rev ) } } -PlaylistInterface* +PlaylistInterface* DynamicWidget::playlistInterface() const { return m_view->proxyModel(); @@ -189,42 +195,44 @@ DynamicWidget::sizeHint() const return QSize( 5000, 5000 ); } -void +void DynamicWidget::resizeEvent(QResizeEvent* ) { layoutFloatingWidgets(); } -void +void DynamicWidget::layoutFloatingWidgets() { if( !m_runningOnDemand ) { int x = ( width() / 2 ) - ( m_setup->size().width() / 2 ); int y = height() - m_setup->size().height() - 40; // padding - + m_setup->move( x, y ); } else if( m_runningOnDemand && m_steering ) { int x = ( width() / 2 ) - ( m_steering->size().width() / 2 ); int y = height() - m_steering->size().height() - 40; // padding - + m_steering->move( x, y ); } } -void -DynamicWidget::playlistStopped( PlaylistInterface* pl ) +void +DynamicWidget::playlistChanged( PlaylistInterface* pl ) { - if( pl == static_cast< PlaylistInterface* >( m_view->proxyModel() ) ) // same playlist, so don't stop - return; - - // user started playing something somewhere else, so give it a rest - if( m_runningOnDemand ) { - stopStation( false ); - m_model->clear(); + if( pl == static_cast< PlaylistInterface* >( m_view->proxyModel() ) ) { // same playlist + m_activePlaylist = true; + } else { + m_activePlaylist = false; + + // user started playing something somewhere else, so give it a rest + if( m_runningOnDemand ) { + stopStation( false ); + } } } -void +void DynamicWidget::showEvent(QShowEvent* ) { if( !m_playlist.isNull() && !m_runningOnDemand ) { @@ -233,7 +241,7 @@ DynamicWidget::showEvent(QShowEvent* ) } -void +void DynamicWidget::generate( int num ) { // get the items from the generator, and put them in the playlist @@ -242,27 +250,27 @@ DynamicWidget::generate( int num ) m_playlist->generator()->generate( num ); } -void +void DynamicWidget::stationFailed( const QString& msg ) { m_view->setDynamicWorking( false ); m_view->showMessage( msg ); m_loading->fadeOut(); - + stopStation( false ); } -void +void DynamicWidget::trackStarted() -{ - if( isVisible() && !m_playlist.isNull() && +{ + if( m_activePlaylist && !m_playlist.isNull() && m_playlist->mode() == OnDemand && !m_runningOnDemand ) { - + startStation(); } } -void +void DynamicWidget::tracksAdded() { if( m_playlist->mode() == OnDemand && m_runningOnDemand && m_setup->isVisible() ) @@ -270,99 +278,104 @@ DynamicWidget::tracksAdded() } -void +void DynamicWidget::stopStation( bool stopPlaying ) { m_model->stopOnDemand( stopPlaying ); m_runningOnDemand = false; - + // TODO until i add a qwidget interface QMetaObject::invokeMethod( m_steering, "fadeOut", Qt::DirectConnection ); m_setup->fadeIn(); } -void +void DynamicWidget::startStation() { m_runningOnDemand = true; m_model->startOnDemand(); - + m_setup->fadeOut(); // show the steering controls if( m_playlist->generator()->onDemandSteerable() ) { // position it horizontally centered, above the botton. m_steering = m_playlist->generator()->steeringWidget(); Q_ASSERT( m_steering ); - + int x = ( width() / 2 ) - ( m_steering->size().width() / 2 ); int y = height() - m_steering->size().height() - 40; // padding - + m_steering->setParent( this ); m_steering->move( x, y ); - + // TODO until i add a qwidget interface QMetaObject::invokeMethod( m_steering, "fadeIn", Qt::DirectConnection ); - + connect( m_steering, SIGNAL( resized() ), this, SLOT( layoutFloatingWidgets() ) ); } } -void +void DynamicWidget::playlistTypeChanged( QString ) { // TODO } -void +void DynamicWidget::tracksGenerated( const QList< query_ptr >& queries ) -{ +{ int limit = -1; // only limit the "preview" of a station if( m_playlist->author()->isLocal() && m_playlist->mode() == Static ) { m_resolveOnNextLoad = true; } else if( m_playlist->mode() == OnDemand ) limit = 5; - + if( m_playlist->mode() != OnDemand ) m_loading->fadeOut(); m_model->tracksGenerated( queries, limit ); } -void +void DynamicWidget::controlsChanged() { // controlsChanged() is emitted when a control is added or removed // in the case of addition, it's blank by default... so to avoid an error // when playing a station just ignore it till we're ready and get a controlChanged() m_controlsChanged = true; - + if( !m_playlist->author()->isLocal() ) return; m_playlist->createNewRevision(); m_seqRevLaunched++; + + emit descriptionChanged( m_playlist->generator()->sentenceSummary() ); } -void +void DynamicWidget::controlChanged( const Tomahawk::dyncontrol_ptr& control ) -{ +{ + Q_UNUSED( control ); if( !m_playlist->author()->isLocal() ) - return; + return; m_playlist->createNewRevision(); m_seqRevLaunched++; - + showPreview(); + + emit descriptionChanged( m_playlist->generator()->sentenceSummary() ); } -void +void DynamicWidget::showPreview() { if( m_playlist->mode() == OnDemand && !m_runningOnDemand && m_model->rowCount( QModelIndex() ) == 0 ) { // if this is a not running station, preview matching tracks generate( 20 ); // ask for more, we'll filter how many we actually want -} + } } -void +void DynamicWidget::generatorError( const QString& title, const QString& content ) { if( m_runningOnDemand ) { @@ -375,17 +388,17 @@ DynamicWidget::generatorError( const QString& title, const QString& content ) void DynamicWidget::paintRoundedFilledRect( QPainter& p, QPalette& pal, QRect& r, qreal opacity ) -{ +{ p.setBackgroundMode( Qt::TransparentMode ); p.setRenderHint( QPainter::Antialiasing ); p.setOpacity( opacity ); - + QPen pen( pal.dark().color(), .5 ); p.setPen( pen ); p.setBrush( pal.highlight() ); - + p.drawRoundedRect( r, 10, 10 ); - + p.setOpacity( opacity + .2 ); p.setBrush( QBrush() ); p.setPen( pen ); @@ -396,4 +409,12 @@ bool DynamicWidget::jumpToCurrentTrack() { m_view->scrollTo( m_view->proxyModel()->currentItem(), QAbstractItemView::PositionAtCenter ); + return true; +} + +void +DynamicWidget::onDeleted() +{ + emit destroyed( widget() ); + deleteLater(); } diff --git a/src/libtomahawk/playlist/dynamic/widgets/DynamicWidget.h b/src/libtomahawk/playlist/dynamic/widgets/DynamicWidget.h index 2fca33856..6b8ef96e3 100644 --- a/src/libtomahawk/playlist/dynamic/widgets/DynamicWidget.h +++ b/src/libtomahawk/playlist/dynamic/widgets/DynamicWidget.h @@ -1,18 +1,20 @@ -/**************************************************************************************** - * Copyright (c) 2010 Leo Franchi * - * * - * This program is free software; you can redistribute it and/or modify it under * - * the terms of the GNU General Public License as published by the Free Software * - * Foundation; either version 2 of the License, or (at your option) any later * - * version. * - * * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY * - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * - * PARTICULAR PURPOSE. See the GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License along with * - * this program. If not, see . * - ****************************************************************************************/ +/* === 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 DYNAMIC_WIDGET_H #define DYNAMIC_WIDGET_H @@ -56,71 +58,78 @@ class CollapsibleControls; */ class DynamicWidget : public QWidget, public Tomahawk::ViewPage { -Q_OBJECT +Q_OBJECT public: explicit DynamicWidget( const dynplaylist_ptr& playlist, QWidget* parent = 0); virtual ~DynamicWidget(); - + void loadDynamicPlaylist( const dynplaylist_ptr& playlist ); - + virtual PlaylistInterface* playlistInterface() const; - + virtual QSize sizeHint() const; virtual void resizeEvent( QResizeEvent* ); virtual void showEvent(QShowEvent* ); - + static void paintRoundedFilledRect( QPainter& p, QPalette& pal, QRect& r, qreal opacity = .95 ); virtual QWidget* widget() { return this; } - + virtual QString title() const { return m_model->title(); } virtual QString description() const { return m_model->description(); } - + virtual QPixmap pixmap() const { return QPixmap( RESPATH "images/playlist-icon.png" ); } + virtual bool jumpToCurrentTrack(); - + public slots: void onRevisionLoaded( const Tomahawk::DynamicPlaylistRevision& rev ); void playlistTypeChanged(QString); - + void startStation(); void stopStation( bool stopPlaying = true ); - + void trackStarted(); void stationFailed( const QString& ); - - void playlistStopped( PlaylistInterface* ); + + void playlistChanged( PlaylistInterface* ); void tracksAdded(); - + +signals: + void descriptionChanged( const QString& caption ); + void destroyed( QWidget* widget ); + private slots: void generate( int = -1 ); void tracksGenerated( const QList< Tomahawk::query_ptr>& queries ); void generatorError( const QString& title, const QString& content ); - + void controlsChanged(); void controlChanged( const Tomahawk::dyncontrol_ptr& control ); void showPreview(); - - void layoutFloatingWidgets(); -private: + void layoutFloatingWidgets(); + void onDeleted(); + +private: dynplaylist_ptr m_playlist; QVBoxLayout* m_layout; bool m_resolveOnNextLoad; int m_seqRevLaunched; // if we shoot off multiple createRevision calls, we don'y want to set one of the middle ones + bool m_activePlaylist; // loading animation LoadingSpinner* m_loading; - + // setup controls DynamicSetupWidget* m_setup; - + // used in OnDemand mode bool m_runningOnDemand; bool m_controlsChanged; QWidget* m_steering; - + CollapsibleControls* m_controls; - + DynamicView* m_view; DynamicModel* m_model; }; diff --git a/src/libtomahawk/playlist/dynamic/widgets/LoadingSpinner.cpp b/src/libtomahawk/playlist/dynamic/widgets/LoadingSpinner.cpp index 8507572cb..b1bd4efc2 100644 --- a/src/libtomahawk/playlist/dynamic/widgets/LoadingSpinner.cpp +++ b/src/libtomahawk/playlist/dynamic/widgets/LoadingSpinner.cpp @@ -1,29 +1,31 @@ -/**************************************************************************************** - * Copyright (c) 2011 Leo Franchi * - * * - * This program is free software; you can redistribute it and/or modify it under * - * the terms of the GNU General Public License as published by the Free Software * - * Foundation; either version 2 of the License, or (at your option) any later * - * version. * - * * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY * - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * - * PARTICULAR PURPOSE. See the GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License along with * - * this program. If not, see . * - ****************************************************************************************/ +/* === 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 "LoadingSpinner.h" -#include "tomahawk/tomahawkapp.h" - #include #include #include #include #include +#include "utils/tomahawkutils.h" + #define ANIM_LENGTH 300 LoadingSpinner::LoadingSpinner( QWidget* parent ) @@ -79,7 +81,7 @@ LoadingSpinner::hideFinished() QSize LoadingSpinner::sizeHint() const { - return QSize( 64, 64 ); + return QSize( 31, 31 ); } void @@ -97,13 +99,14 @@ LoadingSpinner::reposition() int x = ( parentWidget()->width() / 2 ) - ( width() / 2 ); int y = ( parentWidget()->height() / 2 ) - ( height() / 2 ); move( x, y ); - resize( 64, 64 ); + resize( 31, 31 ); } void LoadingSpinner::paintEvent( QPaintEvent* ev ) { + Q_UNUSED( ev ); QPainter p( this ); // qDebug() << "FADING" << ( m_showHide->state() == QTimeLine::Running ) << "at frame:" << m_showHide->currentValue(); diff --git a/src/libtomahawk/playlist/dynamic/widgets/LoadingSpinner.h b/src/libtomahawk/playlist/dynamic/widgets/LoadingSpinner.h index 9c4ade008..0dde3520d 100644 --- a/src/libtomahawk/playlist/dynamic/widgets/LoadingSpinner.h +++ b/src/libtomahawk/playlist/dynamic/widgets/LoadingSpinner.h @@ -1,18 +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 2 of the License, or (at your option) any later * - * version. * - * * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY * - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * - * PARTICULAR PURPOSE. See the GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License along with * - * this program. If not, see . * - ****************************************************************************************/ +/* === 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 LOADING_SPINNER_H #define LOADING_SPINNER_H diff --git a/src/libtomahawk/playlist/dynamic/widgets/MiscControlWidgets.cpp b/src/libtomahawk/playlist/dynamic/widgets/MiscControlWidgets.cpp index 5357130e6..7ac128829 100644 --- a/src/libtomahawk/playlist/dynamic/widgets/MiscControlWidgets.cpp +++ b/src/libtomahawk/playlist/dynamic/widgets/MiscControlWidgets.cpp @@ -1,18 +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 2 of the License, or (at your option) any later * - * version. * - * * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY * - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * - * PARTICULAR PURPOSE. See the GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License along with * - * this program. If not, see . * - ****************************************************************************************/ +/* === 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 "MiscControlWidgets.h" diff --git a/src/libtomahawk/playlist/dynamic/widgets/MiscControlWidgets.h b/src/libtomahawk/playlist/dynamic/widgets/MiscControlWidgets.h index 1a6c383eb..245868264 100644 --- a/src/libtomahawk/playlist/dynamic/widgets/MiscControlWidgets.h +++ b/src/libtomahawk/playlist/dynamic/widgets/MiscControlWidgets.h @@ -1,18 +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 2 of the License, or (at your option) any later * - * version. * - * * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY * - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * - * PARTICULAR PURPOSE. See the GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License along with * - * this program. If not, see . * - ****************************************************************************************/ +/* === 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 MISC_CONTROL_WIDGETS_H #define MISC_CONTROL_WIDGETS_H diff --git a/src/libtomahawk/playlist/dynamic/widgets/ReadOrWriteWidget.cpp b/src/libtomahawk/playlist/dynamic/widgets/ReadOrWriteWidget.cpp index 2c74050ce..2e3da1aad 100644 --- a/src/libtomahawk/playlist/dynamic/widgets/ReadOrWriteWidget.cpp +++ b/src/libtomahawk/playlist/dynamic/widgets/ReadOrWriteWidget.cpp @@ -1,18 +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 2 of the License, or (at your option) any later * - * version. * - * * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY * - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * - * PARTICULAR PURPOSE. See the GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License along with * - * this program. If not, see . * - ****************************************************************************************/ +/* === 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 "ReadOrWriteWidget.h" diff --git a/src/libtomahawk/playlist/dynamic/widgets/ReadOrWriteWidget.h b/src/libtomahawk/playlist/dynamic/widgets/ReadOrWriteWidget.h index 25d5a9da8..5abbbca7a 100644 --- a/src/libtomahawk/playlist/dynamic/widgets/ReadOrWriteWidget.h +++ b/src/libtomahawk/playlist/dynamic/widgets/ReadOrWriteWidget.h @@ -1,18 +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 2 of the License, or (at your option) any later * - * version. * - * * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY * - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * - * PARTICULAR PURPOSE. See the GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License along with * - * this program. If not, see . * - ****************************************************************************************/ +/* === 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 READ_OR_WRITE_WIDGET_H #define READ_OR_WRITE_WIDGET_H diff --git a/src/libtomahawk/playlist/infobar/infobar.cpp b/src/libtomahawk/playlist/infobar/infobar.cpp index cb442b6ac..569c8a247 100644 --- a/src/libtomahawk/playlist/infobar/infobar.cpp +++ b/src/libtomahawk/playlist/infobar/infobar.cpp @@ -1,3 +1,21 @@ +/* === 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 "infobar.h" #include "ui_infobar.h" @@ -25,14 +43,20 @@ InfoBar::InfoBar( QWidget* parent ) boldFont.setPixelSize( 12 ); ui->descriptionLabel->setFont( boldFont ); - ui->descriptionLabel->setMargin( 2 ); + ui->descriptionLabel->setMargin( 10 ); QPalette whitePal = ui->captionLabel->palette(); whitePal.setColor( QPalette::Foreground, Qt::white ); ui->captionLabel->setPalette( whitePal ); ui->descriptionLabel->setPalette( whitePal ); + + ui->captionLabel->setText( QString() ); + ui->captionLabel->setMargin( 6 ); + ui->descriptionLabel->setText( QString() ); + ui->imageLabel->setText( QString() ); + setAutoFillBackground( true ); } diff --git a/src/libtomahawk/playlist/infobar/infobar.h b/src/libtomahawk/playlist/infobar/infobar.h index c46a1201e..4bd677c78 100644 --- a/src/libtomahawk/playlist/infobar/infobar.h +++ b/src/libtomahawk/playlist/infobar/infobar.h @@ -1,3 +1,21 @@ +/* === 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 INFOBAR_H #define INFOBAR_H diff --git a/src/libtomahawk/playlist/infobar/infobar.ui b/src/libtomahawk/playlist/infobar/infobar.ui index 043b8f5d7..764fd040a 100644 --- a/src/libtomahawk/playlist/infobar/infobar.ui +++ b/src/libtomahawk/playlist/infobar/infobar.ui @@ -74,7 +74,7 @@ - + 0 @@ -87,7 +87,7 @@ - + 0 @@ -119,6 +119,13 @@ + + + ElidedLabel + QLabel +
utils/elidedlabel.h
+
+
diff --git a/src/libtomahawk/playlist/playlistitemdelegate.cpp b/src/libtomahawk/playlist/playlistitemdelegate.cpp index 65476c516..e06bdb9c4 100644 --- a/src/libtomahawk/playlist/playlistitemdelegate.cpp +++ b/src/libtomahawk/playlist/playlistitemdelegate.cpp @@ -1,3 +1,21 @@ +/* === 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 "playlistitemdelegate.h" #include @@ -6,10 +24,10 @@ #include "query.h" #include "result.h" -#include "playlist/plitem.h" -#include "playlist/trackproxymodel.h" -#include "playlist/trackview.h" -#include "playlist/trackheader.h" +#include "trackmodelitem.h" +#include "trackproxymodel.h" +#include "trackview.h" +#include "trackheader.h" #include "utils/tomahawkutils.h" @@ -43,6 +61,9 @@ PlaylistItemDelegate::sizeHint( const QStyleOptionViewItem& option, const QModel QWidget* PlaylistItemDelegate::createEditor( QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index ) const { + Q_UNUSED( parent ); + Q_UNUSED( option ); + Q_UNUSED( index ); return 0; } @@ -50,22 +71,21 @@ PlaylistItemDelegate::createEditor( QWidget* parent, const QStyleOptionViewItem& void PlaylistItemDelegate::paint( QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index ) const { - PlItem* item = m_model->itemFromIndex( m_model->mapToSource( index ) ); + TrackModelItem* item = m_model->itemFromIndex( m_model->mapToSource( index ) ); if ( !item || item->query().isNull() ) return; - painter->save(); + float opacity = 0.0; if ( item->query()->results().count() ) - painter->setOpacity( item->query()->results().at( 0 )->score() ); - else - painter->setOpacity( 0.0 ); + opacity = item->query()->results().first()->score(); - if ( painter->opacity() < 0.3 ) - painter->setOpacity( 0.3 ); + opacity = qMax( (float)0.3, opacity ); + QColor textColor = TomahawkUtils::alphaBlend( option.palette.color( QPalette::Foreground ), option.palette.color( QPalette::Background ), opacity ); if ( item->isPlaying() ) { // painter->setRenderHint( QPainter::Antialiasing ); + painter->save(); { QRect r = option.rect.adjusted( 3, 0, 0, 0 ); @@ -92,11 +112,18 @@ PlaylistItemDelegate::paint( QPainter* painter, const QStyleOptionViewItem& opti painter->setPen( pen ); painter->drawRoundedRect( r, 3.0, 3.0 ); } + + painter->restore(); } else { - QStyledItemDelegate::paint( painter, option, index ); + if ( const QStyleOptionViewItem *vioption = qstyleoption_cast(&option)) + { + QStyleOptionViewItemV4 o( *vioption ); + o.palette.setColor( QPalette::Text, textColor ); + QStyledItemDelegate::paint( painter, o, index ); + } + else + QStyledItemDelegate::paint( painter, option, index ); } - - painter->restore(); } diff --git a/src/libtomahawk/playlist/playlistitemdelegate.h b/src/libtomahawk/playlist/playlistitemdelegate.h index 858c80a66..5ba66bbd9 100644 --- a/src/libtomahawk/playlist/playlistitemdelegate.h +++ b/src/libtomahawk/playlist/playlistitemdelegate.h @@ -1,3 +1,21 @@ +/* === 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 PLAYLISTITEMDELEGATE_H #define PLAYLISTITEMDELEGATE_H diff --git a/src/libtomahawk/playlist/playlistmanager.cpp b/src/libtomahawk/playlist/playlistmanager.cpp index 158de0f12..cbf1337cf 100644 --- a/src/libtomahawk/playlist/playlistmanager.cpp +++ b/src/libtomahawk/playlist/playlistmanager.cpp @@ -1,6 +1,25 @@ +/* === 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 "playlistmanager.h" #include +#include #include "audio/audioengine.h" #include "utils/animatedsplitter.h" @@ -9,7 +28,7 @@ #include "widgets/infowidgets/sourceinfowidget.h" #include "widgets/welcomewidget.h" -#include "collectionmodel.h" +#include "treemodel.h" #include "collectionflatmodel.h" #include "collectionview.h" #include "playlistmodel.h" @@ -17,6 +36,7 @@ #include "queueview.h" #include "trackproxymodel.h" #include "trackmodel.h" +#include "artistview.h" #include "albumview.h" #include "albumproxymodel.h" #include "albummodel.h" @@ -27,7 +47,6 @@ #include "widgets/welcomewidget.h" #include "widgets/infowidgets/sourceinfowidget.h" -#include "dynamic/widgets/LoadingSpinner.h" #define FILTER_TIMEOUT 280 @@ -48,7 +67,6 @@ PlaylistManager::PlaylistManager( QObject* parent ) , m_widget( new QWidget() ) , m_welcomeWidget( new WelcomeWidget() ) , m_currentMode( 0 ) - , m_loadingSpinner( 0 ) { s_instance = this; @@ -58,7 +76,7 @@ PlaylistManager::PlaylistManager( QObject* parent ) m_topbar = new TopBar(); m_infobar = new InfoBar(); m_stack = new QStackedWidget(); - + QFrame* line = new QFrame(); line->setFrameStyle( QFrame::HLine ); line->setStyleSheet( "border: 1px solid gray;" ); @@ -72,7 +90,7 @@ PlaylistManager::PlaylistManager( QObject* parent ) m_queueView = new QueueView( m_splitter ); m_queueModel = new PlaylistModel( m_queueView ); - m_queueView->queue()->setModel( m_queueModel ); + m_queueView->queue()->setPlaylistModel( m_queueModel ); AudioEngine::instance()->setQueue( m_queueView->queue()->proxyModel() ); m_splitter->addWidget( m_queueView ); @@ -83,27 +101,25 @@ PlaylistManager::PlaylistManager( QObject* parent ) m_widget->layout()->addWidget( line ); m_widget->layout()->addWidget( m_splitter ); - m_superCollectionView = new CollectionView(); - m_superCollectionFlatModel = new CollectionFlatModel( m_superCollectionView ); - m_superCollectionView->setModel( m_superCollectionFlatModel ); + m_superCollectionView = new ArtistView(); + m_superCollectionModel = new TreeModel( m_superCollectionView ); + m_superCollectionView->setModel( m_superCollectionModel ); m_superCollectionView->setFrameShape( QFrame::NoFrame ); m_superCollectionView->setAttribute( Qt::WA_MacShowFocusRect, 0 ); +// m_superCollectionView->proxyModel()->setShowOfflineResults( false ); m_superAlbumView = new AlbumView(); m_superAlbumModel = new AlbumModel( m_superAlbumView ); - m_superAlbumView->setModel( m_superAlbumModel ); + m_superAlbumView->setAlbumModel( m_superAlbumModel ); m_superAlbumView->setFrameShape( QFrame::NoFrame ); m_superAlbumView->setAttribute( Qt::WA_MacShowFocusRect, 0 ); - + m_stack->setContentsMargins( 0, 0, 0, 0 ); m_widget->setContentsMargins( 0, 0, 0, 0 ); m_widget->layout()->setContentsMargins( 0, 0, 0, 0 ); m_widget->layout()->setMargin( 0 ); m_widget->layout()->setSpacing( 0 ); - m_loadingSpinner = new LoadingSpinner( m_widget ); - connect( m_superCollectionFlatModel, SIGNAL( doneLoadingCollections() ), m_loadingSpinner, SLOT( fadeOut() ) ); - connect( &m_filterTimer, SIGNAL( timeout() ), SLOT( applyFilter() ) ); connect( m_topbar, SIGNAL( filterTextChanged( QString ) ), @@ -111,10 +127,10 @@ PlaylistManager::PlaylistManager( QObject* parent ) connect( m_topbar, SIGNAL( flatMode() ), SLOT( setTableMode() ) ); - + connect( m_topbar, SIGNAL( artistMode() ), SLOT( setTreeMode() ) ); - + connect( m_topbar, SIGNAL( albumMode() ), SLOT( setAlbumMode() ) ); } @@ -141,7 +157,7 @@ PlaylistManager::show( const Tomahawk::playlist_ptr& playlist ) { view = new PlaylistView(); PlaylistModel* model = new PlaylistModel(); - view->setModel( model ); + view->setPlaylistModel( model ); view->setFrameShape( QFrame::NoFrame ); view->setAttribute( Qt::WA_MacShowFocusRect, 0 ); model->loadPlaylist( playlist ); @@ -153,7 +169,7 @@ PlaylistManager::show( const Tomahawk::playlist_ptr& playlist ) { view = m_playlistViews.value( playlist ); } - + setPage( view ); TomahawkSettings::instance()->appendRecentlyPlayedPlaylist( playlist ); emit numSourcesChanged( SourceList::instance()->count() ); @@ -162,23 +178,25 @@ PlaylistManager::show( const Tomahawk::playlist_ptr& playlist ) } -bool +bool PlaylistManager::show( const Tomahawk::dynplaylist_ptr& playlist ) { if ( !m_dynamicWidgets.contains( playlist ) ) { m_dynamicWidgets[ playlist ] = new Tomahawk::DynamicWidget( playlist, m_stack ); + connect( playlist.data(), SIGNAL( deleted( Tomahawk::dynplaylist_ptr ) ), this, SLOT( onDynamicDeleted( Tomahawk::dynplaylist_ptr ) ) ); + playlist->resolve(); } - + setPage( m_dynamicWidgets.value( playlist ) ); if ( playlist->mode() == Tomahawk::OnDemand ) m_queueView->hide(); else m_queueView->show(); - + TomahawkSettings::instance()->appendRecentlyPlayedPlaylist( playlist ); emit numSourcesChanged( SourceList::instance()->count() ); @@ -195,7 +213,7 @@ PlaylistManager::show( const Tomahawk::artist_ptr& artist ) { view = new PlaylistView(); PlaylistModel* model = new PlaylistModel(); - view->setModel( model ); + view->setPlaylistModel( model ); view->setFrameShape( QFrame::NoFrame ); view->setAttribute( Qt::WA_MacShowFocusRect, 0 ); model->append( artist ); @@ -206,7 +224,7 @@ PlaylistManager::show( const Tomahawk::artist_ptr& artist ) { view = m_artistViews.value( artist ); } - + setPage( view ); emit numSourcesChanged( 1 ); @@ -222,7 +240,7 @@ PlaylistManager::show( const Tomahawk::album_ptr& album ) { view = new PlaylistView(); PlaylistModel* model = new PlaylistModel(); - view->setModel( model ); + view->setPlaylistModel( model ); view->setFrameShape( QFrame::NoFrame ); view->setAttribute( Qt::WA_MacShowFocusRect, 0 ); model->append( album ); @@ -233,7 +251,7 @@ PlaylistManager::show( const Tomahawk::album_ptr& album ) { view = m_albumViews.value( album ); } - + setPage( view ); emit numSourcesChanged( 1 ); @@ -244,6 +262,7 @@ PlaylistManager::show( const Tomahawk::album_ptr& album ) bool PlaylistManager::show( const Tomahawk::collection_ptr& collection ) { + qDebug() << Q_FUNC_INFO << m_currentMode; m_currentCollection = collection; if ( m_currentMode == 0 ) { @@ -252,13 +271,11 @@ PlaylistManager::show( const Tomahawk::collection_ptr& collection ) { view = new CollectionView(); CollectionFlatModel* model = new CollectionFlatModel(); - view->setModel( model ); + view->setTrackModel( model ); view->setFrameShape( QFrame::NoFrame ); view->setAttribute( Qt::WA_MacShowFocusRect, 0 ); + model->addCollection( collection ); - - m_loadingSpinner->fadeIn(); - connect( model, SIGNAL( doneLoadingCollections() ), m_loadingSpinner, SLOT( fadeOut() ) ); m_collectionViews.insert( collection, view ); } @@ -270,6 +287,29 @@ PlaylistManager::show( const Tomahawk::collection_ptr& collection ) setPage( view ); } + if ( m_currentMode == 1 ) + { + ArtistView* view; + if ( !m_treeViews.contains( collection ) ) + { + view = new ArtistView(); + TreeModel* model = new TreeModel(); + view->setModel( model ); + view->setFrameShape( QFrame::NoFrame ); + view->setAttribute( Qt::WA_MacShowFocusRect, 0 ); + + model->addCollection( collection ); + + m_treeViews.insert( collection, view ); + } + else + { + view = m_treeViews.value( collection ); + } + + setPage( view ); + } + if ( m_currentMode == 2 ) { AlbumView* aview; @@ -277,7 +317,7 @@ PlaylistManager::show( const Tomahawk::collection_ptr& collection ) { aview = new AlbumView(); AlbumModel* amodel = new AlbumModel( aview ); - aview->setModel( amodel ); + aview->setAlbumModel( amodel ); aview->setFrameShape( QFrame::NoFrame ); aview->setAttribute( Qt::WA_MacShowFocusRect, 0 ); amodel->addCollection( collection ); @@ -322,11 +362,6 @@ PlaylistManager::show( const Tomahawk::source_ptr& source ) bool PlaylistManager::show( ViewPage* page ) { - if ( m_stack->indexOf( page->widget() ) < 0 ) - { - connect( page->widget(), SIGNAL( destroyed( QWidget* ) ), SLOT( onWidgetDestroyed( QWidget* ) ) ); - } - setPage( page ); return true; @@ -336,32 +371,34 @@ PlaylistManager::show( ViewPage* page ) bool PlaylistManager::showSuperCollection() { - QList< collection_ptr > toAdd; + if ( m_superCollections.isEmpty() ) + m_superCollectionModel->addAllCollections(); + foreach( const Tomahawk::source_ptr& source, SourceList::instance()->sources() ) { if ( !m_superCollections.contains( source->collection() ) ) { m_superCollections.append( source->collection() ); - toAdd << source->collection(); - m_superAlbumModel->addCollection( source->collection() ); +// m_superAlbumModel->addCollection( source->collection() ); } - - m_superCollectionFlatModel->setTitle( tr( "All available tracks" ) ); - m_superAlbumModel->setTitle( tr( "All available albums" ) ); } - m_superCollectionFlatModel->addCollections( toAdd ); - + + m_superCollectionModel->setTitle( tr( "All available tracks" ) ); + m_superAlbumModel->setTitle( tr( "All available albums" ) ); + if ( m_currentMode == 0 ) { setPage( m_superCollectionView ); } + else if ( m_currentMode == 1 ) + { + setPage( m_superCollectionView ); + } else if ( m_currentMode == 2 ) { setPage( m_superAlbumView ); } - - m_loadingSpinner->fadeIn(); - + emit numSourcesChanged( m_superCollections.count() ); return true; @@ -392,8 +429,6 @@ PlaylistManager::setTableMode() void PlaylistManager::setTreeMode() { - return; - qDebug() << Q_FUNC_INFO; m_currentMode = 1; @@ -462,7 +497,7 @@ PlaylistManager::historyForward() { if ( m_historyPosition >= m_pageHistory.count() - 1 ) return; - + showHistory( m_historyPosition + 1 ); } @@ -479,6 +514,7 @@ PlaylistManager::showHistory( int historyPosition ) setHistoryPosition( historyPosition ); ViewPage* page = m_pageHistory.at( historyPosition ); + qDebug() << "Showing page after a deleting:" << page->widget()->metaObject()->className(); setPage( page, false ); } @@ -529,11 +565,11 @@ PlaylistManager::setPage( ViewPage* page, bool trackHistory ) setHistoryPosition( m_pageHistory.count() - 1 ); } - if ( playlistForInterface( currentPlaylistInterface() ) ) + if ( !playlistForInterface( currentPlaylistInterface() ).isNull() ) emit playlistActivated( playlistForInterface( currentPlaylistInterface() ) ); - if ( dynamicPlaylistForInterface( currentPlaylistInterface() ) ) + if ( !dynamicPlaylistForInterface( currentPlaylistInterface() ).isNull() ) emit dynamicPlaylistActivated( dynamicPlaylistForInterface( currentPlaylistInterface() ) ); - if ( collectionForInterface( currentPlaylistInterface() ) ) + if ( !collectionForInterface( currentPlaylistInterface() ).isNull() ) emit collectionActivated( collectionForInterface( currentPlaylistInterface() ) ); if ( isSuperCollectionVisible() ) emit superCollectionActivated(); @@ -543,6 +579,20 @@ PlaylistManager::setPage( ViewPage* page, bool trackHistory ) if ( !AudioEngine::instance()->isPlaying() ) AudioEngine::instance()->setPlaylist( currentPlaylistInterface() ); + // UGH! + if ( QObject* obj = dynamic_cast< QObject* >( currentPage() ) ) + { + // if the signal exists (just to hide the qobject runtime warning...) + if( obj->metaObject()->indexOfSignal( "descriptionChanged(QString)" ) > -1 ) + connect( obj, SIGNAL( descriptionChanged( QString ) ), m_infobar, SLOT( setDescription( QString ) ), Qt::UniqueConnection ); + } + if ( QObject* obj = dynamic_cast< QObject* >( currentPage() ) ) + { + // if the signal exists (just to hide the qobject runtime warning...) + if( obj->metaObject()->indexOfSignal( "destroyed(QWidget*)" ) > -1 ) + connect( obj, SIGNAL( destroyed( QWidget* ) ), SLOT( onWidgetDestroyed( QWidget* ) ), Qt::UniqueConnection ); + } + m_stack->setCurrentWidget( page->widget() ); updateView(); } @@ -602,7 +652,7 @@ PlaylistManager::updateView() emit modeChanged( currentPlaylistInterface()->viewMode() ); } - if ( currentPage()->queueVisible() ) + if ( currentPage()->queueVisible() ) m_queueView->show(); else m_queueView->hide(); @@ -624,14 +674,23 @@ PlaylistManager::updateView() void PlaylistManager::onWidgetDestroyed( QWidget* widget ) { - qDebug() << "Destroyed child:" << widget; + qDebug() << "Destroyed child:" << widget << widget->metaObject()->className(); bool resetWidget = ( m_stack->currentWidget() == widget ); - m_stack->removeWidget( widget ); for ( int i = 0; i < m_pageHistory.count(); i++ ) { ViewPage* page = m_pageHistory.at( i ); + + if ( !playlistForInterface( page->playlistInterface() ).isNull() ) + { + m_playlistViews.remove( playlistForInterface( page->playlistInterface() ) ); + } + if ( !dynamicPlaylistForInterface( page->playlistInterface() ).isNull() ) + { + m_dynamicWidgets.remove( dynamicPlaylistForInterface( page->playlistInterface() ) ); + } + if ( page->widget() == widget ) { m_pageHistory.removeAt( i ); @@ -641,6 +700,8 @@ PlaylistManager::onWidgetDestroyed( QWidget* widget ) } } + m_stack->removeWidget( widget ); + if ( resetWidget ) { if ( m_pageHistory.count() ) @@ -665,7 +726,7 @@ PlaylistManager::setShuffled( bool enabled ) } -void +void PlaylistManager::createPlaylist( const Tomahawk::source_ptr& src, const QVariant& contents ) { @@ -675,7 +736,7 @@ PlaylistManager::createPlaylist( const Tomahawk::source_ptr& src, } -void +void PlaylistManager::createDynamicPlaylist( const Tomahawk::source_ptr& src, const QVariant& contents ) { diff --git a/src/libtomahawk/playlist/playlistmanager.h b/src/libtomahawk/playlist/playlistmanager.h index a491fbd91..8ccf91f9d 100644 --- a/src/libtomahawk/playlist/playlistmanager.h +++ b/src/libtomahawk/playlist/playlistmanager.h @@ -1,3 +1,21 @@ +/* === 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 PLAYLISTMANAGER_H #define PLAYLISTMANAGER_H @@ -11,10 +29,10 @@ #include "dllmacro.h" -class LoadingSpinner; class AnimatedSplitter; class AlbumModel; class AlbumView; +class ArtistView; class CollectionModel; class CollectionFlatModel; class CollectionView; @@ -23,6 +41,8 @@ class PlaylistView; class QueueView; class TrackProxyModel; class TrackModel; +class TreeProxyModel; +class TreeModel; class TrackView; class SourceInfoWidget; class InfoBar; @@ -87,7 +107,7 @@ signals: void collectionActivated( const Tomahawk::collection_ptr& collection ); void playlistActivated( const Tomahawk::playlist_ptr& playlist ); void dynamicPlaylistActivated( const Tomahawk::dynplaylist_ptr& playlist ); - + public slots: bool showSuperCollection(); void showWelcomePage(); @@ -106,18 +126,19 @@ public slots: void setRepeatMode( PlaylistInterface::RepeatMode mode ); void setShuffled( bool enabled ); - + // called by the playlist creation dbcmds void createPlaylist( const Tomahawk::source_ptr& src, const QVariant& contents ); void createDynamicPlaylist( const Tomahawk::source_ptr& src, const QVariant& contents ); - + // ugh need to set up the connection in tomahawk to libtomahawk void onPlayClicked(); void onPauseClicked(); - + private slots: void setFilter( const QString& filter ); void applyFilter(); + void onWidgetDestroyed( QWidget* widget ); private: @@ -129,7 +150,7 @@ private: Tomahawk::playlist_ptr playlistForInterface( PlaylistInterface* interface ) const; Tomahawk::dynplaylist_ptr dynamicPlaylistForInterface( PlaylistInterface* interface ) const; Tomahawk::collection_ptr collectionForInterface( PlaylistInterface* interface ) const; - + QWidget* m_widget; InfoBar* m_infobar; TopBar* m_topbar; @@ -141,27 +162,27 @@ private: AlbumModel* m_superAlbumModel; AlbumView* m_superAlbumView; - CollectionFlatModel* m_superCollectionFlatModel; - CollectionView* m_superCollectionView; + TreeModel* m_superCollectionModel; + ArtistView* m_superCollectionView; WelcomeWidget* m_welcomeWidget; - LoadingSpinner* m_loadingSpinner; - + QList< Tomahawk::collection_ptr > m_superCollections; QHash< Tomahawk::dynplaylist_ptr, Tomahawk::DynamicWidget* > m_dynamicWidgets; QHash< Tomahawk::collection_ptr, CollectionView* > m_collectionViews; + QHash< Tomahawk::collection_ptr, ArtistView* > m_treeViews; QHash< Tomahawk::collection_ptr, AlbumView* > m_collectionAlbumViews; QHash< Tomahawk::artist_ptr, PlaylistView* > m_artistViews; QHash< Tomahawk::album_ptr, PlaylistView* > m_albumViews; QHash< Tomahawk::playlist_ptr, PlaylistView* > m_playlistViews; QHash< Tomahawk::source_ptr, SourceInfoWidget* > m_sourceViews; - + QList m_pageHistory; int m_historyPosition; Tomahawk::collection_ptr m_currentCollection; int m_currentMode; - + QTimer m_filterTimer; QString m_filter; diff --git a/src/libtomahawk/playlist/playlistmodel.cpp b/src/libtomahawk/playlist/playlistmodel.cpp index 2a72cb683..c171f2cb1 100644 --- a/src/libtomahawk/playlist/playlistmodel.cpp +++ b/src/libtomahawk/playlist/playlistmodel.cpp @@ -1,3 +1,21 @@ +/* === 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 "playlistmodel.h" #include @@ -53,18 +71,19 @@ void PlaylistModel::loadPlaylist( const Tomahawk::playlist_ptr& playlist, bool loadEntries ) { if ( !m_playlist.isNull() ) + { disconnect( m_playlist.data(), SIGNAL( revisionLoaded( Tomahawk::PlaylistRevision ) ), this, SLOT( onRevisionLoaded( Tomahawk::PlaylistRevision ) ) ); + disconnect( m_playlist.data(), SIGNAL( deleted( Tomahawk::playlist_ptr ) ), this, SIGNAL( playlistDeleted() ) ); + } if ( rowCount( QModelIndex() ) && loadEntries ) { - emit beginRemoveRows( QModelIndex(), 0, rowCount( QModelIndex() ) - 1 ); - delete m_rootItem; - emit endRemoveRows(); - m_rootItem = new PlItem( 0, this ); + clear(); } m_playlist = playlist; connect( playlist.data(), SIGNAL( revisionLoaded( Tomahawk::PlaylistRevision ) ), SLOT( onRevisionLoaded( Tomahawk::PlaylistRevision ) ) ); + connect( playlist.data(), SIGNAL( deleted( Tomahawk::playlist_ptr ) ), this, SIGNAL( playlistDeleted() ) ); setReadOnly( !m_playlist->author()->isLocal() ); setTitle( playlist->title() ); @@ -73,23 +92,28 @@ PlaylistModel::loadPlaylist( const Tomahawk::playlist_ptr& playlist, bool loadEn if ( !loadEntries ) return; - PlItem* plitem; + TrackModelItem* plitem; QList entries = playlist->entries(); if ( entries.count() ) { int c = rowCount( QModelIndex() ); qDebug() << "Starting loading" << playlist->title(); - emit loadingStarts(); emit beginInsertRows( QModelIndex(), c, c + entries.count() - 1 ); + m_waitingForResolved.clear(); foreach( const plentry_ptr& entry, entries ) { qDebug() << entry->query()->toString(); - plitem = new PlItem( entry, m_rootItem ); + plitem = new TrackModelItem( entry, m_rootItem ); plitem->index = createIndex( m_rootItem->children.count() - 1, 0, plitem ); connect( plitem, SIGNAL( dataChanged() ), SLOT( onDataChanged() ) ); + + if( !entry->query()->resolvingFinished() && !entry->query()->playable() ) { + m_waitingForResolved.append( entry->query().data() ); + connect( entry->query().data(), SIGNAL( resolvingFinished( bool ) ), this, SLOT( trackResolved( bool ) ) ); + } } emit endInsertRows(); @@ -97,7 +121,9 @@ PlaylistModel::loadPlaylist( const Tomahawk::playlist_ptr& playlist, bool loadEn else qDebug() << "Playlist seems empty:" << playlist->title(); - emit loadingFinished(); + if( !m_waitingForResolved.isEmpty() ) + emit loadingStarted(); + emit trackCountChanged( rowCount( QModelIndex() ) ); } @@ -107,10 +133,7 @@ PlaylistModel::loadHistory( const Tomahawk::source_ptr& source, unsigned int amo { if ( rowCount( QModelIndex() ) ) { - emit beginRemoveRows( QModelIndex(), 0, rowCount( QModelIndex() ) - 1 ); - delete m_rootItem; - emit endRemoveRows(); - m_rootItem = new PlItem( 0, this ); + clear(); } m_playlist.clear(); @@ -125,15 +148,17 @@ PlaylistModel::loadHistory( const Tomahawk::source_ptr& source, unsigned int amo Database::instance()->enqueue( QSharedPointer( cmd ) ); } -void + +void PlaylistModel::clear() { if ( rowCount( QModelIndex() ) ) { - emit beginRemoveRows( QModelIndex(), 0, rowCount( QModelIndex() ) - 1 ); + emit beginResetModel(); delete m_rootItem; - emit endRemoveRows(); - m_rootItem = new PlItem( 0, this ); + m_rootItem = 0; + emit endResetModel(); + m_rootItem = new TrackModelItem( 0, this ); } } @@ -189,6 +214,19 @@ PlaylistModel::insert( unsigned int row, const Tomahawk::query_ptr& query ) onTracksInserted( row, ql ); } +void +PlaylistModel::trackResolved( bool ) +{ + Tomahawk::Query* q = qobject_cast< Query* >( sender() ); + Q_ASSERT( q ); + + m_waitingForResolved.removeAll( q ); + disconnect( q, SIGNAL( resolvingFinished( bool ) ), this, SLOT( trackResolved( bool ) ) ); + + if( m_waitingForResolved.isEmpty() ) + emit loadingFinished(); +} + void PlaylistModel::onTracksAdded( const QList& tracks ) @@ -214,13 +252,13 @@ PlaylistModel::onTracksInserted( unsigned int row, const QListsetQuery( query ); - plitem = new PlItem( entry, m_rootItem, row + i ); + plitem = new TrackModelItem( entry, m_rootItem, row + i ); plitem->index = createIndex( row + i, 0, plitem ); i++; @@ -236,7 +274,7 @@ PlaylistModel::onTracksInserted( unsigned int row, const QListindex.isValid() ) emit dataChanged( p->index, p->index.sibling( p->index.row(), columnCount() - 1 ) ); } @@ -245,7 +283,6 @@ PlaylistModel::onDataChanged() void PlaylistModel::onRevisionLoaded( Tomahawk::PlaylistRevision revision ) { - qDebug() << "PLAYLIST::onRevisionLoaded"; qDebug() << Q_FUNC_INFO; if ( m_waitForUpdate ) @@ -261,6 +298,7 @@ PlaylistModel::onRevisionLoaded( Tomahawk::PlaylistRevision revision ) bool PlaylistModel::dropMimeData( const QMimeData* data, Qt::DropAction action, int row, int column, const QModelIndex& parent ) { + Q_UNUSED( column ); if ( action == Qt::IgnoreAction || isReadOnly() ) return true; @@ -313,7 +351,7 @@ PlaylistModel::dropMimeData( const QMimeData* data, Qt::DropAction action, int r e->setAnnotation( "" ); // FIXME e->setQuery( query ); - PlItem* plitem = new PlItem( e, m_rootItem, beginRow ); + TrackModelItem* plitem = new TrackModelItem( e, m_rootItem, beginRow ); plitem->index = createIndex( beginRow++, 0, plitem ); connect( plitem, SIGNAL( dataChanged() ), SLOT( onDataChanged() ) ); @@ -347,12 +385,18 @@ PlaylistModel::onPlaylistChanged( bool waitForUpdate ) m_waitForUpdate = waitForUpdate; QString newrev = uuid(); - if( dynplaylist_ptr dynplaylist = m_playlist.dynamicCast() ) { + if( dynplaylist_ptr dynplaylist = m_playlist.dynamicCast() ) + { if( dynplaylist->mode() == OnDemand ) + { dynplaylist->createNewRevision( newrev ); + } else if( dynplaylist->mode() == Static ) + { dynplaylist->createNewRevision( newrev, dynplaylist->currentrevision(), dynplaylist->type(), dynplaylist->generator()->controls(), l ); - } else { + } + } else + { m_playlist->createNewRevision( newrev, m_playlist->currentrevision(), l ); } } @@ -368,7 +412,7 @@ PlaylistModel::playlistEntries() const if ( !idx.isValid() ) continue; - PlItem* item = itemFromIndex( idx ); + TrackModelItem* item = itemFromIndex( idx ); if ( item ) l << item->entry(); } @@ -377,12 +421,16 @@ PlaylistModel::playlistEntries() const } +void +PlaylistModel::remove( unsigned int row, bool moreToCome ) +{ + removeIndex( index( row, 0, QModelIndex() ), moreToCome ); +} + + void PlaylistModel::removeIndex( const QModelIndex& index, bool moreToCome ) { - if ( isReadOnly() ) - return; - TrackModel::removeIndex( index ); if ( !moreToCome && !m_playlist.isNull() ) diff --git a/src/libtomahawk/playlist/playlistmodel.h b/src/libtomahawk/playlist/playlistmodel.h index ad8be01d5..c3ad13e09 100644 --- a/src/libtomahawk/playlist/playlistmodel.h +++ b/src/libtomahawk/playlist/playlistmodel.h @@ -1,10 +1,28 @@ +/* === 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 PLAYLISTMODEL_H #define PLAYLISTMODEL_H #include #include -#include "plitem.h" +#include "trackmodelitem.h" #include "trackmodel.h" #include "collection.h" #include "query.h" @@ -33,7 +51,7 @@ public: Tomahawk::playlist_ptr playlist() const { return m_playlist; } - void loadPlaylist( const Tomahawk::playlist_ptr& playlist, bool loadEntries = true ); + virtual void loadPlaylist( const Tomahawk::playlist_ptr& playlist, bool loadEntries = true ); void loadHistory( const Tomahawk::source_ptr& source, unsigned int amount = 50 ); void clear(); @@ -44,6 +62,7 @@ public: void insert( unsigned int row, const Tomahawk::query_ptr& query ); + void remove( unsigned int row, bool moreToCome = false ); virtual void removeIndex( const QModelIndex& index, bool moreToCome = false ); signals: @@ -52,8 +71,7 @@ signals: void itemSizeChanged( const QModelIndex& index ); - void loadingStarts(); - void loadingFinished(); + void playlistDeleted(); private slots: void onDataChanged(); @@ -64,11 +82,14 @@ private slots: void onTracksAdded( const QList& tracks ); void onTracksInserted( unsigned int row, const QList& tracks ); + void trackResolved( bool ); + private: QList playlistEntries() const; Tomahawk::playlist_ptr m_playlist; bool m_waitForUpdate; + QList< Tomahawk::Query* > m_waitingForResolved; }; #endif // PLAYLISTMODEL_H diff --git a/src/libtomahawk/playlist/playlistproxymodel.cpp b/src/libtomahawk/playlist/playlistproxymodel.cpp index d536d9fce..272721234 100644 --- a/src/libtomahawk/playlist/playlistproxymodel.cpp +++ b/src/libtomahawk/playlist/playlistproxymodel.cpp @@ -1,3 +1,21 @@ +/* === 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 "playlistproxymodel.h" diff --git a/src/libtomahawk/playlist/playlistproxymodel.h b/src/libtomahawk/playlist/playlistproxymodel.h index 9149c74b8..f0cddc616 100644 --- a/src/libtomahawk/playlist/playlistproxymodel.h +++ b/src/libtomahawk/playlist/playlistproxymodel.h @@ -1,3 +1,21 @@ +/* === 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 PLAYLISTPROXYMODEL_H #define PLAYLISTPROXYMODEL_H diff --git a/src/libtomahawk/playlist/playlistview.cpp b/src/libtomahawk/playlist/playlistview.cpp index 18abb0d01..175d0705a 100644 --- a/src/libtomahawk/playlist/playlistview.cpp +++ b/src/libtomahawk/playlist/playlistview.cpp @@ -1,3 +1,21 @@ +/* === 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 "playlistview.h" #include @@ -27,19 +45,29 @@ PlaylistView::~PlaylistView() void -PlaylistView::setModel( PlaylistModel* model ) +PlaylistView::setModel( QAbstractItemModel* model ) +{ + Q_UNUSED( model ); + qDebug() << "Explicitly use setPlaylistModel instead"; + Q_ASSERT( false ); +} + + +void +PlaylistView::setPlaylistModel( PlaylistModel* model ) { m_model = model; - TrackView::setModel( model ); + TrackView::setTrackModel( m_model ); setColumnHidden( 5, true ); // Hide age column per default - if ( !model->playlist().isNull() ) - setGuid( QString( "playlistview/%1" ).arg( model->playlist()->guid() ) ); + if ( !m_model->playlist().isNull() ) + setGuid( QString( "playlistview/%1" ).arg( m_model->playlist()->guid() ) ); else setGuid( "playlistview" ); - connect( model, SIGNAL( trackCountChanged( unsigned int ) ), SLOT( onTrackCountChanged( unsigned int ) ) ); + connect( m_model, SIGNAL( trackCountChanged( unsigned int ) ), SLOT( onTrackCountChanged( unsigned int ) ) ); + connect( m_model, SIGNAL( playlistDeleted() ), SLOT( onDeleted() ) ); } @@ -134,4 +162,14 @@ bool PlaylistView::jumpToCurrentTrack() { scrollTo( proxyModel()->currentItem(), QAbstractItemView::PositionAtCenter ); + return true; +} + + +void +PlaylistView::onDeleted() +{ + qDebug() << Q_FUNC_INFO; + emit destroyed( widget() ); + deleteLater(); } diff --git a/src/libtomahawk/playlist/playlistview.h b/src/libtomahawk/playlist/playlistview.h index 26a543316..84df64805 100644 --- a/src/libtomahawk/playlist/playlistview.h +++ b/src/libtomahawk/playlist/playlistview.h @@ -1,3 +1,21 @@ +/* === 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 PLAYLISTVIEW_H #define PLAYLISTVIEW_H @@ -21,16 +39,21 @@ public: ~PlaylistView(); PlaylistModel* playlistModel() const { return m_model; } - virtual void setModel( PlaylistModel* model ); + virtual void setPlaylistModel( PlaylistModel* model ); + virtual void setModel( QAbstractItemModel* model ); virtual QWidget* widget() { return this; } virtual PlaylistInterface* playlistInterface() const { return proxyModel(); } - + 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(); +signals: + void destroyed( QWidget* widget ); + protected: void keyPressEvent( QKeyEvent* event ); @@ -41,6 +64,8 @@ private slots: void addItemsToPlaylist(); void deleteItems(); + void onDeleted(); + private: void setupMenus(); diff --git a/src/libtomahawk/playlist/plitem.h b/src/libtomahawk/playlist/plitem.h deleted file mode 100644 index 4796e54c1..000000000 --- a/src/libtomahawk/playlist/plitem.h +++ /dev/null @@ -1,52 +0,0 @@ -#ifndef PLITEM_H -#define PLITEM_H - -#include -#include -#include -#include - -#include "query.h" -#include "typedefs.h" - -#include "dllmacro.h" - -class DLLEXPORT PlItem : public QObject -{ -Q_OBJECT - -public: - virtual ~PlItem(); - - explicit PlItem( PlItem* parent = 0, QAbstractItemModel* model = 0 ); - explicit PlItem( const QString& caption, PlItem* parent = 0 ); - explicit PlItem( const Tomahawk::query_ptr& query, PlItem* parent = 0, int row = -1 ); - explicit PlItem( const Tomahawk::plentry_ptr& entry, PlItem* 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(); } - - PlItem* parent; - QVector children; - QHash hash; - QString caption; - int childCount; - QPersistentModelIndex index; - QAbstractItemModel* model; - bool toberemoved; - -signals: - void dataChanged(); - -private: - void setupItem( const Tomahawk::query_ptr& query, PlItem* parent, int row = -1 ); - - Tomahawk::plentry_ptr m_entry; - Tomahawk::query_ptr m_query; - bool m_isPlaying; -}; - -#endif // PLITEM_H diff --git a/src/libtomahawk/playlist/queueproxymodel.cpp b/src/libtomahawk/playlist/queueproxymodel.cpp index 730cf80c8..88950089e 100644 --- a/src/libtomahawk/playlist/queueproxymodel.cpp +++ b/src/libtomahawk/playlist/queueproxymodel.cpp @@ -1,3 +1,21 @@ +/* === 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 "queueproxymodel.h" #include diff --git a/src/libtomahawk/playlist/queueproxymodel.h b/src/libtomahawk/playlist/queueproxymodel.h index d74229e75..fd240dfb8 100644 --- a/src/libtomahawk/playlist/queueproxymodel.h +++ b/src/libtomahawk/playlist/queueproxymodel.h @@ -1,3 +1,21 @@ +/* === 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 QUEUEPROXYMODEL_H #define QUEUEPROXYMODEL_H diff --git a/src/libtomahawk/playlist/queueview.cpp b/src/libtomahawk/playlist/queueview.cpp index 17c2b5f6a..1d060db73 100644 --- a/src/libtomahawk/playlist/queueview.cpp +++ b/src/libtomahawk/playlist/queueview.cpp @@ -1,3 +1,21 @@ +/* === 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 "queueview.h" #include diff --git a/src/libtomahawk/playlist/queueview.h b/src/libtomahawk/playlist/queueview.h index f43b717fc..f617831f9 100644 --- a/src/libtomahawk/playlist/queueview.h +++ b/src/libtomahawk/playlist/queueview.h @@ -1,3 +1,21 @@ +/* === 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 QUEUEVIEW_H #define QUEUEVIEW_H diff --git a/src/libtomahawk/playlist/topbar/topbar.cpp b/src/libtomahawk/playlist/topbar/topbar.cpp index 3aa584bf5..b60741a9e 100644 --- a/src/libtomahawk/playlist/topbar/topbar.cpp +++ b/src/libtomahawk/playlist/topbar/topbar.cpp @@ -1,3 +1,21 @@ +/* === 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 "topbar.h" #include "ui_topbar.h" @@ -54,8 +72,6 @@ TopBar::TopBar( QWidget* parent ) ui->radioDetailed->setFocusPolicy( Qt::NoFocus ); ui->radioCloud->setFocusPolicy( Qt::NoFocus ); - ui->radioDetailed->setEnabled( false ); - connect( ui->radioNormal, SIGNAL( clicked() ), SIGNAL( flatMode() ) ); connect( ui->radioDetailed, SIGNAL( clicked() ), SIGNAL( artistMode() ) ); connect( ui->radioCloud, SIGNAL( clicked() ), SIGNAL( albumMode() ) ); @@ -69,19 +85,19 @@ TopBar::TopBar( QWidget* parent ) connect( PlaylistManager::instance(), SIGNAL( numSourcesChanged( unsigned int ) ), SLOT( setNumSources( unsigned int ) ) ); - + connect( PlaylistManager::instance(), SIGNAL( numTracksChanged( unsigned int ) ), SLOT( setNumTracks( unsigned int ) ) ); - + connect( PlaylistManager::instance(), SIGNAL( numArtistsChanged( unsigned int ) ), SLOT( setNumArtists( unsigned int ) ) ); - + connect( PlaylistManager::instance(), SIGNAL( numShownChanged( unsigned int ) ), SLOT( setNumShown( unsigned int ) ) ); - + connect( PlaylistManager::instance(), SIGNAL( statsAvailable( bool ) ), SLOT( setStatsVisible( bool ) ) ); - + connect( PlaylistManager::instance(), SIGNAL( modesAvailable( bool ) ), SLOT( setModesVisible( bool ) ) ); diff --git a/src/libtomahawk/playlist/topbar/topbar.h b/src/libtomahawk/playlist/topbar/topbar.h index 439768029..c29b8b959 100644 --- a/src/libtomahawk/playlist/topbar/topbar.h +++ b/src/libtomahawk/playlist/topbar/topbar.h @@ -1,3 +1,21 @@ +/* === 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 TOPBAR_H #define TOPBAR_H diff --git a/src/libtomahawk/playlist/trackheader.cpp b/src/libtomahawk/playlist/trackheader.cpp index 42080b98b..750f207d2 100644 --- a/src/libtomahawk/playlist/trackheader.cpp +++ b/src/libtomahawk/playlist/trackheader.cpp @@ -1,3 +1,21 @@ +/* === 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 "trackheader.h" #include diff --git a/src/libtomahawk/playlist/trackheader.h b/src/libtomahawk/playlist/trackheader.h index 21b9e3243..2d62a1170 100644 --- a/src/libtomahawk/playlist/trackheader.h +++ b/src/libtomahawk/playlist/trackheader.h @@ -1,3 +1,21 @@ +/* === 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 TRACKHEADER_H #define TRACKHEADER_H diff --git a/src/libtomahawk/playlist/trackmodel.cpp b/src/libtomahawk/playlist/trackmodel.cpp index f071ea92d..51ae0590a 100644 --- a/src/libtomahawk/playlist/trackmodel.cpp +++ b/src/libtomahawk/playlist/trackmodel.cpp @@ -1,3 +1,21 @@ +/* === 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 "trackmodel.h" #include @@ -8,13 +26,14 @@ #include "utils/tomahawkutils.h" #include "album.h" +#include "pipeline.h" using namespace Tomahawk; TrackModel::TrackModel( QObject* parent ) : QAbstractItemModel( parent ) - , m_rootItem( new PlItem( 0, this ) ) + , m_rootItem( new TrackModelItem( 0, this ) ) , m_readOnly( true ) { qDebug() << Q_FUNC_INFO; @@ -26,7 +45,7 @@ TrackModel::TrackModel( QObject* parent ) TrackModel::~TrackModel() { - delete m_rootItem; +// delete m_rootItem; } @@ -36,8 +55,8 @@ TrackModel::index( int row, int column, const QModelIndex& parent ) const if ( !m_rootItem || row < 0 || column < 0 ) return QModelIndex(); - PlItem* parentItem = itemFromIndex( parent ); - PlItem* childItem = parentItem->children.value( row ); + TrackModelItem* parentItem = itemFromIndex( parent ); + TrackModelItem* childItem = parentItem->children.value( row ); if ( !childItem ) return QModelIndex(); @@ -51,7 +70,7 @@ TrackModel::rowCount( const QModelIndex& parent ) const if ( parent.column() > 0 ) return 0; - PlItem* parentItem = itemFromIndex( parent ); + TrackModelItem* parentItem = itemFromIndex( parent ); if ( !parentItem ) return 0; @@ -62,6 +81,7 @@ TrackModel::rowCount( const QModelIndex& parent ) const int TrackModel::columnCount( const QModelIndex& parent ) const { + Q_UNUSED( parent ); return 9; } @@ -69,15 +89,15 @@ TrackModel::columnCount( const QModelIndex& parent ) const QModelIndex TrackModel::parent( const QModelIndex& child ) const { - PlItem* entry = itemFromIndex( child ); + TrackModelItem* entry = itemFromIndex( child ); if ( !entry ) return QModelIndex(); - PlItem* parentEntry = entry->parent; + TrackModelItem* parentEntry = entry->parent; if ( !parentEntry ) return QModelIndex(); - PlItem* grandparentEntry = parentEntry->parent; + TrackModelItem* grandparentEntry = parentEntry->parent; if ( !grandparentEntry ) return QModelIndex(); @@ -89,7 +109,7 @@ TrackModel::parent( const QModelIndex& child ) const QVariant TrackModel::data( const QModelIndex& index, int role ) const { - PlItem* entry = itemFromIndex( index ); + TrackModelItem* entry = itemFromIndex( index ); if ( !entry ) return QVariant(); @@ -107,21 +127,6 @@ TrackModel::data( const QModelIndex& index, int role ) const return QVariant(); const query_ptr& query = entry->query(); - if ( query.isNull() ) - { - if ( !index.column() ) - { - return entry->caption.isEmpty() ? "Unknown" : entry->caption; - } - - if ( index.column() == 1 ) - { - return entry->childCount; - } - - return QVariant( "" ); - } - if ( !query->numResults() ) { switch( index.column() ) @@ -188,6 +193,7 @@ TrackModel::data( const QModelIndex& index, int role ) const QVariant TrackModel::headerData( int section, Qt::Orientation orientation, int role ) const { + Q_UNUSED( orientation ); QStringList headers; headers << tr( "Artist" ) << tr( "Track" ) << tr( "Album" ) << tr( "Duration" ) << tr( "Bitrate" ) << tr( "Age" ) << tr( "Year" ) << tr( "Size" ) << tr( "Origin" ); if ( role == Qt::DisplayRole && section >= 0 ) @@ -203,13 +209,13 @@ void TrackModel::setCurrentItem( const QModelIndex& index ) { qDebug() << Q_FUNC_INFO; - PlItem* oldEntry = itemFromIndex( m_currentIndex ); + TrackModelItem* oldEntry = itemFromIndex( m_currentIndex ); if ( oldEntry ) { oldEntry->setIsPlaying( false ); } - PlItem* entry = itemFromIndex( index ); + TrackModelItem* entry = itemFromIndex( index ); if ( entry ) { m_currentIndex = index; @@ -264,7 +270,7 @@ TrackModel::mimeData( const QModelIndexList &indexes ) const continue; QModelIndex idx = index( i.row(), 0, i.parent() ); - PlItem* item = itemFromIndex( idx ); + TrackModelItem* item = itemFromIndex( idx ); if ( item ) { const query_ptr& query = item->query(); @@ -284,7 +290,7 @@ TrackModel::removeIndex( const QModelIndex& index, bool moreToCome ) { if ( QThread::currentThread() != thread() ) { - qDebug() << "Reinvoking in correct thread:" << Q_FUNC_INFO; +// qDebug() << "Reinvoking in correct thread:" << Q_FUNC_INFO; QMetaObject::invokeMethod( this, "removeIndex", Qt::QueuedConnection, Q_ARG(const QModelIndex, index), @@ -293,12 +299,10 @@ TrackModel::removeIndex( const QModelIndex& index, bool moreToCome ) return; } - qDebug() << Q_FUNC_INFO; - if ( index.column() > 0 ) return; - PlItem* item = itemFromIndex( index ); + TrackModelItem* item = itemFromIndex( index ); if ( item ) { emit beginRemoveRows( index.parent(), index.row(), index.row() ); @@ -320,11 +324,11 @@ TrackModel::removeIndexes( const QList& indexes ) } -PlItem* +TrackModelItem* TrackModel::itemFromIndex( const QModelIndex& index ) const { if ( index.isValid() ) - return static_cast( index.internalPointer() ); + return static_cast( index.internalPointer() ); else { return m_rootItem; @@ -335,7 +339,7 @@ TrackModel::itemFromIndex( const QModelIndex& index ) const void TrackModel::onPlaybackFinished( const Tomahawk::result_ptr& result ) { - PlItem* oldEntry = itemFromIndex( m_currentIndex ); + TrackModelItem* oldEntry = itemFromIndex( m_currentIndex ); if ( oldEntry && !oldEntry->query().isNull() && oldEntry->query()->results().contains( result ) ) { oldEntry->setIsPlaying( false ); @@ -346,9 +350,22 @@ TrackModel::onPlaybackFinished( const Tomahawk::result_ptr& result ) void TrackModel::onPlaybackStopped() { - PlItem* oldEntry = itemFromIndex( m_currentIndex ); + 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->numResults() ) + Pipeline::instance()->resolve( query ); + } +} diff --git a/src/libtomahawk/playlist/trackmodel.h b/src/libtomahawk/playlist/trackmodel.h index 2f001d5b4..843e8f72a 100644 --- a/src/libtomahawk/playlist/trackmodel.h +++ b/src/libtomahawk/playlist/trackmodel.h @@ -1,10 +1,28 @@ +/* === 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 TRACKMODEL_H #define TRACKMODEL_H #include #include "playlistinterface.h" -#include "playlist/plitem.h" +#include "trackmodelitem.h" #include "dllmacro.h" @@ -58,11 +76,13 @@ public: virtual PlaylistInterface::RepeatMode repeatMode() const { return PlaylistInterface::NoRepeat; } virtual bool shuffled() const { return false; } + virtual void ensureResolved(); + virtual void append( const Tomahawk::query_ptr& query ) = 0; - PlItem* itemFromIndex( const QModelIndex& index ) const; + TrackModelItem* itemFromIndex( const QModelIndex& index ) const; - PlItem* m_rootItem; + TrackModelItem* m_rootItem; signals: void repeatModeChanged( PlaylistInterface::RepeatMode mode ); @@ -70,14 +90,17 @@ signals: void trackCountChanged( unsigned int tracks ); + void loadingStarted(); + void loadingFinished(); + public slots: virtual void setCurrentItem( const QModelIndex& index ); virtual void removeIndex( const QModelIndex& index, bool moreToCome = false ); virtual void removeIndexes( const QList& indexes ); - virtual void setRepeatMode( PlaylistInterface::RepeatMode mode ) {} - virtual void setShuffled( bool shuffled ) {} + virtual void setRepeatMode( PlaylistInterface::RepeatMode /*mode*/ ) {} + virtual void setShuffled( bool /*shuffled*/ ) {} protected: virtual void setReadOnly( bool b ) { m_readOnly = b; } diff --git a/src/libtomahawk/playlist/plitem.cpp b/src/libtomahawk/playlist/trackmodelitem.cpp similarity index 53% rename from src/libtomahawk/playlist/plitem.cpp rename to src/libtomahawk/playlist/trackmodelitem.cpp index b9fcdf916..b421bb3f8 100644 --- a/src/libtomahawk/playlist/plitem.cpp +++ b/src/libtomahawk/playlist/trackmodelitem.cpp @@ -1,4 +1,22 @@ -#include "plitem.h" +/* === 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 "utils/tomahawkutils.h" #include "playlist.h" @@ -9,7 +27,7 @@ using namespace Tomahawk; -PlItem::~PlItem() +TrackModelItem::~TrackModelItem() { // Don't use qDeleteAll here! The children will remove themselves // from the list when they get deleted and the qDeleteAll iterator @@ -24,7 +42,7 @@ PlItem::~PlItem() } -PlItem::PlItem( PlItem* parent, QAbstractItemModel* model ) +TrackModelItem::TrackModelItem( TrackModelItem* parent, QAbstractItemModel* model ) { this->parent = parent; this->model = model; @@ -38,51 +56,40 @@ PlItem::PlItem( PlItem* parent, QAbstractItemModel* model ) } -PlItem::PlItem( const QString& caption, PlItem* parent ) -{ - this->parent = parent; - this->caption = caption; - this->model = parent->model; - childCount = 0; - m_isPlaying = false; - toberemoved = false; - - if ( parent ) - { - parent->children.append( this ); - } -} - - -PlItem::PlItem( const Tomahawk::query_ptr& query, PlItem* parent, int row ) +TrackModelItem::TrackModelItem( const Tomahawk::query_ptr& query, TrackModelItem* parent, int row ) : QObject( parent ) { setupItem( query, parent, row ); } -PlItem::PlItem( const Tomahawk::plentry_ptr& entry, PlItem* parent, int 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& -PlItem::entry() const + +const Tomahawk::plentry_ptr& +TrackModelItem::entry() const { return m_entry; } -const Tomahawk::query_ptr& -PlItem::query() const + +const Tomahawk::query_ptr& +TrackModelItem::query() const { - if ( !m_entry.isNull() ) return m_entry->query(); else return m_query; + if ( !m_entry.isNull() ) + return m_entry->query(); + else + return m_query; } void -PlItem::setupItem( const Tomahawk::query_ptr& query, PlItem* parent, int row ) +TrackModelItem::setupItem( const Tomahawk::query_ptr& query, TrackModelItem* parent, int row ) { this->parent = parent; if ( parent ) @@ -105,7 +112,7 @@ PlItem::setupItem( const Tomahawk::query_ptr& query, PlItem* parent, int row ) m_query = query; if ( query->numResults() ) { - emit dataChanged(); +// emit dataChanged(); } else { @@ -114,7 +121,7 @@ PlItem::setupItem( const Tomahawk::query_ptr& query, PlItem* parent, int row ) connect( query.data(), SIGNAL( resultsRemoved( Tomahawk::result_ptr ) ), SIGNAL( dataChanged() ) ); - + connect( query.data(), SIGNAL( resultsChanged() ), SIGNAL( dataChanged() ) ); } diff --git a/src/libtomahawk/playlist/trackmodelitem.h b/src/libtomahawk/playlist/trackmodelitem.h new file mode 100644 index 000000000..3d0118c6c --- /dev/null +++ b/src/libtomahawk/playlist/trackmodelitem.h @@ -0,0 +1,67 @@ +/* === 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 "query.h" +#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 index 86ec2376f..2ed2c94b1 100644 --- a/src/libtomahawk/playlist/trackproxymodel.cpp +++ b/src/libtomahawk/playlist/trackproxymodel.cpp @@ -1,3 +1,21 @@ +/* === 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 "trackproxymodel.h" #include @@ -5,7 +23,6 @@ #include "album.h" #include "query.h" -#include "collectionmodel.h" TrackProxyModel::TrackProxyModel( QObject* parent ) @@ -14,6 +31,7 @@ TrackProxyModel::TrackProxyModel( QObject* parent ) , m_model( 0 ) , m_repeatMode( PlaylistInterface::NoRepeat ) , m_shuffled( false ) + , m_showOfflineResults( true ) { qsrand( QTime( 0, 0, 0 ).secsTo( QTime::currentTime() ) ); @@ -21,20 +39,28 @@ TrackProxyModel::TrackProxyModel( QObject* parent ) setSortCaseSensitivity( Qt::CaseInsensitive ); setDynamicSortFilter( true ); - setSourceModel( 0 ); + setSourceTrackModel( 0 ); } void -TrackProxyModel::setSourceModel( TrackModel* sourceModel ) +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 ) - connect( m_model, SIGNAL( trackCountChanged( unsigned int ) ), - SIGNAL( sourceTrackCountChanged( unsigned int ) ) ); + connect( m_model, SIGNAL( trackCountChanged( unsigned int ) ), + SIGNAL( sourceTrackCountChanged( unsigned int ) ) ); - QSortFilterProxyModel::setSourceModel( sourceModel ); + QSortFilterProxyModel::setSourceModel( m_model ); } @@ -57,7 +83,7 @@ TrackProxyModel::tracks() for ( int i = 0; i < rowCount( QModelIndex() ); i++ ) { - PlItem* item = itemFromIndex( mapToSource( index( i, 0 ) ) ); + TrackModelItem* item = itemFromIndex( mapToSource( index( i, 0 ) ) ); if ( item ) queries << item->query(); } @@ -116,9 +142,9 @@ TrackProxyModel::siblingItem( int itemsAway ) // Try to find the next available PlaylistItem (with results) if ( idx.isValid() ) do { - PlItem* item = itemFromIndex( mapToSource( idx ) ); + TrackModelItem* item = itemFromIndex( mapToSource( idx ) ); qDebug() << item->query()->toString(); - if ( item && item->query()->solved() ) + if ( item && item->query()->playable() ) { qDebug() << "Next PlaylistItem found:" << item->query()->toString() << item->query()->results().at( 0 )->url(); setCurrentItem( idx ); @@ -137,17 +163,20 @@ TrackProxyModel::siblingItem( int itemsAway ) bool TrackProxyModel::filterAcceptsRow( int sourceRow, const QModelIndex& sourceParent ) const { - PlItem* pi = itemFromIndex( sourceModel()->index( sourceRow, 0, sourceParent ) ); + 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 ( !r.isNull() && !r->collection()->source()->isOnline() ) -// return false; + if ( !m_showOfflineResults && !r.isNull() && !r->collection()->source()->isOnline() ) + return false; if ( filterRegExp().isEmpty() ) return true; diff --git a/src/libtomahawk/playlist/trackproxymodel.h b/src/libtomahawk/playlist/trackproxymodel.h index 7faab3086..853a512d8 100644 --- a/src/libtomahawk/playlist/trackproxymodel.h +++ b/src/libtomahawk/playlist/trackproxymodel.h @@ -1,3 +1,21 @@ +/* === 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 TRACKPROXYMODEL_H #define TRACKPROXYMODEL_H @@ -16,7 +34,8 @@ public: explicit TrackProxyModel ( QObject* parent = 0 ); virtual TrackModel* sourceModel() const { return m_model; } - virtual void setSourceModel( TrackModel* sourceModel ); + virtual void setSourceTrackModel( TrackModel* sourceModel ); + virtual void setSourceModel( QAbstractItemModel* model ); virtual QPersistentModelIndex currentItem() const { return mapFromSource( m_model->currentItem() ); } virtual void setCurrentItem( const QModelIndex& index ) { m_model->setCurrentItem( mapToSource( index ) ); } @@ -38,7 +57,10 @@ public: virtual PlaylistInterface::RepeatMode repeatMode() const { return m_repeatMode; } virtual bool shuffled() const { return m_shuffled; } - PlItem* itemFromIndex( const QModelIndex& index ) const { return sourceModel()->itemFromIndex( index ); } + bool showOfflineResults() const { return m_showOfflineResults; } + void setShowOfflineResults( bool b ) { m_showOfflineResults = b; } + + TrackModelItem* itemFromIndex( const QModelIndex& index ) const { return sourceModel()->itemFromIndex( index ); } signals: void repeatModeChanged( PlaylistInterface::RepeatMode mode ); @@ -60,6 +82,7 @@ private: TrackModel* m_model; RepeatMode m_repeatMode; bool m_shuffled; + bool m_showOfflineResults; }; #endif // TRACKPROXYMODEL_H diff --git a/src/libtomahawk/playlist/trackview.cpp b/src/libtomahawk/playlist/trackview.cpp index bdc139773..4016382d8 100644 --- a/src/libtomahawk/playlist/trackview.cpp +++ b/src/libtomahawk/playlist/trackview.cpp @@ -1,3 +1,21 @@ +/* === 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 "trackview.h" #include @@ -8,12 +26,14 @@ #include "audio/audioengine.h" #include "utils/tomahawkutils.h" #include "widgets/overlaywidget.h" +#include "dynamic/widgets/LoadingSpinner.h" #include "trackheader.h" #include "playlistmanager.h" #include "queueview.h" #include "trackmodel.h" #include "trackproxymodel.h" +#include using namespace Tomahawk; @@ -25,7 +45,9 @@ TrackView::TrackView( QWidget* parent ) , m_delegate( 0 ) , m_header( new TrackHeader( this ) ) , m_overlay( new OverlayWidget( this ) ) + , m_loadingSpinner( new LoadingSpinner( this ) ) , m_resizing( false ) + , m_dragging( false ) { setSortingEnabled( false ); setAlternatingRowColors( true ); @@ -50,6 +72,11 @@ TrackView::TrackView( QWidget* parent ) setFont( f ); #endif +#ifdef Q_WS_MAC + f.setPointSize( f.pointSize() - 2 ); + setFont( f ); +#endif + connect( this, SIGNAL( doubleClicked( QModelIndex ) ), SLOT( onItemActivated( QModelIndex ) ) ); } @@ -82,16 +109,28 @@ TrackView::setProxyModel( TrackProxyModel* model ) void -TrackView::setModel( TrackModel* model ) +TrackView::setModel( QAbstractItemModel* model ) +{ + Q_UNUSED( model ); + qDebug() << "Explicitly use setTrackModel instead"; + Q_ASSERT( false ); +} + + +void +TrackView::setTrackModel( TrackModel* model ) { m_model = model; if ( m_proxyModel ) { - m_proxyModel->setSourceModel( model ); + m_proxyModel->setSourceTrackModel( m_model ); } connect( m_model, SIGNAL( itemSizeChanged( QModelIndex ) ), SLOT( onItemResized( QModelIndex ) ) ); + 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 ) ) ); setAcceptDrops( true ); @@ -101,7 +140,7 @@ TrackView::setModel( TrackModel* model ) void TrackView::onItemActivated( const QModelIndex& index ) { - PlItem* item = m_model->itemFromIndex( m_proxyModel->mapToSource( index ) ); + TrackModelItem* item = m_model->itemFromIndex( m_proxyModel->mapToSource( index ) ); if ( item && item->query()->numResults() ) { qDebug() << "Result activated:" << item->query()->toString() << item->query()->results().first()->url(); @@ -150,7 +189,7 @@ TrackView::addItemsToQueue() if ( idx.column() ) continue; - PlItem* item = model()->itemFromIndex( proxyModel()->mapToSource( idx ) ); + TrackModelItem* item = model()->itemFromIndex( proxyModel()->mapToSource( idx ) ); if ( item && item->query()->numResults() ) { PlaylistManager::instance()->queue()->model()->append( item->query() ); diff --git a/src/libtomahawk/playlist/trackview.h b/src/libtomahawk/playlist/trackview.h index 337d004a5..aac8b2a11 100644 --- a/src/libtomahawk/playlist/trackview.h +++ b/src/libtomahawk/playlist/trackview.h @@ -1,3 +1,21 @@ +/* === 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 TRACKVIEW_H #define TRACKVIEW_H @@ -8,6 +26,7 @@ #include "dllmacro.h" +class LoadingSpinner; class PlaylistInterface; class TrackHeader; class TrackModel; @@ -25,7 +44,8 @@ public: virtual QString guid() const { return m_guid; } virtual void setGuid( const QString& guid ); - virtual void setModel( TrackModel* model ); + virtual void setTrackModel( TrackModel* model ); + virtual void setModel( QAbstractItemModel* model ); void setProxyModel( TrackProxyModel* model ); virtual TrackModel* model() const { return m_model; } @@ -48,7 +68,7 @@ protected: virtual void startDrag( Qt::DropActions supportedActions ); virtual void dragEnterEvent( QDragEnterEvent* event ); - virtual void dragLeaveEvent( QDragLeaveEvent* event ) { m_dragging = false; setDirtyRegion( m_dropRect ); } + virtual void dragLeaveEvent( QDragLeaveEvent* /*event*/ ) { m_dragging = false; setDirtyRegion( m_dropRect ); } virtual void dragMoveEvent( QDragMoveEvent* event ); virtual void dropEvent( QDropEvent* event ); @@ -67,6 +87,7 @@ private: PlaylistItemDelegate* m_delegate; TrackHeader* m_header; OverlayWidget* m_overlay; + LoadingSpinner* m_loadingSpinner; bool m_resizing; bool m_dragging; diff --git a/src/libtomahawk/playlist/treeheader.cpp b/src/libtomahawk/playlist/treeheader.cpp new file mode 100644 index 000000000..72bc75192 --- /dev/null +++ b/src/libtomahawk/playlist/treeheader.cpp @@ -0,0 +1,145 @@ +/* === 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 +#include +#include + +#include "tomahawksettings.h" +#include "playlist/treemodel.h" +#include "playlist/artistview.h" + + +TreeHeader::TreeHeader( ArtistView* parent ) + : QHeaderView( Qt::Horizontal, parent ) + , m_parent( parent ) + , m_menu( new QMenu( this ) ) + , m_sigmap( new QSignalMapper( this ) ) + , m_init( false ) +{ + setResizeMode( QHeaderView::Interactive ); + setMinimumSectionSize( 60 ); + setDefaultAlignment( Qt::AlignLeft ); + setMovable( true ); + setStretchLastSection( true ); +// setCascadingSectionResizes( true ); + +// m_menu->addAction( tr( "Resize columns to fit window" ), this, SLOT( onToggleResizeColumns() ) ); +// m_menu->addSeparator(); + + connect( this, SIGNAL( sectionResized( int, int, int ) ), SLOT( onSectionResized() ) ); + connect( m_sigmap, SIGNAL( mapped( int ) ), SLOT( toggleVisibility( int ) ) ); +} + + +TreeHeader::~TreeHeader() +{ +} + + +void +TreeHeader::onSectionResized() +{ + if ( !m_init ) + return; + + TomahawkSettings::instance()->setPlaylistColumnSizes( m_parent->guid(), saveState() ); +} + + +int +TreeHeader::visibleSectionCount() const +{ + return count() - hiddenSectionCount(); +} + + +void +TreeHeader::checkState() +{ + if ( !count() || m_init ) + return; + + QByteArray state = TomahawkSettings::instance()->playlistColumnSizes( m_parent->guid() ); + if ( !state.isEmpty() ) + restoreState( state ); + else + { + QList< double > m_columnWeights; + m_columnWeights << 0.50 << 0.07 << 0.07 << 0.07 << 0.07 << 0.07; // << 0.12; + + for ( int i = 0; i < count() - 1; i++ ) + { + if ( isSectionHidden( i ) ) + continue; + + double nw = (double)m_parent->width() * m_columnWeights.at( i ); + qDebug() << "Setting default size:" << i << nw; + resizeSection( i, qMax( minimumSectionSize(), int( nw - 0.5 ) ) ); + } + } + + m_init = true; +} + + +void +TreeHeader::addColumnToMenu( int index ) +{ + QString title = m_parent->model()->headerData( index, Qt::Horizontal, Qt::DisplayRole ).toString(); + + QAction* action = m_menu->addAction( title, m_sigmap, SLOT( map() ) ); + action->setCheckable( true ); + action->setChecked( !isSectionHidden( index ) ); + m_visActions << action; + + m_sigmap->setMapping( action, index ); +} + + +void +TreeHeader::contextMenuEvent( QContextMenuEvent* e ) +{ + qDeleteAll( m_visActions ); + m_visActions.clear(); + + for ( int i = 0; i < count(); i++ ) + addColumnToMenu( i ); + + m_menu->popup( e->globalPos() ); +} + + +void +TreeHeader::onToggleResizeColumns() +{ +} + + +void +TreeHeader::toggleVisibility( int index ) +{ + qDebug() << Q_FUNC_INFO << index; + + if ( isSectionHidden( index ) ) + showSection( index ); + else + hideSection( index ); +} diff --git a/src/libtomahawk/playlist/treeheader.h b/src/libtomahawk/playlist/treeheader.h new file mode 100644 index 000000000..73a81ada6 --- /dev/null +++ b/src/libtomahawk/playlist/treeheader.h @@ -0,0 +1,61 @@ +/* === 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 TREEHEADER_H +#define TREEHEADER_H + +#include +#include + +#include "dllmacro.h" + +class ArtistView; + +class DLLEXPORT TreeHeader : public QHeaderView +{ +Q_OBJECT + +public: + explicit TreeHeader( ArtistView* parent = 0 ); + ~TreeHeader(); + + int visibleSectionCount() const; + +public slots: + void toggleVisibility( int index ); + void checkState(); + +protected: + void contextMenuEvent( QContextMenuEvent* e ); + +private slots: + void onSectionResized(); + void onToggleResizeColumns(); + +private: + void addColumnToMenu( int index ); + + ArtistView* m_parent; + + QMenu* m_menu; + QSignalMapper* m_sigmap; + QList m_visActions; + bool m_init; +}; + +#endif diff --git a/src/libtomahawk/playlist/treeitemdelegate.cpp b/src/libtomahawk/playlist/treeitemdelegate.cpp new file mode 100644 index 000000000..50ac99979 --- /dev/null +++ b/src/libtomahawk/playlist/treeitemdelegate.cpp @@ -0,0 +1,119 @@ +/* === 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 "treeitemdelegate.h" + +#include +#include +#include +#include + +#include "query.h" +#include "result.h" + +#include "utils/tomahawkutils.h" + +#include "treemodelitem.h" +#include "treeproxymodel.h" + + +TreeItemDelegate::TreeItemDelegate( QAbstractItemView* parent, TreeProxyModel* proxy ) + : QStyledItemDelegate( (QObject*)parent ) + , m_view( parent ) + , m_model( proxy ) +{ +} + + +QSize +TreeItemDelegate::sizeHint( const QStyleOptionViewItem& option, const QModelIndex& index ) const +{ + QSize size = QStyledItemDelegate::sizeHint( option, index ); + return size; +} + + +void +TreeItemDelegate::paint( QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index ) const +{ + TreeModelItem* item = m_model->sourceModel()->itemFromIndex( m_model->mapToSource( index ) ); + if ( !item ) + return; + + QString text; + if ( !item->artist().isNull() ) + { + text = item->artist()->name(); + } + else if ( !item->album().isNull() ) + { + text = item->album()->name(); + } + else if ( !item->result().isNull() ) + { + float opacity = item->result()->score(); + opacity = qMax( (float)0.3, opacity ); + QColor textColor = TomahawkUtils::alphaBlend( option.palette.color( QPalette::Foreground ), option.palette.color( QPalette::Background ), opacity ); + + if ( const QStyleOptionViewItem *vioption = qstyleoption_cast(&option)) + { + QStyleOptionViewItemV4 o( *vioption ); + o.palette.setColor( QPalette::Text, textColor ); + return QStyledItemDelegate::paint( painter, o, index ); + } + } + else + return; + + if ( text.trimmed().isEmpty() ) + text = tr( "Unknown" ); + + QStyleOptionViewItemV4 opt = option; + initStyleOption( &opt, QModelIndex() ); + qApp->style()->drawControl( QStyle::CE_ItemViewItem, &opt, painter ); + + if ( option.state & QStyle::State_Selected ) + { + opt.palette.setColor( QPalette::Text, opt.palette.color( QPalette::HighlightedText ) ); + } + + if ( index.column() > 0 ) + return; + + painter->save(); + painter->setRenderHint( QPainter::Antialiasing ); + painter->setPen( opt.palette.color( QPalette::Text ) ); + + QRect r = option.rect.adjusted( 4, 4, -option.rect.width() + option.rect.height() - 4, -4 ); +// painter->drawPixmap( option.rect.adjusted( 4, 4, -4, -38 ), QPixmap( RESPATH "images/cover-shadow.png" ) ); + painter->drawPixmap( r, item->cover ); + + QTextOption to; + to.setAlignment( Qt::AlignVCenter ); + QFont font = opt.font; + QFont boldFont = opt.font; + boldFont.setBold( true ); + + r = option.rect.adjusted( option.rect.height(), 6, -4, -option.rect.height() + 22 ); + text = painter->fontMetrics().elidedText( text, Qt::ElideRight, r.width() ); + painter->drawText( r, text, to ); + +// painter->setFont( boldFont ); + + painter->restore(); +} diff --git a/src/libtomahawk/playlist/treeitemdelegate.h b/src/libtomahawk/playlist/treeitemdelegate.h new file mode 100644 index 000000000..234b0d326 --- /dev/null +++ b/src/libtomahawk/playlist/treeitemdelegate.h @@ -0,0 +1,46 @@ +/* === 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 TREEITEMDELEGATE_H +#define TREEITEMDELEGATE_H + +#include + +#include "dllmacro.h" + +class TreeProxyModel; + +class DLLEXPORT TreeItemDelegate : public QStyledItemDelegate +{ +Q_OBJECT + +public: + TreeItemDelegate( QAbstractItemView* parent = 0, TreeProxyModel* proxy = 0 ); + +protected: + void paint( QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index ) const; + QSize sizeHint( const QStyleOptionViewItem& option, const QModelIndex& index ) const; + +// QWidget* createEditor( QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index ) const; + +private: + QAbstractItemView* m_view; + TreeProxyModel* m_model; +}; + +#endif // TREEITEMDELEGATE_H diff --git a/src/libtomahawk/playlist/treemodel.cpp b/src/libtomahawk/playlist/treemodel.cpp new file mode 100644 index 000000000..7d7974974 --- /dev/null +++ b/src/libtomahawk/playlist/treemodel.cpp @@ -0,0 +1,592 @@ +/* === 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 "treemodel.h" + +#include +#include +#include +#include + +#include "database/databasecommand_allalbums.h" +#include "database/databasecommand_alltracks.h" +#include "database/database.h" +#include "utils/tomahawkutils.h" + +static QString s_tmInfoIdentifier = QString( "TREEMODEL" ); + +using namespace Tomahawk; + + +TreeModel::TreeModel( QObject* parent ) + : QAbstractItemModel( parent ) + , m_rootItem( new TreeModelItem( 0, this ) ) +{ + qDebug() << Q_FUNC_INFO; + + m_defaultCover = QPixmap( RESPATH "images/no-album-art-placeholder.png" ) + .scaled( QSize( 120, 120 ), Qt::IgnoreAspectRatio, Qt::SmoothTransformation ); + + connect( Tomahawk::InfoSystem::InfoSystem::instance(), + SIGNAL( info( QString, Tomahawk::InfoSystem::InfoType, QVariant, QVariant, Tomahawk::InfoSystem::InfoCustomData ) ), + SLOT( infoSystemInfo( QString, Tomahawk::InfoSystem::InfoType, QVariant, QVariant, Tomahawk::InfoSystem::InfoCustomData ) ) ); + + connect( Tomahawk::InfoSystem::InfoSystem::instance(), SIGNAL( finished( QString ) ), SLOT( infoSystemFinished( QString ) ) ); +} + + +TreeModel::~TreeModel() +{ +} + + +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 ); + + if ( parentItem->fetchingMore ) + return false; + + if ( !parentItem->artist().isNull() ) + { + return true; + } + else if ( !parentItem->album().isNull() ) + { + return true; + } + + return false; +} + + +void +TreeModel::fetchMore( const QModelIndex& parent ) +{ + TreeModelItem* parentItem = itemFromIndex( parent ); + if ( !parentItem || parentItem->fetchingMore ) + return; + + parentItem->fetchingMore = true; + if ( !parentItem->artist().isNull() ) + { + qDebug() << Q_FUNC_INFO << "Loading Artist:" << parentItem->artist()->name(); + addAlbums( parentItem->artist(), parent ); + } + else if ( !parentItem->album().isNull() ) + { + qDebug() << Q_FUNC_INFO << "Loading Album:" << parentItem->album()->name(); + addTracks( parentItem->album(), parent ); + } + else + Q_ASSERT( false ); +} + + +int +TreeModel::rowCount( const QModelIndex& parent ) const +{ + if ( parent.column() > 0 ) + return 0; + + TreeModelItem* parentItem = itemFromIndex( parent ); + if ( !parentItem ) + return 0; + + if ( !parentItem->artist().isNull() || !parentItem->album().isNull() ) + { + if ( !parentItem->children.count() ) + return 1; + } + + return parentItem->children.count(); +} + + +int +TreeModel::columnCount( const QModelIndex& parent ) const +{ + Q_UNUSED( parent ); + return 7; +} + + +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() ) + { + 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 + { + const result_ptr& result = entry->result(); + switch( index.column() ) + { + case Name: + return QString( "%1%2" ).arg( result->albumpos() > 0 ? QString( "%1. ").arg( result->albumpos() ) : QString() ) + .arg( result->track() ); + + case Duration: + return TomahawkUtils::timeToString( result->duration() ); + + case Bitrate: + return result->bitrate(); + + case Age: + return TomahawkUtils::ageToString( QDateTime::fromTime_t( result->modificationTime() ) ); + + case Year: + return result->year(); + + case Filesize: + return TomahawkUtils::filesizeToString( result->size() ); + + case Origin: + return result->friendlySource(); + + case AlbumPosition: + return result->albumpos(); + + default: + return QVariant(); + } + } + + return QVariant(); +} + + +QVariant +TreeModel::headerData( int section, Qt::Orientation orientation, int role ) const +{ + QStringList headers; + headers << tr( "Name" ) << 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 ) + return Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled | defaultFlags; + else + return defaultFlags; +} + + +QStringList +TreeModel::mimeTypes() const +{ + QStringList types; + types << "application/tomahawk.query.list"; + return types; +} + + +QMimeData* +TreeModel::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() ); + TreeModelItem* item = itemFromIndex( idx ); + if ( item ) + { + const album_ptr& album = item->album(); + queryStream << qlonglong( &album ); + } + } + + QMimeData* mimeData = new QMimeData(); + mimeData->setData( "application/tomahawk.query.list", queryData ); + + 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() +{ + qDebug() << Q_FUNC_INFO; + + DatabaseCommand_AllArtists* cmd = new DatabaseCommand_AllArtists(); + + connect( cmd, SIGNAL( artists( QList ) ), + SLOT( onArtistsAdded( QList ) ) ); + + Database::instance()->enqueue( QSharedPointer( cmd ) ); + + m_title = tr( "All Artists" ); +} + + +void +TreeModel::addAlbums( const artist_ptr& artist, const QModelIndex& parent ) +{ + qDebug() << Q_FUNC_INFO; + + DatabaseCommand_AllAlbums* cmd = new DatabaseCommand_AllAlbums( m_collection, artist ); + cmd->setData( parent.row() ); + + connect( cmd, SIGNAL( albums( QList, QVariant ) ), + SLOT( onAlbumsAdded( QList, QVariant ) ) ); + + Database::instance()->enqueue( QSharedPointer( cmd ) ); +} + + +void +TreeModel::addTracks( const album_ptr& album, const QModelIndex& parent ) +{ + qDebug() << Q_FUNC_INFO; + + DatabaseCommand_AllTracks* cmd = new DatabaseCommand_AllTracks( m_collection ); + cmd->setAlbum( album.data() ); +// cmd->setArtist( album->artist().data() ); + + QList< QVariant > rows; + rows << parent.row(); + rows << parent.parent().row(); + cmd->setData( QVariant( rows ) ); + + connect( cmd, SIGNAL( tracks( QList, QVariant ) ), + SLOT( onTracksAdded( QList, QVariant ) ) ); + + Database::instance()->enqueue( QSharedPointer( cmd ) ); +} + + +void +TreeModel::addCollection( const collection_ptr& collection ) +{ + qDebug() << Q_FUNC_INFO << collection->name() + << collection->source()->id() + << collection->source()->userName(); + + m_collection = collection; + DatabaseCommand_AllArtists* cmd = new DatabaseCommand_AllArtists( collection ); + + connect( cmd, SIGNAL( artists( QList ) ), + SLOT( onArtistsAdded( QList ) ) ); + + Database::instance()->enqueue( QSharedPointer( cmd ) ); + + m_title = tr( "All Artists from %1" ).arg( collection->source()->friendlyName() ); +} + + +void +TreeModel::addFilteredCollection( const collection_ptr& collection, unsigned int amount, DatabaseCommand_AllArtists::SortOrder order ) +{ + qDebug() << Q_FUNC_INFO << collection->name() + << collection->source()->id() + << collection->source()->userName() + << amount << order; + + DatabaseCommand_AllArtists* cmd = new DatabaseCommand_AllArtists( collection ); + cmd->setLimit( amount ); + cmd->setSortOrder( order ); + cmd->setSortDescending( true ); + + connect( cmd, SIGNAL( artists( QList, Tomahawk::collection_ptr ) ), + SLOT( onArtistsAdded( QList, Tomahawk::collection_ptr ) ) ); + + Database::instance()->enqueue( QSharedPointer( cmd ) ); + + m_title = tr( "All albums from %1" ).arg( collection->source()->friendlyName() ); +} + + +void +TreeModel::onArtistsAdded( const QList& artists ) +{ + if ( !artists.count() ) + return; + + int c = rowCount( QModelIndex() ); + QPair< int, int > crows; + crows.first = c; + crows.second = c + artists.count() - 1; + + emit beginInsertRows( QModelIndex(), crows.first, crows.second ); + + TreeModelItem* artistitem; + foreach( const artist_ptr& artist, artists ) + { + artistitem = new TreeModelItem( artist, m_rootItem ); + artistitem->cover = m_defaultCover; + artistitem->index = createIndex( m_rootItem->children.count() - 1, 0, artistitem ); + connect( artistitem, SIGNAL( dataChanged() ), SLOT( onDataChanged() ) ); + } + + emit endInsertRows(); + qDebug() << rowCount( QModelIndex() ); +} + + +void +TreeModel::onAlbumsAdded( const QList& albums, const QVariant& data ) +{ + qDebug() << Q_FUNC_INFO << albums.count(); + if ( !albums.count() ) + return; + + QModelIndex parent = index( data.toUInt(), 0, QModelIndex() ); + TreeModelItem* parentItem = itemFromIndex( parent ); + + // the -1 is because we fake a rowCount of 1 to trigger Qt calling fetchMore() + int c = rowCount( parent ) - 1; + QPair< int, int > crows; + crows.first = c; + crows.second = c + albums.count() - 1; + + if ( crows.second > 0 ) + emit beginInsertRows( parent, crows.first + 1, crows.second ); + + TreeModelItem* albumitem = 0; + foreach( const album_ptr& album, albums ) + { + albumitem = new TreeModelItem( album, parentItem ); + albumitem->cover = m_defaultCover; + albumitem->index = createIndex( parentItem->children.count() - 1, 0, albumitem ); + connect( albumitem, SIGNAL( dataChanged() ), SLOT( onDataChanged() ) ); + + Tomahawk::InfoSystem::InfoCustomData trackInfo; + trackInfo["artist"] = QVariant::fromValue< QString >( album->artist()->name() ); + trackInfo["album"] = QVariant::fromValue< QString >( album->name() ); + trackInfo["pptr"] = QVariant::fromValue< qlonglong >( (qlonglong)albumitem ); + + Tomahawk::InfoSystem::InfoSystem::instance()->getInfo( + s_tmInfoIdentifier, Tomahawk::InfoSystem::InfoAlbumCoverArt, + QVariant::fromValue< Tomahawk::InfoSystem::InfoCustomData >( trackInfo ), Tomahawk::InfoSystem::InfoCustomData() ); + } + + if ( crows.second > 0 ) + emit endInsertRows(); + else + emit dataChanged( albumitem->index, albumitem->index.sibling( albumitem->index.row(), columnCount( QModelIndex() ) - 1 ) ); + + qDebug() << rowCount( parent ); +} + + +void +TreeModel::onTracksAdded( const QList& tracks, const QVariant& data ) +{ + qDebug() << Q_FUNC_INFO << tracks.count(); + if ( !tracks.count() ) + return; + + QList< QVariant > rows = data.toList(); + + QModelIndex parent = index( rows.first().toUInt(), 0, index( rows.at( 1 ).toUInt(), 0, QModelIndex() ) ); + TreeModelItem* parentItem = itemFromIndex( parent ); + + // the -1 is because we fake a rowCount of 1 to trigger Qt calling fetchMore() + int c = rowCount( parent ) - 1; + QPair< int, int > crows; + crows.first = c; + crows.second = c + tracks.count() - 1; + + if ( crows.second > 0 ) + emit beginInsertRows( parent, crows.first + 1, crows.second ); + + TreeModelItem* item = 0; + foreach( const query_ptr& query, tracks ) + { + qDebug() << query->toString(); + item = new TreeModelItem( query->results().first(), parentItem ); + item->cover = m_defaultCover; + item->index = createIndex( parentItem->children.count() - 1, 0, item ); + + connect( item, SIGNAL( dataChanged() ), SLOT( onDataChanged() ) ); + } + + if ( crows.second > 0 ) + emit endInsertRows(); + else + emit dataChanged( item->index, item->index.sibling( item->index.row(), columnCount( QModelIndex() ) - 1 ) ); + + qDebug() << rowCount( parent ); +} + + +void +TreeModel::infoSystemInfo( QString caller, Tomahawk::InfoSystem::InfoType type, QVariant input, QVariant output, Tomahawk::InfoSystem::InfoCustomData customData ) +{ + Q_UNUSED( customData ); + qDebug() << Q_FUNC_INFO; + + if ( caller != s_tmInfoIdentifier || + ( type != Tomahawk::InfoSystem::InfoAlbumCoverArt && type != Tomahawk::InfoSystem::InfoArtistImages ) ) + { + qDebug() << "Info of wrong type or not with our identifier"; + return; + } + + if ( !output.canConvert< Tomahawk::InfoSystem::InfoCustomData >() ) + { + qDebug() << "Cannot convert fetched art from a QByteArray"; + return; + } + + Tomahawk::InfoSystem::InfoCustomData pptr = input.value< Tomahawk::InfoSystem::InfoCustomData >(); + Tomahawk::InfoSystem::InfoCustomData returnedData = output.value< Tomahawk::InfoSystem::InfoCustomData >(); + const QByteArray ba = returnedData["imgbytes"].toByteArray(); + if ( ba.length() ) + { + QPixmap pm; + pm.loadFromData( ba ); + + qlonglong p = pptr["pptr"].toLongLong(); + TreeModelItem* ai = reinterpret_cast(p); + + if ( pm.isNull() ) + ai->cover = m_defaultCover; + else + ai->cover = pm; + + emit dataChanged( ai->index, ai->index.sibling( ai->index.row(), columnCount( QModelIndex() ) - 1 ) ); + } +} + + +void +TreeModel::infoSystemFinished( QString target ) +{ + Q_UNUSED( target ); + qDebug() << Q_FUNC_INFO; +} + + +void +TreeModel::onDataChanged() +{ + TreeModelItem* p = (TreeModelItem*)sender(); + emit dataChanged( p->index, p->index.sibling( p->index.row(), columnCount( QModelIndex() ) - 1 ) ); +} diff --git a/src/libtomahawk/playlist/treemodel.h b/src/libtomahawk/playlist/treemodel.h new file mode 100644 index 000000000..68f23bd98 --- /dev/null +++ b/src/libtomahawk/playlist/treemodel.h @@ -0,0 +1,134 @@ +/* === 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 TREEMODEL_H +#define TREEMODEL_H + +#include +#include + +#include "album.h" +#include "collection.h" +#include "playlistinterface.h" +#include "database/databasecommand_allartists.h" + +#include "treemodelitem.h" +#include "infosystem/infosystem.h" + +#include "dllmacro.h" + +class QMetaData; + +class DLLEXPORT TreeModel : public QAbstractItemModel +{ +Q_OBJECT + +public: + enum Columns { + Name = 0, + Duration, + Bitrate, + Age, + Year, + Filesize, + Origin, + AlbumPosition + }; + + 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 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; + + void addAllCollections(); + void addCollection( const Tomahawk::collection_ptr& collection ); + void addFilteredCollection( const Tomahawk::collection_ptr& collection, unsigned int amount, DatabaseCommand_AllArtists::SortOrder order ); + + void addAlbums( const Tomahawk::artist_ptr& artist, const QModelIndex& parent ); + void addTracks( const Tomahawk::album_ptr& album, const QModelIndex& parent ); + + 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; } + + TreeModelItem* itemFromIndex( const QModelIndex& index ) const + { + if ( index.isValid() ) + return static_cast( index.internalPointer() ); + else + { + return m_rootItem; + } + } + +public slots: + virtual void setRepeatMode( PlaylistInterface::RepeatMode /*mode*/ ) {} + virtual void setShuffled( bool /*shuffled*/ ) {} + +signals: + void repeatModeChanged( PlaylistInterface::RepeatMode mode ); + void shuffleModeChanged( bool enabled ); + + void trackCountChanged( unsigned int tracks ); + +protected: + bool canFetchMore( const QModelIndex& parent ) const; + void fetchMore( const QModelIndex& parent ); + +private slots: + void onArtistsAdded( const QList& artists ); + void onAlbumsAdded( const QList& albums, const QVariant& data ); + void onTracksAdded( const QList& tracks, const QVariant& data ); + + void infoSystemInfo( QString caller, Tomahawk::InfoSystem::InfoType type, QVariant input, QVariant output, Tomahawk::InfoSystem::InfoCustomData customData ); + void infoSystemFinished( QString target ); + + void onDataChanged(); + +private: + QPersistentModelIndex m_currentIndex; + TreeModelItem* m_rootItem; + QPixmap m_defaultCover; + + QString m_title; + QString m_description; + + Tomahawk::collection_ptr m_collection; +}; + +#endif // ALBUMMODEL_H diff --git a/src/libtomahawk/playlist/treemodelitem.cpp b/src/libtomahawk/playlist/treemodelitem.cpp new file mode 100644 index 000000000..9e1c03fa2 --- /dev/null +++ b/src/libtomahawk/playlist/treemodelitem.cpp @@ -0,0 +1,133 @@ +/* === 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 "treemodelitem.h" + +#include "utils/tomahawkutils.h" + +#include + +using namespace Tomahawk; + + +TreeModelItem::~TreeModelItem() +{ + // 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() ); + } +} + + +TreeModelItem::TreeModelItem( TreeModelItem* parent, QAbstractItemModel* model ) +{ + this->parent = parent; + this->model = model; + childCount = 0; + toberemoved = false; + fetchingMore = false; + + if ( parent ) + { + parent->children.append( this ); + } +} + + +TreeModelItem::TreeModelItem( const Tomahawk::album_ptr& album, TreeModelItem* parent, int row ) + : QObject( parent ) + , m_album( album ) +{ + this->parent = parent; + fetchingMore = 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; +} + + +TreeModelItem::TreeModelItem( const Tomahawk::artist_ptr& artist, TreeModelItem* parent, int row ) + : QObject( parent ) + , m_artist( artist ) +{ + this->parent = parent; + fetchingMore = 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; +} + + +TreeModelItem::TreeModelItem( const Tomahawk::result_ptr& result, TreeModelItem* parent, int row ) + : QObject( parent ) + , m_result( result ) +{ + this->parent = parent; + fetchingMore = 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; +} diff --git a/src/libtomahawk/playlist/treemodelitem.h b/src/libtomahawk/playlist/treemodelitem.h new file mode 100644 index 000000000..16538727b --- /dev/null +++ b/src/libtomahawk/playlist/treemodelitem.h @@ -0,0 +1,69 @@ +/* === 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 TREEMODELITEM_H +#define TREEMODELITEM_H + +#include +#include +#include +#include + +#include "result.h" + +#include "dllmacro.h" + +class DLLEXPORT TreeModelItem : public QObject +{ +Q_OBJECT + +public: + ~TreeModelItem(); + + 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 ); + + const Tomahawk::artist_ptr& artist() const { return m_artist; }; + const Tomahawk::album_ptr& album() const { return m_album; }; + const Tomahawk::result_ptr& result() const { return m_result; }; + + void setCover( const QPixmap& cover ) { this->cover = cover; emit dataChanged(); } + + TreeModelItem* parent; + QList children; + QHash hash; + int childCount; + QPersistentModelIndex index; + QAbstractItemModel* model; + QPixmap cover; + + bool toberemoved; + bool fetchingMore; + +signals: + void dataChanged(); + +private: + Tomahawk::artist_ptr m_artist; + Tomahawk::album_ptr m_album; + Tomahawk::result_ptr m_result; +}; + +#endif // TREEMODELITEM_H diff --git a/src/libtomahawk/playlist/treeproxymodel.cpp b/src/libtomahawk/playlist/treeproxymodel.cpp new file mode 100644 index 000000000..3f8b86b16 --- /dev/null +++ b/src/libtomahawk/playlist/treeproxymodel.cpp @@ -0,0 +1,164 @@ +/* === 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 "treeproxymodel.h" + +#include +#include + +#include "query.h" + + +TreeProxyModel::TreeProxyModel( QObject* parent ) + : QSortFilterProxyModel( parent ) + , PlaylistInterface( this ) + , m_model( 0 ) + , m_repeatMode( PlaylistInterface::NoRepeat ) + , m_shuffled( false ) +{ + qsrand( QTime( 0, 0, 0 ).secsTo( QTime::currentTime() ) ); + + setFilterCaseSensitivity( Qt::CaseInsensitive ); + setSortCaseSensitivity( Qt::CaseInsensitive ); + setDynamicSortFilter( true ); + + setSourceModel( 0 ); +} + + +void +TreeProxyModel::setSourceModel( TreeModel* sourceModel ) +{ + m_model = sourceModel; + + connect( m_model, SIGNAL( trackCountChanged( unsigned int ) ), + SIGNAL( sourceTrackCountChanged( unsigned int ) ) ); + + QSortFilterProxyModel::setSourceModel( sourceModel ); +} + + +void +TreeProxyModel::setFilter( const QString& pattern ) +{ + qDebug() << Q_FUNC_INFO; + setFilterRegExp( pattern ); + + emit filterChanged( pattern ); +} + + +bool +TreeProxyModel::filterAcceptsRow( int sourceRow, const QModelIndex& sourceParent ) const +{ + if ( filterRegExp().isEmpty() ) + return true; + + TreeModelItem* pi = sourceModel()->itemFromIndex( sourceModel()->index( sourceRow, 0, sourceParent ) ); + if ( !pi ) + return false; + + QStringList sl = filterRegExp().pattern().split( " ", QString::SkipEmptyParts ); + + bool found = true; + foreach( const QString& s, sl ) + { + if ( !textForItem( pi ).contains( s, Qt::CaseInsensitive ) ) + { + found = false; + } + } + + return found; +} + + +bool +TreeProxyModel::lessThan( const QModelIndex& left, const QModelIndex& right ) const +{ + TreeModelItem* p1 = sourceModel()->itemFromIndex( left ); + TreeModelItem* p2 = sourceModel()->itemFromIndex( right ); + + if ( !p1 ) + return true; + if ( !p2 ) + return false; + + if ( !p1->result().isNull() ) + { + if ( p1->result()->albumpos() != p2->result()->albumpos() ) + return p1->result()->albumpos() < p2->result()->albumpos(); + } + + return QString::localeAwareCompare( textForItem( p1 ), textForItem( p2 ) ) < 0; +} + + +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 ); + } +} + + +Tomahawk::result_ptr +TreeProxyModel::siblingItem( int itemsAway ) +{ + qDebug() << Q_FUNC_INFO; + return Tomahawk::result_ptr( 0 ); +} + + +QString +TreeProxyModel::textForItem( TreeModelItem* item ) const +{ + if ( !item->artist().isNull() ) + { + return item->artist()->name(); + } + else if ( !item->album().isNull() ) + { + return item->album()->name(); + } + else if ( !item->result().isNull() ) + { + return item->result()->track(); + } + + return QString(); +} diff --git a/src/libtomahawk/playlist/treeproxymodel.h b/src/libtomahawk/playlist/treeproxymodel.h new file mode 100644 index 000000000..b441e155b --- /dev/null +++ b/src/libtomahawk/playlist/treeproxymodel.h @@ -0,0 +1,81 @@ +/* === 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 TREEPROXYMODEL_H +#define TREEPROXYMODEL_H + +#include + +#include "playlistinterface.h" +#include "treemodel.h" + +#include "dllmacro.h" + +class DLLEXPORT TreeProxyModel : public QSortFilterProxyModel, public PlaylistInterface +{ +Q_OBJECT + +public: + explicit TreeProxyModel( QObject* parent = 0 ); + + virtual TreeModel* sourceModel() const { return m_model; } + virtual void setSourceModel( TreeModel* sourceModel ); + + virtual QList tracks() { Q_ASSERT( FALSE ); QList queries; return queries; } + + virtual int unfilteredTrackCount() const { return sourceModel()->rowCount( QModelIndex() ); } + virtual int trackCount() const { return rowCount( QModelIndex() ); } + virtual int albumCount() const { return rowCount( QModelIndex() ); } + + virtual void removeIndex( const QModelIndex& index ); + virtual void removeIndexes( const QList& indexes ); + + virtual Tomahawk::result_ptr siblingItem( int direction ); + + virtual void setFilter( const QString& pattern ); + + virtual PlaylistInterface::RepeatMode repeatMode() const { return m_repeatMode; } + virtual bool shuffled() const { return m_shuffled; } + virtual PlaylistInterface::ViewMode viewMode() const { return PlaylistInterface::Tree; } + +signals: + void repeatModeChanged( PlaylistInterface::RepeatMode mode ); + void shuffleModeChanged( bool enabled ); + + void trackCountChanged( unsigned int tracks ); + void sourceTrackCountChanged( unsigned int tracks ); + + void filterChanged( const QString& filter ); + +public slots: + virtual void setRepeatMode( RepeatMode mode ) { m_repeatMode = mode; emit repeatModeChanged( mode ); } + virtual void setShuffled( bool enabled ) { m_shuffled = enabled; emit shuffleModeChanged( enabled ); } + +protected: + bool filterAcceptsRow( int sourceRow, const QModelIndex& sourceParent ) const; + bool lessThan( const QModelIndex& left, const QModelIndex& right ) const; + +private: + QString textForItem( TreeModelItem* item ) const; + + TreeModel* m_model; + RepeatMode m_repeatMode; + bool m_shuffled; +}; + +#endif // TREEPROXYMODEL_H diff --git a/src/libtomahawk/playlistinterface.h b/src/libtomahawk/playlistinterface.h index f8a56fa32..2743a7d13 100644 --- a/src/libtomahawk/playlistinterface.h +++ b/src/libtomahawk/playlistinterface.h @@ -1,3 +1,21 @@ +/* === 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 PLAYLISTINTERFACE_H #define PLAYLISTINTERFACE_H diff --git a/src/libtomahawk/pluginapi.cpp b/src/libtomahawk/pluginapi.cpp deleted file mode 100644 index 4bf86d317..000000000 --- a/src/libtomahawk/pluginapi.cpp +++ /dev/null @@ -1,46 +0,0 @@ -#include "pluginapi.h" - -#include "pipeline.h" -#include "sourcelist.h" - -using namespace Tomahawk; - - -PluginAPI::PluginAPI( Pipeline* p ) - : m_pipeline( p ) -{ -} - - -/*void -PluginAPI::reportResults( const QString& qid, const QList& vresults ) -{ - QList< result_ptr > results; - foreach( QVariantMap m, vresults ) - { - result_ptr rp( new Result( m ) ); - results.append( rp ); - } - m_pipeline->reportResults( QID( qid ), results ); -}*/ - - -void -PluginAPI::addSource( source_ptr s ) -{ -// SourceList::instance()->add( s ); -} - - -void -PluginAPI::removeSource( source_ptr s ) -{ -// SourceList::instance()->remove( s ); -} - - -void -PluginAPI::addResolver( Resolver* r ) -{ - Pipeline::instance()->addResolver( r ); -} diff --git a/src/libtomahawk/pluginapi.h b/src/libtomahawk/pluginapi.h deleted file mode 100644 index b824a3204..000000000 --- a/src/libtomahawk/pluginapi.h +++ /dev/null @@ -1,49 +0,0 @@ -#ifndef PLUGINAPI_H -#define PLUGINAPI_H - -#include -#include - -#include "collection.h" -#include "source.h" - -#include "dllmacro.h" - -/* - This is the only API plugins have access to. - This class must proxy calls to internal functions, because plugins can't - get a pointer to any old object and start calling methods on it. -*/ - -namespace Tomahawk -{ -class Resolver; -class Pipeline; - -class DLLEXPORT PluginAPI : public QObject -{ -Q_OBJECT - -public: - explicit PluginAPI( Pipeline * p ); - - /// call every time new results are available for a running query -// void reportResults( const QString& qid, const QList& results ); - - /// add/remove sources (which have collections) - void addSource( source_ptr s ); - void removeSource( source_ptr s ); - - /// register object capable of searching - void addResolver( Resolver * r ); - - Pipeline * pipeline() const { return m_pipeline; } - -private: - Pipeline * m_pipeline; -}; - - -}; //ns - -#endif // PLUGINAPI_H diff --git a/src/libtomahawk/qtsingleapp/qtlocalpeer.cpp b/src/libtomahawk/qtsingleapp/qtlocalpeer.cpp deleted file mode 100644 index 382d182dc..000000000 --- a/src/libtomahawk/qtsingleapp/qtlocalpeer.cpp +++ /dev/null @@ -1,199 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of a Qt Solutions component. -** -** You may use this file under the terms of the BSD license as follows: -** -** "Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions are -** met: -** * Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** * Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in -** the documentation and/or other materials provided with the -** distribution. -** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor -** the names of its contributors may be used to endorse or promote -** products derived from this software without specific prior written -** permission. -** -** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." -** -****************************************************************************/ - - -#include "qtlocalpeer.h" -#include -#include - -#if defined(Q_OS_WIN) -#include -#include -typedef BOOL(WINAPI*PProcessIdToSessionId)(DWORD,DWORD*); -static PProcessIdToSessionId pProcessIdToSessionId = 0; -#endif -#if defined(Q_OS_UNIX) -#include -#endif - -namespace QtLP_Private { -#include "qtlockedfile.cpp" -#if defined(Q_OS_WIN) -#include "qtlockedfile_win.cpp" -#else -#include "qtlockedfile_unix.cpp" -#endif -} - -const char* QtLocalPeer::ack = "ack"; - -QtLocalPeer::QtLocalPeer(QObject* parent, const QString &appId) - : QObject(parent), id(appId) -{ - QString prefix = id; - if (id.isEmpty()) { - id = QCoreApplication::applicationFilePath(); -#if defined(Q_OS_WIN) - id = id.toLower(); -#endif - prefix = id.section(QLatin1Char('/'), -1); - } - prefix.remove(QRegExp("[^a-zA-Z]")); - prefix.truncate(6); - - QByteArray idc = id.toUtf8(); - quint16 idNum = qChecksum(idc.constData(), idc.size()); - socketName = QLatin1String("qtsingleapp-") + prefix - + QLatin1Char('-') + QString::number(idNum, 16); - -#if defined(Q_OS_WIN) - if (!pProcessIdToSessionId) { - QLibrary lib("kernel32"); - pProcessIdToSessionId = (PProcessIdToSessionId)lib.resolve("ProcessIdToSessionId"); - } - if (pProcessIdToSessionId) { - DWORD sessionId = 0; - pProcessIdToSessionId(GetCurrentProcessId(), &sessionId); - socketName += QLatin1Char('-') + QString::number(sessionId, 16); - } -#else - socketName += QLatin1Char('-') + QString::number(::getuid(), 16); -#endif - - server = new QLocalServer(this); - QString lockName = QDir(QDir::tempPath()).absolutePath() - + QLatin1Char('/') + socketName - + QLatin1String("-lockfile"); - lockFile.setFileName(lockName); - lockFile.open(QIODevice::ReadWrite); -} - - - -bool QtLocalPeer::isClient() -{ - if (lockFile.isLocked()) - return false; - - if (!lockFile.lock(QtLP_Private::QtLockedFile::WriteLock, false)) - return true; - - bool res = server->listen(socketName); -#if defined(Q_OS_UNIX) && (QT_VERSION >= QT_VERSION_CHECK(4,5,0)) - // ### Workaround - if (!res && server->serverError() == QAbstractSocket::AddressInUseError) { - QFile::remove(QDir::cleanPath(QDir::tempPath())+QLatin1Char('/')+socketName); - res = server->listen(socketName); - } -#endif - if (!res) - qWarning("QtSingleCoreApplication: listen on local socket failed, %s", qPrintable(server->errorString())); - QObject::connect(server, SIGNAL(newConnection()), SLOT(receiveConnection())); - return false; -} - - -bool QtLocalPeer::sendMessage(const QString &message, int timeout) -{ - if (!isClient()) - return false; - - QLocalSocket socket; - bool connOk = false; - for(int i = 0; i < 2; i++) { - // Try twice, in case the other instance is just starting up - socket.connectToServer(socketName); - connOk = socket.waitForConnected(timeout/2); - if (connOk || i) - break; - int ms = 250; -#if defined(Q_OS_WIN) - Sleep(DWORD(ms)); -#else - struct timespec ts = { ms / 1000, (ms % 1000) * 1000 * 1000 }; - nanosleep(&ts, NULL); -#endif - } - if (!connOk) - return false; - - QByteArray uMsg(message.toUtf8()); - QDataStream ds(&socket); - ds.writeBytes(uMsg.constData(), uMsg.size()); - bool res = socket.waitForBytesWritten(timeout); - if (res) { - res &= socket.waitForReadyRead(timeout); // wait for ack - if (res) - res &= (socket.read(qstrlen(ack)) == ack); - } - return res; -} - - -void QtLocalPeer::receiveConnection() -{ - QLocalSocket* socket = server->nextPendingConnection(); - if (!socket) - return; - - while (socket->bytesAvailable() < (int)sizeof(quint32)) - socket->waitForReadyRead(); - QDataStream ds(socket); - QByteArray uMsg; - quint32 remaining; - ds >> remaining; - uMsg.resize(remaining); - int got = 0; - char* uMsgBuf = uMsg.data(); - do { - got = ds.readRawData(uMsgBuf, remaining); - remaining -= got; - uMsgBuf += got; - } while (remaining && got >= 0 && socket->waitForReadyRead(2000)); - if (got < 0) { - qWarning("QtLocalPeer: Message reception failed %s", socket->errorString().toLatin1().constData()); - delete socket; - return; - } - QString message(QString::fromUtf8(uMsg)); - socket->write(ack, qstrlen(ack)); - socket->waitForBytesWritten(1000); - delete socket; - emit messageReceived(message); //### (might take a long time to return) -} diff --git a/src/libtomahawk/qtsingleapp/qtlocalpeer.h b/src/libtomahawk/qtsingleapp/qtlocalpeer.h deleted file mode 100644 index 869af2ac2..000000000 --- a/src/libtomahawk/qtsingleapp/qtlocalpeer.h +++ /dev/null @@ -1,72 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of a Qt Solutions component. -** -** You may use this file under the terms of the BSD license as follows: -** -** "Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions are -** met: -** * Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** * Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in -** the documentation and/or other materials provided with the -** distribution. -** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor -** the names of its contributors may be used to endorse or promote -** products derived from this software without specific prior written -** permission. -** -** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." -** -****************************************************************************/ - - -#include -#include -#include - -#include "qtlockedfile.h" - -class QtLocalPeer : public QObject -{ - Q_OBJECT - -public: - QtLocalPeer(QObject *parent = 0, const QString &appId = QString()); - bool isClient(); - bool sendMessage(const QString &message, int timeout); - QString applicationId() const - { return id; } - -Q_SIGNALS: - void messageReceived(const QString &message); - -protected Q_SLOTS: - void receiveConnection(); - -protected: - QString id; - QString socketName; - QLocalServer* server; - QtLP_Private::QtLockedFile lockFile; - -private: - static const char* ack; -}; diff --git a/src/libtomahawk/qtsingleapp/qtlockedfile.cpp b/src/libtomahawk/qtsingleapp/qtlockedfile.cpp deleted file mode 100644 index 3e73ba652..000000000 --- a/src/libtomahawk/qtsingleapp/qtlockedfile.cpp +++ /dev/null @@ -1,192 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of a Qt Solutions component. -** -** You may use this file under the terms of the BSD license as follows: -** -** "Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions are -** met: -** * Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** * Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in -** the documentation and/or other materials provided with the -** distribution. -** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor -** the names of its contributors may be used to endorse or promote -** products derived from this software without specific prior written -** permission. -** -** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." -** -****************************************************************************/ - -#include "qtlockedfile.h" - -/*! - \class QtLockedFile - - \brief The QtLockedFile class extends QFile with advisory locking - functions. - - A file may be locked in read or write mode. Multiple instances of - \e QtLockedFile, created in multiple processes running on the same - machine, may have a file locked in read mode. Exactly one instance - may have it locked in write mode. A read and a write lock cannot - exist simultaneously on the same file. - - The file locks are advisory. This means that nothing prevents - another process from manipulating a locked file using QFile or - file system functions offered by the OS. Serialization is only - guaranteed if all processes that access the file use - QLockedFile. Also, while holding a lock on a file, a process - must not open the same file again (through any API), or locks - can be unexpectedly lost. - - The lock provided by an instance of \e QtLockedFile is released - whenever the program terminates. This is true even when the - program crashes and no destructors are called. -*/ - -/*! \enum QtLockedFile::LockMode - - This enum describes the available lock modes. - - \value ReadLock A read lock. - \value WriteLock A write lock. - \value NoLock Neither a read lock nor a write lock. -*/ - -/*! - Constructs an unlocked \e QtLockedFile object. This constructor - behaves in the same way as \e QFile::QFile(). - - \sa QFile::QFile() -*/ -QtLockedFile::QtLockedFile() - : QFile() -{ -#ifdef Q_OS_WIN - wmutex = 0; - rmutex = 0; -#endif - m_lock_mode = NoLock; -} - -/*! - Constructs an unlocked QtLockedFile object with file \a name. This - constructor behaves in the same way as \e QFile::QFile(const - QString&). - - \sa QFile::QFile() -*/ -QtLockedFile::QtLockedFile(const QString &name) - : QFile(name) -{ -#ifdef Q_OS_WIN - wmutex = 0; - rmutex = 0; -#endif - m_lock_mode = NoLock; -} - -/*! - Opens the file in OpenMode \a mode. - - This is identical to QFile::open(), with the one exception that the - Truncate mode flag is disallowed. Truncation would conflict with the - advisory file locking, since the file would be modified before the - write lock is obtained. If truncation is required, use resize(0) - after obtaining the write lock. - - Returns true if successful; otherwise false. - - \sa QFile::open(), QFile::resize() -*/ -bool QtLockedFile::open(OpenMode mode) -{ - if (mode & QIODevice::Truncate) { - qWarning("QtLockedFile::open(): Truncate mode not allowed."); - return false; - } - return QFile::open(mode); -} - -/*! - Returns \e true if this object has a in read or write lock; - otherwise returns \e false. - - \sa lockMode() -*/ -bool QtLockedFile::isLocked() const -{ - return m_lock_mode != NoLock; -} - -/*! - Returns the type of lock currently held by this object, or \e - QtLockedFile::NoLock. - - \sa isLocked() -*/ -QtLockedFile::LockMode QtLockedFile::lockMode() const -{ - return m_lock_mode; -} - -/*! - \fn bool QtLockedFile::lock(LockMode mode, bool block = true) - - Obtains a lock of type \a mode. The file must be opened before it - can be locked. - - If \a block is true, this function will block until the lock is - aquired. If \a block is false, this function returns \e false - immediately if the lock cannot be aquired. - - If this object already has a lock of type \a mode, this function - returns \e true immediately. If this object has a lock of a - different type than \a mode, the lock is first released and then a - new lock is obtained. - - This function returns \e true if, after it executes, the file is - locked by this object, and \e false otherwise. - - \sa unlock(), isLocked(), lockMode() -*/ - -/*! - \fn bool QtLockedFile::unlock() - - Releases a lock. - - If the object has no lock, this function returns immediately. - - This function returns \e true if, after it executes, the file is - not locked by this object, and \e false otherwise. - - \sa lock(), isLocked(), lockMode() -*/ - -/*! - \fn QtLockedFile::~QtLockedFile() - - Destroys the \e QtLockedFile object. If any locks were held, they - are released. -*/ diff --git a/src/libtomahawk/qtsingleapp/qtlockedfile.h b/src/libtomahawk/qtsingleapp/qtlockedfile.h deleted file mode 100644 index 07a42bffb..000000000 --- a/src/libtomahawk/qtsingleapp/qtlockedfile.h +++ /dev/null @@ -1,96 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of a Qt Solutions component. -** -** You may use this file under the terms of the BSD license as follows: -** -** "Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions are -** met: -** * Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** * Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in -** the documentation and/or other materials provided with the -** distribution. -** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor -** the names of its contributors may be used to endorse or promote -** products derived from this software without specific prior written -** permission. -** -** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." -** -****************************************************************************/ - -#ifndef QTLOCKEDFILE_H -#define QTLOCKEDFILE_H - -#include -#ifdef Q_OS_WIN -#include -#endif - -#if defined(Q_WS_WIN) -# if !defined(QT_QTLOCKEDFILE_EXPORT) && !defined(QT_QTLOCKEDFILE_IMPORT) -# define QT_QTLOCKEDFILE_EXPORT -# elif defined(QT_QTLOCKEDFILE_IMPORT) -# if defined(QT_QTLOCKEDFILE_EXPORT) -# undef QT_QTLOCKEDFILE_EXPORT -# endif -# define QT_QTLOCKEDFILE_EXPORT __declspec(dllimport) -# elif defined(QT_QTLOCKEDFILE_EXPORT) -# undef QT_QTLOCKEDFILE_EXPORT -# define QT_QTLOCKEDFILE_EXPORT __declspec(dllexport) -# endif -#else -# define QT_QTLOCKEDFILE_EXPORT -#endif - -namespace QtLP_Private { - -class QT_QTLOCKEDFILE_EXPORT QtLockedFile : public QFile -{ -public: - enum LockMode { NoLock = 0, ReadLock, WriteLock }; - - QtLockedFile(); - QtLockedFile(const QString &name); - ~QtLockedFile(); - - bool open(OpenMode mode); - - bool lock(LockMode mode, bool block = true); - bool unlock(); - bool isLocked() const; - LockMode lockMode() const; - -private: -#ifdef Q_OS_WIN - Qt::HANDLE wmutex; - Qt::HANDLE rmutex; - QVector rmutexes; - QString mutexname; - - Qt::HANDLE getMutexHandle(int idx, bool doCreate); - bool waitMutex(Qt::HANDLE mutex, bool doBlock); - -#endif - LockMode m_lock_mode; -}; -} -#endif diff --git a/src/libtomahawk/qtsingleapp/qtlockedfile_unix.cpp b/src/libtomahawk/qtsingleapp/qtlockedfile_unix.cpp deleted file mode 100644 index 715c7d9b1..000000000 --- a/src/libtomahawk/qtsingleapp/qtlockedfile_unix.cpp +++ /dev/null @@ -1,114 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of a Qt Solutions component. -** -** You may use this file under the terms of the BSD license as follows: -** -** "Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions are -** met: -** * Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** * Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in -** the documentation and/or other materials provided with the -** distribution. -** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor -** the names of its contributors may be used to endorse or promote -** products derived from this software without specific prior written -** permission. -** -** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." -** -****************************************************************************/ - -#include -#include -#include -#include - -#include "qtlockedfile.h" - -bool QtLockedFile::lock(LockMode mode, bool block) -{ - if (!isOpen()) { - qWarning("QtLockedFile::lock(): file is not opened"); - return false; - } - - if (mode == NoLock) - return unlock(); - - if (mode == m_lock_mode) - return true; - - if (m_lock_mode != NoLock) - unlock(); - - struct flock fl; - fl.l_whence = SEEK_SET; - fl.l_start = 0; - fl.l_len = 0; - fl.l_type = (mode == ReadLock) ? F_RDLCK : F_WRLCK; - int cmd = block ? F_SETLKW : F_SETLK; - int ret = fcntl(handle(), cmd, &fl); - - if (ret == -1) { - if (errno != EINTR && errno != EAGAIN) - qWarning("QtLockedFile::lock(): fcntl: %s", strerror(errno)); - return false; - } - - - m_lock_mode = mode; - return true; -} - - -bool QtLockedFile::unlock() -{ - if (!isOpen()) { - qWarning("QtLockedFile::unlock(): file is not opened"); - return false; - } - - if (!isLocked()) - return true; - - struct flock fl; - fl.l_whence = SEEK_SET; - fl.l_start = 0; - fl.l_len = 0; - fl.l_type = F_UNLCK; - int ret = fcntl(handle(), F_SETLKW, &fl); - - if (ret == -1) { - qWarning("QtLockedFile::lock(): fcntl: %s", strerror(errno)); - return false; - } - - m_lock_mode = NoLock; - return true; -} - -QtLockedFile::~QtLockedFile() -{ - if (isOpen()) - unlock(); -} - diff --git a/src/libtomahawk/qtsingleapp/qtlockedfile_win.cpp b/src/libtomahawk/qtsingleapp/qtlockedfile_win.cpp deleted file mode 100644 index 8090470cd..000000000 --- a/src/libtomahawk/qtsingleapp/qtlockedfile_win.cpp +++ /dev/null @@ -1,208 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of a Qt Solutions component. -** -** You may use this file under the terms of the BSD license as follows: -** -** "Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions are -** met: -** * Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** * Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in -** the documentation and/or other materials provided with the -** distribution. -** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor -** the names of its contributors may be used to endorse or promote -** products derived from this software without specific prior written -** permission. -** -** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." -** -****************************************************************************/ - -#include "qtlockedfile.h" -#include -#include - -#define MUTEX_PREFIX "QtLockedFile mutex " -// Maximum number of concurrent read locks. Must not be greater than MAXIMUM_WAIT_OBJECTS -#define MAX_READERS MAXIMUM_WAIT_OBJECTS - -#define TCHAR WCHAR - -Qt::HANDLE QtLockedFile::getMutexHandle(int idx, bool doCreate) -{ - if (mutexname.isEmpty()) { - QFileInfo fi(*this); - mutexname = QString::fromLatin1(MUTEX_PREFIX) - + fi.absoluteFilePath().toLower(); - } - QString mname(mutexname); - if (idx >= 0) - mname += QString::number(idx); - - Qt::HANDLE mutex; - if (doCreate) { - QT_WA( { mutex = CreateMutexW(NULL, FALSE, (TCHAR*)mname.utf16()); }, - { mutex = CreateMutexA(NULL, FALSE, mname.toLocal8Bit().constData()); } ); - if (!mutex) { - qErrnoWarning("QtLockedFile::lock(): CreateMutex failed"); - return 0; - } - } - else { - QT_WA( { mutex = OpenMutexW(SYNCHRONIZE | MUTEX_MODIFY_STATE, FALSE, (TCHAR*)mname.utf16()); }, - { mutex = OpenMutexA(SYNCHRONIZE | MUTEX_MODIFY_STATE, FALSE, mname.toLocal8Bit().constData()); } ); - if (!mutex) { - if (GetLastError() != ERROR_FILE_NOT_FOUND) - qErrnoWarning("QtLockedFile::lock(): OpenMutex failed"); - return 0; - } - } - return mutex; -} - -bool QtLockedFile::waitMutex(Qt::HANDLE mutex, bool doBlock) -{ - Q_ASSERT(mutex); - DWORD res = WaitForSingleObject(mutex, doBlock ? INFINITE : 0); - switch (res) { - case WAIT_OBJECT_0: - case WAIT_ABANDONED: - return true; - break; - case WAIT_TIMEOUT: - break; - default: - qErrnoWarning("QtLockedFile::lock(): WaitForSingleObject failed"); - } - return false; -} - - - -bool QtLockedFile::lock(LockMode mode, bool block) -{ - if (!isOpen()) { - qWarning("QtLockedFile::lock(): file is not opened"); - return false; - } - - if (mode == NoLock) - return unlock(); - - if (mode == m_lock_mode) - return true; - - if (m_lock_mode != NoLock) - unlock(); - - if (!wmutex && !(wmutex = getMutexHandle(-1, true))) - return false; - - if (!waitMutex(wmutex, block)) - return false; - - if (mode == ReadLock) { - int idx = 0; - for (; idx < MAX_READERS; idx++) { - rmutex = getMutexHandle(idx, false); - if (!rmutex || waitMutex(rmutex, false)) - break; - CloseHandle(rmutex); - } - bool ok = true; - if (idx >= MAX_READERS) { - qWarning("QtLockedFile::lock(): too many readers"); - rmutex = 0; - ok = false; - } - else if (!rmutex) { - rmutex = getMutexHandle(idx, true); - if (!rmutex || !waitMutex(rmutex, false)) - ok = false; - } - if (!ok && rmutex) { - CloseHandle(rmutex); - rmutex = 0; - } - ReleaseMutex(wmutex); - if (!ok) - return false; - } - else { - Q_ASSERT(rmutexes.isEmpty()); - for (int i = 0; i < MAX_READERS; i++) { - Qt::HANDLE mutex = getMutexHandle(i, false); - if (mutex) - rmutexes.append(mutex); - } - if (rmutexes.size()) { - DWORD res = WaitForMultipleObjects(rmutexes.size(), rmutexes.constData(), - TRUE, block ? INFINITE : 0); - if (res != WAIT_OBJECT_0 && res != WAIT_ABANDONED) { - if (res != WAIT_TIMEOUT) - qErrnoWarning("QtLockedFile::lock(): WaitForMultipleObjects failed"); - m_lock_mode = WriteLock; // trick unlock() to clean up - semiyucky - unlock(); - return false; - } - } - } - - m_lock_mode = mode; - return true; -} - -bool QtLockedFile::unlock() -{ - if (!isOpen()) { - qWarning("QtLockedFile::unlock(): file is not opened"); - return false; - } - - if (!isLocked()) - return true; - - if (m_lock_mode == ReadLock) { - ReleaseMutex(rmutex); - CloseHandle(rmutex); - rmutex = 0; - } - else { - foreach(Qt::HANDLE mutex, rmutexes) { - ReleaseMutex(mutex); - CloseHandle(mutex); - } - rmutexes.clear(); - ReleaseMutex(wmutex); - } - - m_lock_mode = QtLockedFile::NoLock; - return true; -} - -QtLockedFile::~QtLockedFile() -{ - if (isOpen()) - unlock(); - if (wmutex) - CloseHandle(wmutex); -} diff --git a/src/libtomahawk/qtsingleapp/qtsingleapplication.cpp b/src/libtomahawk/qtsingleapp/qtsingleapplication.cpp deleted file mode 100644 index 5a8f1b035..000000000 --- a/src/libtomahawk/qtsingleapp/qtsingleapplication.cpp +++ /dev/null @@ -1,344 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of a Qt Solutions component. -** -** You may use this file under the terms of the BSD license as follows: -** -** "Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions are -** met: -** * Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** * Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in -** the documentation and/or other materials provided with the -** distribution. -** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor -** the names of its contributors may be used to endorse or promote -** products derived from this software without specific prior written -** permission. -** -** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." -** -****************************************************************************/ - - -#include "qtsingleapplication.h" -#include "qtlocalpeer.h" -#include - - -/*! - \class QtSingleApplication qtsingleapplication.h - \brief The QtSingleApplication class provides an API to detect and - communicate with running instances of an application. - - This class allows you to create applications where only one - instance should be running at a time. I.e., if the user tries to - launch another instance, the already running instance will be - activated instead. Another usecase is a client-server system, - where the first started instance will assume the role of server, - and the later instances will act as clients of that server. - - By default, the full path of the executable file is used to - determine whether two processes are instances of the same - application. You can also provide an explicit identifier string - that will be compared instead. - - The application should create the QtSingleApplication object early - in the startup phase, and call isRunning() to find out if another - instance of this application is already running. If isRunning() - returns false, it means that no other instance is running, and - this instance has assumed the role as the running instance. In - this case, the application should continue with the initialization - of the application user interface before entering the event loop - with exec(), as normal. - - The messageReceived() signal will be emitted when the running - application receives messages from another instance of the same - application. When a message is received it might be helpful to the - user to raise the application so that it becomes visible. To - facilitate this, QtSingleApplication provides the - setActivationWindow() function and the activateWindow() slot. - - If isRunning() returns true, another instance is already - running. It may be alerted to the fact that another instance has - started by using the sendMessage() function. Also data such as - startup parameters (e.g. the name of the file the user wanted this - new instance to open) can be passed to the running instance with - this function. Then, the application should terminate (or enter - client mode). - - If isRunning() returns true, but sendMessage() fails, that is an - indication that the running instance is frozen. - - Here's an example that shows how to convert an existing - application to use QtSingleApplication. It is very simple and does - not make use of all QtSingleApplication's functionality (see the - examples for that). - - \code - // Original - int main(int argc, char **argv) - { - QApplication app(argc, argv); - - MyMainWidget mmw; - mmw.show(); - return app.exec(); - } - - // Single instance - int main(int argc, char **argv) - { - QtSingleApplication app(argc, argv); - - if (app.isRunning()) - return !app.sendMessage(someDataString); - - MyMainWidget mmw; - app.setActivationWindow(&mmw); - mmw.show(); - return app.exec(); - } - \endcode - - Once this QtSingleApplication instance is destroyed (normally when - the process exits or crashes), when the user next attempts to run the - application this instance will not, of course, be encountered. The - next instance to call isRunning() or sendMessage() will assume the - role as the new running instance. - - For console (non-GUI) applications, QtSingleCoreApplication may be - used instead of this class, to avoid the dependency on the QtGui - library. - - \sa QtSingleCoreApplication -*/ - - -void QtSingleApplication::sysInit(const QString &appId) -{ - actWin = 0; - peer = new QtLocalPeer(this, appId); - connect(peer, SIGNAL(messageReceived(const QString&)), SIGNAL(messageReceived(const QString&))); -} - - -/*! - Creates a QtSingleApplication object. The application identifier - will be QCoreApplication::applicationFilePath(). \a argc, \a - argv, and \a GUIenabled are passed on to the QAppliation constructor. - - If you are creating a console application (i.e. setting \a - GUIenabled to false), you may consider using - QtSingleCoreApplication instead. -*/ - -QtSingleApplication::QtSingleApplication(int &argc, char **argv, bool GUIenabled) - : QApplication(argc, argv, GUIenabled) -{ - sysInit(); -} - - -/*! - Creates a QtSingleApplication object with the application - identifier \a appId. \a argc and \a argv are passed on to the - QAppliation constructor. -*/ - -QtSingleApplication::QtSingleApplication(const QString &appId, int &argc, char **argv) - : QApplication(argc, argv) -{ - sysInit(appId); -} - - -/*! - Creates a QtSingleApplication object. The application identifier - will be QCoreApplication::applicationFilePath(). \a argc, \a - argv, and \a type are passed on to the QAppliation constructor. -*/ -QtSingleApplication::QtSingleApplication(int &argc, char **argv, Type type) - : QApplication(argc, argv, type) -{ - sysInit(); -} - - -#if defined(Q_WS_X11) -/*! - Special constructor for X11, ref. the documentation of - QApplication's corresponding constructor. The application identifier - will be QCoreApplication::applicationFilePath(). \a dpy, \a visual, - and \a cmap are passed on to the QApplication constructor. -*/ -QtSingleApplication::QtSingleApplication(Display* dpy, Qt::HANDLE visual, Qt::HANDLE cmap) - : QApplication(dpy, visual, cmap) -{ - sysInit(); -} - -/*! - Special constructor for X11, ref. the documentation of - QApplication's corresponding constructor. The application identifier - will be QCoreApplication::applicationFilePath(). \a dpy, \a argc, \a - argv, \a visual, and \a cmap are passed on to the QApplication - constructor. -*/ -QtSingleApplication::QtSingleApplication(Display *dpy, int &argc, char **argv, Qt::HANDLE visual, Qt::HANDLE cmap) - : QApplication(dpy, argc, argv, visual, cmap) -{ - sysInit(); -} - -/*! - Special constructor for X11, ref. the documentation of - QApplication's corresponding constructor. The application identifier - will be \a appId. \a dpy, \a argc, \a - argv, \a visual, and \a cmap are passed on to the QApplication - constructor. -*/ -QtSingleApplication::QtSingleApplication(Display* dpy, const QString &appId, int argc, char **argv, Qt::HANDLE visual, Qt::HANDLE cmap) - : QApplication(dpy, argc, argv, visual, cmap) -{ - sysInit(appId); -} -#endif - - -/*! - Returns true if another instance of this application is running; - otherwise false. - - This function does not find instances of this application that are - being run by a different user (on Windows: that are running in - another session). - - \sa sendMessage() -*/ - -bool QtSingleApplication::isRunning() -{ - return peer->isClient(); -} - - -/*! - Tries to send the text \a message to the currently running - instance. The QtSingleApplication object in the running instance - will emit the messageReceived() signal when it receives the - message. - - This function returns true if the message has been sent to, and - processed by, the current instance. If there is no instance - currently running, or if the running instance fails to process the - message within \a timeout milliseconds, this function return false. - - \sa isRunning(), messageReceived() -*/ -bool QtSingleApplication::sendMessage(const QString &message, int timeout) -{ - return peer->sendMessage(message, timeout); -} - - -/*! - Returns the application identifier. Two processes with the same - identifier will be regarded as instances of the same application. -*/ -QString QtSingleApplication::id() const -{ - return peer->applicationId(); -} - - -/*! - Sets the activation window of this application to \a aw. The - activation window is the widget that will be activated by - activateWindow(). This is typically the application's main window. - - If \a activateOnMessage is true (the default), the window will be - activated automatically every time a message is received, just prior - to the messageReceived() signal being emitted. - - \sa activateWindow(), messageReceived() -*/ - -void QtSingleApplication::setActivationWindow(QWidget* aw, bool activateOnMessage) -{ - actWin = aw; - if (activateOnMessage) - connect(peer, SIGNAL(messageReceived(const QString&)), this, SLOT(activateWindow())); - else - disconnect(peer, SIGNAL(messageReceived(const QString&)), this, SLOT(activateWindow())); -} - - -/*! - Returns the applications activation window if one has been set by - calling setActivationWindow(), otherwise returns 0. - - \sa setActivationWindow() -*/ -QWidget* QtSingleApplication::activationWindow() const -{ - return actWin; -} - - -/*! - De-minimizes, raises, and activates this application's activation window. - This function does nothing if no activation window has been set. - - This is a convenience function to show the user that this - application instance has been activated when he has tried to start - another instance. - - This function should typically be called in response to the - messageReceived() signal. By default, that will happen - automatically, if an activation window has been set. - - \sa setActivationWindow(), messageReceived(), initialize() -*/ -void QtSingleApplication::activateWindow() -{ - if (actWin) { - actWin->setWindowState(actWin->windowState() & ~Qt::WindowMinimized); - actWin->raise(); - actWin->activateWindow(); - } -} - - -/*! - \fn void QtSingleApplication::messageReceived(const QString& message) - - This signal is emitted when the current instance receives a \a - message from another instance of this application. - - \sa sendMessage(), setActivationWindow(), activateWindow() -*/ - - -/*! - \fn void QtSingleApplication::initialize(bool dummy = true) - - \obsolete -*/ diff --git a/src/libtomahawk/qtsingleapp/qtsingleapplication.h b/src/libtomahawk/qtsingleapp/qtsingleapplication.h deleted file mode 100644 index c696d60ce..000000000 --- a/src/libtomahawk/qtsingleapp/qtsingleapplication.h +++ /dev/null @@ -1,84 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of a Qt Solutions component. -** -** You may use this file under the terms of the BSD license as follows: -** -** "Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions are -** met: -** * Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** * Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in -** the documentation and/or other materials provided with the -** distribution. -** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor -** the names of its contributors may be used to endorse or promote -** products derived from this software without specific prior written -** permission. -** -** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." -** -****************************************************************************/ - - -#include - -#include "dllmacro.h" - -class QtLocalPeer; - -class DLLEXPORT QtSingleApplication : public QApplication -{ - Q_OBJECT - -public: - QtSingleApplication(int &argc, char **argv, bool GUIenabled = true); - QtSingleApplication(const QString &id, int &argc, char **argv); - QtSingleApplication(int &argc, char **argv, Type type); -#if defined(Q_WS_X11) - QtSingleApplication(Display* dpy, Qt::HANDLE visual = 0, Qt::HANDLE colormap = 0); - QtSingleApplication(Display *dpy, int &argc, char **argv, Qt::HANDLE visual = 0, Qt::HANDLE cmap= 0); - QtSingleApplication(Display* dpy, const QString &appId, int argc, char **argv, Qt::HANDLE visual = 0, Qt::HANDLE colormap = 0); -#endif - - bool isRunning(); - QString id() const; - - void setActivationWindow(QWidget* aw, bool activateOnMessage = true); - QWidget* activationWindow() const; - - // Obsolete: - void initialize(bool dummy = true) - { isRunning(); Q_UNUSED(dummy) } - -public Q_SLOTS: - bool sendMessage(const QString &message, int timeout = 5000); - void activateWindow(); - - -Q_SIGNALS: - void messageReceived(const QString &message); - - -private: - void sysInit(const QString &appId = QString()); - QtLocalPeer *peer; - QWidget *actWin; -}; diff --git a/src/libtomahawk/qtsingleapp/qtsinglecoreapplication.cpp b/src/libtomahawk/qtsingleapp/qtsinglecoreapplication.cpp deleted file mode 100644 index cf607710e..000000000 --- a/src/libtomahawk/qtsingleapp/qtsinglecoreapplication.cpp +++ /dev/null @@ -1,148 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of a Qt Solutions component. -** -** You may use this file under the terms of the BSD license as follows: -** -** "Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions are -** met: -** * Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** * Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in -** the documentation and/or other materials provided with the -** distribution. -** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor -** the names of its contributors may be used to endorse or promote -** products derived from this software without specific prior written -** permission. -** -** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." -** -****************************************************************************/ - - -#include "qtsinglecoreapplication.h" -#include "qtlocalpeer.h" - -/*! - \class QtSingleCoreApplication qtsinglecoreapplication.h - \brief A variant of the QtSingleApplication class for non-GUI applications. - - This class is a variant of QtSingleApplication suited for use in - console (non-GUI) applications. It is an extension of - QCoreApplication (instead of QApplication). It does not require - the QtGui library. - - The API and usage is identical to QtSingleApplication, except that - functions relating to the "activation window" are not present, for - obvious reasons. Please refer to the QtSingleApplication - documentation for explanation of the usage. - - A QtSingleCoreApplication instance can communicate to a - QtSingleApplication instance if they share the same application - id. Hence, this class can be used to create a light-weight - command-line tool that sends commands to a GUI application. - - \sa QtSingleApplication -*/ - -/*! - Creates a QtSingleCoreApplication object. The application identifier - will be QCoreApplication::applicationFilePath(). \a argc and \a - argv are passed on to the QCoreAppliation constructor. -*/ - -QtSingleCoreApplication::QtSingleCoreApplication(int &argc, char **argv) - : QCoreApplication(argc, argv) -{ - peer = new QtLocalPeer(this); - connect(peer, SIGNAL(messageReceived(const QString&)), SIGNAL(messageReceived(const QString&))); -} - - -/*! - Creates a QtSingleCoreApplication object with the application - identifier \a appId. \a argc and \a argv are passed on to the - QCoreAppliation constructor. -*/ -QtSingleCoreApplication::QtSingleCoreApplication(const QString &appId, int &argc, char **argv) - : QCoreApplication(argc, argv) -{ - peer = new QtLocalPeer(this, appId); - connect(peer, SIGNAL(messageReceived(const QString&)), SIGNAL(messageReceived(const QString&))); -} - - -/*! - Returns true if another instance of this application is running; - otherwise false. - - This function does not find instances of this application that are - being run by a different user (on Windows: that are running in - another session). - - \sa sendMessage() -*/ - -bool QtSingleCoreApplication::isRunning() -{ - return peer->isClient(); -} - - -/*! - Tries to send the text \a message to the currently running - instance. The QtSingleCoreApplication object in the running instance - will emit the messageReceived() signal when it receives the - message. - - This function returns true if the message has been sent to, and - processed by, the current instance. If there is no instance - currently running, or if the running instance fails to process the - message within \a timeout milliseconds, this function return false. - - \sa isRunning(), messageReceived() -*/ - -bool QtSingleCoreApplication::sendMessage(const QString &message, int timeout) -{ - return peer->sendMessage(message, timeout); -} - - -/*! - Returns the application identifier. Two processes with the same - identifier will be regarded as instances of the same application. -*/ - -QString QtSingleCoreApplication::id() const -{ - return peer->applicationId(); -} - - -/*! - \fn void QtSingleCoreApplication::messageReceived(const QString& message) - - This signal is emitted when the current instance receives a \a - message from another instance of this application. - - \sa sendMessage() -*/ diff --git a/src/libtomahawk/qtsingleapp/qtsinglecoreapplication.h b/src/libtomahawk/qtsingleapp/qtsinglecoreapplication.h deleted file mode 100644 index ef529a8f6..000000000 --- a/src/libtomahawk/qtsingleapp/qtsinglecoreapplication.h +++ /dev/null @@ -1,66 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of a Qt Solutions component. -** -** You may use this file under the terms of the BSD license as follows: -** -** "Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions are -** met: -** * Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** * Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in -** the documentation and/or other materials provided with the -** distribution. -** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor -** the names of its contributors may be used to endorse or promote -** products derived from this software without specific prior written -** permission. -** -** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." -** -****************************************************************************/ - - -#include - -class QtLocalPeer; - -class QtSingleCoreApplication : public QCoreApplication -{ - Q_OBJECT - -public: - QtSingleCoreApplication(int &argc, char **argv); - QtSingleCoreApplication(const QString &id, int &argc, char **argv); - - bool isRunning(); - QString id() const; - -public Q_SLOTS: - bool sendMessage(const QString &message, int timeout = 5000); - - -Q_SIGNALS: - void messageReceived(const QString &message); - - -private: - QtLocalPeer* peer; -}; diff --git a/src/libtomahawk/query.cpp b/src/libtomahawk/query.cpp index 55ef56959..413ab4c96 100644 --- a/src/libtomahawk/query.cpp +++ b/src/libtomahawk/query.cpp @@ -1,3 +1,21 @@ +/* === 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 "query.h" #include "collection.h" @@ -26,6 +44,8 @@ Query::get( const QString& artist, const QString& track, const QString& album, c Query::Query( const QString& artist, const QString& track, const QString& album, const QID& qid ) : m_solved( false ) + , m_playable( false ) + , m_resolveFinished( false ) , m_qid( qid ) , m_artist( artist ) , m_album( album ) @@ -42,9 +62,8 @@ Query::Query( const QString& artist, const QString& track, const QString& album, void Query::addResults( const QList< Tomahawk::result_ptr >& newresults ) { - bool becameSolved = false; { -// QMutexLocker lock( &m_mut ); + QMutexLocker lock( &m_mutex ); m_results.append( newresults ); qStableSort( m_results.begin(), m_results.end(), Query::resultSorter ); @@ -70,10 +89,13 @@ Query::refreshResults() void Query::onResultStatusChanged() { - if ( m_results.count() ) - qStableSort( m_results.begin(), m_results.end(), Query::resultSorter ); - checkResults(); + { + QMutexLocker lock( &m_mutex ); + if ( m_results.count() ) + qStableSort( m_results.begin(), m_results.end(), Query::resultSorter ); + } + checkResults(); emit resultsChanged(); } @@ -82,7 +104,7 @@ void Query::removeResult( const Tomahawk::result_ptr& result ) { { -// QMutexLocker lock( &m_mut ); + QMutexLocker lock( &m_mutex ); m_results.removeAll( result ); } @@ -95,6 +117,7 @@ void Query::onResolvingFinished() { // qDebug() << Q_FUNC_INFO << "Finished resolving." << toString(); + m_resolveFinished = true; emit resolvingFinished( m_solved ); } @@ -102,7 +125,7 @@ Query::onResolvingFinished() QList< result_ptr > Query::results() const { -// QMutexLocker lock( &m_mut ); + QMutexLocker lock( &m_mutex ); return m_results; } @@ -110,7 +133,7 @@ Query::results() const unsigned int Query::numResults() const { -// QMutexLocker lock( &m_mut ); + QMutexLocker lock( &m_mutex ); return m_results.length(); } @@ -130,7 +153,18 @@ Query::id() const bool Query::resultSorter( const result_ptr& left, const result_ptr& right ) { - return left->score() > right->score(); + const float ls = left->score(); + const float rs = right->score(); + + if ( ls == rs ) + { + if ( !left->collection().isNull() && left->collection()->source()->isLocal() ) + return true; + else + return false; + } + + return ls > rs; } @@ -149,18 +183,33 @@ Query::checkResults() { bool becameSolved = false; bool becameUnsolved = true; - - // hook up signals, and check solved status - foreach( const result_ptr& rp, m_results ) { - if ( !m_solved && rp->score() > 0.99 && rp->collection()->source()->isOnline() ) + QMutexLocker lock( &m_mutex ); + + m_playable = false; + + // hook up signals, and check solved status + foreach( const result_ptr& rp, m_results ) { - m_solved = true; - becameSolved = true; - } - if ( rp->score() > 0.99 && rp->collection()->source()->isOnline() ) - { - becameUnsolved = false; + if ( rp->score() > 0.0 && rp->collection().isNull() ) + { + m_playable = true; + } + if ( !rp->collection().isNull() && rp->collection()->source()->isOnline() ) + { + m_playable = true; + + if ( rp->score() > 0.99 ) + { + becameUnsolved = false; + + if ( !m_solved ) + { + m_solved = true; + becameSolved = true; + } + } + } } } @@ -184,7 +233,7 @@ Query::toVariant() const m.insert( "track", track() ); m.insert( "duration", duration() ); m.insert( "qid", id() ); - + return m; } diff --git a/src/libtomahawk/query.h b/src/libtomahawk/query.h index d82972f10..ddc447729 100644 --- a/src/libtomahawk/query.h +++ b/src/libtomahawk/query.h @@ -1,3 +1,21 @@ +/* === 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 QUERY_H #define QUERY_H @@ -41,9 +59,13 @@ public: /// sorter for list of results static bool resultSorter( const result_ptr &left, const result_ptr& right ); - /// solved=true when a perfect result has been found (score of 1.0) + /// true when a perfect result has been found (score of 1.0) bool solved() const { return m_solved; } + /// true when any result has been found (score may be less than 1.0) + bool playable() const { return m_playable; } + bool resolvingFinished() const { return m_resolveFinished; } + unsigned int lastPipelineWeight() const { return m_lastpipelineweight; } void setLastPipelineWeight( unsigned int w ) { m_lastpipelineweight = w; } @@ -52,7 +74,8 @@ public: void setTrack( const QString& track ) { m_track = track; } void setResultHint( const QString& resultHint ) { m_resultHint = resultHint; } void setDuration( int duration ) { m_duration = duration; } - + void setResolveFinished( bool resolved ) { m_resolveFinished = resolved; } + QVariant toVariant() const; QString toString() const; @@ -61,7 +84,7 @@ public: QString album() const { return m_album; } QString track() const { return m_track; } int duration() const { return m_duration; } - + signals: void resultsAdded( const QList& ); void resultsRemoved( const Tomahawk::result_ptr& ); @@ -87,6 +110,8 @@ private: QList< Tomahawk::result_ptr > m_results; bool m_solved; + bool m_playable; + bool m_resolveFinished; mutable QID m_qid; unsigned int m_lastpipelineweight; @@ -95,6 +120,8 @@ private: QString m_track; int m_duration; QString m_resultHint; + + mutable QMutex m_mutex; }; }; //ns diff --git a/src/libtomahawk/resolver.cpp b/src/libtomahawk/resolver.cpp new file mode 100644 index 000000000..4e6a757fe --- /dev/null +++ b/src/libtomahawk/resolver.cpp @@ -0,0 +1,116 @@ +/* === 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 "resolver.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +QVariant +Tomahawk::ExternalResolver::configMsgFromWidget( QWidget* w ) +{ + if( !w ) + return QVariant(); + + // generate a qvariantmap of all the widgets in the hierarchy, and for each one include the list of properties and values + QVariantMap widgetMap; + addChildProperties( w, widgetMap ); +// qDebug() << "Generated widget variant:" << widgetMap; + return widgetMap; +} + + +void +Tomahawk::ExternalResolver::addChildProperties( QObject* widget, QVariantMap& m ) +{ + // recursively add all properties of this widget to the map, then repeat on all children. + // bare QWidgets are boring---so skip them! They have no input that the user can set. + if( !widget || !widget->isWidgetType() ) + return; + + if( qstrcmp( widget->metaObject()->className(), "QWidget" ) != 0 ) + { +// qDebug() << "Adding properties for this:" << widget->metaObject()->className(); + // add this widget's properties + QVariantMap props; + for( int i = widget->metaObject()->propertyOffset(); i < widget->metaObject()->propertyCount(); i++ ) + { + QString prop = widget->metaObject()->property( i ).name(); + QVariant val = widget->property( prop.toLatin1() ); + // clean up for QJson.... + if( val.canConvert< QPixmap >() || val.canConvert< QImage >() || val.canConvert< QIcon >() ) + continue; + props[ prop ] = val; +// qDebug() << QString( "%1: %2" ).arg( prop ).arg( props[ prop ].toString() ); + } + m[ widget->objectName() ] = props; + } + // and recurse + foreach( QObject* child, widget->children() ) + addChildProperties( child, m ); +} + + +QWidget* +Tomahawk::ExternalResolver::widgetFromData( QByteArray& data, QWidget* parent ) +{ + if( data.isEmpty() ) + return 0; + + QUiLoader l; + QBuffer b( &data ); + QWidget* w = l.load( &b, parent ); + + return w; +} + +QByteArray +Tomahawk::ExternalResolver::fixDataImagePaths( const QByteArray& data, bool compressed, const QVariantMap& images ) +{ + // with a list of images and image data, write each to a temp file, replace the path in the .ui file with the temp file path + QString uiFile = QString::fromUtf8( data ); + foreach( const QString& filename, images.keys() ) + { + if( !uiFile.contains( filename ) ) // make sure the image is used + continue; + + QString fullPath = QDir::tempPath() + "/" + filename; + QFile imgF( fullPath ); + if( !imgF.open( QIODevice::WriteOnly ) ) + { + qWarning() << "Failed to write to temporary image in UI file:" << filename << fullPath; + continue; + } + QByteArray data = images[ filename ].toByteArray(); + qDebug() << "expanding data:" << data << compressed; + data = compressed ? qUncompress( QByteArray::fromBase64( data ) ) : QByteArray::fromBase64( data ); + imgF.write( data ); + imgF.close(); + + // replace the path to the image with the real path + uiFile.replace( filename, fullPath ); + } + return uiFile.toUtf8(); +} + diff --git a/src/libtomahawk/resolver.h b/src/libtomahawk/resolver.h index ce20baa34..c3a3bae7e 100644 --- a/src/libtomahawk/resolver.h +++ b/src/libtomahawk/resolver.h @@ -1,9 +1,27 @@ +/* === 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 RESOLVER_H #define RESOLVER_H #include -#include "pluginapi.h" +#include "query.h" #include "dllmacro.h" @@ -15,9 +33,11 @@ weighted resolver */ + +class QWidget; + namespace Tomahawk { -class PluginAPI; class DLLEXPORT Resolver : public QObject { @@ -28,19 +48,10 @@ public: virtual QString name() const = 0; virtual unsigned int weight() const = 0; - virtual unsigned int preference() const { return 100; }; virtual unsigned int timeout() const = 0; - //virtual QWidget * configUI() { return 0; }; - //etc - - PluginAPI * api() const { return m_api; } - public slots: virtual void resolve( const Tomahawk::query_ptr& query ) = 0; - -private: - PluginAPI * m_api; }; class DLLEXPORT ExternalResolver : public Resolver @@ -52,10 +63,18 @@ public: virtual QString filePath() const { return m_filePath; } + virtual QWidget* configUI() const = 0; + virtual void saveConfig() = 0; public slots: virtual void stop() = 0; +protected: + QWidget* widgetFromData( QByteArray& data, QWidget* parent = 0 ); + QVariant configMsgFromWidget( QWidget* w ); + QByteArray fixDataImagePaths( const QByteArray& data, bool compressed, const QVariantMap& images ); private: + void addChildProperties( QObject* parent, QVariantMap& m ); + QString m_filePath; }; diff --git a/src/libtomahawk/result.cpp b/src/libtomahawk/result.cpp index 6cdbfb510..9f655f149 100644 --- a/src/libtomahawk/result.cpp +++ b/src/libtomahawk/result.cpp @@ -1,3 +1,21 @@ +/* === 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 "result.h" #include "album.h" @@ -76,7 +94,12 @@ Result::toVariant() const m.insert( "artist", artist()->name() ); m.insert( "album", album()->name() ); m.insert( "track", track() ); - m.insert( "source", collection()->source()->friendlyName() ); + + if ( !collection().isNull() ) + m.insert( "source", collection()->source()->friendlyName() ); + else + m.insert( "source", friendlySource() ); + m.insert( "mimetype", mimetype() ); m.insert( "size", size() ); m.insert( "bitrate", bitrate() ); diff --git a/src/libtomahawk/result.h b/src/libtomahawk/result.h index 4b62f662f..aff9604ba 100644 --- a/src/libtomahawk/result.h +++ b/src/libtomahawk/result.h @@ -1,3 +1,21 @@ +/* === 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 RESULT_H #define RESULT_H diff --git a/src/libtomahawk/sip/SipPlugin.cpp b/src/libtomahawk/sip/SipPlugin.cpp index 9e8a799d9..4af179694 100644 --- a/src/libtomahawk/sip/SipPlugin.cpp +++ b/src/libtomahawk/sip/SipPlugin.cpp @@ -1,11 +1,31 @@ +/* === 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 + QMenu* SipPlugin::menu() { return 0; } + QWidget* SipPlugin::configWidget() { diff --git a/src/libtomahawk/sip/SipPlugin.h b/src/libtomahawk/sip/SipPlugin.h index 0fa150ba6..47af2fcf3 100644 --- a/src/libtomahawk/sip/SipPlugin.h +++ b/src/libtomahawk/sip/SipPlugin.h @@ -1,3 +1,21 @@ +/* === 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 SIPPLUGIN_H #define SIPPLUGIN_H @@ -26,6 +44,7 @@ public: public slots: virtual bool connectPlugin( bool startup = false ) = 0; virtual void disconnectPlugin() = 0; + virtual void checkSettings() = 0; virtual void addContact( const QString &jid, const QString& msg = QString() ) = 0; virtual void sendMsg( const QString& to, const QString& msg ) = 0; diff --git a/src/libtomahawk/source.cpp b/src/libtomahawk/source.cpp index 2f959141c..d825a7724 100644 --- a/src/libtomahawk/source.cpp +++ b/src/libtomahawk/source.cpp @@ -1,3 +1,21 @@ +/* === 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 "source.h" #include "collection.h" @@ -12,7 +30,7 @@ using namespace Tomahawk; -Source::Source( int id, const QString &username ) +Source::Source( int id, const QString& username ) : QObject() , m_isLocal( false ) , m_online( false ) @@ -20,7 +38,7 @@ Source::Source( int id, const QString &username ) , m_id( id ) , m_cc( 0 ) { - qDebug() << Q_FUNC_INFO; + qDebug() << Q_FUNC_INFO << id << username; if ( id == 0 ) { @@ -128,6 +146,7 @@ Source::setOnline() { if ( m_online ) return; + m_online = true; // ensure username is in the database DatabaseCommand_addSource* cmd = new DatabaseCommand_addSource( m_username, m_friendlyname ); @@ -135,7 +154,6 @@ Source::setOnline() SLOT( dbLoaded( unsigned int, const QString& ) ) ); Database::instance()->enqueue( QSharedPointer(cmd) ); - m_online = true; emit online(); } @@ -163,6 +181,7 @@ Source::scanningProgress( unsigned int files ) void Source::scanningFinished( unsigned int files ) { + Q_UNUSED( files ); m_textStatus = QString(); emit stateChanged(); } @@ -171,6 +190,7 @@ Source::scanningFinished( unsigned int files ) void Source::onStateChanged( DBSyncConnection::State newstate, DBSyncConnection::State oldstate, const QString& info ) { + Q_UNUSED( oldstate ); QString msg; switch( newstate ) { @@ -202,6 +222,16 @@ Source::onStateChanged( DBSyncConnection::State newstate, DBSyncConnection::Stat } +unsigned int +Source::trackCount() const +{ + if ( m_stats.contains( "numfiles" ) ) + return m_stats.value( "numfiles" ).toUInt(); + else + return 0; +} + + void Source::onPlaybackStarted( const Tomahawk::query_ptr& query ) { diff --git a/src/libtomahawk/source.h b/src/libtomahawk/source.h index f1719cfce..ed48dfd1b 100644 --- a/src/libtomahawk/source.h +++ b/src/libtomahawk/source.h @@ -1,3 +1,21 @@ +/* === 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 SOURCE_H #define SOURCE_H @@ -13,7 +31,6 @@ class DatabaseCommand_LogPlayback; class ControlConnection; -class FileTransferConnection; namespace Tomahawk { @@ -48,11 +65,11 @@ public: void scanningProgress( unsigned int files ); void scanningFinished( unsigned int files ); - + void setOffline(); void setOnline(); - unsigned int trackCount() const { return m_stats.value( "numfiles" ).toUInt(); } + unsigned int trackCount() const; Tomahawk::query_ptr currentTrack() const { return m_currentTrack; } QString textStatus() const { return m_textStatus; } @@ -85,7 +102,7 @@ private slots: void onStateChanged( DBSyncConnection::State newstate, DBSyncConnection::State oldstate, const QString& info ); void onPlaybackStarted( const Tomahawk::query_ptr& query ); void onPlaybackFinished( const Tomahawk::query_ptr& query ); - + private: bool m_isLocal; bool m_online; @@ -99,7 +116,6 @@ private: QString m_textStatus; ControlConnection* m_cc; - FileTransferConnection* m_ftc; }; }; diff --git a/src/libtomahawk/sourcelist.cpp b/src/libtomahawk/sourcelist.cpp index a97fbd392..917a96c00 100644 --- a/src/libtomahawk/sourcelist.cpp +++ b/src/libtomahawk/sourcelist.cpp @@ -1,3 +1,21 @@ +/* === 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 "sourcelist.h" #include diff --git a/src/libtomahawk/sourcelist.h b/src/libtomahawk/sourcelist.h index 8b1861476..3ec7304ef 100644 --- a/src/libtomahawk/sourcelist.h +++ b/src/libtomahawk/sourcelist.h @@ -1,3 +1,21 @@ +/* === 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 SOURCELIST_H #define SOURCELIST_H diff --git a/src/libtomahawk/tomahawksettings.cpp b/src/libtomahawk/tomahawksettings.cpp index 78f9fb569..f1670839f 100644 --- a/src/libtomahawk/tomahawksettings.cpp +++ b/src/libtomahawk/tomahawksettings.cpp @@ -1,3 +1,21 @@ +/* === 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 "tomahawksettings.h" #ifndef TOMAHAWK_HEADLESS @@ -8,7 +26,7 @@ #include #include -#define VERSION 1 +#define VERSION 2 TomahawkSettings* TomahawkSettings::s_instance = 0; @@ -34,9 +52,12 @@ TomahawkSettings::TomahawkSettings( QObject* parent ) qDebug() << "Config version outdated, old:" << value( "configversion" ).toUInt() << "new:" << VERSION << "Doing upgrade, if any..."; - + // insert upgrade code here as required setValue( "configversion", VERSION ); + if( contains( "script/resolvers") ) { + setValue( "script/loadedresolvers", value( "script/resolvers" ) ); + } } } @@ -47,31 +68,63 @@ TomahawkSettings::~TomahawkSettings() } -QString -TomahawkSettings::scannerPath() const +QStringList +TomahawkSettings::scannerPaths() { + //FIXME: After enough time, remove this hack (and make const) #ifndef TOMAHAWK_HEADLESS - return value( "scannerpath", QDesktopServices::storageLocation( QDesktopServices::MusicLocation ) ).toString(); + if( value( "scannerpaths" ).isNull() ) + setValue( "scannerpaths", value( "scannerpath" ) ); + return value( "scannerpaths", QDesktopServices::storageLocation( QDesktopServices::MusicLocation ) ).toStringList(); #else - return value( "scannerpath", "" ).toString(); + if( value( "scannerpaths" ).isNull() ) + setValue( "scannerpaths", value( "scannerpath" ) ); + return value( "scannerpaths", "" ).toStringList(); #endif } void -TomahawkSettings::setScannerPath( const QString& path ) +TomahawkSettings::setScannerPaths( const QStringList& paths ) { - setValue( "scannerpath", path ); + setValue( "scannerpaths", paths ); } bool -TomahawkSettings::hasScannerPath() const +TomahawkSettings::hasScannerPaths() const { - return contains( "scannerpath" ); + //FIXME: After enough time, remove this hack + return contains( "scannerpaths" ) || contains( "scannerpath" ); } +bool +TomahawkSettings::watchForChanges() const +{ + return value( "watchForChanges", true ).toBool(); +} + + +void +TomahawkSettings::setWatchForChanges( bool watch ) +{ + setValue( "watchForChanges", watch ); +} + + +void +TomahawkSettings::setAcceptedLegalWarning( bool accept ) +{ + setValue( "acceptedLegalWarning", accept ); +} + +bool +TomahawkSettings::acceptedLegalWarning() const +{ + return value( "acceptedLegalWarning", false ).toBool(); +} + bool TomahawkSettings::httpEnabled() const { @@ -156,6 +209,19 @@ TomahawkSettings::setProxyType( const int type ) } +QStringList +TomahawkSettings::aclEntries() const +{ + return value( "acl/entries", QStringList() ).toStringList(); +} + +void +TomahawkSettings::setAclEntries( const QStringList &entries ) +{ + setValue( "acl/entries", entries ); +} + + QByteArray TomahawkSettings::mainWindowGeometry() const { @@ -348,6 +414,12 @@ TomahawkSettings::setExternalHostname(const QString& externalHostname) setValue( "network/external-hostname", externalHostname ); } +int +TomahawkSettings::defaultPort() const +{ + return 50210; +} + int TomahawkSettings::externalPort() const { @@ -416,7 +488,7 @@ TomahawkSettings::setTwitterScreenName( const QString& screenName ) { setValue( "twitter/ScreenName", screenName ); } - + QString TomahawkSettings::twitterOAuthToken() const { @@ -555,23 +627,36 @@ TomahawkSettings::xmppBotPort() const void TomahawkSettings::setXmppBotPort( const int port ) { - setValue( "xmppBot/port", -1 ); + setValue( "xmppBot/port", port ); } -void +void TomahawkSettings::addScriptResolver(const QString& resolver) { - setValue( "script/resolvers", scriptResolvers() << resolver ); + setValue( "script/resolvers", allScriptResolvers() << resolver ); } -QStringList -TomahawkSettings::scriptResolvers() const +QStringList +TomahawkSettings::allScriptResolvers() const { return value( "script/resolvers" ).toStringList(); } -void -TomahawkSettings::setScriptResolvers( const QStringList& resolver ) +void +TomahawkSettings::setAllScriptResolvers( const QStringList& resolver ) { setValue( "script/resolvers", resolver ); } + + +QStringList +TomahawkSettings::enabledScriptResolvers() const +{ + return value( "script/loadedresolvers" ).toStringList(); +} + +void +TomahawkSettings::setEnabledScriptResolvers( const QStringList& resolvers ) +{ + setValue( "script/loadedresolvers", resolvers ); +} diff --git a/src/libtomahawk/tomahawksettings.h b/src/libtomahawk/tomahawksettings.h index 5f7946df6..0b47ca685 100644 --- a/src/libtomahawk/tomahawksettings.h +++ b/src/libtomahawk/tomahawksettings.h @@ -1,3 +1,21 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2010-2011, Christian Muehlhaeuser + * + * Tomahawk is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Tomahawk is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Tomahawk. If not, see . + */ + #ifndef TOMAHAWK_SETTINGS_H #define TOMAHAWK_SETTINGS_h @@ -23,14 +41,20 @@ public: void applyChanges() { emit changed(); } /// General settings - QString scannerPath() const; /// QDesktopServices::MusicLocation by default - void setScannerPath( const QString& path ); - bool hasScannerPath() const; - + QStringList scannerPaths(); /// QDesktopServices::MusicLocation by default + void setScannerPaths( const QStringList& paths ); + bool hasScannerPaths() const; + + bool watchForChanges() const; + void setWatchForChanges( bool watch ); + + bool acceptedLegalWarning() const; + void setAcceptedLegalWarning( bool accept ); + /// UI settings QByteArray mainWindowGeometry() const; void setMainWindowGeometry( const QByteArray& geom ); - + QByteArray mainWindowState() const; void setMainWindowState( const QByteArray& state ); @@ -47,24 +71,24 @@ public: /// Jabber settings bool jabberAutoConnect() const; /// true by default void setJabberAutoConnect( bool autoconnect = false ); - + QString jabberUsername() const; void setJabberUsername( const QString& username ); - + QString jabberPassword() const; void setJabberPassword( const QString& pw ); - + QString jabberServer() const; void setJabberServer( const QString& server ); - + unsigned int jabberPort() const; // default is 5222 void setJabberPort( int port ); - + /// Network settings enum ExternalAddressMode { Lan, Upnp }; ExternalAddressMode externalAddressMode() const; void setExternalAddressMode( ExternalAddressMode externalAddressMode ); - + bool preferStaticHostPort() const; void setPreferStaticHostPort( bool prefer ); @@ -74,6 +98,7 @@ public: QString externalHostname() const; void setExternalHostname( const QString& externalHostname ); + int defaultPort() const; int externalPort() const; void setExternalPort( int externalPort ); @@ -92,41 +117,45 @@ public: int proxyType() const; void setProxyType( const int type ); + /// ACL settings + QStringList aclEntries() const; + void setAclEntries( const QStringList &entries ); + /// Last.fm settings bool scrobblingEnabled() const; /// false by default void setScrobblingEnabled( bool enable ); - + QString lastFmUsername() const; void setLastFmUsername( const QString& username ); - + QString lastFmPassword() const; void setLastFmPassword( const QString& password ); - + QByteArray lastFmSessionKey() const; void setLastFmSessionKey( const QByteArray& key ); - + /// Twitter settings QString twitterScreenName() const; void setTwitterScreenName( const QString& screenName ); - + QString twitterOAuthToken() const; void setTwitterOAuthToken( const QString& oauthtoken ); - + QString twitterOAuthTokenSecret() const; void setTwitterOAuthTokenSecret( const QString& oauthtokensecret ); qint64 twitterCachedFriendsSinceId() const; void setTwitterCachedFriendsSinceId( qint64 sinceid ); - + qint64 twitterCachedMentionsSinceId() const; void setTwitterCachedMentionsSinceId( qint64 sinceid ); - + qint64 twitterCachedDirectMessagesSinceId() const; void setTwitterCachedDirectMessagesSinceId( qint64 sinceid ); - + QHash twitterCachedPeers() const; void setTwitterCachedPeers( const QHash &cachedPeers ); - + /// XMPP Component Settings QString xmppBotServer() const; void setXmppBotServer( const QString &server ); @@ -139,11 +168,14 @@ public: int xmppBotPort() const; void setXmppBotPort( const int port ); - + /// Script resolver settings - QStringList scriptResolvers() const; - void setScriptResolvers( const QStringList& resolver ); + QStringList allScriptResolvers() const; + void setAllScriptResolvers( const QStringList& resolvers ); void addScriptResolver( const QString& resolver ); + QStringList enabledScriptResolvers() const; + void setEnabledScriptResolvers( const QStringList& resolvers ); + signals: void changed(); diff --git a/src/libtomahawk/track.h b/src/libtomahawk/track.h index 55ce10404..c379c620a 100644 --- a/src/libtomahawk/track.h +++ b/src/libtomahawk/track.h @@ -1,3 +1,21 @@ +/* === 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 TOMAHAWKTRACK_H #define TOMAHAWKTRACK_H diff --git a/src/libtomahawk/typedefs.h b/src/libtomahawk/typedefs.h index 1283c7e11..8ae84aed8 100644 --- a/src/libtomahawk/typedefs.h +++ b/src/libtomahawk/typedefs.h @@ -1,3 +1,21 @@ +/* === 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 TYPEDEFS_H #define TYPEDEFS_H diff --git a/src/libtomahawk/utils/animatedcounterlabel.h b/src/libtomahawk/utils/animatedcounterlabel.h index 2ea8b67ca..ab430397e 100644 --- a/src/libtomahawk/utils/animatedcounterlabel.h +++ b/src/libtomahawk/utils/animatedcounterlabel.h @@ -1,3 +1,21 @@ +/* === 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 ANIMATEDCOUNTERLABEL_H #define ANIMATEDCOUNTERLABEL_H diff --git a/src/libtomahawk/utils/animatedsplitter.cpp b/src/libtomahawk/utils/animatedsplitter.cpp index 696f76959..3ec748052 100644 --- a/src/libtomahawk/utils/animatedsplitter.cpp +++ b/src/libtomahawk/utils/animatedsplitter.cpp @@ -1,3 +1,21 @@ +/* === 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 "animatedsplitter.h" #define ANIMATION_TIME 400 @@ -65,7 +83,7 @@ AnimatedSplitter::hide( int index, bool animate ) emit hidden( w ); w->setMinimumHeight( minHeight ); - qDebug() << "animating to:" << w->height() << "from" << minHeight; +// qDebug() << "animating to:" << w->height() << "from" << minHeight; m_animateForward = false; if ( animate ) diff --git a/src/libtomahawk/utils/animatedsplitter.h b/src/libtomahawk/utils/animatedsplitter.h index 1744a144b..ba1671f4e 100644 --- a/src/libtomahawk/utils/animatedsplitter.h +++ b/src/libtomahawk/utils/animatedsplitter.h @@ -1,3 +1,21 @@ +/* === 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 ANIMATEDSPLITTER_H #define ANIMATEDSPLITTER_H diff --git a/src/libtomahawk/utils/elidedlabel.cpp b/src/libtomahawk/utils/elidedlabel.cpp index a17f31dca..9b4343383 100644 --- a/src/libtomahawk/utils/elidedlabel.cpp +++ b/src/libtomahawk/utils/elidedlabel.cpp @@ -1,9 +1,28 @@ +/* === 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 "elidedlabel.h" #include #include #include #include +#include ElidedLabel::ElidedLabel( QWidget* parent, Qt::WindowFlags flags ) @@ -47,16 +66,16 @@ ElidedLabel::setText( const QString& text ) Qt::Alignment ElidedLabel::alignment() const { - return align; + return m_align; } void ElidedLabel::setAlignment( Qt::Alignment alignment ) { - if ( this->align != alignment ) + if ( m_align != alignment ) { - this->align = alignment; + m_align = alignment; update(); // no geometry change, repaint is sufficient } } @@ -65,27 +84,40 @@ ElidedLabel::setAlignment( Qt::Alignment alignment ) Qt::TextElideMode ElidedLabel::elideMode() const { - return mode; + return m_mode; } void ElidedLabel::setElideMode( Qt::TextElideMode mode ) { - if ( this->mode != mode ) + if ( m_mode != mode ) { - this->mode = mode; + m_mode = mode; updateLabel(); } } +void +ElidedLabel::setMargin( int margin ) +{ + m_margin = margin; +} + +int +ElidedLabel::margin() const +{ + return m_margin; +} + void ElidedLabel::init( const QString& txt ) { m_text = txt; - align = Qt::AlignLeft; - mode = Qt::ElideMiddle; + m_align = Qt::AlignLeft; + m_mode = Qt::ElideMiddle; + m_margin = 0; setContentsMargins( 0, 0, 0, 0 ); } @@ -110,7 +142,7 @@ ElidedLabel::sizeHint() const QSize ElidedLabel::minimumSizeHint() const { - switch ( mode ) + switch ( m_mode ) { case Qt::ElideNone: return sizeHint(); @@ -131,8 +163,10 @@ ElidedLabel::paintEvent( QPaintEvent* event ) QFrame::paintEvent( event ); QPainter p( this ); QRect r = contentsRect(); - const QString elidedText = fontMetrics().elidedText( m_text, mode, r.width() ); - p.drawText( r, align, elidedText ); + r.adjust( m_margin, m_margin, -m_margin, -m_margin ); + + const QString elidedText = fontMetrics().elidedText( m_text, m_mode, r.width() ); + p.drawText( r, m_align, elidedText ); } @@ -158,7 +192,7 @@ void ElidedLabel::mousePressEvent( QMouseEvent* event ) { QFrame::mousePressEvent( event ); - time.start(); + m_time.start(); } @@ -166,6 +200,6 @@ void ElidedLabel::mouseReleaseEvent( QMouseEvent* event ) { QFrame::mouseReleaseEvent( event ); - if ( time.elapsed() < qApp->doubleClickInterval() ) + if ( m_time.elapsed() < qApp->doubleClickInterval() ) emit clicked(); } diff --git a/src/libtomahawk/utils/elidedlabel.h b/src/libtomahawk/utils/elidedlabel.h index 2756ac73e..2f45e1c97 100644 --- a/src/libtomahawk/utils/elidedlabel.h +++ b/src/libtomahawk/utils/elidedlabel.h @@ -1,3 +1,21 @@ +/* === 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 ELIDEDLABEL_H #define ELIDEDLABEL_H @@ -26,6 +44,9 @@ public: Qt::TextElideMode elideMode() const; void setElideMode( Qt::TextElideMode mode ); + void setMargin( int margin ); + int margin() const; + virtual QSize sizeHint() const; virtual QSize minimumSizeHint() const; @@ -46,10 +67,11 @@ protected: virtual void paintEvent( QPaintEvent* event ); private: - QTime time; + QTime m_time; QString m_text; - Qt::Alignment align; - Qt::TextElideMode mode; + Qt::Alignment m_align; + Qt::TextElideMode m_mode; + int m_margin; }; #endif // ELIDEDLABEL_H diff --git a/src/libtomahawk/utils/imagebutton.cpp b/src/libtomahawk/utils/imagebutton.cpp index de2a8a580..90eb1c521 100644 --- a/src/libtomahawk/utils/imagebutton.cpp +++ b/src/libtomahawk/utils/imagebutton.cpp @@ -1,3 +1,21 @@ +/* === 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 "imagebutton.h" #include diff --git a/src/libtomahawk/utils/imagebutton.h b/src/libtomahawk/utils/imagebutton.h index 822d77cb3..d3b060ab4 100644 --- a/src/libtomahawk/utils/imagebutton.h +++ b/src/libtomahawk/utils/imagebutton.h @@ -1,3 +1,21 @@ +/* === 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 IMAGE_BUTTON_H #define IMAGE_BUTTON_H diff --git a/src/libtomahawk/utils/modeltest.cpp b/src/libtomahawk/utils/modeltest.cpp deleted file mode 100644 index 40d7d282a..000000000 --- a/src/libtomahawk/utils/modeltest.cpp +++ /dev/null @@ -1,539 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2007 Trolltech ASA. All rights reserved. -** -** This file is part of the Qt Concurrent project on Trolltech Labs. -** -** This file may be used under the terms of the GNU General Public -** License version 2.0 as published by the Free Software Foundation -** and appearing in the file LICENSE.GPL included in the packaging of -** this file. Please review the following information to ensure GNU -** General Public Licensing requirements will be met: -** http://www.trolltech.com/products/qt/opensource.html -** -** If you are unsure which license is appropriate for your use, please -** review the following information: -** http://www.trolltech.com/products/qt/licensing.html or contact the -** sales department at sales@trolltech.com. -** -** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE -** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. -** -****************************************************************************/ - -#include - -#include "modeltest.h" - -Q_DECLARE_METATYPE(QModelIndex) - -/*! - Connect to all of the models signals. Whenever anything happens recheck everything. -*/ -ModelTest::ModelTest(QAbstractItemModel *_model, QObject *parent) : QObject(parent), model(_model), fetchingMore(false) -{ - Q_ASSERT(model); - - connect(model, SIGNAL(columnsAboutToBeInserted(const QModelIndex &, int, int)), - this, SLOT(runAllTests())); - connect(model, SIGNAL(columnsAboutToBeRemoved(const QModelIndex &, int, int)), - this, SLOT(runAllTests())); - connect(model, SIGNAL(columnsInserted(const QModelIndex &, int, int)), - this, SLOT(runAllTests())); - connect(model, SIGNAL(columnsRemoved(const QModelIndex &, int, int)), - this, SLOT(runAllTests())); - connect(model, SIGNAL(dataChanged(const QModelIndex &, const QModelIndex &)), - this, SLOT(runAllTests())); - connect(model, SIGNAL(headerDataChanged(Qt::Orientation, int, int)), - this, SLOT(runAllTests())); - connect(model, SIGNAL(layoutAboutToBeChanged ()), this, SLOT(runAllTests())); - connect(model, SIGNAL(layoutChanged ()), this, SLOT(runAllTests())); - connect(model, SIGNAL(modelReset ()), this, SLOT(runAllTests())); - connect(model, SIGNAL(rowsAboutToBeInserted(const QModelIndex &, int, int)), - this, SLOT(runAllTests())); - connect(model, SIGNAL(rowsAboutToBeRemoved(const QModelIndex &, int, int)), - this, SLOT(runAllTests())); - connect(model, SIGNAL(rowsInserted(const QModelIndex &, int, int)), - this, SLOT(runAllTests())); - connect(model, SIGNAL(rowsRemoved(const QModelIndex &, int, int)), - this, SLOT(runAllTests())); - - // Special checks for inserting/removing - connect(model, SIGNAL(layoutAboutToBeChanged()), - this, SLOT(layoutAboutToBeChanged())); - connect(model, SIGNAL(layoutChanged()), - this, SLOT(layoutChanged())); - - connect(model, SIGNAL(rowsAboutToBeInserted(const QModelIndex &, int, int)), - this, SLOT(rowsAboutToBeInserted(const QModelIndex &, int, int))); - connect(model, SIGNAL(rowsAboutToBeRemoved(const QModelIndex &, int, int)), - this, SLOT(rowsAboutToBeRemoved(const QModelIndex &, int, int))); - connect(model, SIGNAL(rowsInserted(const QModelIndex &, int, int)), - this, SLOT(rowsInserted(const QModelIndex &, int, int))); - connect(model, SIGNAL(rowsRemoved(const QModelIndex &, int, int)), - this, SLOT(rowsRemoved(const QModelIndex &, int, int))); - - runAllTests(); -} - -void ModelTest::runAllTests() -{ - if (fetchingMore) - return; - nonDestructiveBasicTest(); - rowCount(); - columnCount(); - hasIndex(); - index(); - parent(); - data(); -} - -/*! - nonDestructiveBasicTest tries to call a number of the basic functions (not all) - to make sure the model doesn't outright segfault, testing the functions that makes sense. -*/ -void ModelTest::nonDestructiveBasicTest() -{ - Q_ASSERT(model->buddy(QModelIndex()) == QModelIndex()); - model->canFetchMore(QModelIndex()); - Q_ASSERT(model->columnCount(QModelIndex()) >= 0); - Q_ASSERT(model->data(QModelIndex()) == QVariant()); - fetchingMore = true; - model->fetchMore(QModelIndex()); - fetchingMore = false; - Qt::ItemFlags flags = model->flags(QModelIndex()); - Q_ASSERT(flags == Qt::ItemIsDropEnabled || flags == 0); - model->hasChildren(QModelIndex()); - model->hasIndex(0, 0); - model->headerData(0, Qt::Horizontal); - model->index(0, 0); - Q_ASSERT(model->index(-1, -1) == QModelIndex()); - model->itemData(QModelIndex()); - QVariant cache; - model->match(QModelIndex(), -1, cache); - model->mimeTypes(); - Q_ASSERT(model->parent(QModelIndex()) == QModelIndex()); - Q_ASSERT(model->rowCount() >= 0); - QVariant variant; - model->setData(QModelIndex(), variant, -1); - model->setHeaderData(-1, Qt::Horizontal, QVariant()); - model->setHeaderData(0, Qt::Horizontal, QVariant()); - model->setHeaderData(999999, Qt::Horizontal, QVariant()); - QMap roles; - model->sibling(0, 0, QModelIndex()); - model->span(QModelIndex()); - model->supportedDropActions(); -} - -/*! - Tests model's implementation of QAbstractItemModel::rowCount() and hasChildren() - - Models that are dynamically populated are not as fully tested here. - */ -void ModelTest::rowCount() -{ - // check top row - QModelIndex topIndex = model->index(0, 0, QModelIndex()); - int rows = model->rowCount(topIndex); - Q_ASSERT(rows >= 0); - if (rows > 0) - Q_ASSERT(model->hasChildren(topIndex) == true); - - QModelIndex secondLevelIndex = model->index(0, 0, topIndex); - if (secondLevelIndex.isValid()) { // not the top level - // check a row count where parent is valid - rows = model->rowCount(secondLevelIndex); - Q_ASSERT(rows >= 0); - if (rows > 0) - Q_ASSERT(model->hasChildren(secondLevelIndex) == true); - } - - // The models rowCount() is tested more extensively in checkChildren(), - // but this catches the big mistakes -} - -/*! - Tests model's implementation of QAbstractItemModel::columnCount() and hasChildren() - */ -void ModelTest::columnCount() -{ - // check top row - QModelIndex topIndex = model->index(0, 0, QModelIndex()); - Q_ASSERT(model->columnCount(topIndex) >= 0); - - // check a column count where parent is valid - QModelIndex childIndex = model->index(0, 0, topIndex); - if (childIndex.isValid()) - Q_ASSERT(model->columnCount(childIndex) >= 0); - - // columnCount() is tested more extensively in checkChildren(), - // but this catches the big mistakes -} - -/*! - Tests model's implementation of QAbstractItemModel::hasIndex() - */ -void ModelTest::hasIndex() -{ - // Make sure that invalid values returns an invalid index - Q_ASSERT(model->hasIndex(-2, -2) == false); - Q_ASSERT(model->hasIndex(-2, 0) == false); - Q_ASSERT(model->hasIndex(0, -2) == false); - - int rows = model->rowCount(); - int columns = model->columnCount(); - - // check out of bounds - Q_ASSERT(model->hasIndex(rows, columns) == false); - Q_ASSERT(model->hasIndex(rows + 1, columns + 1) == false); - - if (rows > 0) - Q_ASSERT(model->hasIndex(0, 0) == true); - - // hasIndex() is tested more extensively in checkChildren(), - // but this catches the big mistakes -} - -/*! - Tests model's implementation of QAbstractItemModel::index() - */ -void ModelTest::index() -{ - // Make sure that invalid values returns an invalid index - Q_ASSERT(model->index(-2, -2) == QModelIndex()); - Q_ASSERT(model->index(-2, 0) == QModelIndex()); - Q_ASSERT(model->index(0, -2) == QModelIndex()); - - int rows = model->rowCount(); - int columns = model->columnCount(); - - if (rows == 0) - return; - - // Catch off by one errors - Q_ASSERT(model->index(rows, columns) == QModelIndex()); - Q_ASSERT(model->index(0, 0).isValid() == true); - - // Make sure that the same index is *always* returned - QModelIndex a = model->index(0, 0); - QModelIndex b = model->index(0, 0); - Q_ASSERT(a == b); - - // index() is tested more extensively in checkChildren(), - // but this catches the big mistakes -} - -/*! - Tests model's implementation of QAbstractItemModel::parent() - */ -void ModelTest::parent() -{ - // Make sure the model wont crash and will return an invalid QModelIndex - // when asked for the parent of an invalid index. - Q_ASSERT(model->parent(QModelIndex()) == QModelIndex()); - - if (model->rowCount() == 0) - return; - - // Column 0 | Column 1 | - // QModelIndex() | | - // \- topIndex | topIndex1 | - // \- childIndex | childIndex1 | - - // Common error test #1, make sure that a top level index has a parent - // that is a invalid QModelIndex. - QModelIndex topIndex = model->index(0, 0, QModelIndex()); - Q_ASSERT(model->parent(topIndex) == QModelIndex()); - - // Common error test #2, make sure that a second level index has a parent - // that is the first level index. - if (model->rowCount(topIndex) > 0) { - QModelIndex childIndex = model->index(0, 0, topIndex); - Q_ASSERT(model->parent(childIndex) == topIndex); - } - - // Common error test #3, the second column should NOT have the same children - // as the first column in a row. - // Usually the second column shouldn't have children. - QModelIndex topIndex1 = model->index(0, 1, QModelIndex()); - if (model->rowCount(topIndex1) > 0) { - QModelIndex childIndex = model->index(0, 0, topIndex); - QModelIndex childIndex1 = model->index(0, 0, topIndex1); - Q_ASSERT(childIndex != childIndex1); - } - - // Full test, walk n levels deep through the model making sure that all - // parent's children correctly specify their parent. - checkChildren(QModelIndex()); -} - -/*! - Called from the parent() test. - - A model that returns an index of parent X should also return X when asking - for the parent of the index. - - This recursive function does pretty extensive testing on the whole model in an - effort to catch edge cases. - - This function assumes that rowCount(), columnCount() and index() already work. - If they have a bug it will point it out, but the above tests should have already - found the basic bugs because it is easier to figure out the problem in - those tests then this one. - */ -void ModelTest::checkChildren(const QModelIndex &parent, int currentDepth) -{ - // First just try walking back up the tree. - QModelIndex p = parent; - while (p.isValid()) - p = p.parent(); - - // For models that are dynamically populated - if (model->canFetchMore(parent)) { - fetchingMore = true; - model->fetchMore(parent); - fetchingMore = false; - } - - int rows = model->rowCount(parent); - int columns = model->columnCount(parent); - - if (rows > 0) - Q_ASSERT(model->hasChildren(parent)); - - // Some further testing against rows(), columns(), and hasChildren() - Q_ASSERT(rows >= 0); - Q_ASSERT(columns >= 0); - if (rows > 0) - Q_ASSERT(model->hasChildren(parent) == true); - - //qDebug() << "parent:" << model->data(parent).toString() << "rows:" << rows - // << "columns:" << columns << "parent column:" << parent.column(); - - Q_ASSERT(model->hasIndex(rows + 1, 0, parent) == false); - for (int r = 0; r < rows; ++r) { - if (model->canFetchMore(parent)) { - fetchingMore = true; - model->fetchMore(parent); - fetchingMore = false; - } - Q_ASSERT(model->hasIndex(r, columns + 1, parent) == false); - for (int c = 0; c < columns; ++c) { - Q_ASSERT(model->hasIndex(r, c, parent) == true); - QModelIndex index = model->index(r, c, parent); - // rowCount() and columnCount() said that it existed... - Q_ASSERT(index.isValid() == true); - - // index() should always return the same index when called twice in a row - QModelIndex modifiedIndex = model->index(r, c, parent); - Q_ASSERT(index == modifiedIndex); - - // Make sure we get the same index if we request it twice in a row - QModelIndex a = model->index(r, c, parent); - QModelIndex b = model->index(r, c, parent); - Q_ASSERT(a == b); - - // Some basic checking on the index that is returned - Q_ASSERT(index.model() == model); - Q_ASSERT(index.row() == r); - Q_ASSERT(index.column() == c); - // While you can technically return a QVariant usually this is a sign - // of an bug in data() Disable if this really is ok in your model. - //Q_ASSERT(model->data(index, Qt::DisplayRole).isValid() == true); - - // If the next test fails here is some somewhat useful debug you play with. - /* - if (model->parent(index) != parent) { - qDebug() << r << c << currentDepth << model->data(index).toString() - << model->data(parent).toString(); - qDebug() << index << parent << model->parent(index); - // And a view that you can even use to show the model. - //QTreeView view; - //view.setModel(model); - //view.show(); - }*/ - - // Check that we can get back our real parent. - QModelIndex p = model->parent(index); - //qDebug() << "child:" << index; - //qDebug() << p; - //qDebug() << parent; - Q_ASSERT(model->parent(index) == parent); - - // recursively go down the children - if (model->hasChildren(index) && currentDepth < 10 ) { - //qDebug() << r << c << "has children" << model->rowCount(index); - checkChildren(index, ++currentDepth); - }/* else { if (currentDepth >= 10) qDebug() << "checked 10 deep"; };*/ - - // make sure that after testing the children that the index doesn't change. - QModelIndex newerIndex = model->index(r, c, parent); - Q_ASSERT(index == newerIndex); - } - } -} - -/*! - Tests model's implementation of QAbstractItemModel::data() - */ -void ModelTest::data() -{ - // Invalid index should return an invalid qvariant - Q_ASSERT(!model->data(QModelIndex()).isValid()); - - if (model->rowCount() == 0) - return; - - // A valid index should have a valid QVariant data - Q_ASSERT(model->index(0, 0).isValid()); - - // shouldn't be able to set data on an invalid index - Q_ASSERT(model->setData(QModelIndex(), QLatin1String("foo"), Qt::DisplayRole) == false); - - // General Purpose roles that should return a QString - QVariant variant = model->data(model->index(0, 0), Qt::ToolTipRole); - if (variant.isValid()) { - Q_ASSERT(qVariantCanConvert(variant)); - } - variant = model->data(model->index(0, 0), Qt::StatusTipRole); - if (variant.isValid()) { - Q_ASSERT(qVariantCanConvert(variant)); - } - variant = model->data(model->index(0, 0), Qt::WhatsThisRole); - if (variant.isValid()) { - Q_ASSERT(qVariantCanConvert(variant)); - } - - // General Purpose roles that should return a QSize - variant = model->data(model->index(0, 0), Qt::SizeHintRole); - if (variant.isValid()) { - Q_ASSERT(qVariantCanConvert(variant)); - } - - // General Purpose roles that should return a QFont - QVariant fontVariant = model->data(model->index(0, 0), Qt::FontRole); - if (fontVariant.isValid()) { - Q_ASSERT(qVariantCanConvert(fontVariant)); - } - - // Check that the alignment is one we know about - QVariant textAlignmentVariant = model->data(model->index(0, 0), Qt::TextAlignmentRole); - if (textAlignmentVariant.isValid()) { - int alignment = textAlignmentVariant.toInt(); - Q_ASSERT(alignment == Qt::AlignLeft || - alignment == Qt::AlignRight || - alignment == Qt::AlignHCenter || - alignment == Qt::AlignJustify || - alignment == Qt::AlignTop || - alignment == Qt::AlignBottom || - alignment == Qt::AlignVCenter || - alignment == Qt::AlignCenter || - alignment == Qt::AlignAbsolute || - alignment == Qt::AlignLeading || - alignment == Qt::AlignTrailing); - } - - // General Purpose roles that should return a QColor - QVariant colorVariant = model->data(model->index(0, 0), Qt::BackgroundColorRole); - if (colorVariant.isValid()) { - Q_ASSERT(qVariantCanConvert(colorVariant)); - } - - colorVariant = model->data(model->index(0, 0), Qt::TextColorRole); - if (colorVariant.isValid()) { - Q_ASSERT(qVariantCanConvert(colorVariant)); - } - - // Check that the "check state" is one we know about. - QVariant checkStateVariant = model->data(model->index(0, 0), Qt::CheckStateRole); - if (checkStateVariant.isValid()) { - int state = checkStateVariant.toInt(); - Q_ASSERT(state == Qt::Unchecked || - state == Qt::PartiallyChecked || - state == Qt::Checked); - } -} - -/*! - Store what is about to be inserted to make sure it actually happens - - \sa rowsInserted() - */ -void ModelTest::rowsAboutToBeInserted(const QModelIndex &parent, int start, int end) -{ - Q_UNUSED(end); - Changing c; - c.parent = parent; - c.oldSize = model->rowCount(parent); - c.last = model->data(model->index(start - 1, 0, parent)); - c.next = model->data(model->index(start, 0, parent)); - insert.push(c); -} - -/*! - Confirm that what was said was going to happen actually did - - \sa rowsAboutToBeInserted() - */ -void ModelTest::rowsInserted(const QModelIndex & parent, int start, int end) -{ - Changing c = insert.pop(); - Q_ASSERT(c.parent == parent); - Q_ASSERT(c.oldSize + (end - start + 1) == model->rowCount(parent)); - Q_ASSERT(c.last == model->data(model->index(start - 1, 0, c.parent))); - /* - if (c.next != model->data(model->index(end + 1, 0, c.parent))) { - qDebug() << start << end; - for (int i=0; i < model->rowCount(); ++i) - qDebug() << model->index(i, 0).data().toString(); - qDebug() << c.next << model->data(model->index(end + 1, 0, c.parent)); - } - */ - Q_ASSERT(c.next == model->data(model->index(end + 1, 0, c.parent))); -} - -void ModelTest::layoutAboutToBeChanged() -{ - for (int i = 0; i < qBound(0, model->rowCount(), 100); ++i) - changing.append(QPersistentModelIndex(model->index(i, 0))); -} - -void ModelTest::layoutChanged() -{ - for (int i = 0; i < changing.count(); ++i) { - QPersistentModelIndex p = changing[i]; - Q_ASSERT(p == model->index(p.row(), p.column(), p.parent())); - } - changing.clear(); -} - -/*! - Store what is about to be inserted to make sure it actually happens - - \sa rowsRemoved() - */ -void ModelTest::rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end) -{ - Changing c; - c.parent = parent; - c.oldSize = model->rowCount(parent); - c.last = model->data(model->index(start - 1, 0, parent)); - c.next = model->data(model->index(end + 1, 0, parent)); - remove.push(c); -} - -/*! - Confirm that what was said was going to happen actually did - - \sa rowsAboutToBeRemoved() - */ -void ModelTest::rowsRemoved(const QModelIndex & parent, int start, int end) -{ - Changing c = remove.pop(); - Q_ASSERT(c.parent == parent); - Q_ASSERT(c.oldSize - (end - start + 1) == model->rowCount(parent)); - Q_ASSERT(c.last == model->data(model->index(start - 1, 0, c.parent))); - Q_ASSERT(c.next == model->data(model->index(start, 0, c.parent))); -} - diff --git a/src/libtomahawk/utils/modeltest.h b/src/libtomahawk/utils/modeltest.h deleted file mode 100644 index 38b6b2bed..000000000 --- a/src/libtomahawk/utils/modeltest.h +++ /dev/null @@ -1,76 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2007 Trolltech ASA. All rights reserved. -** -** This file is part of the Qt Concurrent project on Trolltech Labs. -** -** This file may be used under the terms of the GNU General Public -** License version 2.0 as published by the Free Software Foundation -** and appearing in the file LICENSE.GPL included in the packaging of -** this file. Please review the following information to ensure GNU -** General Public Licensing requirements will be met: -** http://www.trolltech.com/products/qt/opensource.html -** -** If you are unsure which license is appropriate for your use, please -** review the following information: -** http://www.trolltech.com/products/qt/licensing.html or contact the -** sales department at sales@trolltech.com. -** -** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE -** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. -** -****************************************************************************/ - -#ifndef MODELTEST_H -#define MODELTEST_H - -#include -#include -#include - -class ModelTest : public QObject -{ - Q_OBJECT - -public: - ModelTest(QAbstractItemModel *model, QObject *parent = 0); - -private Q_SLOTS: - void nonDestructiveBasicTest(); - void rowCount(); - void columnCount(); - void hasIndex(); - void index(); - void parent(); - void data(); - -protected Q_SLOTS: - void runAllTests(); - void layoutAboutToBeChanged(); - void layoutChanged(); - void rowsAboutToBeInserted(const QModelIndex &parent, int start, int end); - void rowsInserted(const QModelIndex & parent, int start, int end); - void rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end); - void rowsRemoved(const QModelIndex & parent, int start, int end); - -private: - void checkChildren(const QModelIndex &parent, int currentDepth = 0); - - QAbstractItemModel *model; - - struct Changing - { - QModelIndex parent; - int oldSize; - QVariant last; - QVariant next; - }; - QStack insert; - QStack remove; - - bool fetchingMore; - - QList changing; -}; - -#endif diff --git a/src/libtomahawk/utils/progresstreeview.cpp b/src/libtomahawk/utils/progresstreeview.cpp index 58ca9edef..2458de911 100644 --- a/src/libtomahawk/utils/progresstreeview.cpp +++ b/src/libtomahawk/utils/progresstreeview.cpp @@ -1,3 +1,21 @@ +/* === 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 "progresstreeview.h" ProgressTreeView::ProgressTreeView( QWidget* parent ) diff --git a/src/libtomahawk/utils/progresstreeview.h b/src/libtomahawk/utils/progresstreeview.h index 154e1e7d0..e77971eaf 100644 --- a/src/libtomahawk/utils/progresstreeview.h +++ b/src/libtomahawk/utils/progresstreeview.h @@ -1,3 +1,21 @@ +/* === 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 PROGRESSTREEVIEW_H #define PROGRESSTREEVIEW_H diff --git a/src/libtomahawk/utils/proxystyle.cpp b/src/libtomahawk/utils/proxystyle.cpp index 617cd348f..038208a85 100644 --- a/src/libtomahawk/utils/proxystyle.cpp +++ b/src/libtomahawk/utils/proxystyle.cpp @@ -1,3 +1,21 @@ +/* === 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 "proxystyle.h" #include diff --git a/src/libtomahawk/utils/proxystyle.h b/src/libtomahawk/utils/proxystyle.h index c104ac0fe..488c87908 100644 --- a/src/libtomahawk/utils/proxystyle.h +++ b/src/libtomahawk/utils/proxystyle.h @@ -1,3 +1,21 @@ +/* === 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 PROXYSTYLE_H #define PROXYSTYLE_H diff --git a/src/libtomahawk/utils/querylabel.cpp b/src/libtomahawk/utils/querylabel.cpp index 5a211f350..d765f65dd 100644 --- a/src/libtomahawk/utils/querylabel.cpp +++ b/src/libtomahawk/utils/querylabel.cpp @@ -1,3 +1,21 @@ +/* === 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 "querylabel.h" #include @@ -526,6 +544,7 @@ QueryLabel::mouseMoveEvent( QMouseEvent* event ) void QueryLabel::leaveEvent( QEvent* event ) { + Q_UNUSED( event ); m_hoverArea = QRect(); m_hoverType = None; repaint(); diff --git a/src/libtomahawk/utils/querylabel.h b/src/libtomahawk/utils/querylabel.h index b584d255b..1fed3d9c2 100644 --- a/src/libtomahawk/utils/querylabel.h +++ b/src/libtomahawk/utils/querylabel.h @@ -1,3 +1,21 @@ +/* === 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 QUERYLABEL_H #define QUERYLABEL_H diff --git a/src/libtomahawk/utils/tomahawkutils.cpp b/src/libtomahawk/utils/tomahawkutils.cpp index ac766b178..e652f5e43 100644 --- a/src/libtomahawk/utils/tomahawkutils.cpp +++ b/src/libtomahawk/utils/tomahawkutils.cpp @@ -1,6 +1,25 @@ +/* === 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 "tomahawkutils.h" #include +#include #include #include #include @@ -247,6 +266,19 @@ extensionToMimetype( const QString& extension ) } +QColor +alphaBlend( const QColor& colorFrom, const QColor& colorTo, float opacity ) +{ + opacity = qMax( (float)0.3, opacity ); + int r = colorFrom.red(), g = colorFrom.green(), b = colorFrom.blue(); + r = opacity * r + ( 1 - opacity ) * colorTo.red(); + g = opacity * g + ( 1 - opacity ) * colorTo.green(); + b = opacity * b + ( 1 - opacity ) * colorTo.blue(); + + return QColor( r, g, b ); +} + + QPixmap createDragPixmap( int itemCount ) { @@ -348,7 +380,7 @@ dnsResolver() { if( !s_dnsResolver ) s_dnsResolver = new DNSResolver(); - + return s_dnsResolver; } @@ -369,7 +401,7 @@ DNSResolver::resolve( QString &host, QString type ) { // For the moment, assume we are looking for XMPP... QString fullHost( "_xmpp-client._tcp." + host ); - + qDebug() << "Looking up SRV record for " << fullHost.toUtf8(); m_dnsSharedRequest->query( fullHost.toUtf8(), QJDns::Srv ); diff --git a/src/libtomahawk/utils/tomahawkutils.h b/src/libtomahawk/utils/tomahawkutils.h index 5c1b68eaf..8490829f4 100644 --- a/src/libtomahawk/utils/tomahawkutils.h +++ b/src/libtomahawk/utils/tomahawkutils.h @@ -1,3 +1,21 @@ +/* === 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 TOMAHAWKUTILS_H #define TOMAHAWKUTILS_H @@ -7,6 +25,7 @@ #define RESPATH ":/data/" +class QColor; class QDir; class QDateTime; class QString; @@ -52,11 +71,11 @@ namespace TomahawkUtils { QThread::sleep( secs ); } - static void msleep( unsigned long msecs ) + static void msleep( unsigned long msecs ) { QThread::msleep( msecs ); } - static void usleep( unsigned long usecs ) + static void usleep( unsigned long usecs ) { QThread::usleep( usecs ); } @@ -71,6 +90,7 @@ namespace TomahawkUtils DLLEXPORT QString filesizeToString( unsigned int size ); DLLEXPORT QString extensionToMimetype( const QString& extension ); + DLLEXPORT QColor alphaBlend( const QColor& colorFrom, const QColor& colorTo, float opacity ); DLLEXPORT QPixmap createDragPixmap( int itemCount = 1 ); DLLEXPORT QNetworkAccessManager* nam(); diff --git a/src/libtomahawk/utils/widgetdragfilter.cpp b/src/libtomahawk/utils/widgetdragfilter.cpp index 40aec2aa8..fe5216669 100644 --- a/src/libtomahawk/utils/widgetdragfilter.cpp +++ b/src/libtomahawk/utils/widgetdragfilter.cpp @@ -1,3 +1,21 @@ +/* === 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 "widgetdragfilter.h" #include #include diff --git a/src/libtomahawk/utils/widgetdragfilter.h b/src/libtomahawk/utils/widgetdragfilter.h index 18bfc36dd..ab6c5c5ea 100644 --- a/src/libtomahawk/utils/widgetdragfilter.h +++ b/src/libtomahawk/utils/widgetdragfilter.h @@ -1,3 +1,21 @@ +/* === 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 WIDGET_DRAG_FILTER_H #define WIDGET_DRAG_FILTER_H diff --git a/src/libtomahawk/utils/xspfloader.cpp b/src/libtomahawk/utils/xspfloader.cpp index 88ad39b6f..d8b31d40d 100644 --- a/src/libtomahawk/utils/xspfloader.cpp +++ b/src/libtomahawk/utils/xspfloader.cpp @@ -1,3 +1,21 @@ +/* === 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 "xspfloader.h" #include @@ -119,7 +137,7 @@ XSPFLoader::gotBody() track = n.text(); } } - + if( artist.isEmpty() || track.isEmpty() ) { if( !shownError ) { QMessageBox::warning( 0, tr( "Failed to save tracks" ), tr( "Some tracks in the playlist do not contain an artist and a title. They will be ignored." ), QMessageBox::Ok ); diff --git a/src/libtomahawk/utils/xspfloader.h b/src/libtomahawk/utils/xspfloader.h index 7e0e1ece3..39e5088f7 100644 --- a/src/libtomahawk/utils/xspfloader.h +++ b/src/libtomahawk/utils/xspfloader.h @@ -1,3 +1,21 @@ +/* === 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 . + */ + /* Fetches and parses an XSPF document from a QFile or QUrl. */ diff --git a/src/libtomahawk/viewpage.cpp b/src/libtomahawk/viewpage.cpp index 321bbbdf8..4cd52c941 100644 --- a/src/libtomahawk/viewpage.cpp +++ b/src/libtomahawk/viewpage.cpp @@ -1,3 +1,21 @@ +/* === 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 "viewpage.h" #include diff --git a/src/libtomahawk/viewpage.h b/src/libtomahawk/viewpage.h index af087c669..356fabcf8 100644 --- a/src/libtomahawk/viewpage.h +++ b/src/libtomahawk/viewpage.h @@ -1,3 +1,21 @@ +/* === 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 VIEWPAGE_H #define VIEWPAGE_H @@ -11,12 +29,14 @@ namespace Tomahawk { - + class DLLEXPORT ViewPage { public: ViewPage() {} + virtual ~ViewPage() {} + virtual QWidget* widget() = 0; virtual PlaylistInterface* playlistInterface() const = 0; @@ -27,12 +47,18 @@ public: virtual bool showStatsBar() const { return true; } virtual bool showModes() const { return false; } virtual bool queueVisible() const { return true; } - + virtual bool jumpToCurrentTrack() = 0; + /** subclasses implementing ViewPage can emit the following signals: + * descriptionChanged( const QString& ) + * destroyed( QWidget* widget ); + * + * See DynamicWidget for an example + */ private: }; - + }; // ns #endif //VIEWPAGE_H diff --git a/src/libtomahawk/widgets/infowidgets/sourceinfowidget.cpp b/src/libtomahawk/widgets/infowidgets/sourceinfowidget.cpp index 80469499e..c78783538 100644 --- a/src/libtomahawk/widgets/infowidgets/sourceinfowidget.cpp +++ b/src/libtomahawk/widgets/infowidgets/sourceinfowidget.cpp @@ -1,3 +1,21 @@ +/* === 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 "sourceinfowidget.h" #include "ui_sourceinfowidget.h" diff --git a/src/libtomahawk/widgets/infowidgets/sourceinfowidget.h b/src/libtomahawk/widgets/infowidgets/sourceinfowidget.h index f12fd942d..12528129e 100644 --- a/src/libtomahawk/widgets/infowidgets/sourceinfowidget.h +++ b/src/libtomahawk/widgets/infowidgets/sourceinfowidget.h @@ -1,3 +1,21 @@ +/* === 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 SOURCEINFOWIDGET_H #define SOURCEINFOWIDGET_H diff --git a/src/libtomahawk/widgets/newplaylistwidget.cpp b/src/libtomahawk/widgets/newplaylistwidget.cpp index 7882532c0..bc633e6a2 100644 --- a/src/libtomahawk/widgets/newplaylistwidget.cpp +++ b/src/libtomahawk/widgets/newplaylistwidget.cpp @@ -1,3 +1,21 @@ +/* === 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 "newplaylistwidget.h" #include "ui_newplaylistwidget.h" @@ -36,7 +54,7 @@ NewPlaylistWidget::NewPlaylistWidget( QWidget* parent ) connect( ui->buttonBox, SIGNAL( rejected() ), SLOT( cancel() ) ); m_suggestionsModel = new PlaylistModel( ui->suggestionsView ); - ui->suggestionsView->setModel( m_suggestionsModel ); + ui->suggestionsView->setPlaylistModel( m_suggestionsModel ); ui->suggestionsView->overlay()->setEnabled( false ); connect( &m_filterTimer, SIGNAL( timeout() ), SLOT( updateSuggestions() ) ); @@ -105,7 +123,7 @@ NewPlaylistWidget::suggestionsFound() delete m_suggestionsModel; m_suggestionsModel = new PlaylistModel( ui->suggestionsView ); - ui->suggestionsView->setModel( m_suggestionsModel ); + ui->suggestionsView->setPlaylistModel( m_suggestionsModel ); QList ql; foreach( const Tomahawk::plentry_ptr& entry, m_entries ) diff --git a/src/libtomahawk/widgets/newplaylistwidget.h b/src/libtomahawk/widgets/newplaylistwidget.h index dfe54a988..61799ffd6 100644 --- a/src/libtomahawk/widgets/newplaylistwidget.h +++ b/src/libtomahawk/widgets/newplaylistwidget.h @@ -1,3 +1,21 @@ +/* === 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 NEWPLAYLISTWIDGET_H #define NEWPLAYLISTWIDGET_H @@ -29,7 +47,7 @@ public: virtual QWidget* widget() { return this; } virtual PlaylistInterface* playlistInterface() const { return 0; } - + virtual QString title() const { return tr( "Create a new playlist" ); } virtual QString description() const { return QString(); } diff --git a/src/libtomahawk/widgets/overlaywidget.cpp b/src/libtomahawk/widgets/overlaywidget.cpp index 780d5c4ce..64471b272 100644 --- a/src/libtomahawk/widgets/overlaywidget.cpp +++ b/src/libtomahawk/widgets/overlaywidget.cpp @@ -1,3 +1,21 @@ +/* === 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 "overlaywidget.h" #include @@ -6,7 +24,7 @@ #define CORNER_ROUNDNESS 16.0 #define FADING_DURATION 500 -#define FONT_SIZE 18 +#define FONT_SIZE 16 #define OPACITY 0.86 @@ -19,9 +37,15 @@ OverlayWidget::OverlayWidget( QWidget* parent ) setAttribute( Qt::WA_TranslucentBackground, true ); 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 } @@ -63,10 +87,9 @@ OverlayWidget::show( int timeoutSecs ) QPropertyAnimation* animation = new QPropertyAnimation( this, "opacity" ); animation->setDuration( FADING_DURATION ); - animation->setStartValue( 0.00 ); animation->setEndValue( OPACITY ); animation->start(); - + if( timeoutSecs > 0 ) m_timer.start( timeoutSecs * 1000 ); } @@ -98,6 +121,7 @@ OverlayWidget::shown() const void OverlayWidget::paintEvent( QPaintEvent* event ) { + Q_UNUSED( event ); QPoint center( ( m_parent->width() - width() ) / 2, ( m_parent->height() - height() ) / 2 ); move( center ); @@ -117,11 +141,26 @@ OverlayWidget::paintEvent( QPaintEvent* event ) QTextOption to( Qt::AlignCenter ); to.setWrapMode( QTextOption::WrapAtWordBoundaryOrAnywhere ); + // shrink to fit if needed QFont f( font() ); - f.setPixelSize( FONT_SIZE ); + f.setPointSize( FONT_SIZE ); f.setBold( true ); + QRectF textRect = r.adjusted( 8, 8, -8, -8 ); + qreal availHeight = textRect.height(); + + QFontMetricsF fm( f ); + qreal textHeight = fm.boundingRect( textRect, Qt::AlignCenter | Qt::TextWordWrap, text() ).height(); + while( textHeight > availHeight ) + { + if( f.pointSize() <= 4 ) // don't try harder + break; + + f.setPointSize( f.pointSize() - 1 ); + fm = QFontMetricsF( f ); + textHeight = fm.boundingRect( textRect, Qt::AlignCenter | Qt::TextWordWrap, text() ).height(); + } p.setFont( f ); p.setPen( palette().highlightedText().color() ); - p.drawText( r.adjusted( 16, 16, -16, -16 ), text(), to ); + p.drawText( r.adjusted( 8, 8, -8, -8 ), text(), to ); } diff --git a/src/libtomahawk/widgets/overlaywidget.h b/src/libtomahawk/widgets/overlaywidget.h index 5eeecdde9..5775b991d 100644 --- a/src/libtomahawk/widgets/overlaywidget.h +++ b/src/libtomahawk/widgets/overlaywidget.h @@ -1,3 +1,21 @@ +/* === 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 OVERLAYWIDGET_H #define OVERLAYWIDGET_H @@ -23,6 +41,7 @@ public: void setText( const QString& text ); bool shown() const; + public slots: void show( int timeoutSecs = 0 ); void hide(); diff --git a/src/libtomahawk/widgets/welcomewidget.cpp b/src/libtomahawk/widgets/welcomewidget.cpp index feb2d988a..5297710e5 100644 --- a/src/libtomahawk/widgets/welcomewidget.cpp +++ b/src/libtomahawk/widgets/welcomewidget.cpp @@ -1,3 +1,21 @@ +/* === 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 "welcomewidget.h" #include "ui_welcomewidget.h" @@ -13,7 +31,9 @@ #include -#define FILTER_TIMEOUT 280 +#define HISTORY_TRACK_ITEMS 50 +#define HISTORY_PLAYLIST_ITEMS 10 +#define HISTORY_RESOLVING_TIMEOUT 2500 WelcomeWidget::WelcomeWidget( QWidget* parent ) @@ -23,11 +43,15 @@ WelcomeWidget::WelcomeWidget( QWidget* parent ) ui->setupUi( this ); ui->playlistWidget->setItemDelegate( new PlaylistDelegate() ); + ui->playlistWidget->overlay()->resize( 380, 86 ); ui->tracksView->overlay()->setEnabled( false ); m_tracksModel = new PlaylistModel( ui->tracksView ); - ui->tracksView->setModel( m_tracksModel ); - m_tracksModel->loadHistory( Tomahawk::source_ptr() ); + ui->tracksView->setPlaylistModel( m_tracksModel ); + m_tracksModel->loadHistory( Tomahawk::source_ptr(), HISTORY_TRACK_ITEMS ); + + m_timer = new QTimer( this ); + connect( m_timer, SIGNAL( timeout() ), SLOT( checkQueries() ) ); connect( SourceList::instance(), SIGNAL( sourceAdded( Tomahawk::source_ptr ) ), SLOT( onSourceAdded( Tomahawk::source_ptr ) ) ); @@ -77,10 +101,25 @@ WelcomeWidget::onSourceAdded( const Tomahawk::source_ptr& source ) } +void +WelcomeWidget::checkQueries() +{ + m_timer->stop(); + m_tracksModel->ensureResolved(); +} + + void WelcomeWidget::onPlaybackFinished( const Tomahawk::query_ptr& query ) { m_tracksModel->insert( 0, query ); + + if ( m_tracksModel->trackCount() > HISTORY_TRACK_ITEMS ) + m_tracksModel->remove( HISTORY_TRACK_ITEMS ); + + if ( m_timer->isActive() ) + m_timer->stop(); + m_timer->start( HISTORY_RESOLVING_TIMEOUT ); } @@ -151,6 +190,8 @@ PlaylistWidgetItem::data( int role ) const QSize PlaylistDelegate::sizeHint( const QStyleOptionViewItem& option, const QModelIndex& index ) const { + Q_UNUSED( option ); + Q_UNUSED( index ); return QSize( 0, 64 ); } @@ -177,7 +218,7 @@ PlaylistDelegate::paint( QPainter* painter, const QStyleOptionViewItem& option, QFont boldFont = opt.font; boldFont.setBold( true ); - painter->drawPixmap( option.rect.adjusted( 10, 12, -option.rect.width() + 42, -12 ), m_playlistIcon ); + painter->drawPixmap( option.rect.adjusted( 10, 13, -option.rect.width() + 48, -13 ), m_playlistIcon ); painter->drawText( option.rect.adjusted( 56, 26, -100, -8 ), index.data( PlaylistWidgetItem::ArtistRole ).toString() ); diff --git a/src/libtomahawk/widgets/welcomewidget.h b/src/libtomahawk/widgets/welcomewidget.h index 9d14859e8..036e3d5ca 100644 --- a/src/libtomahawk/widgets/welcomewidget.h +++ b/src/libtomahawk/widgets/welcomewidget.h @@ -1,3 +1,21 @@ +/* === 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 WELCOMEWIDGET_H #define WELCOMEWIDGET_H @@ -106,10 +124,13 @@ private slots: void onPlaylistActivated( QListWidgetItem* item ); void onPlaybackFinished( const Tomahawk::query_ptr& query ); + void checkQueries(); + private: Ui::WelcomeWidget *ui; PlaylistModel* m_tracksModel; + QTimer* m_timer; }; #endif // WELCOMEWIDGET_H diff --git a/src/mac/macshortcuthandler.cpp b/src/mac/macshortcuthandler.cpp index 80bc64815..34701f7f1 100644 --- a/src/mac/macshortcuthandler.cpp +++ b/src/mac/macshortcuthandler.cpp @@ -1,3 +1,21 @@ +/* === 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 "macshortcuthandler.h" #include diff --git a/src/mac/macshortcuthandler.h b/src/mac/macshortcuthandler.h index 014d71421..b7739c20d 100644 --- a/src/mac/macshortcuthandler.h +++ b/src/mac/macshortcuthandler.h @@ -1,3 +1,21 @@ +/* === 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 MACSHORTCUTHANDLER_H #define MACSHORTCUTHANDLER_H diff --git a/src/mac/tomahawkapp_mac.h b/src/mac/tomahawkapp_mac.h index 0d3f59f61..86ae6702c 100644 --- a/src/mac/tomahawkapp_mac.h +++ b/src/mac/tomahawkapp_mac.h @@ -1,3 +1,21 @@ +/* === 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 TOMAHAWKAPP_MAC_H #define TOMAHAWKAPP_MAC_H diff --git a/src/mac/tomahawkapp_mac.mm b/src/mac/tomahawkapp_mac.mm index 3ed942031..44660e742 100644 --- a/src/mac/tomahawkapp_mac.mm +++ b/src/mac/tomahawkapp_mac.mm @@ -1,3 +1,21 @@ +/* === 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 "tomahawkapp_mac.h" #include "tomahawkapp_macdelegate.h" #include "macshortcuthandler.h" diff --git a/src/main.cpp b/src/main.cpp index 137172c69..b55cf2800 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,12 +1,34 @@ +/* === 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 "tomahawk/tomahawkapp.h" +#include "kdsingleapplicationguard/kdsingleapplicationguard.h" + +#include + #ifdef Q_WS_MAC #include "tomahawkapp_mac.h" #include static pascal OSErr appleEventHandler( const AppleEvent*, AppleEvent*, long ); #endif -#include + int main( int argc, char *argv[] ) { @@ -18,19 +40,25 @@ main( int argc, char *argv[] ) // used for url handler AEEventHandlerUPP h = AEEventHandlerUPP( appleEventHandler ); AEInstallEventHandler( 'GURL', 'GURL', h, 0, false ); - #endif - try - { - TomahawkApp a( argc, argv ); - return a.exec(); - } - catch( const std::runtime_error& e ) - { - return 0; - } + + TomahawkApp a( argc, argv ); + KDSingleApplicationGuard guard( &a, KDSingleApplicationGuard::AutoKillOtherInstances ); + QObject::connect( &guard, SIGNAL( instanceStarted( KDSingleApplicationGuard::Instance ) ), &a, SLOT( instanceStarted( KDSingleApplicationGuard::Instance ) ) ); + + if ( guard.isPrimaryInstance() ) + a.init(); + + QString locale = QLocale::system().name(); + + QTranslator translator; + translator.load( QString( ":/lang/tomahawk_" ) + locale ); + a.installTranslator( &translator ); + + return a.exec(); } + #ifdef Q_WS_MAC static pascal OSErr appleEventHandler( const AppleEvent* e, AppleEvent*, long ) diff --git a/src/musicscanner.cpp b/src/musicscanner.cpp index 3f5f7d34a..0fc26cb17 100644 --- a/src/musicscanner.cpp +++ b/src/musicscanner.cpp @@ -1,6 +1,25 @@ +/* === 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 "musicscanner.h" #include "tomahawk/tomahawkapp.h" +#include "tomahawksettings.h" #include "sourcelist.h" #include "database/database.h" #include "database/databasecommand_dirmtimes.h" @@ -13,25 +32,58 @@ using namespace Tomahawk; void DirLister::go() { - scanDir( m_dir, 0 ); + qDebug() << Q_FUNC_INFO; + qDebug() << "Recursive? : " << (m_recursive ? "true" : "false"); + if( !m_recursive ) + { + foreach( QString dir, m_dirs ) + { + if( m_dirmtimes.contains( dir ) ) + { + qDebug() << "Removing " << dir << " from m_dirmtimes because it's specifically requested"; + m_dirmtimes.remove( dir ); + } + QStringList filtered = QStringList( m_dirmtimes.keys() ).filter( dir ); + foreach( QString filteredDir, filtered ) + { + if( !QDir( filteredDir ).exists() ) + { + qDebug() << "Removing " << filteredDir << " from m_dirmtimes because it does not exist"; + m_dirmtimes.remove( filteredDir ); + } + } + } + m_newdirmtimes = m_dirmtimes; + } + + foreach( QString dir, m_dirs ) + scanDir( QDir( dir, 0 ), 0, ( m_recursive ? DirLister::Recursive : DirLister::NonRecursive ) ); emit finished( m_newdirmtimes ); } void -DirLister::scanDir( QDir dir, int depth ) +DirLister::scanDir( QDir dir, int depth, DirLister::Mode mode ) { + qDebug() << "DirLister::scanDir scanning: " << dir.canonicalPath() << " with mode " << mode; + + if( !dir.exists() ) + { + qDebug() << "Dir no longer exists, not scanning"; + return; + } + QFileInfoList dirs; - const uint mtime = QFileInfo( dir.absolutePath() ).lastModified().toUTC().toTime_t(); - m_newdirmtimes.insert( dir.absolutePath(), mtime ); + const uint mtime = QFileInfo( dir.canonicalPath() ).lastModified().toUTC().toTime_t(); + m_newdirmtimes.insert( dir.canonicalPath(), mtime ); - if ( m_dirmtimes.contains( dir.absolutePath() ) && mtime == m_dirmtimes.value( dir.absolutePath() ) ) + if ( m_dirmtimes.contains( dir.canonicalPath() ) && mtime == m_dirmtimes.value( dir.canonicalPath() ) ) { // dont scan this dir, unchanged since last time. } else { - if ( m_dirmtimes.contains( dir.absolutePath() ) ) + if( m_dirmtimes.contains( dir.canonicalPath() ) || !m_recursive ) Database::instance()->enqueue( QSharedPointer( new DatabaseCommand_DeleteFiles( dir, SourceList::instance()->getLocal() ) ) ); dir.setFilter( QDir::Files | QDir::Readable | QDir::NoDotAndDotDot ); @@ -44,17 +96,24 @@ DirLister::scanDir( QDir dir, int depth ) } dir.setFilter( QDir::Dirs | QDir::Readable | QDir::NoDotAndDotDot ); dirs = dir.entryInfoList(); - + foreach( const QFileInfo& di, dirs ) { - scanDir( di.absoluteFilePath(), depth + 1 ); + const QString canonical = di.canonicalFilePath(); + qDebug() << "Considering dir " << canonical; + const bool haveDi = m_dirmtimes.contains( canonical ); + qDebug() << "m_dirmtimes contains it?" << haveDi; + if( !m_newdirmtimes.contains( canonical ) && ( mode == DirLister::Recursive || !haveDi ) ) { + scanDir( di.canonicalFilePath(), depth + 1, DirLister::Recursive ); + } } } -MusicScanner::MusicScanner( const QString& dir, quint32 bs ) +MusicScanner::MusicScanner( const QStringList& dirs, bool recursive, quint32 bs ) : QObject() - , m_dir( dir ) + , m_dirs( dirs ) + , m_recursive( recursive ) , m_batchsize( bs ) , m_dirLister( 0 ) , m_dirListerThreadController( 0 ) @@ -104,11 +163,9 @@ MusicScanner::startScan() m_skippedFiles.clear(); // trigger the scan once we've loaded old mtimes for dirs below our path - DatabaseCommand_DirMtimes* cmd = new DatabaseCommand_DirMtimes( m_dir ); + DatabaseCommand_DirMtimes* cmd = new DatabaseCommand_DirMtimes( TomahawkSettings::instance()->scannerPaths() ); connect( cmd, SIGNAL( done( QMap ) ), SLOT( setMtimes( QMap ) ) ); - connect( cmd, SIGNAL( done( QMap ) ), - SLOT( scan() ) ); Database::instance()->enqueue( QSharedPointer(cmd) ); } @@ -117,7 +174,9 @@ MusicScanner::startScan() void MusicScanner::setMtimes( const QMap& m ) { + qDebug() << Q_FUNC_INFO << m.count(); m_dirmtimes = m; + scan(); } @@ -130,7 +189,8 @@ MusicScanner::scan() SLOT( commitBatch( QVariantList ) ), Qt::DirectConnection ); m_dirListerThreadController = new QThread( this ); - m_dirLister = new DirLister( QDir( m_dir, 0 ), m_dirmtimes ); + + m_dirLister = new DirLister( m_dirs, m_dirmtimes, m_recursive ); m_dirLister->moveToThread( m_dirListerThreadController ); connect( m_dirLister, SIGNAL( fileToScan( QFileInfo ) ), @@ -164,9 +224,12 @@ MusicScanner::listerFinished( const QMap& newmtimes ) { qDebug() << "Removing stale dir:" << path; Database::instance()->enqueue( QSharedPointer( new DatabaseCommand_DeleteFiles( path, SourceList::instance()->getLocal() ) ) ); + emit removeWatchedDir( path ); } } + emit addWatchedDirs( newmtimes.keys() ); + // save mtimes, then quit thread DatabaseCommand_DirMtimes* cmd = new DatabaseCommand_DirMtimes( newmtimes ); connect( cmd, SIGNAL( finished() ), SLOT( deleteLister() ) ); @@ -203,6 +266,7 @@ MusicScanner::listerQuit() void MusicScanner::listerDestroyed( QObject* dirLister ) { + Q_UNUSED( dirLister ); qDebug() << Q_FUNC_INFO; m_dirListerThreadController->deleteLater(); m_dirListerThreadController = 0; @@ -256,20 +320,20 @@ MusicScanner::readFile( const QFileInfo& fi ) if( m_scanned % 3 == 0 ) SourceList::instance()->getLocal()->scanningProgress( m_scanned ); if( m_scanned % 100 == 0 ) - qDebug() << "SCAN" << m_scanned << fi.absoluteFilePath(); + qDebug() << "SCAN" << m_scanned << fi.canonicalFilePath(); #ifdef COMPLEX_TAGLIB_FILENAME - const wchar_t *encodedName = reinterpret_cast< const wchar_t * >( fi.absoluteFilePath().utf16() ); + const wchar_t *encodedName = reinterpret_cast< const wchar_t * >( fi.canonicalFilePath().utf16() ); #else - QByteArray fileName = QFile::encodeName( fi.absoluteFilePath() ); + QByteArray fileName = QFile::encodeName( fi.canonicalFilePath() ); const char *encodedName = fileName.constData(); #endif TagLib::FileRef f( encodedName ); if ( f.isNull() || !f.tag() ) { - // qDebug() << "Doesn't seem to be a valid audiofile:" << fi.absoluteFilePath(); - m_skippedFiles << fi.absoluteFilePath(); + // qDebug() << "Doesn't seem to be a valid audiofile:" << fi.canonicalFilePath(); + m_skippedFiles << fi.canonicalFilePath(); m_skipped++; return QVariantMap(); } @@ -290,8 +354,8 @@ MusicScanner::readFile( const QFileInfo& fi ) if ( artist.isEmpty() || track.isEmpty() ) { // FIXME: do some clever filename guessing - // qDebug() << "No tags found, skipping" << fi.absoluteFilePath(); - m_skippedFiles << fi.absoluteFilePath(); + // qDebug() << "No tags found, skipping" << fi.canonicalFilePath(); + m_skippedFiles << fi.canonicalFilePath(); m_skipped++; return QVariantMap(); } @@ -300,7 +364,7 @@ MusicScanner::readFile( const QFileInfo& fi ) QString url( "file://%1" ); QVariantMap m; - m["url"] = url.arg( fi.absoluteFilePath() ); + m["url"] = url.arg( fi.canonicalFilePath() ); m["mtime"] = fi.lastModified().toUTC().toTime_t(); m["size"] = (unsigned int)fi.size(); m["mimetype"] = mimetype; diff --git a/src/musicscanner.h b/src/musicscanner.h index 0787f5486..0944c2f90 100644 --- a/src/musicscanner.h +++ b/src/musicscanner.h @@ -1,8 +1,27 @@ +/* === 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 MUSICSCANNER_H #define MUSICSCANNER_H -#include -#include +/* taglib */ +#include +#include #include #include @@ -20,8 +39,15 @@ class DirLister : public QObject Q_OBJECT public: - DirLister( QDir d, QMap& mtimes ) - : QObject(), m_dir( d ), m_dirmtimes( mtimes ) + + enum Mode { + NonRecursive, + Recursive, + MTimeOnly + }; + + DirLister( const QStringList& dirs, const QMap& mtimes, bool recursive ) + : QObject(), m_dirs( dirs ), m_dirmtimes( mtimes ), m_recursive( recursive ) { qDebug() << Q_FUNC_INFO; } @@ -37,11 +63,13 @@ signals: private slots: void go(); - void scanDir( QDir dir, int depth ); + void scanDir( QDir dir, int depth, DirLister::Mode mode ); private: - QDir m_dir; + QStringList m_dirs; QMap m_dirmtimes; + bool m_recursive; + QMap m_newdirmtimes; }; @@ -50,13 +78,15 @@ class MusicScanner : public QObject Q_OBJECT public: - MusicScanner( const QString& dir, quint32 bs = 0 ); + MusicScanner( const QStringList& dirs, bool recursive = true, quint32 bs = 0 ); ~MusicScanner(); signals: //void fileScanned( QVariantMap ); void finished(); void batchReady( const QVariantList& ); + void addWatchedDirs( const QStringList & ); + void removeWatchedDir( const QString & ); private: QVariant readFile( const QFileInfo& fi ); @@ -73,7 +103,7 @@ private slots: void commitBatch( const QVariantList& ); private: - QString m_dir; + QStringList m_dirs; QMap m_ext2mime; // eg: mp3 -> audio/mpeg unsigned int m_scanned; unsigned int m_skipped; @@ -84,6 +114,7 @@ private: QMap m_newdirmtimes; QList m_scannedfiles; + bool m_recursive; quint32 m_batchsize; DirLister* m_dirLister; diff --git a/src/plugins/fake/CMakeLists.txt b/src/plugins/fake/CMakeLists.txt deleted file mode 100644 index 8fe1f24bf..000000000 --- a/src/plugins/fake/CMakeLists.txt +++ /dev/null @@ -1,45 +0,0 @@ -project( tomahawk ) -cmake_minimum_required(VERSION 2.6) -find_package( Qt4 REQUIRED ) - -include( ${QT_USE_FILE} ) - - -SET(TOMAHAWK_INC_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../../../include/") - -SET(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${TOMAHAWK_INC_DIR}/..") - -set(cpps - fakeplugin.cpp - fakecollection.cpp -) - -set(hs -fakeplugin.h -fakecollection.h -) - -include_directories( - . - .. - ${TOMAHAWK_INC_DIR} - ${CMAKE_CURRENT_BINARY_DIR} - ${QT_INCLUDE_DIR} -) - -qt4_wrap_cpp( mocs ${hs} ) - -ADD_DEFINITIONS(${QT_DEFINITIONS}) -ADD_DEFINITIONS(-DQT_PLUGIN) -#ADD_DEFINITIONS(-DQT_NO_DEBUG) -ADD_DEFINITIONS(-DQT_SHARED) - -add_library(fake SHARED - ${cpps} - ${mocs} -) - -target_link_libraries(fake - ${QT_LIBRARIES} - ${QT_QTSQL_LIBRARIES} -) diff --git a/src/plugins/fake/fakecollection.cpp b/src/plugins/fake/fakecollection.cpp deleted file mode 100644 index fc01399f8..000000000 --- a/src/plugins/fake/fakecollection.cpp +++ /dev/null @@ -1,27 +0,0 @@ -#include "fakecollection.h" -#include "tomahawk/functimeout.h" - -FakeCollection::FakeCollection(QObject *parent) : - Collection("FakeCollection", parent) -{ -} - -void FakeCollection::load() -{ - QList tracks; - QVariantMap t1, t2, t3; - t1["artist"] = "0AAAAAArtist 1"; - t1["track"] = "0TTTTTTrack 1"; - t1["album"] = "0AAAAAAlbum 1"; - t1["url"] = "fake://1"; - t1["filesize"] = 5000000; - t1["duration"] = 300; - t1["bitrate"] = 192; - tracks << t1; - - new Tomahawk::FuncTimeout(5000, boost::bind(&FakeCollection::removeTracks, - this, tracks)); - - addTracks(tracks); - reportFinishedLoading(); - } diff --git a/src/plugins/fake/fakecollection.h b/src/plugins/fake/fakecollection.h deleted file mode 100644 index 72cb9bc70..000000000 --- a/src/plugins/fake/fakecollection.h +++ /dev/null @@ -1,23 +0,0 @@ -#ifndef FAKECOLLECTION_H -#define FAKECOLLECTION_H -#include "tomahawk/collection.h" - -class FakeCollection : public Collection -{ -Q_OBJECT -public: - explicit FakeCollection(QObject *parent = 0); - ~FakeCollection() - { - qDebug() << Q_FUNC_INFO; - } - - virtual void load(); - -signals: - -public slots: - -}; - -#endif // FAKECOLLECTION_H diff --git a/src/plugins/fake/fakeplugin.cpp b/src/plugins/fake/fakeplugin.cpp deleted file mode 100644 index 90f50c74e..000000000 --- a/src/plugins/fake/fakeplugin.cpp +++ /dev/null @@ -1,26 +0,0 @@ -#include "fakeplugin.h" -#include "fakecollection.h" - -Q_EXPORT_PLUGIN2(fake, FakePlugin) - -FakePlugin::FakePlugin(Tomahawk::PluginAPI* api) - : TomahawkPlugin(api), m_api(api) -{ - init(); -} - -TomahawkPlugin * -FakePlugin::factory(Tomahawk::PluginAPI* api) -{ - return new FakePlugin(api); -} - -void FakePlugin::init() -{ - source_ptr src(new Source("Mr. Fake")); - collection_ptr coll(new FakeCollection); - src->addCollection(coll); - m_api->addSource(src); - coll->load(); -}; - diff --git a/src/plugins/fake/fakeplugin.h b/src/plugins/fake/fakeplugin.h deleted file mode 100644 index 4244aa64c..000000000 --- a/src/plugins/fake/fakeplugin.h +++ /dev/null @@ -1,31 +0,0 @@ -#ifndef TOMAHAWK_LIB_PLUGIN_H -#define TOMAHAWK_LIB_PLUGIN_H -#include - -#include "tomahawk/plugin_includes.h" - -class FakePlugin : public QObject, public TomahawkPlugin -{ - Q_OBJECT - Q_INTERFACES(TomahawkPlugin) - -public: - - FakePlugin(){}; - - FakePlugin(Tomahawk::PluginAPI* api); - TomahawkPlugin * factory(Tomahawk::PluginAPI* api); - QString name() const { return "FakePlugin"; }; - QString description() const { return "Fake stuff, hardcoded"; }; - -private: - - void init(); - - Tomahawk::PluginAPI* m_api; -}; - - - -#endif - diff --git a/src/resolverconfigdelegate.cpp b/src/resolverconfigdelegate.cpp new file mode 100644 index 000000000..0f98e49f8 --- /dev/null +++ b/src/resolverconfigdelegate.cpp @@ -0,0 +1,176 @@ +/* === 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 "resolverconfigdelegate.h" + +#include "resolversmodel.h" +#include "tomahawk/tomahawkapp.h" + +#include +#include +#include + +#define PADDING 4 + +ResolverConfigDelegate::ResolverConfigDelegate( QObject* parent ) + : QStyledItemDelegate( parent ) + , m_configPressed( false ) +{ + +} + +void +ResolverConfigDelegate::paint( QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index ) const +{ + QStyleOptionViewItemV4 opt = option; + initStyleOption( &opt, index ); + QRect itemRect = opt.rect; + int top = itemRect.top(); + + QFont name = opt.font; + name.setPointSize( name.pointSize() + 2 ); + name.setBold( true ); + + QFont path = opt.font; + path.setItalic( true ); + path.setPointSize( path.pointSize() - 1 ); + + + QFontMetrics bfm( name ); + QFontMetrics sfm( path ); + + // draw the background + const QWidget* w = opt.widget; + QStyle* style = w ? w->style() : QApplication::style(); + style->drawPrimitive( QStyle::PE_PanelItemViewItem, &opt, painter, w ); + + int rightSplit = itemRect.width(); + int rectW = opt.rect.height() - 4 * PADDING; + QRect confRect = QRect( rightSplit - rectW - 2 * PADDING, 2 * PADDING + top, rectW, rectW ); + // if the resolver has a config widget, paint it first (right-aligned) + if( index.data( ResolversModel::HasConfig ).toBool() ) { + // draw it the same size as the check belox + QStyleOptionToolButton topt; + topt.font = opt.font; + topt.icon = QIcon( RESPATH "images/configure.png" ); + topt.iconSize = QSize( 16, 16 ); + topt.rect = confRect; + topt.subControls = QStyle::SC_ToolButton; + topt.activeSubControls = QStyle::SC_None; + topt.features = QStyleOptionToolButton::None; + topt.pos = confRect.topLeft(); + topt.state = m_configPressed ? QStyle::State_On : QStyle::State_Raised; + if( opt.state & QStyle::State_MouseOver || m_configPressed ) + topt.state |= QStyle::State_HasFocus; + style->drawComplexControl( QStyle::CC_ToolButton, &topt, painter, w ); + } + + // draw check + confRect.moveTo( 2 * PADDING, 2 * PADDING + top ); + opt.rect = confRect; + opt.checkState == Qt::Checked ? opt.state |= QStyle::State_On : opt.state |= QStyle::State_Off; + style->drawPrimitive( QStyle::PE_IndicatorViewItemCheck, &opt, painter, w ); + itemRect.setX( opt.rect.topRight().x() + PADDING ); + + painter->save(); + painter->setFont( name ); + QRect textRect = itemRect.adjusted( PADDING, PADDING, -PADDING, -PADDING ); + textRect.setBottom( itemRect.height() / 2 + top ); + QString nameStr = bfm.elidedText( index.data( ResolversModel::ResolverName ).toString(),Qt::ElideRight, textRect.width() ); + painter->drawText( textRect, nameStr ); + painter->restore(); + + painter->save(); + painter->setFont( path ); + painter->setBrush( Qt::gray ); + textRect.moveTop( itemRect.height() / 2 + top ); + QString pathStr = sfm.elidedText( index.data( ResolversModel::ResolverPath ).toString(),Qt::ElideMiddle, textRect.width() ); + painter->drawText( textRect, pathStr ); + painter->restore(); + +} + +QSize +ResolverConfigDelegate::sizeHint( const QStyleOptionViewItem& option, const QModelIndex& index ) const +{ + int width = QStyledItemDelegate::sizeHint( option, index ).width(); + + QStyleOptionViewItemV4 opt = option; + initStyleOption( &opt, index ); + + + QFont name = opt.font; + name.setPointSize( name.pointSize() + 2 ); + name.setBold( true ); + + QFont path = opt.font; + path.setItalic( true ); + path.setPointSize( path.pointSize() - 1 ); + + + QFontMetrics bfm( name ); + QFontMetrics sfm( path ); + return QSize( width, 3 * PADDING + bfm.height() + sfm.height() ); +} + +bool +ResolverConfigDelegate::editorEvent(QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option, const QModelIndex& index) +{ +// qDebug() << "EDITOR EVENT!" << ( event->type() == QEvent::MouseButtonRelease ); + + QStyleOptionViewItemV4 viewOpt( option ); + initStyleOption( &viewOpt, index ); + const QWidget* w = viewOpt.widget; + QStyle* style = w ? w->style() : QApplication::style(); + int top = viewOpt.rect.top(); + + if( event->type() == QEvent::MouseButtonRelease || event->type() == QEvent::MouseButtonDblClick ) { + m_configPressed = false; + + int rectW = option.rect.height() - 4 * PADDING; + QRect checkRect = QRect( 2 * PADDING, 2 * PADDING + top, rectW, rectW ); + QMouseEvent* me = static_cast< QMouseEvent* >( event ); + if( me->button() != Qt::LeftButton || !checkRect.contains( me->pos() ) ) + return false; + + // eat the double click events inside the check rect + if( event->type() == QEvent::MouseButtonDblClick ) { + return true; + } + + Qt::CheckState curState = static_cast< Qt::CheckState >( index.data( Qt::CheckStateRole ).toInt() ); + Qt::CheckState newState = curState == Qt::Checked ? Qt::Unchecked : Qt::Checked; + return model->setData( index, newState, Qt::CheckStateRole ); + + } else if( event->type() == QEvent::MouseButtonPress ) { + int rightSplit = viewOpt.rect.width(); + int rectW = viewOpt.rect.height() - 4 * PADDING; + QRect confRect = QRect( rightSplit - rectW - 2 * PADDING, 2 * PADDING + top, rectW, rectW ); + + QMouseEvent* me = static_cast< QMouseEvent* >( event ); + if( me->button() == Qt::LeftButton && confRect.contains( me->pos() ) ) { + m_configPressed = true; + + emit openConfig( index.data( ResolversModel::ResolverPath ).toString() ); + return true; + } + } + + return QStyledItemDelegate::editorEvent( event, model, option, index ); +} diff --git a/src/resolverconfigdelegate.h b/src/resolverconfigdelegate.h new file mode 100644 index 000000000..280e63ccb --- /dev/null +++ b/src/resolverconfigdelegate.h @@ -0,0 +1,42 @@ +/* === 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 RESOLVERCONFIGDELEGATE_H +#define RESOLVERCONFIGDELEGATE_H + +#include + + +class ResolverConfigDelegate : public QStyledItemDelegate +{ + Q_OBJECT +public: + explicit ResolverConfigDelegate(QObject* parent = 0); + virtual void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const; + virtual QSize sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const; + virtual bool editorEvent( QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option, const QModelIndex& index ); + +signals: + void openConfig( const QString& resolverPath ); + +private: + bool m_configPressed; +}; + +#endif // RESOLVERCONFIGDELEGATE_H diff --git a/src/resolverconfigwrapper.h b/src/resolverconfigwrapper.h new file mode 100644 index 000000000..e43700e38 --- /dev/null +++ b/src/resolverconfigwrapper.h @@ -0,0 +1,68 @@ +/* === 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 RESOLVER_CONFIG_WRAPPER +#define RESOLVER_CONFIG_WRAPPER + +#include +#include +#include + +class ResolverConfigWrapper : public QDialog +{ + Q_OBJECT +public: + ResolverConfigWrapper( QWidget* conf, const QString& title, QWidget* parent ) : QDialog( parent ), m_widget( conf ) + { + setWindowTitle( title ); + + QVBoxLayout* v = new QVBoxLayout( this ); + v->addWidget( m_widget ); + + QDialogButtonBox* buttons = new QDialogButtonBox( QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal, this ); + connect( buttons, SIGNAL( clicked( QAbstractButton*) ), this, SLOT( closed( QAbstractButton* ) ) ); + connect( this, SIGNAL( rejected() ), this, SLOT( rejected() ) ); + v->addWidget( buttons ); + + setLayout( v ); + } +public slots: + void closed( QAbstractButton* b ) + { + // let the config widget live to see another day + layout()->removeWidget( m_widget ); + m_widget->setParent( 0 ); + + QDialogButtonBox* buttons = qobject_cast< QDialogButtonBox* >( sender() ); + if( buttons->standardButton( b ) == QDialogButtonBox::Ok ) + done( QDialog::Accepted ); + else + done( QDialog::Rejected ); + } + + // we get a rejected() signal emitted if the user presses escape (and no clicked() signal ) + void rejected() + { + layout()->removeWidget( m_widget ); + m_widget->setParent( 0 ); + } + +private: + QWidget* m_widget; +}; + +#endif diff --git a/src/resolvers/qtscriptresolver.cpp b/src/resolvers/qtscriptresolver.cpp index cfa1925d2..78375c522 100644 --- a/src/resolvers/qtscriptresolver.cpp +++ b/src/resolvers/qtscriptresolver.cpp @@ -1,3 +1,21 @@ +/* === 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 "qtscriptresolver.h" #include "artist.h" @@ -9,17 +27,20 @@ QtScriptResolver::QtScriptResolver( const QString& scriptPath ) : Tomahawk::ExternalResolver( scriptPath ) - , m_engine( new ScriptEngine( this ) ) - , m_thread( new QThread( this ) ) , m_ready( false ) , m_stopped( false ) { qDebug() << Q_FUNC_INFO << scriptPath; - m_thread->start(); - + m_engine = new ScriptEngine( this ); QFile scriptFile( scriptPath ); - scriptFile.open( QIODevice::ReadOnly ); + if ( !scriptFile.open( QIODevice::ReadOnly ) ) + { + qDebug() << Q_FUNC_INFO << "Failed loading JavaScript resolver:" << scriptPath; + deleteLater(); + return; + } + m_engine->mainFrame()->setHtml( "" ); m_engine->mainFrame()->evaluateJavaScript( scriptFile.readAll() ); scriptFile.close(); @@ -28,15 +49,9 @@ QtScriptResolver::QtScriptResolver( const QString& scriptPath ) m_name = m.value( "name" ).toString(); m_weight = m.value( "weight", 0 ).toUInt(); m_timeout = m.value( "timeout", 25 ).toUInt() * 1000; - m_preference = m.value( "preference", 0 ).toUInt(); - qDebug() << "QTSCRIPT" << filePath() << "READY," << endl - << "name" << m_name << endl - << "weight" << m_weight << endl - << "timeout" << m_timeout << endl - << "preference" << m_preference; + qDebug() << Q_FUNC_INFO << m_name << m_weight << m_timeout; - m_engine->moveToThread( m_thread ); m_ready = true; Tomahawk::Pipeline::instance()->addResolver( this ); } @@ -45,32 +60,33 @@ QtScriptResolver::QtScriptResolver( const QString& scriptPath ) QtScriptResolver::~QtScriptResolver() { Tomahawk::Pipeline::instance()->removeResolver( this ); + delete m_engine; } void QtScriptResolver::resolve( const Tomahawk::query_ptr& query ) { - QMetaObject::invokeMethod( m_engine, "resolve", Qt::QueuedConnection, Q_ARG( Tomahawk::query_ptr, query ) ); -} + if ( QThread::currentThread() != thread() ) + { + qDebug() << "Reinvoking in correct thread:" << Q_FUNC_INFO; + QMetaObject::invokeMethod( this, "resolve", Qt::QueuedConnection, Q_ARG(Tomahawk::query_ptr, query) ); + return; + } - -void -ScriptEngine::resolve( const Tomahawk::query_ptr& query ) -{ qDebug() << Q_FUNC_INFO << query->toString(); QString eval = QString( "resolve( '%1', '%2', '%3', '%4' );" ) - .arg( query->id().replace( "'", "\\'" ) ) - .arg( query->artist().replace( "'", "\\'" ) ) - .arg( query->album().replace( "'", "\\'" ) ) - .arg( query->track().replace( "'", "\\'" ) ); + .arg( query->id().replace( "'", "\\'" ) ) + .arg( query->artist().replace( "'", "\\'" ) ) + .arg( query->album().replace( "'", "\\'" ) ) + .arg( query->track().replace( "'", "\\'" ) ); QList< Tomahawk::result_ptr > results; - QVariantMap m = mainFrame()->evaluateJavaScript( eval ).toMap(); + QVariantMap m = m_engine->mainFrame()->evaluateJavaScript( eval ).toMap(); qDebug() << "JavaScript Result:" << m; - const QString qid = m.value( "qid" ).toString(); + const QString qid = query->id(); const QVariantList reslist = m.value( "results" ).toList(); foreach( const QVariant& rv, reslist ) @@ -83,13 +99,26 @@ ScriptEngine::resolve( const Tomahawk::query_ptr& query ) rp->setArtist( ap ); rp->setAlbum( Tomahawk::Album::get( 0, m.value( "album" ).toString(), ap ) ); rp->setTrack( m.value( "track" ).toString() ); - rp->setDuration( m.value( "duration" ).toUInt() ); rp->setBitrate( m.value( "bitrate" ).toUInt() ); rp->setUrl( m.value( "url" ).toString() ); rp->setSize( m.value( "size" ).toUInt() ); - rp->setScore( m.value( "score" ).toFloat() * ( (float)m_parent->weight() / 100.0 ) ); + rp->setScore( m.value( "score" ).toFloat() * ( (float)weight() / 100.0 ) ); rp->setRID( uuid() ); - rp->setFriendlySource( m_parent->name() ); + rp->setFriendlySource( name() ); + + if ( m.contains( "year" ) ) + { + QVariantMap attr; + attr[ "releaseyear" ] = m.value( "year" ); + rp->setAttributes( attr ); + } + + rp->setDuration( m.value( "duration", 0 ).toUInt() ); + if ( rp->duration() <= 0 && m.contains( "durationString" ) ) + { + QTime time = QTime::fromString( m.value( "durationString" ).toString(), "hh:mm:ss" ); + rp->setDuration( time.secsTo( QTime( 0, 0 ) ) * -1 ); + } rp->setMimetype( m.value( "mimetype" ).toString() ); if ( rp->mimetype().isEmpty() ) @@ -109,4 +138,5 @@ void QtScriptResolver::stop() { m_stopped = true; + emit finished(); } diff --git a/src/resolvers/qtscriptresolver.h b/src/resolvers/qtscriptresolver.h index 9df82a1f4..ed90f8261 100644 --- a/src/resolvers/qtscriptresolver.h +++ b/src/resolvers/qtscriptresolver.h @@ -1,3 +1,21 @@ +/* === 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 QTSCRIPTRESOLVER_H #define QTSCRIPTRESOLVER_H @@ -8,6 +26,7 @@ #include #include #include +#include #include #include @@ -19,16 +38,15 @@ Q_OBJECT public: explicit ScriptEngine( QtScriptResolver* parent ) - : QWebPage( (QObject*)parent ) + : QWebPage( (QObject*) parent ) , m_parent( parent ) - {} + { + } public slots: - void resolve( const Tomahawk::query_ptr& query ); - bool shouldInterruptJavaScript() { - return false; + return true; } protected: @@ -39,6 +57,7 @@ private: QtScriptResolver* m_parent; }; + class QtScriptResolver : public Tomahawk::ExternalResolver { Q_OBJECT @@ -49,21 +68,22 @@ public: virtual QString name() const { return m_name; } virtual unsigned int weight() const { return m_weight; } - virtual unsigned int preference() const { return m_preference; } virtual unsigned int timeout() const { return m_timeout; } + virtual QWidget* configUI() const { return 0; } // TODO support properly for qtscript resolvers too! + virtual void saveConfig() {} public slots: virtual void resolve( const Tomahawk::query_ptr& query ); virtual void stop(); -private slots: +signals: + void finished(); private: ScriptEngine* m_engine; - QThread* m_thread; QString m_name; - unsigned int m_weight, m_preference, m_timeout; + unsigned int m_weight, m_timeout; bool m_ready, m_stopped; }; diff --git a/src/resolvers/scriptresolver.cpp b/src/resolvers/scriptresolver.cpp index 375f301e0..a713f186c 100644 --- a/src/resolvers/scriptresolver.cpp +++ b/src/resolvers/scriptresolver.cpp @@ -1,3 +1,21 @@ +/* === 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 "scriptresolver.h" #include @@ -28,7 +46,12 @@ ScriptResolver::ScriptResolver( const QString& exe ) ScriptResolver::~ScriptResolver() { + stop(); + Tomahawk::Pipeline::instance()->removeResolver( this ); + + if( !m_configWidget.isNull() ) + delete m_configWidget.data(); } @@ -74,8 +97,6 @@ ScriptResolver::sendMsg( const QByteArray& msg ) { qDebug() << Q_FUNC_INFO << m_ready << msg << msg.length(); - if( !m_ready ) return; - quint32 len; qToBigEndian( msg.length(), (uchar*) &len ); m_proc.write( (const char*) &len, 4 ); @@ -101,6 +122,9 @@ ScriptResolver::handleMsg( const QByteArray& msg ) { doSetup( m ); return; + } else if( msgtype == "confwidget" ) { + setupConfWidget( m ); + return; } if( msgtype == "results" ) @@ -157,7 +181,7 @@ ScriptResolver::cmdExited( int code, QProcess::ExitStatus status ) qDebug() << Q_FUNC_INFO << "SCRIPT EXITED, code" << code << "status" << status << filePath(); Tomahawk::Pipeline::instance()->removeResolver( this ); - if( m_stopped ) + if( m_stopped ) { qDebug() << "*** Script resolver stopped "; emit finished(); @@ -192,7 +216,7 @@ ScriptResolver::resolve( const Tomahawk::query_ptr& query ) sendMsg( msg ); m_queryState.insert( query->id(), 1 ); - new Tomahawk::FuncTimeout( m_timeout, boost::bind( &ScriptResolver::onTimeout, this, query ) ); + new Tomahawk::FuncTimeout( m_timeout, boost::bind( &ScriptResolver::onTimeout, this, query ), this ); } @@ -203,22 +227,60 @@ ScriptResolver::doSetup( const QVariantMap& m ) m_name = m.value( "name" ).toString(); m_weight = m.value( "weight", 0 ).toUInt(); m_timeout = m.value( "timeout", 25 ).toUInt() * 1000; - m_preference = m.value( "preference", 0 ).toUInt(); qDebug() << "SCRIPT" << filePath() << "READY," << endl << "name" << m_name << endl << "weight" << m_weight << endl - << "timeout" << m_timeout << endl - << "preference" << m_preference; + << "timeout" << m_timeout; m_ready = true; Tomahawk::Pipeline::instance()->addResolver( this ); } +void +ScriptResolver::setupConfWidget( const QVariantMap& m ) +{ + bool compressed = m.value( "compressed", "false" ).toString() == "true"; + qDebug() << "Resolver has a preferences widget! compressed?" << compressed << m; -void + QByteArray uiData = m[ "widget" ].toByteArray(); + if( compressed ) + uiData = qUncompress( QByteArray::fromBase64( uiData ) ); + else + uiData = QByteArray::fromBase64( uiData ); + + if( m.contains( "images" ) ) + uiData = fixDataImagePaths( uiData, compressed, m[ "images" ].toMap() ); + m_configWidget = QWeakPointer< QWidget >( widgetFromData( uiData, 0 ) ); +} + + +void +ScriptResolver::saveConfig() +{ + Q_ASSERT( !m_configWidget.isNull() ); + + QVariantMap m; + m.insert( "_msgtype", "setpref" ); + QVariant widgets = configMsgFromWidget( m_configWidget.data() ); + m.insert( "widgets", widgets ); + QByteArray data = m_serializer.serialize( m ); + sendMsg( data ); +} + +QWidget* ScriptResolver::configUI() const +{ + if( m_configWidget.isNull() ) + return 0; + else + return m_configWidget.data(); +} + + +void ScriptResolver::stop() { m_stopped = true; + qDebug() << "KILLING PROCESS!"; m_proc.kill(); } diff --git a/src/resolvers/scriptresolver.h b/src/resolvers/scriptresolver.h index e21d52e64..34e29fdd2 100644 --- a/src/resolvers/scriptresolver.h +++ b/src/resolvers/scriptresolver.h @@ -1,3 +1,21 @@ +/* === 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 SCRIPTRESOLVER_H #define SCRIPTRESOLVER_H @@ -11,6 +29,7 @@ #include "query.h" #include "result.h" +class QWidget; class ScriptResolver : public Tomahawk::ExternalResolver { Q_OBJECT @@ -24,6 +43,9 @@ public: virtual unsigned int preference() const { return m_preference; } virtual unsigned int timeout() const { return m_timeout; } + virtual QWidget* configUI() const; + virtual void saveConfig(); + signals: void finished(); @@ -42,10 +64,12 @@ private: void handleMsg( const QByteArray& msg ); void sendMsg( const QByteArray& msg ); void doSetup( const QVariantMap& m ); + void setupConfWidget( const QVariantMap& m ); QProcess m_proc; QString m_name; unsigned int m_weight, m_preference, m_timeout, m_num_restarts; + QWeakPointer< QWidget > m_configWidget; quint32 m_msgsize; QByteArray m_msg; diff --git a/src/resolversmodel.cpp b/src/resolversmodel.cpp new file mode 100644 index 000000000..07e7c8aca --- /dev/null +++ b/src/resolversmodel.cpp @@ -0,0 +1,154 @@ +/* === 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 "resolversmodel.h" +#include +#include +#include + + +ResolversModel::ResolversModel( const QStringList& allResolvers, const QStringList& enabledResolvers, QObject* parent ) + : QAbstractListModel( parent ) + , m_allResolvers( allResolvers ) + , m_enabledResolvers( enabledResolvers ) +{ + // do some sanity checking just in case + bool changed = false; + foreach( const QString& l, m_enabledResolvers ) { + if( !m_allResolvers.contains( l ) ) { + m_enabledResolvers.removeAll( l ); + changed = true; + } + } + if( changed ) + TomahawkSettings::instance()->setEnabledScriptResolvers( m_enabledResolvers ); + +} + + +ResolversModel::~ResolversModel() +{ + +} + +QVariant +ResolversModel::data( const QModelIndex& index, int role ) const +{ + if( !index.isValid() ) + return QVariant(); + + switch( role ) + { + case Qt::DisplayRole: + case ResolversModel::ResolverName: + { + QFileInfo info( m_allResolvers.at( index.row() ) ); + return info.baseName(); + } + case ResolversModel::ResolverPath: + return m_allResolvers.at( index.row() ); + case ResolversModel::HasConfig: + if( Tomahawk::ExternalResolver* r = TomahawkApp::instance()->resolverForPath( m_allResolvers.at( index.row() ) ) ) // if we have one, it means we are loaded too! + return r->configUI() != 0; + return false; + case Qt::CheckStateRole: + return m_enabledResolvers.contains( m_allResolvers.at( index.row() ) ) ? Qt::Checked : Qt::Unchecked; + default: + return QVariant(); + } +} + +bool +ResolversModel::setData( const QModelIndex& index, const QVariant& value, int role ) +{ + if( role == Qt::CheckStateRole ) { + Qt::CheckState state = static_cast< Qt::CheckState >( value.toInt() ); + QString resolver = m_allResolvers.at( index.row() ); + + if( state == Qt::Checked && !m_enabledResolvers.contains( resolver ) ) { + m_enabledResolvers.append( resolver ); + + TomahawkApp::instance()->enableScriptResolver( resolver ); + } else if( state == Qt::Unchecked ) { + m_enabledResolvers.removeAll( resolver ); + + TomahawkApp::instance()->disableScriptResolver( resolver ); + } + dataChanged( index, index ); + + return true; + } + return false; +} + + +int +ResolversModel::rowCount( const QModelIndex& parent ) const +{ + return m_allResolvers.size(); +} + +int +ResolversModel::columnCount(const QModelIndex& parent) const +{ + return 1; +} + +Qt::ItemFlags +ResolversModel::flags( const QModelIndex& index ) const +{ + return QAbstractItemModel::flags(index) | Qt::ItemIsUserCheckable; +} + + +void +ResolversModel::addResolver( const QString& resolver, bool enable ) +{ + beginInsertRows( QModelIndex(), m_allResolvers.count(), m_allResolvers.count() ); + m_allResolvers << resolver; + if( enable ) + m_enabledResolvers << resolver; + endInsertRows(); +} + +void +ResolversModel::removeResolver( const QString& resolver ) +{ + for( int i = 0; i < m_allResolvers.count(); i++ ) { + if( m_allResolvers.at( i ) == resolver ) { + beginRemoveRows( QModelIndex(), i, i ); + m_allResolvers.takeAt( i ); + endRemoveRows(); + } + } + m_enabledResolvers.removeAll( resolver ); +} + +QStringList +ResolversModel::allResolvers() const +{ + return m_allResolvers; +} + +QStringList +ResolversModel::enabledResolvers() const +{ + return m_enabledResolvers; +} + diff --git a/src/resolversmodel.h b/src/resolversmodel.h new file mode 100644 index 000000000..c6cf8fa73 --- /dev/null +++ b/src/resolversmodel.h @@ -0,0 +1,55 @@ +/* === 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 RESOLVERSMODEL_H +#define RESOLVERSMODEL_H + +#include +#include + + +class ResolversModel : public QAbstractListModel +{ +public: + enum Roles { + ResolverName = Qt::UserRole + 15, + ResolverPath = Qt::UserRole + 16, + HasConfig = Qt::UserRole + 17 + }; + + explicit ResolversModel( const QStringList& allResolvers, const QStringList& enabledResolvers, QObject* parent = 0 ); + virtual ~ResolversModel(); + + virtual QVariant data( const QModelIndex& index, int role = Qt::DisplayRole ) const; + virtual int rowCount( const QModelIndex& parent = QModelIndex() ) const; + virtual int columnCount( const QModelIndex& parent ) const; + virtual Qt::ItemFlags flags(const QModelIndex& index) const; + virtual bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole); + + void addResolver( const QString& resolver, bool enable = false ); + void removeResolver( const QString& resolver ); + + QStringList allResolvers() const; + QStringList enabledResolvers() const; +private: + QStringList m_allResolvers; + QStringList m_enabledResolvers; +}; + +#endif // RESOLVERSMODEL_H diff --git a/src/scanmanager.cpp b/src/scanmanager.cpp index 911381fc2..577da910d 100644 --- a/src/scanmanager.cpp +++ b/src/scanmanager.cpp @@ -1,12 +1,35 @@ +/* === 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 "scanmanager.h" #include #include #include +#include +#include #include "musicscanner.h" #include "tomahawksettings.h" -#include "tomahawkutils.h" +#include "utils/tomahawkutils.h" + +#include "database/database.h" +#include "database/databasecommand_dirmtimes.h" ScanManager* ScanManager::s_instance = 0; @@ -22,33 +45,58 @@ ScanManager::ScanManager( QObject* parent ) : QObject( parent ) , m_scanner( 0 ) , m_musicScannerThreadController( 0 ) + , m_currScannerPaths() + , m_dirWatcher( 0 ) + , m_queuedScanTimer( 0 ) + , m_deferredScanTimer( 0 ) + , m_queuedChangedDirs() + , m_deferredDirs() { s_instance = this; + m_queuedScanTimer = new QTimer( this ); + m_queuedScanTimer->setSingleShot( true ); + m_deferredScanTimer = new QTimer( this ); + m_deferredScanTimer->setSingleShot( false ); + m_deferredScanTimer->setInterval( 1000 ); + m_dirWatcher = new QFileSystemWatcher( this ); + connect( TomahawkSettings::instance(), SIGNAL( changed() ), SLOT( onSettingsChanged() ) ); + connect( m_queuedScanTimer, SIGNAL( timeout() ), SLOT( queuedScanTimeout() ) ); + connect( m_deferredScanTimer, SIGNAL( timeout() ), SLOT( deferredScanTimeout() ) ); + connect( m_dirWatcher, SIGNAL( directoryChanged( const QString & ) ), SLOT( handleChangedDir( const QString & ) ) ); + + if ( TomahawkSettings::instance()->hasScannerPaths() ) + { + m_currScannerPaths = TomahawkSettings::instance()->scannerPaths(); + if ( TomahawkSettings::instance()->watchForChanges() ) + QTimer::singleShot( 1000, this, SLOT( runStartupScan() ) ); + } + + m_deferredScanTimer->start(); } ScanManager::~ScanManager() { qDebug() << Q_FUNC_INFO; - + if( m_musicScannerThreadController ) { m_musicScannerThreadController->quit(); - + while( !m_musicScannerThreadController->isFinished() ) { QCoreApplication::processEvents( QEventLoop::AllEvents, 200 ); TomahawkUtils::Sleep::msleep( 100 ); } - + if( m_scanner ) { delete m_scanner; m_scanner = 0; } - + delete m_musicScannerThreadController; m_musicScannerThreadController = 0; } @@ -58,26 +106,133 @@ ScanManager::~ScanManager() void ScanManager::onSettingsChanged() { - if ( TomahawkSettings::instance()->hasScannerPath() ) - runManualScan( TomahawkSettings::instance()->scannerPath() ); + if ( TomahawkSettings::instance()->hasScannerPaths() && + m_currScannerPaths != TomahawkSettings::instance()->scannerPaths() ) + { + m_currScannerPaths = TomahawkSettings::instance()->scannerPaths(); + m_dirWatcher->removePaths( m_dirWatcher->directories() ); + runManualScan( m_currScannerPaths ); + } + + if( TomahawkSettings::instance()->watchForChanges() && + !m_queuedChangedDirs.isEmpty() ) + runManualScan( m_queuedChangedDirs, false ); +} + + +void ScanManager::runStartupScan() +{ + qDebug() << Q_FUNC_INFO; + if( !Database::instance() || ( Database::instance() && !Database::instance()->isReady() ) ) + QTimer::singleShot( 1000, this, SLOT( runStartupScan() ) ); + else + runManualScan( m_currScannerPaths ); } void -ScanManager::runManualScan( const QString& path ) +ScanManager::runManualScan( const QStringList& paths, bool recursive ) { qDebug() << Q_FUNC_INFO; + + if( !Database::instance() || ( Database::instance() && !Database::instance()->isReady() ) ) + return; + if ( !m_musicScannerThreadController && !m_scanner ) //still running if these are not zero { m_musicScannerThreadController = new QThread( this ); - m_scanner = new MusicScanner( path ); + QStringList allPaths = paths; + foreach( QString path, m_deferredDirs[recursive] ) + { + if( !allPaths.contains( path ) ) + allPaths << path; + } + m_scanner = new MusicScanner( paths, recursive ); m_scanner->moveToThread( m_musicScannerThreadController ); connect( m_scanner, SIGNAL( finished() ), SLOT( scannerFinished() ) ); + connect( m_scanner, SIGNAL( addWatchedDirs( const QStringList & ) ), SLOT( addWatchedDirs( const QStringList & ) ) ); + connect( m_scanner, SIGNAL( removeWatchedDir( const QString & ) ), SLOT( removeWatchedDir( const QString & ) ) ); m_musicScannerThreadController->start( QThread::IdlePriority ); QMetaObject::invokeMethod( m_scanner, "startScan" ); + m_deferredDirs[recursive].clear(); } else - qDebug() << "Could not run manual scan, old scan still running"; + { + qDebug() << "Could not run manual scan, old scan still running; deferring paths"; + foreach( QString path, paths ) + { + if( !m_deferredDirs[recursive].contains( path ) ) + { + qDebug() << "Deferring path " << path; + m_deferredDirs[recursive] << path; + } + } + } +} + + +void +ScanManager::addWatchedDirs( const QStringList& paths ) +{ + qDebug() << Q_FUNC_INFO; + if ( !TomahawkSettings::instance()->watchForChanges() ) + return; + + QStringList currentWatchedPaths = m_dirWatcher->directories(); + foreach ( QString path, paths ) + { + if ( !currentWatchedPaths.contains( path ) ) + { + qDebug() << "adding " << path << " to watched dirs"; + m_dirWatcher->addPath( path ); + } + } +} + + +void +ScanManager::removeWatchedDir( const QString& path ) +{ + qDebug() << Q_FUNC_INFO; + qDebug() << "removing " << path << " from watched dirs"; + m_dirWatcher->removePath( path ); +} + + +void +ScanManager::handleChangedDir( const QString& path ) +{ + qDebug() << Q_FUNC_INFO; + qDebug() << "Dir changed: " << path; + if( !m_queuedChangedDirs.contains( path ) ) + m_queuedChangedDirs << path; + if( TomahawkSettings::instance()->watchForChanges() ) + m_queuedScanTimer->start( 10000 ); +} + + +void +ScanManager::queuedScanTimeout() +{ + qDebug() << Q_FUNC_INFO; + runManualScan( m_queuedChangedDirs, false ); + m_queuedChangedDirs.clear(); +} + + +void +ScanManager::deferredScanTimeout() +{ + if( !m_deferredDirs[true].isEmpty() ) + { + qDebug() << "Running scan for deferred recursive paths"; + runManualScan( m_deferredDirs[true], true ); + } + else if( !m_deferredDirs[false].isEmpty() ) + { + qDebug() << "Running scan for deferred non-recursive paths"; + runManualScan( m_deferredDirs[false], false ); + } } @@ -103,6 +258,7 @@ ScanManager::scannerQuit() void ScanManager::scannerDestroyed( QObject* scanner ) { + Q_UNUSED( scanner ); qDebug() << Q_FUNC_INFO; m_musicScannerThreadController->deleteLater(); m_musicScannerThreadController = 0; diff --git a/src/scanmanager.h b/src/scanmanager.h index 1c69a15e1..3cd57fd09 100644 --- a/src/scanmanager.h +++ b/src/scanmanager.h @@ -1,12 +1,35 @@ +/* === 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 SCANMANAGER_H #define SCANMANAGER_H +#include +#include #include +#include #include "dllmacro.h" class MusicScanner; class QThread; +class QFileSystemWatcher; +class QTimer; class ScanManager : public QObject { @@ -17,16 +40,24 @@ public: explicit ScanManager( QObject* parent = 0 ); virtual ~ScanManager(); - - void runManualScan( const QString& path ); signals: void finished(); +public slots: + void runManualScan( const QStringList& paths, bool recursive = true ); + void handleChangedDir( const QString& path ); + void addWatchedDirs( const QStringList& paths ); + void removeWatchedDir( const QString& path ); + private slots: void scannerQuit(); void scannerFinished(); void scannerDestroyed( QObject* scanner ); + + void runStartupScan(); + void queuedScanTimeout(); + void deferredScanTimeout(); void onSettingsChanged(); @@ -35,6 +66,13 @@ private: MusicScanner* m_scanner; QThread* m_musicScannerThreadController; + QStringList m_currScannerPaths; + QFileSystemWatcher* m_dirWatcher; + + QTimer* m_queuedScanTimer; + QTimer* m_deferredScanTimer; + QStringList m_queuedChangedDirs; + QHash< bool, QStringList > m_deferredDirs; }; #endif diff --git a/src/scrobbler.cpp b/src/scrobbler.cpp index e1cdcbb93..74ad40ae8 100644 --- a/src/scrobbler.cpp +++ b/src/scrobbler.cpp @@ -1,3 +1,21 @@ +/* === 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 "scrobbler.h" #include @@ -8,113 +26,83 @@ #include "typedefs.h" #include "audio/audioengine.h" #include "tomahawksettings.h" -#include "tomahawk/tomahawkapp.h" - -#include -#include - - -static QString -md5( const QByteArray& src ) -{ - QByteArray const digest = QCryptographicHash::hash( src, QCryptographicHash::Md5 ); - return QString::fromLatin1( digest.toHex() ).rightJustified( 32, '0' ); -} +#include "infosystem/infosystem.h" +static QString s_scInfoIdentifier = QString( "SCROBBLER" ); Scrobbler::Scrobbler( QObject* parent ) : QObject( parent ) - , m_scrobbler( 0 ) , m_reachedScrobblePoint( false ) - , m_authJob( 0 ) { -/* - Your API Key is 7194b85b6d1f424fe1668173a78c0c4a - Your secret is ba80f1df6d27ae63e9cb1d33ccf2052f -*/ - - // Flush session key cache - TomahawkSettings::instance()->setLastFmSessionKey( QByteArray() ); - - lastfm::ws::ApiKey = "7194b85b6d1f424fe1668173a78c0c4a"; - lastfm::ws::SharedSecret = "ba80f1df6d27ae63e9cb1d33ccf2052f"; - lastfm::ws::Username = TomahawkSettings::instance()->lastFmUsername(); - - m_pw = TomahawkSettings::instance()->lastFmPassword(); - - if( TomahawkSettings::instance()->scrobblingEnabled() && !lastfm::ws::Username.isEmpty() ) - { - createScrobbler(); - } - - //HACK work around a bug in liblastfm---it doesn't create its config dir, so when it - // tries to write the track cache, it fails silently. until we have a fixed version, do this - // code taken from Amarok (src/services/lastfm/ScrobblerAdapter.cpp) - QString lpath = QDir::home().filePath( ".local/share/Last.fm" ); - QDir ldir = QDir( lpath ); - if( !ldir.exists() ) - { - ldir.mkpath( lpath ); - } - - connect( TomahawkSettings::instance(), SIGNAL( changed() ), - SLOT( settingsChanged() ), Qt::QueuedConnection ); - connect( AudioEngine::instance(), SIGNAL( timerSeconds( unsigned int ) ), SLOT( engineTick( unsigned int ) ), Qt::QueuedConnection ); + + connect( Tomahawk::InfoSystem::InfoSystem::instance(), + SIGNAL( info( QString, Tomahawk::InfoSystem::InfoType, QVariant, QVariant, Tomahawk::InfoSystem::InfoCustomData ) ), + SLOT( infoSystemInfo( QString, Tomahawk::InfoSystem::InfoType, QVariant, QVariant, Tomahawk::InfoSystem::InfoCustomData ) ) ); + + connect( AudioEngine::instance(), SIGNAL( started( const Tomahawk::result_ptr& ) ), + SLOT( trackStarted( const Tomahawk::result_ptr& ) ), Qt::QueuedConnection ); + + connect( AudioEngine::instance(), SIGNAL( paused() ), + SLOT( trackPaused() ), Qt::QueuedConnection ); + + connect( AudioEngine::instance(), SIGNAL( resumed() ), + SLOT( trackResumed() ), Qt::QueuedConnection ); + + connect( AudioEngine::instance(), SIGNAL( stopped() ), + SLOT( trackStopped() ), Qt::QueuedConnection ); + + connect( Tomahawk::InfoSystem::InfoSystem::instance(), SIGNAL( finished( QString ) ), SLOT( infoSystemFinished( QString ) ) ); } Scrobbler::~Scrobbler() { - delete m_scrobbler; } -void +void Scrobbler::trackStarted( const Tomahawk::result_ptr& track ) { Q_ASSERT( QThread::currentThread() == thread() ); // qDebug() << Q_FUNC_INFO; - if( !m_scrobbler ) - return; - if( m_reachedScrobblePoint ) { m_reachedScrobblePoint = false; scrobble(); } - m_track = lastfm::MutableTrack(); - m_track.stamp(); + Tomahawk::InfoSystem::InfoCustomData trackInfo; - m_track.setTitle( track->track() ); - m_track.setArtist( track->artist()->name() ); - m_track.setAlbum( track->album()->name() ); - m_track.setDuration( track->duration() ); - m_track.setSource( lastfm::Track::Player ); + trackInfo["title"] = QVariant::fromValue< QString >( track->track() ); + trackInfo["artist"] = QVariant::fromValue< QString >( track->artist()->name() ); + trackInfo["album"] = QVariant::fromValue< QString >( track->album()->name() ); + trackInfo["duration"] = QVariant::fromValue< uint >( track->duration() ); + Tomahawk::InfoSystem::InfoSystem::instance()->getInfo( + s_scInfoIdentifier, Tomahawk::InfoSystem::InfoMiscSubmitNowPlaying, + QVariant::fromValue< Tomahawk::InfoSystem::InfoCustomData >( trackInfo ), Tomahawk::InfoSystem::InfoCustomData() ); - m_scrobbler->nowPlaying( m_track ); - m_scrobblePoint = ScrobblePoint( m_track.duration() / 2 ); + m_scrobblePoint = ScrobblePoint( track->duration() / 2 ); } -void +void Scrobbler::trackPaused() { Q_ASSERT( QThread::currentThread() == thread() ); } -void +void Scrobbler::trackResumed() { Q_ASSERT( QThread::currentThread() == thread() ); } -void +void Scrobbler::trackStopped() { Q_ASSERT( QThread::currentThread() == thread() ); @@ -140,101 +128,35 @@ Scrobbler::scrobble() { Q_ASSERT( QThread::currentThread() == thread() ); - if ( !m_scrobbler || m_track.isNull() ) - return; - - qDebug() << Q_FUNC_INFO << m_track.toString(); - m_scrobbler->cache( m_track ); - m_scrobbler->submit(); + Tomahawk::InfoSystem::InfoSystem::instance()->getInfo( + s_scInfoIdentifier, Tomahawk::InfoSystem::InfoMiscSubmitScrobble, + QVariant(), Tomahawk::InfoSystem::InfoCustomData() ); } void -Scrobbler::settingsChanged() +Scrobbler::infoSystemInfo( QString caller, Tomahawk::InfoSystem::InfoType type, QVariant input, QVariant output, Tomahawk::InfoSystem::InfoCustomData customData ) { - if( !m_scrobbler && TomahawkSettings::instance()->scrobblingEnabled() ) - { // can simply create the scrobbler - lastfm::ws::Username = TomahawkSettings::instance()->lastFmUsername(); - m_pw = TomahawkSettings::instance()->lastFmPassword(); - - createScrobbler(); - } - else if( m_scrobbler && !TomahawkSettings::instance()->scrobblingEnabled() ) + Q_UNUSED( input ); + Q_UNUSED( output ); + Q_UNUSED( customData ); + if ( caller == s_scInfoIdentifier ) { - delete m_scrobbler; - m_scrobbler = 0; - } - else if( TomahawkSettings::instance()->lastFmUsername() != lastfm::ws::Username || - TomahawkSettings::instance()->lastFmPassword() != m_pw ) - { - lastfm::ws::Username = TomahawkSettings::instance()->lastFmUsername(); - // credentials have changed, have to re-create scrobbler for them to take effect - if( m_scrobbler ) - delete m_scrobbler; - - createScrobbler(); + qDebug() << Q_FUNC_INFO; + if ( type == Tomahawk::InfoSystem::InfoMiscSubmitNowPlaying ) + qDebug() << "Scrobbler received now playing response from InfoSystem"; + else if ( type == Tomahawk::InfoSystem::InfoMiscSubmitScrobble ) + qDebug() << "Scrobbler received scrobble response from InfoSystem"; } } void -Scrobbler::onAuthenticated() +Scrobbler::infoSystemFinished( QString target ) { - if( !m_authJob ) + if ( target == s_scInfoIdentifier ) { - qDebug() << Q_FUNC_INFO << "Help! No longer got a last.fm auth job!"; - return; - } - - if( m_authJob->error() == QNetworkReply::NoError ) - { - lastfm::XmlQuery lfm = lastfm::XmlQuery( m_authJob->readAll() ); - - if( lfm.children( "error" ).size() > 0 ) - { - qDebug() << "Error from authenticating with Last.fm service:" << lfm.text(); - TomahawkSettings::instance()->setLastFmSessionKey( QByteArray() ); - - } - else - { - lastfm::ws::SessionKey = lfm[ "session" ][ "key" ].text(); - - TomahawkSettings::instance()->setLastFmSessionKey( lastfm::ws::SessionKey.toLatin1() ); - - if( TomahawkSettings::instance()->scrobblingEnabled() ) - m_scrobbler = new lastfm::Audioscrobbler( "thk" ); - } - } - else - { - qDebug() << "Got error in Last.fm authentication job:" << m_authJob->errorString(); - } - - m_authJob->deleteLater(); -} - - -void -Scrobbler::createScrobbler() -{ - if( TomahawkSettings::instance()->lastFmSessionKey().isEmpty() ) // no session key, so get one - { - QString authToken = md5( ( lastfm::ws::Username.toLower() + md5( m_pw.toUtf8() ) ).toUtf8() ); - - QMap query; - query[ "method" ] = "auth.getMobileSession"; - query[ "username" ] = lastfm::ws::Username; - query[ "authToken" ] = authToken; - m_authJob = lastfm::ws::post( query ); - - connect( m_authJob, SIGNAL( finished() ), SLOT( onAuthenticated() ) ); - } - else - { - lastfm::ws::SessionKey = TomahawkSettings::instance()->lastFmSessionKey(); - - m_scrobbler = new lastfm::Audioscrobbler( "thk" ); - m_scrobbler->moveToThread( thread() ); + qDebug() << Q_FUNC_INFO; + qDebug() << "Scrobbler received done signal from InfoSystem"; } } diff --git a/src/scrobbler.h b/src/scrobbler.h index 3daca7537..f2c484aa9 100644 --- a/src/scrobbler.h +++ b/src/scrobbler.h @@ -1,16 +1,32 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2010-2011, Christian Muehlhaeuser + * + * Tomahawk is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Tomahawk is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Tomahawk. If not, see . + */ #ifndef TOMAHAWK_SCROBBLER_H #define TOMAHAWK_SCROBBLER_H #include "result.h" -#include -#include -#include +#include "lastfm/ScrobblePoint" + +#include "infosystem/infosystem.h" #include -class QNetworkReply; /** * Simple class that listens to signals from AudioEngine and scrobbles * what it is playing. @@ -21,28 +37,22 @@ class Scrobbler : public QObject public: Scrobbler( QObject* parent = 0 ); virtual ~Scrobbler(); - + public slots: void trackStarted( const Tomahawk::result_ptr& ); void trackPaused(); void trackResumed(); void trackStopped(); void engineTick( unsigned int secondsElapsed ); - - void settingsChanged(); - void onAuthenticated(); - + + void infoSystemInfo( QString caller, Tomahawk::InfoSystem::InfoType type, QVariant input, QVariant output, Tomahawk::InfoSystem::InfoCustomData customData ); + void infoSystemFinished( QString target ); + private: void scrobble(); - void createScrobbler(); - - lastfm::MutableTrack m_track; - lastfm::Audioscrobbler* m_scrobbler; - QString m_pw; + bool m_reachedScrobblePoint; ScrobblePoint m_scrobblePoint; - - QNetworkReply* m_authJob; }; diff --git a/src/settingsdialog.cpp b/src/settingsdialog.cpp index 4b290e24a..ca481749c 100644 --- a/src/settingsdialog.cpp +++ b/src/settingsdialog.cpp @@ -1,3 +1,21 @@ +/* === 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 "config.h" #include @@ -6,6 +24,7 @@ #include #include #include +#include #ifdef LIBLASTFM_FOUND #include @@ -21,6 +40,9 @@ #include "sip/SipHandler.h" #include #include "scanmanager.h" +#include "resolverconfigdelegate.h" +#include "resolversmodel.h" +#include "resolverconfigwrapper.h" static QString md5( const QByteArray& src ) @@ -29,7 +51,6 @@ md5( const QByteArray& src ) return QString::fromLatin1( digest.toHex() ).rightJustified( 32, '0' ); } - SettingsDialog::SettingsDialog( QWidget *parent ) : QDialog( parent ) , ui( new Ui::SettingsDialog ) @@ -68,25 +89,29 @@ SettingsDialog::SettingsDialog( QWidget *parent ) } // MUSIC SCANNER - ui->lineEditMusicPath->setText( s->scannerPath() ); + //FIXME: MULTIPLECOLLECTIONDIRS + if ( s->scannerPaths().count() ) + ui->lineEditMusicPath->setText( s->scannerPaths().first() ); + ui->checkBoxWatchForChanges->setChecked( s->watchForChanges() ); // LAST FM ui->checkBoxEnableLastfm->setChecked( s->scrobblingEnabled() ); ui->lineEditLastfmUsername->setText( s->lastFmUsername() ); ui->lineEditLastfmPassword->setText(s->lastFmPassword() ); connect( ui->pushButtonTestLastfmLogin, SIGNAL( clicked( bool) ), this, SLOT( testLastFmLogin() ) ); - + // SCRIPT RESOLVER ui->removeScript->setEnabled( false ); - foreach( const QString& resolver, s->scriptResolvers() ) { - QFileInfo info( resolver ); - ui->scriptList->addTopLevelItem( new QTreeWidgetItem( QStringList() << info.baseName() << resolver ) ); - - } - connect( ui->scriptList, SIGNAL( itemClicked( QTreeWidgetItem*, int ) ), this, SLOT( scriptSelectionChanged() ) ); + ResolverConfigDelegate* del = new ResolverConfigDelegate( this ); + connect( del, SIGNAL( openConfig( QString ) ), this, SLOT( openResolverConfig( QString ) ) ); + ui->scriptList->setItemDelegate( del ); + m_resolversModel = new ResolversModel( s->allScriptResolvers(), s->enabledScriptResolvers(), this ); + ui->scriptList->setModel( m_resolversModel ); + + connect( ui->scriptList->selectionModel(), SIGNAL( selectionChanged( QItemSelection,QItemSelection ) ), this, SLOT( scriptSelectionChanged() ) ); connect( ui->addScript, SIGNAL( clicked( bool ) ), this, SLOT( addScriptResolver() ) ); connect( ui->removeScript, SIGNAL( clicked( bool ) ), this, SLOT( removeScriptResolver() ) ); - + connect( ui->buttonBrowse, SIGNAL( clicked() ), SLOT( showPathSelector() ) ); connect( ui->proxyButton, SIGNAL( clicked() ), SLOT( showProxySettings() ) ); connect( ui->checkBoxStaticPreferred, SIGNAL( toggled(bool) ), SLOT( toggleUpnp(bool) ) ); @@ -111,22 +136,19 @@ SettingsDialog::~SettingsDialog() s->setJabberPassword( ui->jabberPassword->text() ); s->setJabberServer( ui->jabberServer->text() ); s->setJabberPort( ui->jabberPort->value() ); - + s->setExternalHostname( ui->staticHostName->text() ); s->setExternalPort( ui->staticPort->value() ); - s->setScannerPath( ui->lineEditMusicPath->text() ); - + s->setScannerPaths( QStringList( ui->lineEditMusicPath->text() ) ); + s->setWatchForChanges( ui->checkBoxWatchForChanges->isChecked() ); + s->setScrobblingEnabled( ui->checkBoxEnableLastfm->isChecked() ); s->setLastFmUsername( ui->lineEditLastfmUsername->text() ); s->setLastFmPassword( ui->lineEditLastfmPassword->text() ); - QStringList resolvers; - for( int i = 0; i < ui->scriptList->topLevelItemCount(); i++ ) - { - resolvers << ui->scriptList->topLevelItem( i )->data( 1, Qt::DisplayRole ).toString(); - } - s->setScriptResolvers( resolvers ); + s->setAllScriptResolvers( m_resolversModel->allResolvers() ); + s->setEnabledScriptResolvers( m_resolversModel->enabledResolvers() ); s->applyChanges(); } @@ -238,13 +260,13 @@ SettingsDialog::onLastFmFinished() ui->pushButtonTestLastfmLogin->setEnabled( false ); } break; - + case QNetworkReply::ContentOperationNotPermittedError: case QNetworkReply::AuthenticationRequiredError: ui->pushButtonTestLastfmLogin->setText( tr( "Failed" ) ); ui->pushButtonTestLastfmLogin->setEnabled( true ); break; - + default: qDebug() << "Couldn't get last.fm auth result"; ui->pushButtonTestLastfmLogin->setText( tr( "Could not contact server" ) ); @@ -297,10 +319,10 @@ ProxyDialog::saveSettings() s->setProxyPassword( ui->passwordLineEdit->text() ); s->setProxyType( ui->typeBox->itemData( ui->typeBox->currentIndex() ).toInt() ); - // Now, if a proxy is defined, set QNAM - if( s->proxyType() == QNetworkProxy::NoProxy || s->proxyHost().isEmpty() ) + if( s->proxyHost().isEmpty() ) return; + // Now, set QNAM QNetworkProxy proxy( static_cast(s->proxyType()), s->proxyHost(), s->proxyPort(), s->proxyUsername(), s->proxyPassword() ); QNetworkAccessManager* nam = TomahawkUtils::nam(); nam->setProxy( proxy ); @@ -314,38 +336,50 @@ ProxyDialog::saveSettings() } -void +void SettingsDialog::addScriptResolver() { QString resolver = QFileDialog::getOpenFileName( this, tr( "Load script resolver file" ), qApp->applicationDirPath() ); if( !resolver.isEmpty() ) { - QFileInfo info( resolver ); - ui->scriptList->addTopLevelItem( new QTreeWidgetItem( QStringList() << info.baseName() << resolver ) ); - - TomahawkApp::instance()->addScriptResolver( resolver ); + m_resolversModel->addResolver( resolver, true ); + TomahawkApp::instance()->enableScriptResolver( resolver ); } } -void +void SettingsDialog::removeScriptResolver() { // only one selection - if( !ui->scriptList->selectedItems().isEmpty() ) { - QString resolver = ui->scriptList->selectedItems().first()->data( 1, Qt::DisplayRole ).toString(); - delete ui->scriptList->takeTopLevelItem( ui->scriptList->indexOfTopLevelItem( ui->scriptList->selectedItems().first() ) ); - - TomahawkApp::instance()->removeScriptResolver( resolver ); + if( !ui->scriptList->selectionModel()->selectedIndexes().isEmpty() ) { + QString resolver = ui->scriptList->selectionModel()->selectedIndexes().first().data( ResolversModel::ResolverPath ).toString(); + m_resolversModel->removeResolver( resolver ); + + TomahawkApp::instance()->disableScriptResolver( resolver ); } } - -void +void SettingsDialog::scriptSelectionChanged() { - if( !ui->scriptList->selectedItems().isEmpty() ) { + if( !ui->scriptList->selectionModel()->selectedIndexes().isEmpty() ) { ui->removeScript->setEnabled( true ); } else { ui->removeScript->setEnabled( false ); } } + +void +SettingsDialog::openResolverConfig( const QString& resolver ) +{ + Tomahawk::ExternalResolver* r = TomahawkApp::instance()->resolverForPath( resolver ); + if( r && r->configUI() ) { + ResolverConfigWrapper dialog( r->configUI(), "Resolver Config", this ); + QWeakPointer< ResolverConfigWrapper > watcher( &dialog ); + int ret = dialog.exec(); + if( !watcher.isNull() && ret == QDialog::Accepted ) { + // send changed config to resolver + r->saveConfig(); + } + } +} diff --git a/src/settingsdialog.h b/src/settingsdialog.h index 8f04c0ee4..880c319da 100644 --- a/src/settingsdialog.h +++ b/src/settingsdialog.h @@ -1,8 +1,27 @@ +/* === 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 SETTINGSDIALOG_H #define SETTINGSDIALOG_H #include +class ResolversModel; class QNetworkReply; namespace Ui @@ -35,7 +54,7 @@ public: signals: void settingsChanged(); - + protected: void changeEvent( QEvent* e ); @@ -53,13 +72,15 @@ private slots: void addScriptResolver(); void scriptSelectionChanged(); void removeScriptResolver(); - + void openResolverConfig( const QString& ); + private: Ui::SettingsDialog* ui; ProxyDialog m_proxySettings; bool m_rejected; QNetworkReply* m_testLastFmQuery; + ResolversModel* m_resolversModel; }; #endif // SETTINGSDIALOG_H diff --git a/src/settingsdialog.ui b/src/settingsdialog.ui index 282665429..292e7cf59 100644 --- a/src/settingsdialog.ui +++ b/src/settingsdialog.ui @@ -23,7 +23,7 @@ - 1 + 4 @@ -65,6 +65,9 @@ 0 + + e.g. user@example.com + @@ -451,6 +454,19 @@
+ + + + + 0 + 0 + + + + Watch for changes + + + @@ -574,7 +590,7 @@ - + true @@ -584,40 +600,18 @@ false + + true + false true - - 2 + + true - - false - - - true - - - 150 - - - true - - - true - - - - 1 - - - - - 2 - - diff --git a/src/shortcuthandler.cpp b/src/shortcuthandler.cpp index 7709789ca..766b971be 100644 --- a/src/shortcuthandler.cpp +++ b/src/shortcuthandler.cpp @@ -1,3 +1,21 @@ +/* === 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 "shortcuthandler.h" using namespace Tomahawk; diff --git a/src/shortcuthandler.h b/src/shortcuthandler.h index 6462c0ebf..a74e7660d 100644 --- a/src/shortcuthandler.h +++ b/src/shortcuthandler.h @@ -1,3 +1,21 @@ +/* === 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 SHORTCUTHANDLER_H #define SHORTCUTHANDLER_H diff --git a/src/sip/SipHandler.cpp b/src/sip/SipHandler.cpp index b4f5297a8..59aa8ea12 100644 --- a/src/sip/SipHandler.cpp +++ b/src/sip/SipHandler.cpp @@ -1,3 +1,21 @@ +/* === 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 "SipHandler.h" #include "sip/SipPlugin.h" @@ -10,6 +28,9 @@ #include "network/controlconnection.h" #include "sourcelist.h" #include "tomahawksettings.h" +#include "tomahawk/tomahawkapp.h" + +#include "config.h" SipHandler::SipHandler( QObject* parent ) @@ -38,8 +59,7 @@ SipHandler::plugins() const void SipHandler::onSettingsChanged() { - disconnectPlugins(); - connectPlugins(); + checkSettings(); } @@ -60,21 +80,19 @@ SipHandler::findPlugins() } #endif - QDir libDir( appDir ); - libDir.cdUp(); - libDir.cd( "lib" ); + 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 ) { qDebug() << "Checking directory for plugins:" << pluginDir; - foreach ( QString fileName, pluginDir.entryList( QDir::Files ) ) + foreach ( QString fileName, pluginDir.entryList( QStringList() << "*tomahawk_sip*.so" << "*tomahawk_sip*.dylib" << "*tomahawk_sip*.dll", QDir::Files ) ) { - if ( fileName.startsWith( "libsip_" ) ) + if ( fileName.startsWith( "libtomahawk_sip" ) ) { const QString path = pluginDir.absoluteFilePath( fileName ); if ( !paths.contains( path ) ) @@ -96,29 +114,31 @@ SipHandler::loadPlugins( const QStringList& paths ) continue; qDebug() << "Trying to load plugin:" << fileName; - - QPluginLoader loader( fileName ); - QObject* plugin = loader.instance(); - if ( plugin ) - { - // Connect via that plugin - qDebug() << "Loaded plugin:" << loader.fileName(); - loadPlugin( plugin ); - } - else - { - qDebug() << "Error loading library:" << loader.errorString(); - } + loadPlugin( fileName ); } } void -SipHandler::loadPlugin( QObject* plugin ) +SipHandler::loadPlugin( const QString& path ) { + QPluginLoader loader( path ); + QObject* plugin = loader.instance(); + if ( !plugin ) + { + qDebug() << "Error loading plugin:" << loader.errorString(); + } + SipPlugin* sip = qobject_cast(plugin); if ( sip ) { + if ( pluginLoaded( sip->name() ) ) + { + qDebug() << "Plugin" << sip->name() << "already loaded! Not loading:" << loader.fileName(); + return; + } + qDebug() << "Loaded plugin:" << loader.fileName(); + QObject::connect( sip, SIGNAL( peerOnline( QString ) ), SLOT( onPeerOnline( QString ) ) ); QObject::connect( sip, SIGNAL( peerOffline( QString ) ), SLOT( onPeerOffline( QString ) ) ); QObject::connect( sip, SIGNAL( msgReceived( QString, QString ) ), SLOT( onMessage( QString, QString ) ) ); @@ -132,9 +152,46 @@ SipHandler::loadPlugin( QObject* plugin ) } +bool +SipHandler::pluginLoaded( const QString& name ) const +{ + foreach( SipPlugin* plugin, m_plugins ) + { + if ( plugin->name() == name ) + return true; + } + + return false; +} + + +void +SipHandler::checkSettings() +{ + foreach( SipPlugin* sip, m_plugins ) + { + sip->checkSettings(); + } +} + + void SipHandler::connectPlugins( bool startup, const QString &pluginName ) { +#ifndef TOMAHAWK_HEADLESS + if ( !TomahawkSettings::instance()->acceptedLegalWarning() ) + { + int result = QMessageBox::question( + TomahawkApp::instance()->mainWindow(), tr( "Legal Warning" ), + tr( "By pressing OK below, you agree that your use of Tomahawk will be in accordance with any applicable laws, including copyright and intellectual property laws, in effect in your country of residence, and indemnify the Tomahawk developers and project from liability should you choose to break those laws.\n\nFor more information, please see http://gettomahawk.com/legal" ), + tr( "I Do Not Agree" ), tr( "I Agree" ) + ); + if ( result != 1 ) + return; + else + TomahawkSettings::instance()->setAcceptedLegalWarning( true ); + } +#endif foreach( SipPlugin* sip, m_plugins ) { if ( pluginName.isEmpty() || ( !pluginName.isEmpty() && sip->name() == pluginName ) ) diff --git a/src/sip/SipHandler.h b/src/sip/SipHandler.h index 47464e748..0d3dcfa63 100644 --- a/src/sip/SipHandler.h +++ b/src/sip/SipHandler.h @@ -1,3 +1,21 @@ +/* === 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 SIPHANDLER_H #define SIPHANDLER_H @@ -21,6 +39,7 @@ public: public slots: void addContact( const QString& id ) { qDebug() << Q_FUNC_INFO << id; } + void checkSettings(); void connectPlugins( bool startup = false, const QString &pluginName = QString() ); void disconnectPlugins( const QString &pluginName = QString() ); void toggleConnect(); @@ -40,8 +59,10 @@ private slots: private: QStringList findPlugins(); + bool pluginLoaded( const QString& name ) const; + void loadPlugins( const QStringList& paths ); - void loadPlugin( QObject* plugin ); + void loadPlugin( const QString& path ); QList< SipPlugin* > m_plugins; bool m_connected; diff --git a/src/sip/jabber/CMakeLists.txt b/src/sip/jabber/CMakeLists.txt index d064a3aeb..56ff17970 100644 --- a/src/sip/jabber/CMakeLists.txt +++ b/src/sip/jabber/CMakeLists.txt @@ -21,7 +21,7 @@ include_directories( . ${CMAKE_CURRENT_BINARY_DIR} .. ) qt4_wrap_cpp( jabberMoc ${jabberHeaders} ) -add_library( sip_jabber SHARED ${jabberSources} ${jabberMoc} ) +add_library( tomahawk_sipjabber SHARED ${jabberSources} ${jabberMoc} ) IF( WIN32 ) SET( OS_SPECIFIC_LINK_LIBRARIES @@ -32,7 +32,7 @@ SET( OS_SPECIFIC_LINK_LIBRARIES ) ENDIF( WIN32 ) -target_link_libraries( sip_jabber +target_link_libraries( tomahawk_sipjabber ${QT_LIBRARIES} ${GLOOX_LIBRARIES} ${OS_SPECIFIC_LINK_LIBRARIES} @@ -43,4 +43,4 @@ IF( APPLE ) # SET( CMAKE_SHARED_LINKER_FLAGS ${CMAKE_SHARED_LINKER_FLAGS} "-undefined dynamic_lookup" ) ENDIF( APPLE ) -install( TARGETS sip_jabber DESTINATION lib ) +install( TARGETS tomahawk_sipjabber DESTINATION lib${LIB_SUFFIX} ) diff --git a/src/sip/jabber/jabber.cpp b/src/sip/jabber/jabber.cpp index 89c5cfcbe..dab22ac5b 100644 --- a/src/sip/jabber/jabber.cpp +++ b/src/sip/jabber/jabber.cpp @@ -1,3 +1,21 @@ +/* === 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 "jabber.h" #include "tomahawksettings.h" @@ -7,6 +25,7 @@ #include #include + JabberPlugin::JabberPlugin() : p( 0 ), m_menu ( 0 ), @@ -14,6 +33,7 @@ JabberPlugin::JabberPlugin() { } + void JabberPlugin::setProxy( QNetworkProxy* proxy ) { @@ -27,24 +47,28 @@ JabberPlugin::name() return QString( MYNAME ); } + const QString JabberPlugin::friendlyName() { return QString( "Jabber" ); } + const QString JabberPlugin::accountName() { return TomahawkSettings::instance()->jabberUsername(); } + QMenu* JabberPlugin::menu() { return m_menu; } + bool JabberPlugin::connectPlugin( bool startup ) { @@ -52,12 +76,13 @@ JabberPlugin::connectPlugin( bool startup ) if ( startup && !TomahawkSettings::instance()->jabberAutoConnect() ) return false; - QString jid = TomahawkSettings::instance()->jabberUsername(); - QString server = TomahawkSettings::instance()->jabberServer(); - QString password = TomahawkSettings::instance()->jabberPassword(); - unsigned int port = TomahawkSettings::instance()->jabberPort(); + m_currentUsername = TomahawkSettings::instance()->jabberUsername(); + m_currentServer = TomahawkSettings::instance()->jabberServer(); + m_currentPassword = TomahawkSettings::instance()->jabberPassword(); + m_currentPort = TomahawkSettings::instance()->jabberPort(); + QString server = m_currentServer; - QStringList splitJid = jid.split( '@', QString::SkipEmptyParts ); + QStringList splitJid = m_currentUsername.split( '@', QString::SkipEmptyParts ); if ( splitJid.size() < 2 ) { qDebug() << "JID did not have an @ in it, could not find a server part"; @@ -67,77 +92,82 @@ JabberPlugin::connectPlugin( bool startup ) if ( server.isEmpty() ) server = splitJid[1]; - if ( port < 1 || port > 65535 || jid.isEmpty() || password.isEmpty() ) + if ( m_currentPort < 1 || m_currentPort > 65535 || m_currentUsername.isEmpty() || m_currentPassword.isEmpty() ) { qDebug() << "Jabber credentials look wrong, not connecting"; return false; } delete p; - p = new Jabber_p( jid, password, server, port ); + p = new Jabber_p( m_currentUsername, m_currentPassword, server, m_currentPort ); QObject::connect( p, SIGNAL( peerOnline( QString ) ), SIGNAL( peerOnline( QString ) ) ); QObject::connect( p, SIGNAL( peerOffline( QString ) ), SIGNAL( peerOffline( QString ) ) ); QObject::connect( p, SIGNAL( msgReceived( QString, QString ) ), SIGNAL( msgReceived( QString, QString ) ) ); QObject::connect( p, SIGNAL( connected() ), SLOT( onConnected() ) ); - QObject::connect( p, SIGNAL( disconnected() ), SIGNAL( onDisconnected() ) ); + QObject::connect( p, SIGNAL( disconnected() ), SLOT( onDisconnected() ) ); QObject::connect( p, SIGNAL( authError( int, QString ) ), SLOT( onAuthError( int, QString ) ) ); p->resolveHostSRV(); - + return true; } -void + +void JabberPlugin::onConnected() { - if( !m_menu ) { - m_menu = new QMenu( QString( "Jabber (").append( accountName() ).append( ")" ) ); - - m_addFriendAction = m_menu->addAction( "Add Friend..." ); - - connect( m_addFriendAction, SIGNAL( triggered() ), - this, SLOT( showAddFriendDialog() ) ) ; - + if ( !m_menu ) + { + m_menu = new QMenu( tr( "Jabber (%1)" ).arg( accountName() ) ); + m_addFriendAction = m_menu->addAction( tr( "Add Friend..." ) ); + + connect( m_addFriendAction, SIGNAL( triggered() ), SLOT( showAddFriendDialog() ) ) ; + emit addMenu( m_menu ); } - + emit connected(); } -void + +void JabberPlugin::onDisconnected() { - if( m_menu && m_addFriendAction ) { + if ( m_menu && m_addFriendAction ) + { emit removeMenu( m_menu ); - + delete m_menu; m_menu = 0; m_addFriendAction = 0; } - + emit disconnected(); } - void JabberPlugin::onAuthError( int code, const QString& message ) { if ( code == gloox::ConnAuthenticationFailed ) + { emit error( SipPlugin::AuthError, message ); + } else + { emit error( SipPlugin::ConnectionError, message ); + } } + void JabberPlugin::showAddFriendDialog() { bool ok; QString id = QInputDialog::getText( 0, tr( "Add Friend" ), - tr( "Enter Jabber ID:" ), QLineEdit::Normal, - "", &ok ); + tr( "Enter Jabber ID:" ), QLineEdit::Normal, "", &ok ); if ( !ok ) return; @@ -145,4 +175,30 @@ JabberPlugin::showAddFriendDialog() addContact( id ); } + +void +JabberPlugin::checkSettings() +{ + bool reconnect = false; + if ( m_currentUsername != TomahawkSettings::instance()->jabberUsername() ) + reconnect = true; + if ( m_currentPassword != TomahawkSettings::instance()->jabberPassword() ) + reconnect = true; + if ( m_currentServer != TomahawkSettings::instance()->jabberServer() ) + reconnect = true; + if ( m_currentPort != TomahawkSettings::instance()->jabberPort() ) + reconnect = true; + + m_currentUsername = TomahawkSettings::instance()->jabberUsername(); + m_currentPassword = TomahawkSettings::instance()->jabberPassword(); + m_currentServer = TomahawkSettings::instance()->jabberServer(); + m_currentPort = TomahawkSettings::instance()->jabberPort(); + + if ( reconnect && ( p || TomahawkSettings::instance()->jabberAutoConnect() ) ) + { + disconnectPlugin(); + connectPlugin( false ); + } +} + Q_EXPORT_PLUGIN2( sip, JabberPlugin ) diff --git a/src/sip/jabber/jabber.h b/src/sip/jabber/jabber.h index 025cb03c1..b3d868896 100644 --- a/src/sip/jabber/jabber.h +++ b/src/sip/jabber/jabber.h @@ -1,3 +1,21 @@ +/* === 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 JABBER_H #define JABBER_H @@ -41,6 +59,8 @@ public slots: p = 0; } + void checkSettings(); + void sendMsg( const QString& to, const QString& msg ) { if ( p ) @@ -69,6 +89,11 @@ private: Jabber_p* p; QMenu* m_menu; QAction* m_addFriendAction; + + QString m_currentUsername; + QString m_currentPassword; + QString m_currentServer; + unsigned int m_currentPort; }; #endif diff --git a/src/sip/jabber/jabber_p.cpp b/src/sip/jabber/jabber_p.cpp index 69801b946..6f38dd292 100644 --- a/src/sip/jabber/jabber_p.cpp +++ b/src/sip/jabber/jabber_p.cpp @@ -1,3 +1,21 @@ +/* === 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 "jabber_p.h" #include @@ -6,11 +24,12 @@ #include #include #include +#include #include #include - -using namespace gloox; -using namespace std; +#include +#include +#include #define TOMAHAWK_CAP_NODE_NAME QString::fromAscii("http://tomahawk-player.org/") @@ -21,33 +40,37 @@ Jabber_p::Jabber_p( const QString& jid, const QString& password, const QString& qDebug() << Q_FUNC_INFO; qsrand( QTime( 0, 0, 0 ).secsTo( QTime::currentTime() ) ); - m_presences[Presence::Available] = "available"; - m_presences[Presence::Chat] = "chat"; - m_presences[Presence::Away] = "away"; - m_presences[Presence::DND] = "dnd"; - m_presences[Presence::XA] = "xa"; - m_presences[Presence::Unavailable] = "unavailable"; - m_presences[Presence::Probe] = "probe"; - m_presences[Presence::Error] = "error"; - m_presences[Presence::Invalid] = "invalid"; + m_presences[gloox::Presence::Available] = "available"; + m_presences[gloox::Presence::Chat] = "chat"; + m_presences[gloox::Presence::Away] = "away"; + m_presences[gloox::Presence::DND] = "dnd"; + m_presences[gloox::Presence::XA] = "xa"; + m_presences[gloox::Presence::Unavailable] = "unavailable"; + m_presences[gloox::Presence::Probe] = "probe"; + m_presences[gloox::Presence::Error] = "error"; + m_presences[gloox::Presence::Invalid] = "invalid"; - m_jid = JID( jid.toStdString() ); + m_jid = gloox::JID( jid.toStdString() ); if( m_jid.resource().find( "tomahawk" ) == std::string::npos ) { - qDebug() << "!!! Setting your resource to 'tomahawk' prior to logging in to jabber"; +// qDebug() << "!!! Setting your resource to 'tomahawk' prior to logging in to jabber"; m_jid.setResource( QString( "tomahawk%1" ).arg( qrand() ).toStdString() ); } qDebug() << "Our JID set to:" << m_jid.full().c_str(); m_client = QSharedPointer( new gloox::Client( m_jid, password.toStdString(), port ) ); - const Capabilities *caps = m_client->presence().capabilities(); + const gloox::Capabilities *caps = m_client->presence().capabilities(); if( caps ) { - const_cast(caps)->setNode(TOMAHAWK_CAP_NODE_NAME.toStdString()); + const_cast(caps)->setNode(TOMAHAWK_CAP_NODE_NAME.toStdString()); } m_server = server; + + qDebug() << "proxy type is " << TomahawkUtils::proxy()->type(); + + setProxy( TomahawkUtils::proxy() ); } @@ -65,11 +88,26 @@ Jabber_p::~Jabber_p() void Jabber_p::resolveHostSRV() { + qDebug() << Q_FUNC_INFO; if( m_server.isEmpty() ) { qDebug() << "No server found!"; return; } + if( TomahawkUtils::proxy()->type() == QNetworkProxy::Socks5Proxy || + ( TomahawkUtils::proxy()->type() == QNetworkProxy::DefaultProxy && + QNetworkProxy::applicationProxy().type() == QNetworkProxy::Socks5Proxy ) ) + { + if( TomahawkSettings::instance()->jabberServer().isEmpty() ) + { + qDebug() << "Right now, you must explicitly set your jabber server if you are using a proxy, due to a bug in the DNS lookup library"; + m_server = QString(); + } + QMetaObject::invokeMethod( this, "go", Qt::QueuedConnection ); + return; + } + + TomahawkUtils::DNSResolver *resolver = TomahawkUtils::dnsResolver(); connect( resolver, SIGNAL(result(QString &)), SLOT(resolveResult(QString &)) ); qDebug() << "Resolving SRV record of " << m_server; @@ -81,7 +119,7 @@ Jabber_p::setProxy( QNetworkProxy* proxy ) { qDebug() << Q_FUNC_INFO; - if( !m_client.isNull() || !proxy ) + if( m_client.isNull() || !proxy ) { qDebug() << "No client or no proxy"; return; @@ -98,6 +136,8 @@ Jabber_p::setProxy( QNetworkProxy* proxy ) else if( proxy->type() == QNetworkProxy::Socks5Proxy ) { qDebug() << "Setting proxy to SOCKS5"; + qDebug() << "proxy host = " << proxy->hostName(); + qDebug() << "proxy port = " << proxy->port(); m_client->setConnectionImpl( new gloox::ConnectionSOCKS5Proxy( m_client.data(), new gloox::ConnectionTCPClient( m_client->logInstance(), proxy->hostName().toStdString(), proxy->port() ), m_client->logInstance(), m_client->server(), m_client->port() ) ); @@ -120,6 +160,7 @@ Jabber_p::resolveResult( QString& result ) void Jabber_p::go() { + qDebug() << Q_FUNC_INFO; if( !m_server.isEmpty() ) m_client->setServer( m_server.toStdString() ); else @@ -130,7 +171,8 @@ Jabber_p::go() m_client->registerPresenceHandler( this ); m_client->registerConnectionListener( this ); - m_client->logInstance().registerLogHandler( LogLevelWarning, LogAreaAll, this ); + m_client->rosterManager()->registerRosterListener( this, false ); // false means async + m_client->logInstance().registerLogHandler( gloox::LogLevelWarning, gloox::LogAreaAll, this ); m_client->registerMessageHandler( this ); /* @@ -140,7 +182,7 @@ Jabber_p::go() m_client->disco()->addFeature( "tomahawk:player" ); */ - m_client->setPresence( Presence::Available, 1, "Tomahawk available" ); + m_client->setPresence( gloox::Presence::XA, -127, "Tomahawk available" ); // m_client->connect(); // return; @@ -150,7 +192,7 @@ Jabber_p::go() qDebug() << "Connecting to the XMPP server..."; if( m_client->connect( false ) ) { - int sock = static_cast( m_client->connectionImpl() )->socket(); + int sock = static_cast( m_client->connectionImpl() )->socket(); m_notifier.reset( new QSocketNotifier( sock, QSocketNotifier::Read ) ); connect( m_notifier.data(), SIGNAL( activated(int) ), SLOT( doJabberRecv() )); } @@ -165,8 +207,8 @@ Jabber_p::doJabberRecv() if ( m_client.isNull() ) return; - ConnectionError ce = m_client->recv( 100 ); - if ( ce != ConnNoError ) + gloox::ConnectionError ce = m_client->recv( 100 ); + if ( ce != gloox::ConnNoError ) { qDebug() << "Jabber_p::Recv failed, disconnected"; } @@ -188,8 +230,8 @@ Jabber_p::sendMsg( const QString& to, const QString& msg ) { if ( QThread::currentThread() != thread() ) { - qDebug() << Q_FUNC_INFO << "invoking in correct thread, not" - << QThread::currentThread(); +// qDebug() << Q_FUNC_INFO << "invoking in correct thread, not" +// << QThread::currentThread(); QMetaObject::invokeMethod( this, "sendMsg", Qt::QueuedConnection, @@ -203,7 +245,7 @@ Jabber_p::sendMsg( const QString& to, const QString& msg ) return; qDebug() << Q_FUNC_INFO << to << msg; - Message m( Message::Chat, JID(to.toStdString()), msg.toStdString(), "" ); + gloox::Message m( gloox::Message::Chat, gloox::JID(to.toStdString()), msg.toStdString(), "" ); m_client->send( m ); // assuming this is threadsafe } @@ -227,7 +269,7 @@ Jabber_p::broadcastMsg( const QString &msg ) std::string msg_s = msg.toStdString(); foreach( const QString& jidstr, m_peers.keys() ) { - Message m(Message::Chat, JID(jidstr.toStdString()), msg_s, ""); + gloox::Message m(gloox::Message::Chat, gloox::JID(jidstr.toStdString()), msg_s, ""); m_client->send( m ); } } @@ -246,7 +288,7 @@ Jabber_p::addContact( const QString& jid, const QString& msg ) return; } - handleSubscription(JID(jid.toStdString()), msg.toStdString()); + handleSubscription(gloox::JID(jid.toStdString()), msg.toStdString()); return; } @@ -255,7 +297,7 @@ Jabber_p::addContact( const QString& jid, const QString& msg ) void Jabber_p::onConnect() { - qDebug() << "Connected to the XMPP server"; + qDebug() << "Connected to the XMPP server" << m_jid.full().c_str(); // update jid resource, servers like gtalk use resource binding and may // have changed our requested /resource if ( m_client->resource() != m_jid.resource() ) @@ -265,13 +307,12 @@ Jabber_p::onConnect() emit jidChanged( jidstr ); } - qDebug() << "Connected as:" << m_jid.full().c_str(); emit connected(); } void -Jabber_p::onDisconnect( ConnectionError e ) +Jabber_p::onDisconnect( gloox::ConnectionError e ) { qDebug() << "Jabber Disconnected"; QString error; @@ -279,16 +320,16 @@ Jabber_p::onDisconnect( ConnectionError e ) switch( e ) { - case AuthErrorUndefined: + case gloox::AuthErrorUndefined: error = " No error occurred, or error condition is unknown"; break; - case SaslAborted: + case gloox::SaslAborted: error = "The receiving entity acknowledges an <abort/> element sent " "by the initiating entity; sent in reply to the <abort/> element."; break; - case SaslIncorrectEncoding: + case gloox::SaslIncorrectEncoding: error = "The data provided by the initiating entity could not be processed " "because the [BASE64] encoding is incorrect (e.g., because the encoding " "does not adhere to the definition in Section 3 of [BASE64]); sent in " @@ -296,7 +337,7 @@ Jabber_p::onDisconnect( ConnectionError e ) "initial response data."; break; - case SaslInvalidAuthzid: + case gloox::SaslInvalidAuthzid: error = "The authzid provided by the initiating entity is invalid, either " "because it is incorrectly formatted or because the initiating entity " "does not have permissions to authorize that ID; sent in reply to a " @@ -304,56 +345,56 @@ Jabber_p::onDisconnect( ConnectionError e ) "response data."; break; - case SaslInvalidMechanism: + case gloox::SaslInvalidMechanism: error = "The initiating entity did not provide a mechanism or requested a " "mechanism that is not supported by the receiving entity; sent in reply " "to an <auth/> element."; break; - case SaslMalformedRequest: + case gloox::SaslMalformedRequest: error = "The request is malformed (e.g., the <auth/> element includes " "an initial response but the mechanism does not allow that); sent in " "reply to an <abort/>, <auth/>, <challenge/>, or " "<response/> element."; break; - case SaslMechanismTooWeak: + case gloox::SaslMechanismTooWeak: error = "The mechanism requested by the initiating entity is weaker than " "server policy permits for that initiating entity; sent in reply to a " "<response/> element or an <auth/> element with initial " "response data."; break; - case SaslNotAuthorized: + case gloox::SaslNotAuthorized: error = "The authentication failed because the initiating entity did not " "provide valid credentials (this includes but is not limited to the " "case of an unknown username); sent in reply to a <response/> " "element or an <auth/> element with initial response data. "; break; - case SaslTemporaryAuthFailure: + case gloox::SaslTemporaryAuthFailure: error = "The authentication failed because of a temporary error condition " "within the receiving entity; sent in reply to an <auth/> element " "or <response/> element."; break; - case NonSaslConflict: + case gloox::NonSaslConflict: error = "XEP-0078: Resource Conflict"; break; - case NonSaslNotAcceptable: + case gloox::NonSaslNotAcceptable: error = "XEP-0078: Required Information Not Provided"; break; - case NonSaslNotAuthorized: + case gloox::NonSaslNotAuthorized: error = "XEP-0078: Incorrect Credentials"; break; - case ConnAuthenticationFailed: + case gloox::ConnAuthenticationFailed: error = "Authentication failed"; break; - case ConnNoSupportedAuth: + case gloox::ConnNoSupportedAuth: error = "No supported auth mechanism"; break; @@ -374,7 +415,7 @@ Jabber_p::onDisconnect( ConnectionError e ) bool -Jabber_p::onTLSConnect( const CertInfo& info ) +Jabber_p::onTLSConnect( const gloox::CertInfo& info ) { qDebug() << Q_FUNC_INFO << "Status" << info.status @@ -384,8 +425,8 @@ Jabber_p::onTLSConnect( const CertInfo& info ) << "mac" << info.mac.c_str() << "cipher" << info.cipher.c_str() << "compression" << info.compression.c_str() - << "from" << ctime( (const time_t*)&info.date_from ) - << "to" << ctime( (const time_t*)&info.date_to ) + << "from" << std::ctime( (const time_t*)&info.date_from ) + << "to" << std::ctime( (const time_t*)&info.date_to ) ; //onConnect(); @@ -394,7 +435,7 @@ Jabber_p::onTLSConnect( const CertInfo& info ) void -Jabber_p::handleMessage( const Message& m, MessageSession * /*session*/ ) +Jabber_p::handleMessage( const gloox::Message& m, gloox::MessageSession * /*session*/ ) { QString from = QString::fromStdString( m.from().full() ); QString msg = QString::fromStdString( m.body() ); @@ -410,12 +451,21 @@ Jabber_p::handleMessage( const Message& m, MessageSession * /*session*/ ) //sendMsg( from, QString("You said %1").arg(msg) ); + QJson::Parser parser; + bool ok; + QVariant v = parser.parse( msg.toAscii(), &ok ); + if ( !ok || v.type() != QVariant::Map ) + { + sendMsg( from, QString( "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!" ) ); + return; + } + emit msgReceived( from, msg ); } void -Jabber_p::handleLog( LogLevel level, LogArea area, const std::string& message ) +Jabber_p::handleLog( gloox::LogLevel level, gloox::LogArea area, const std::string& message ) { qDebug() << Q_FUNC_INFO << "level:" << level @@ -427,49 +477,51 @@ Jabber_p::handleLog( LogLevel level, LogArea area, const std::string& message ) /// ROSTER STUFF // {{{ void -Jabber_p::onResourceBindError( ResourceBindError error ) +Jabber_p::onResourceBindError( gloox::ResourceBindError error ) { + Q_UNUSED( error ); qDebug() << Q_FUNC_INFO; } void -Jabber_p::onSessionCreateError( SessionCreateError error ) +Jabber_p::onSessionCreateError( gloox::SessionCreateError error ) { + Q_UNUSED( error ); qDebug() << Q_FUNC_INFO; } void -Jabber_p::handleItemSubscribed( const JID& jid ) +Jabber_p::handleItemSubscribed( const gloox::JID& jid ) { qDebug() << Q_FUNC_INFO << jid.full().c_str(); } void -Jabber_p::handleItemAdded( const JID& jid ) +Jabber_p::handleItemAdded( const gloox::JID& jid ) { qDebug() << Q_FUNC_INFO << jid.full().c_str(); } void -Jabber_p::handleItemUnsubscribed( const JID& jid ) +Jabber_p::handleItemUnsubscribed( const gloox::JID& jid ) { qDebug() << Q_FUNC_INFO << jid.full().c_str(); } void -Jabber_p::handleItemRemoved( const JID& jid ) +Jabber_p::handleItemRemoved( const gloox::JID& jid ) { qDebug() << Q_FUNC_INFO << jid.full().c_str(); } void -Jabber_p::handleItemUpdated( const JID& jid ) +Jabber_p::handleItemUpdated( const gloox::JID& jid ) { qDebug() << Q_FUNC_INFO << jid.full().c_str(); } @@ -477,15 +529,15 @@ Jabber_p::handleItemUpdated( const JID& jid ) void -Jabber_p::handleRoster( const Roster& roster ) +Jabber_p::handleRoster( const gloox::Roster& roster ) { // qDebug() << Q_FUNC_INFO; - Roster::const_iterator it = roster.begin(); +gloox::Roster::const_iterator it = roster.begin(); for ( ; it != roster.end(); ++it ) { - if ( (*it).second->subscription() != S10nBoth ) continue; - qDebug() << (*it).second->jid().c_str() << (*it).second->name().c_str(); + if ( (*it).second->subscription() != gloox::S10nBoth ) continue; +// qDebug() << (*it).second->jid().c_str() << (*it).second->name().c_str(); //printf("JID: %s\n", (*it).second->jid().c_str()); } @@ -496,7 +548,7 @@ Jabber_p::handleRoster( const Roster& roster ) void -Jabber_p::handleRosterError( const IQ& /*iq*/ ) +Jabber_p::handleRosterError( const gloox::IQ& /*iq*/ ) { qDebug() << Q_FUNC_INFO; } @@ -505,11 +557,9 @@ Jabber_p::handleRosterError( const IQ& /*iq*/ ) void Jabber_p::handlePresence( const gloox::Presence& presence ) { - JID jid = presence.from(); + gloox::JID jid = presence.from(); QString fulljid( jid.full().c_str() ); - qDebug() << "* handleRosterPresence" << fulljid << presence.subtype(); - if( jid == m_jid ) return; @@ -531,6 +581,7 @@ Jabber_p::handlePresence( const gloox::Presence& presence ) return; } + qDebug() << "* handleRosterPresence" << fulljid << presence.subtype(); //qDebug() << "handling presence for resource of" << res; //qDebug() << Q_FUNC_INFO << "jid:" << QString::fromStdString(item.jid()) @@ -565,11 +616,11 @@ Jabber_p::handlePresence( const gloox::Presence& presence ) bool -Jabber_p::handleSubscription( const JID& jid, const std::string& /*msg*/ ) +Jabber_p::handleSubscription( const gloox::JID& jid, const std::string& /*msg*/ ) { qDebug() << Q_FUNC_INFO << jid.bare().c_str(); - StringList groups; + gloox::StringList groups; groups.push_back( "Tomahawk" ); m_client->rosterManager()->subscribe( jid, "", groups, "" ); return true; @@ -577,18 +628,94 @@ Jabber_p::handleSubscription( const JID& jid, const std::string& /*msg*/ ) bool -Jabber_p::handleSubscriptionRequest( const JID& jid, const std::string& /*msg*/ ) +Jabber_p::handleSubscriptionRequest( const gloox::JID& jid, const std::string& /*msg*/ ) { qDebug() << Q_FUNC_INFO << jid.bare().c_str(); - StringList groups; - groups.push_back( "Tomahawk" ); - m_client->rosterManager()->subscribe( jid, "", groups, "" ); - return true; + + // check if the requester is already on the roster + gloox::RosterItem *item = m_client->rosterManager()->getRosterItem(jid); + if(item) qDebug() << Q_FUNC_INFO << jid.bare().c_str() << "subscription status:" << static_cast( item->subscription() ); + if(item && + ( + item->subscription() == gloox::S10nNoneOut || // Contact and user are not subscribed to each other, and user has sent contact a subscription request but contact has not replied yet. + item->subscription() == gloox::S10nTo || // User is subscribed to contact (one-way). + item->subscription() == gloox::S10nToIn // User is subscribed to contact, and contact has sent user a subscription request but user has not replied yet. + ) + ) + { + qDebug() << Q_FUNC_INFO << jid.bare().c_str() << "already on the roster so we assume ack'ing subscription request is okay..."; + + // ack the request + m_client->rosterManager()->ackSubscriptionRequest( jid, true ); + + // return anything, the result is ignored + return false; + } + + // we don't have to check for an already open check box because gloox doesnt call this method until a former request was ack'ed + qDebug() << Q_FUNC_INFO << jid.bare().c_str() << "open subscription request box"; + + // 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( QLatin1String( jid.bare().c_str() ) ), + QMessageBox::Yes | QMessageBox::No, + 0 + ); + + // add confirmBox to m_subscriptionConfirmBoxes + m_subscriptionConfirmBoxes.insert( jid, confirmBox ); + + // display the box and wait for the answer + confirmBox->open( this, SLOT( onSubscriptionRequestConfirmed( int ) ) ); + + return false; +} + + +void +Jabber_p::onSubscriptionRequestConfirmed( int result ) +{ + qDebug() << Q_FUNC_INFO << result; + + QList< QMessageBox* > confirmBoxes = m_subscriptionConfirmBoxes.values(); + gloox::JID jid; + + foreach( QMessageBox* currentBox, confirmBoxes ) + { + if( currentBox == sender() ) + { + jid = m_subscriptionConfirmBoxes.key( currentBox ); + } + } + + qDebug() << Q_FUNC_INFO << "box confirmed for" << jid.bare().c_str(); + + // we got an answer, deleting the box + m_subscriptionConfirmBoxes.remove( jid ); + sender()->deleteLater(); + + QMessageBox::StandardButton allowSubscription = static_cast( result ); + + if ( allowSubscription == QMessageBox::Yes ) + { + qDebug() << Q_FUNC_INFO << jid.bare().c_str() << "accepted by user, adding to roster"; + gloox::StringList groups; + groups.push_back( "Tomahawk" ); + m_client->rosterManager()->subscribe( jid, "", groups, "" ); + } + else + { + qDebug() << Q_FUNC_INFO << jid.bare().c_str() << "declined by user"; + } + + m_client->rosterManager()->ackSubscriptionRequest( jid, allowSubscription == QMessageBox::Yes ); } bool -Jabber_p::handleUnsubscriptionRequest( const JID& jid, const std::string& /*msg*/ ) +Jabber_p::handleUnsubscriptionRequest( const gloox::JID& jid, const std::string& /*msg*/ ) { qDebug() << Q_FUNC_INFO << jid.bare().c_str(); return true; @@ -596,7 +723,7 @@ Jabber_p::handleUnsubscriptionRequest( const JID& jid, const std::string& /*msg* void -Jabber_p::handleNonrosterPresence( const Presence& presence ) +Jabber_p::handleNonrosterPresence( const gloox::Presence& presence ) { qDebug() << Q_FUNC_INFO << presence.from().full().c_str(); } @@ -604,29 +731,33 @@ Jabber_p::handleNonrosterPresence( const Presence& presence ) void -Jabber_p::handleVCard( const JID& jid, const VCard* vcard ) +Jabber_p::handleVCard( const gloox::JID& jid, const gloox::VCard* vcard ) { + Q_UNUSED( vcard ); qDebug() << "VCARD RECEIVED!" << jid.bare().c_str(); } void -Jabber_p::handleVCardResult( VCardContext context, const JID& jid, StanzaError se ) +Jabber_p::handleVCardResult( gloox::VCardHandler::VCardContext context, const gloox::JID& jid, gloox::StanzaError se ) { + Q_UNUSED( context ); + Q_UNUSED( se ); qDebug() << "VCARD RESULT RECEIVED!" << jid.bare().c_str(); } /// DISCO STUFF void -Jabber_p::handleDiscoInfo( const JID& from, const Disco::Info& info, int context) +Jabber_p::handleDiscoInfo( const gloox::JID& from, const gloox::Disco::Info& info, int context ) { + Q_UNUSED( context ); QString jidstr( from.full().c_str() ); //qDebug() << "DISCOinfo" << jidstr; if ( info.hasFeature("tomahawk:player") ) { qDebug() << "Peer online and DISCOed ok:" << jidstr; - m_peers.insert( jidstr, Presence::XA ); + m_peers.insert( jidstr, gloox::Presence::XA ); emit peerOnline( jidstr ); } else @@ -637,28 +768,32 @@ Jabber_p::handleDiscoInfo( const JID& from, const Disco::Info& info, int context void -Jabber_p::handleDiscoItems( const JID& /*iq*/, const Disco::Items&, int /*context*/ ) +Jabber_p::handleDiscoItems( const gloox::JID& iq, const gloox::Disco::Items& items, int context ) { + Q_UNUSED( iq ); + Q_UNUSED( items ); + Q_UNUSED( context ); qDebug() << Q_FUNC_INFO; } void -Jabber_p::handleDiscoError( const JID& j, const Error* e, int /*context*/ ) +Jabber_p::handleDiscoError( const gloox::JID& j, const gloox::Error* e, int context ) { + Q_UNUSED( context ); qDebug() << Q_FUNC_INFO << j.full().c_str() << e->text().c_str() << e->type(); } /// END DISCO STUFF bool -Jabber_p::presenceMeansOnline( Presence::PresenceType p ) +Jabber_p::presenceMeansOnline( gloox::Presence::PresenceType p ) { switch(p) { - case Presence::Invalid: - case Presence::Unavailable: - case Presence::Error: + case gloox::Presence::Invalid: + case gloox::Presence::Unavailable: + case gloox::Presence::Error: return false; break; default: diff --git a/src/sip/jabber/jabber_p.h b/src/sip/jabber/jabber_p.h index 3b0b74e5b..3a120fbac 100644 --- a/src/sip/jabber/jabber_p.h +++ b/src/sip/jabber/jabber_p.h @@ -1,3 +1,21 @@ +/* === 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 . + */ + /* This is the Jabber client that the rest of the app sees Gloox stuff should NOT leak outside this class. @@ -12,6 +30,7 @@ #include #include #include +#include #include @@ -58,6 +77,7 @@ class SIPDLLEXPORT Jabber_p : public gloox::MessageHandler, public gloox::VCardHandler, public gloox::PresenceHandler, + public gloox::RosterListener, gloox::LogHandler //public gloox::DiscoHandler, { @@ -91,6 +111,11 @@ public: virtual void handleRoster( const gloox::Roster& roster ); virtual void handleRosterError( const gloox::IQ& /*iq*/ ); + + // not actually used, just needed for the interface to support subscription requests, see handlePresence for presence handling + virtual void handleRosterPresence( const gloox::RosterItem&, const std::string&, gloox::Presence::PresenceType, const std::string& ) {} + virtual void handleSelfPresence( const gloox::RosterItem&, const std::string&, gloox::Presence::PresenceType, const std::string& ) {} + virtual void handlePresence( const gloox::Presence& presence ); virtual bool handleSubscription( const gloox::JID& jid, const std::string& /*msg*/ ); virtual bool handleSubscriptionRequest( const gloox::JID& jid, const std::string& /*msg*/ ); @@ -129,6 +154,7 @@ public slots: private slots: void doJabberRecv(); + void onSubscriptionRequestConfirmed( int result ); private: bool presenceMeansOnline( gloox::Presence::PresenceType p ); @@ -140,6 +166,7 @@ private: QSharedPointer m_vcardManager; QString m_server; QScopedPointer m_notifier; + QHash m_subscriptionConfirmBoxes; }; #endif // JABBER_H diff --git a/src/sip/jreen/CMakeLists.txt b/src/sip/jreen/CMakeLists.txt index 177bb6025..b06fac816 100644 --- a/src/sip/jreen/CMakeLists.txt +++ b/src/sip/jreen/CMakeLists.txt @@ -22,7 +22,7 @@ include_directories( . ${CMAKE_CURRENT_BINARY_DIR} .. ) qt4_wrap_cpp( jabberMoc ${jabberHeaders} ) -add_library( sip_jreen SHARED ${jabberSources} ${jabberMoc} ) +add_library( tomahawk_sipjreen SHARED ${jabberSources} ${jabberMoc} ) IF( WIN32 ) SET( OS_SPECIFIC_LINK_LIBRARIES @@ -33,14 +33,15 @@ SET( OS_SPECIFIC_LINK_LIBRARIES ) ENDIF( WIN32 ) -target_link_libraries( sip_jreen +target_link_libraries( tomahawk_sipjreen ${QT_LIBRARIES} ${LIBJREEN_LIBRARY} ${OS_SPECIFIC_LINK_LIBRARIES} + tomahawklib ) IF( APPLE ) # SET( CMAKE_SHARED_LINKER_FLAGS ${CMAKE_SHARED_LINKER_FLAGS} "-undefined dynamic_lookup" ) ENDIF( APPLE ) -install( TARGETS sip_jreen DESTINATION lib ) +install( TARGETS tomahawk_sipjreen DESTINATION lib${LIB_SUFFIX} ) diff --git a/src/sip/jreen/jabber.cpp b/src/sip/jreen/jabber.cpp index 147f59d0d..c97067f59 100644 --- a/src/sip/jreen/jabber.cpp +++ b/src/sip/jreen/jabber.cpp @@ -1,3 +1,21 @@ +/* === 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 "jabber.h" #include "tomahawksettings.h" @@ -59,10 +77,10 @@ JabberPlugin::connectPlugin( bool startup ) if ( startup && !TomahawkSettings::instance()->jabberAutoConnect() ) return false; - QString jid = TomahawkSettings::instance()->jabberUsername(); - QString server = TomahawkSettings::instance()->jabberServer(); - QString password = TomahawkSettings::instance()->jabberPassword(); - unsigned int port = TomahawkSettings::instance()->jabberPort(); + QString jid = m_currentUsername = TomahawkSettings::instance()->jabberUsername(); + QString server = m_currentServer = TomahawkSettings::instance()->jabberServer(); + QString password = m_currentPassword = TomahawkSettings::instance()->jabberPassword(); + unsigned int port = m_currentPort = TomahawkSettings::instance()->jabberPort(); QStringList splitJid = jid.split( '@', QString::SkipEmptyParts ); if ( splitJid.size() < 2 ) @@ -97,7 +115,7 @@ void JabberPlugin::disconnectPlugin() { onDisconnected(); - + if ( p ) p->disconnect(); @@ -105,35 +123,35 @@ JabberPlugin::disconnectPlugin() p = 0; } -void +void JabberPlugin::onConnected() { if( !m_menu ) { m_menu = new QMenu( QString( "JREEN (" ).append( accountName() ).append(")" ) ); m_addFriendAction = m_menu->addAction( "Add Friend..." ); QAction *connectAction = m_menu->addAction( "Connect" ); - + connect( m_addFriendAction, SIGNAL(triggered() ), this, SLOT( showAddFriendDialog() ) ); connect( connectAction, SIGNAL( triggered() ), SLOT( connectPlugin() ) ); - + emit addMenu( m_menu ); } - + emit connected(); } -void +void JabberPlugin::onDisconnected() { if( m_menu && m_addFriendAction ) { emit removeMenu( m_menu ); - + delete m_menu; m_menu = 0; m_addFriendAction = 0; // deleted by menu } - + emit disconnected(); } @@ -172,4 +190,29 @@ JabberPlugin::showAddFriendDialog() addContact( id ); } +void +JabberPlugin::checkSettings() +{ + bool reconnect = false; + if ( m_currentUsername != TomahawkSettings::instance()->jabberUsername() ) + reconnect = true; + if ( m_currentPassword != TomahawkSettings::instance()->jabberPassword() ) + reconnect = true; + if ( m_currentServer != TomahawkSettings::instance()->jabberServer() ) + reconnect = true; + if ( m_currentPort != TomahawkSettings::instance()->jabberPort() ) + reconnect = true; + + m_currentUsername = TomahawkSettings::instance()->jabberUsername(); + m_currentPassword = TomahawkSettings::instance()->jabberPassword(); + m_currentServer = TomahawkSettings::instance()->jabberServer(); + m_currentPort = TomahawkSettings::instance()->jabberPort(); + + if ( reconnect && ( p || TomahawkSettings::instance()->jabberAutoConnect() ) ) + { + disconnectPlugin(); + connectPlugin( false ); + } +} + Q_EXPORT_PLUGIN2( sip, JabberPlugin ) diff --git a/src/sip/jreen/jabber.h b/src/sip/jreen/jabber.h index 430c69058..ccbbafc0e 100644 --- a/src/sip/jreen/jabber.h +++ b/src/sip/jreen/jabber.h @@ -1,3 +1,21 @@ +/* === 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 JABBER_H #define JABBER_H @@ -29,6 +47,7 @@ public: public slots: virtual bool connectPlugin( bool startup ); void disconnectPlugin(); + void checkSettings(); void sendMsg( const QString& to, const QString& msg ); void broadcastMsg( const QString &msg ); void addContact( const QString &jid, const QString& msg = QString() ); @@ -42,6 +61,11 @@ private: Jabber_p* p; QMenu* m_menu; QAction* m_addFriendAction; + + QString m_currentUsername; + QString m_currentPassword; + QString m_currentServer; + unsigned int m_currentPort; }; #endif diff --git a/src/sip/jreen/jabber_p.cpp b/src/sip/jreen/jabber_p.cpp index ef7a9a080..9e4aa8798 100644 --- a/src/sip/jreen/jabber_p.cpp +++ b/src/sip/jreen/jabber_p.cpp @@ -1,3 +1,21 @@ +/* === 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 "jabber_p.h" #include @@ -29,22 +47,22 @@ Jabber_p::Jabber_p( const QString& jid, const QString& password, const QString& //qsrand( QTime( 0, 0, 0 ).secsTo( QTime::currentTime() ) ); qsrand(QDateTime::currentDateTime().toTime_t()); - m_presences[jreen::Presence::Available] = "available"; - m_presences[jreen::Presence::Chat] = "chat"; - m_presences[jreen::Presence::Away] = "away"; - m_presences[jreen::Presence::DND] = "dnd"; - m_presences[jreen::Presence::XA] = "xa"; - m_presences[jreen::Presence::Unavailable] = "unavailable"; - m_presences[jreen::Presence::Probe] = "probe"; - m_presences[jreen::Presence::Error] = "error"; - m_presences[jreen::Presence::Invalid] = "invalid"; + m_presences[Jreen::Presence::Available] = "available"; + m_presences[Jreen::Presence::Chat] = "chat"; + m_presences[Jreen::Presence::Away] = "away"; + m_presences[Jreen::Presence::DND] = "dnd"; + m_presences[Jreen::Presence::XA] = "xa"; + m_presences[Jreen::Presence::Unavailable] = "unavailable"; + m_presences[Jreen::Presence::Probe] = "probe"; + m_presences[Jreen::Presence::Error] = "error"; + m_presences[Jreen::Presence::Invalid] = "invalid"; - m_jid = jreen::JID( jid ); + m_jid = Jreen::JID( jid ); - m_client = new jreen::Client( jid, password ); - m_client->setResource( QString( "tomahawk%1" ).arg( "DOMME" ) ); + m_client = new Jreen::Client( jid, password ); + m_client->setResource( QString( "tomahawk-jreen%1" ).arg( QString::number( qrand() % 10000 ) ) ); - jreen::Capabilities::Ptr caps = m_client->presence().findExtension(); + Jreen::Capabilities::Ptr caps = m_client->presence().findExtension(); caps->setNode(TOMAHAWK_CAP_NODE_NAME); qDebug() << "Our JID set to:" << m_client->jid().full(); @@ -53,10 +71,10 @@ Jabber_p::Jabber_p( const QString& jid, const QString& password, const QString& connect(m_client->connection(), SIGNAL(error(SocketError)), SLOT(onError(SocketError))); connect(m_client, SIGNAL(serverFeaturesReceived(QSet)), SLOT(onConnect())); - connect(m_client, SIGNAL(disconnected(jreen::Client::DisconnectReason)), SLOT(onDisconnect(jreen::Client::DisconnectReason))); + connect(m_client, SIGNAL(disconnected(Jreen::Client::DisconnectReason)), SLOT(onDisconnect(Jreen::Client::DisconnectReason))); connect(m_client, SIGNAL(destroyed(QObject*)), this, SLOT(onDestroy())); - connect(m_client, SIGNAL(newMessage(jreen::Message)), SLOT(onNewMessage(jreen::Message))); - connect(m_client, SIGNAL(newPresence(jreen::Presence)), SLOT(onNewPresence(jreen::Presence))); + connect(m_client, SIGNAL(newMessage(Jreen::Message)), SLOT(onNewMessage(Jreen::Message))); + connect(m_client, SIGNAL(newPresence(Jreen::Presence)), SLOT(onNewPresence(Jreen::Presence))); qDebug() << "Connecting to the XMPP server..."; m_client->connectToServer(); @@ -106,7 +124,7 @@ Jabber_p::sendMsg( const QString& to, const QString& msg ) } qDebug() << Q_FUNC_INFO << to << msg; - jreen::Message m( jreen::Message::Chat, jreen::JID(to), msg); + Jreen::Message m( Jreen::Message::Chat, Jreen::JID(to), msg); m_client->send( m ); // assuming this is threadsafe } @@ -131,7 +149,7 @@ Jabber_p::broadcastMsg( const QString &msg ) foreach( const QString& jidstr, m_peers.keys() ) { qDebug() << "Broadcasting to" << jidstr <<"..."; - jreen::Message m(jreen::Message::Chat, jreen::JID(jidstr), msg, ""); + Jreen::Message m(Jreen::Message::Chat, Jreen::JID(jidstr), msg, ""); m_client->send( m ); } } @@ -173,27 +191,27 @@ Jabber_p::onConnect() emit connected(); qDebug() << "Connected as:" << m_jid.full(); - m_client->setPresence(jreen::Presence::Available, "Tomahawk-JREEN available", 1); + m_client->setPresence(Jreen::Presence::Available, "Tomahawk-JREEN available", 1); m_client->disco()->setSoftwareVersion( "Tomahawk JREEN", "0.0.0.0", "Foobar" ); m_client->setPingInterval(60000); - m_roster = new jreen::SimpleRoster( m_client ); + m_roster = new Jreen::SimpleRoster( m_client ); m_roster->load(); // join MUC with bare jid as nickname //TODO: make the room a list of rooms and make that configurable QString bare(m_jid.bare()); - m_room = new jreen::MUCRoom(m_client, jreen::JID(QString("tomahawk@conference.qutim.org/").append(bare.replace("@", "-")))); - m_room->setHistorySeconds(0); - m_room->join(); + m_room = new Jreen::MUCRoom(m_client, Jreen::JID(QString("tomahawk@conference.qutim.org/").append(bare.replace("@", "-")))); + //m_room->setHistorySeconds(0); + //m_room->join(); // treat muc participiants like contacts - connect(m_room, SIGNAL(messageReceived(jreen::Message, bool)), this, SLOT(onNewMessage(jreen::Message))); - connect(m_room, SIGNAL(presenceReceived(jreen::Presence,const jreen::MUCRoom::Participant*)), this, SLOT(onNewPresence(jreen::Presence))); + connect(m_room, SIGNAL(messageReceived(Jreen::Message, bool)), this, SLOT(onNewMessage(Jreen::Message))); + connect(m_room, SIGNAL(presenceReceived(Jreen::Presence,const Jreen::MUCRoom::Participant*)), this, SLOT(onNewPresence(Jreen::Presence))); } void -Jabber_p::onDisconnect( jreen::Client::DisconnectReason reason ) +Jabber_p::onDisconnect( Jreen::Client::DisconnectReason reason ) { QString error; bool reconnect = false; @@ -201,39 +219,39 @@ Jabber_p::onDisconnect( jreen::Client::DisconnectReason reason ) switch( reason ) { - case jreen::Client::User: + case Jreen::Client::User: error = "User Interaction"; break; - case jreen::Client::HostUnknown: + case Jreen::Client::HostUnknown: error = "Host is unknown"; break; - case jreen::Client::ItemNotFound: + case Jreen::Client::ItemNotFound: error = "Item not found"; break; - case jreen::Client::AuthorizationError: + case Jreen::Client::AuthorizationError: error = "Authorization Error"; break; - case jreen::Client::RemoteStreamError: + case Jreen::Client::RemoteStreamError: error = "Remote Stream Error"; reconnect = true; break; - case jreen::Client::RemoteConnectionFailed: + case Jreen::Client::RemoteConnectionFailed: error = "Remote Connection failed"; break; - case jreen::Client::InternalServerError: + case Jreen::Client::InternalServerError: error = "Internal Server Error"; reconnect = true; break; - case jreen::Client::SystemShutdown: + case Jreen::Client::SystemShutdown: error = "System shutdown"; reconnect = true; reconnectInSeconds = 60; break; - case jreen::Client::Conflict: + case Jreen::Client::Conflict: error = "Conflict"; break; - case jreen::Client::Unknown: + case Jreen::Client::Unknown: error = "Unknown"; break; @@ -252,7 +270,7 @@ Jabber_p::onDisconnect( jreen::Client::DisconnectReason reason ) } void -Jabber_p::onNewMessage( const jreen::Message& m ) +Jabber_p::onNewMessage( const Jreen::Message& m ) { QString from = m.from().full(); QString msg = m.body(); @@ -265,10 +283,10 @@ Jabber_p::onNewMessage( const jreen::Message& m ) } -void Jabber_p::onNewPresence( const jreen::Presence& presence) +void Jabber_p::onNewPresence( const Jreen::Presence& presence) { - jreen::JID jid = presence.from(); + Jreen::JID jid = presence.from(); QString fulljid( jid.full() ); qDebug() << Q_FUNC_INFO << "handle presence" << fulljid << presence.subtype(); @@ -282,7 +300,7 @@ void Jabber_p::onNewPresence( const jreen::Presence& presence) } // ignore anyone not running tomahawk: - jreen::Capabilities::Ptr caps = presence.findExtension(); + Jreen::Capabilities::Ptr caps = presence.findExtension(); if ( caps && (caps->node() == TOMAHAWK_CAP_NODE_NAME )) { qDebug() << Q_FUNC_INFO << presence.from().full() << "tomahawk detected by caps"; @@ -338,13 +356,13 @@ void Jabber_p::onNewPresence( const jreen::Presence& presence) } bool -Jabber_p::presenceMeansOnline( jreen::Presence::Type p ) +Jabber_p::presenceMeansOnline( Jreen::Presence::Type p ) { switch(p) { - case jreen::Presence::Invalid: - case jreen::Presence::Unavailable: - case jreen::Presence::Error: + case Jreen::Presence::Invalid: + case Jreen::Presence::Unavailable: + case Jreen::Presence::Error: return false; break; default: diff --git a/src/sip/jreen/jabber_p.h b/src/sip/jreen/jabber_p.h index b69369b6e..ab0826f20 100644 --- a/src/sip/jreen/jabber_p.h +++ b/src/sip/jreen/jabber_p.h @@ -1,3 +1,21 @@ +/* === 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 . + */ + /* This is the Jabber client that the rest of the app sees Gloox stuff should NOT leak outside this class. @@ -59,25 +77,25 @@ public slots: void addContact( const QString& jid, const QString& msg = QString() ); void disconnect(); - void onDisconnect(jreen::Client::DisconnectReason reason); + void onDisconnect(Jreen::Client::DisconnectReason reason); void onConnect(); private slots: - virtual void onNewPresence( const jreen::Presence& presence ); - virtual void onNewMessage( const jreen::Message& msg ); - virtual void onError( const jreen::Connection::SocketError& e ) + virtual void onNewPresence( const Jreen::Presence& presence ); + virtual void onNewMessage( const Jreen::Message& msg ); + virtual void onError( const Jreen::Connection::SocketError& e ) { qDebug() << e; } private: - bool presenceMeansOnline( jreen::Presence::Type p ); - jreen::Client *m_client; - jreen::MUCRoom *m_room; - jreen::SimpleRoster *m_roster; - jreen::JID m_jid; - QMap m_presences; - QMap m_peers; + bool presenceMeansOnline( Jreen::Presence::Type p ); + Jreen::Client *m_client; + Jreen::MUCRoom *m_room; + Jreen::SimpleRoster *m_roster; + Jreen::JID m_jid; + QMap m_presences; + QMap m_peers; QString m_server; }; diff --git a/src/sip/sipdllmacro.h b/src/sip/sipdllmacro.h index 8c93a8767..74a8eec22 100644 --- a/src/sip/sipdllmacro.h +++ b/src/sip/sipdllmacro.h @@ -1,3 +1,21 @@ +/* === 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 SIPDLLMACRO_H #define SIPDLLMACRO_H diff --git a/src/sip/twitter/CMakeLists.txt b/src/sip/twitter/CMakeLists.txt index f3bc32bd5..f50abba2d 100644 --- a/src/sip/twitter/CMakeLists.txt +++ b/src/sip/twitter/CMakeLists.txt @@ -28,7 +28,7 @@ include_directories( . ${CMAKE_CURRENT_BINARY_DIR} .. qt4_wrap_cpp( twitterMoc ${twitterHeaders} ) qt4_wrap_ui( twitterUI_H ${twitterUI} ) -add_library( sip_twitter SHARED ${twitterUI_H} ${twitterSources} ${twitterMoc} ) +add_library( tomahawk_siptwitter SHARED ${twitterUI_H} ${twitterSources} ${twitterMoc} ) IF( WIN32 ) SET( OS_SPECIFIC_LINK_LIBRARIES @@ -40,14 +40,14 @@ SET( OS_SPECIFIC_LINK_LIBRARIES ) ENDIF( WIN32 ) -target_link_libraries( sip_twitter +target_link_libraries( tomahawk_siptwitter ${QT_LIBRARIES} ${OS_SPECIFIC_LINK_LIBRARIES} tomahawklib ) IF( APPLE ) - SET( CMAKE_SHARED_LINKER_FLAGS ${CMAKE_SHARED_LINKER_FLAGS} "-undefined dynamic_lookup" ) +# SET( CMAKE_SHARED_LINKER_FLAGS ${CMAKE_SHARED_LINKER_FLAGS} "-undefined dynamic_lookup" ) ENDIF( APPLE ) -install( TARGETS sip_twitter DESTINATION lib ) +install( TARGETS tomahawk_siptwitter DESTINATION lib${LIB_SUFFIX} ) diff --git a/src/sip/twitter/twitter.cpp b/src/sip/twitter/twitter.cpp index 4dd0bcd0c..643f11fcd 100644 --- a/src/sip/twitter/twitter.cpp +++ b/src/sip/twitter/twitter.cpp @@ -1,3 +1,21 @@ +/* === 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 "twitter.h" #include "twitterconfigwidget.h" @@ -15,6 +33,7 @@ #include #include +static QString s_gotTomahawkRegex = QString( "^(@[a-zA-Z0-9]+ )?(Got Tomahawk\\?) (\\{[a-fA-F0-9\\-]+\\}) (.*)$" ); TwitterPlugin::TwitterPlugin() : SipPlugin() @@ -35,31 +54,40 @@ TwitterPlugin::TwitterPlugin() m_checkTimer.setInterval( 60000 ); m_checkTimer.setSingleShot( false ); connect( &m_checkTimer, SIGNAL( timeout() ), SLOT( checkTimerFired() ) ); - m_checkTimer.start(); m_connectTimer.setInterval( 60000 ); m_connectTimer.setSingleShot( false ); connect( &m_connectTimer, SIGNAL( timeout() ), SLOT( connectTimerFired() ) ); - m_connectTimer.start(); +} + +void +TwitterPlugin::configDialogAuthedSignalSlot( bool authed ) +{ + m_isAuthed = authed; + if ( !authed ) + { + TomahawkSettings::instance()->setTwitterScreenName( QString() ); + TomahawkSettings::instance()->setTwitterOAuthToken( QString() ); + TomahawkSettings::instance()->setTwitterOAuthTokenSecret( QString() ); + } } bool TwitterPlugin::isValid() { - return m_isAuthed || !m_cachedPeers.isEmpty(); + return m_isAuthed; } const QString TwitterPlugin::name() { - qDebug() << "TwitterPlugin returning plugin name " << QString( MYNAME ); return QString( MYNAME ); } const QString TwitterPlugin::friendlyName() { - return QString("Twitter"); + return tr("Twitter"); } const QString @@ -72,11 +100,11 @@ QWidget* TwitterPlugin::configWidget() { m_configWidget = new TwitterConfigWidget( this ); + connect( m_configWidget, SIGNAL( twitterAuthed(bool) ), SLOT( configDialogAuthedSignalSlot(bool) ) ); + return m_configWidget; } - - bool TwitterPlugin::connectPlugin( bool /*startup*/ ) { @@ -101,16 +129,32 @@ TwitterPlugin::connectPlugin( bool /*startup*/ ) return m_cachedPeers.isEmpty(); } - delete m_twitterAuth.data(); + if ( refreshTwitterAuth() ) + { + QTweetAccountVerifyCredentials *credVerifier = new QTweetAccountVerifyCredentials( m_twitterAuth.data(), this ); + connect( credVerifier, SIGNAL( parsedUser(const QTweetUser &) ), SLOT( connectAuthVerifyReply(const QTweetUser &) ) ); + credVerifier->verify(); + } + + return true; +} + +bool +TwitterPlugin::refreshTwitterAuth() +{ + if( !m_twitterAuth.isNull() ) + delete m_twitterAuth.data(); m_twitterAuth = QWeakPointer( new TomahawkOAuthTwitter( this ) ); + + if( m_twitterAuth.isNull() ) + return false; + + TomahawkSettings *settings = TomahawkSettings::instance(); + m_twitterAuth.data()->setNetworkAccessManager( TomahawkUtils::nam() ); m_twitterAuth.data()->setOAuthToken( settings->twitterOAuthToken().toLatin1() ); m_twitterAuth.data()->setOAuthTokenSecret( settings->twitterOAuthTokenSecret().toLatin1() ); - - QTweetAccountVerifyCredentials *credVerifier = new QTweetAccountVerifyCredentials( m_twitterAuth.data(), this ); - connect( credVerifier, SIGNAL( parsedUser(const QTweetUser &) ), SLOT( connectAuthVerifyReply(const QTweetUser &) ) ); - credVerifier->verify(); - + return true; } @@ -118,13 +162,22 @@ void TwitterPlugin::disconnectPlugin() { qDebug() << Q_FUNC_INFO; + m_checkTimer.stop(); + m_connectTimer.stop(); if( !m_friendsTimeline.isNull() ) - m_friendsTimeline.data()->deleteLater(); + delete m_friendsTimeline.data(); + if( !m_mentions.isNull() ) + delete m_mentions.data(); + if( !m_directMessages.isNull() ) + delete m_directMessages.data(); + if( !m_directMessageNew.isNull() ) + delete m_directMessageNew.data(); + if( !m_directMessageDestroy.isNull() ) + delete m_directMessageDestroy.data(); if( !m_twitterAuth.isNull() ) - m_twitterAuth.data()->deleteLater(); - + delete m_twitterAuth.data(); + m_cachedPeers.empty(); - delete m_twitterAuth.data(); m_isOnline = false; } @@ -155,13 +208,24 @@ TwitterPlugin::connectAuthVerifyReply( const QTweetUser &user ) connect( m_directMessageNew.data(), SIGNAL( error(QTweetNetBase::ErrorCode, const QString &) ), SLOT( directMessagePostError(QTweetNetBase::ErrorCode, const QString &) ) ); connect( m_directMessageDestroy.data(), SIGNAL( parsedDirectMessage(const QTweetDMStatus &) ), SLOT( directMessageDestroyed(const QTweetDMStatus &) ) ); m_isOnline = true; + m_connectTimer.start(); + m_checkTimer.start(); QMetaObject::invokeMethod( this, "checkTimerFired", Qt::AutoConnection ); QTimer::singleShot( 20000, this, SLOT( connectTimerFired() ) ); } else { - qDebug() << "TwitterPlugin auth pointer was null!"; - m_isAuthed = false; + if ( refreshTwitterAuth() ) + { + QTweetAccountVerifyCredentials *credVerifier = new QTweetAccountVerifyCredentials( m_twitterAuth.data(), this ); + connect( credVerifier, SIGNAL( parsedUser(const QTweetUser &) ), SLOT( connectAuthVerifyReply(const QTweetUser &) ) ); + credVerifier->verify(); + } + else + { + qDebug() << "TwitterPlugin auth pointer was null!"; + m_isAuthed = false; + } } } } @@ -169,7 +233,7 @@ TwitterPlugin::connectAuthVerifyReply( const QTweetUser &user ) void TwitterPlugin::checkTimerFired() { - if ( !isValid() ) + if ( !isValid() || m_twitterAuth.isNull() ) return; if ( m_cachedFriendsSinceId == 0 ) @@ -178,8 +242,7 @@ TwitterPlugin::checkTimerFired() qDebug() << "TwitterPlugin looking at friends timeline since id " << m_cachedFriendsSinceId; if ( !m_friendsTimeline.isNull() ) - m_friendsTimeline.data()->fetch( m_cachedFriendsSinceId, 0, 800 ); - + m_friendsTimeline.data()->fetch( m_cachedFriendsSinceId, 0, 800 ); if ( m_cachedMentionsSinceId == 0 ) m_cachedMentionsSinceId = TomahawkSettings::instance()->twitterCachedMentionsSinceId(); @@ -193,19 +256,32 @@ TwitterPlugin::checkTimerFired() void TwitterPlugin::connectTimerFired() { - if ( !isValid() || m_cachedPeers.isEmpty() ) + if ( !isValid() || m_cachedPeers.isEmpty() || m_twitterAuth.isNull() ) return; - + QString myScreenName = TomahawkSettings::instance()->twitterScreenName(); QList peerlist = m_cachedPeers.keys(); qStableSort( peerlist.begin(), peerlist.end() ); foreach( QString screenName, peerlist ) { QHash< QString, QVariant > peerData = m_cachedPeers[screenName].toHash(); + + if ( Servent::instance()->connectedToSession( peerData["node"].toString() ) ) + { + peerData["lastseen"] = QDateTime::currentMSecsSinceEpoch(); + m_cachedPeers[screenName] = peerData; + } + + if ( QDateTime::currentMSecsSinceEpoch() - peerData["lastseen"].toLongLong() > 1209600000 ) // 2 weeks + { + qDebug() << "Aging peer " << screenName << " out of cache"; + m_cachedPeers.remove( screenName ); + continue; + } if ( !peerData.contains( "host" ) || !peerData.contains( "port" ) || !peerData.contains( "pkey" ) ) { - qDebug() << "TwitterPlugin does not have host, port and/or pkey values for " << screenName; + qDebug() << "TwitterPlugin does not have host, port and/or pkey values for " << screenName << " (this is usually *not* a bug or problem but a normal part of the process)"; continue; } @@ -213,16 +289,66 @@ TwitterPlugin::connectTimerFired() } } +void +TwitterPlugin::parseGotTomahawk( const QRegExp ®ex, const QString &screenName, const QString &text ) +{ + QString myScreenName = TomahawkSettings::instance()->twitterScreenName(); + qDebug() << "TwitterPlugin found an exact matching Got Tomahawk? mention or direct message from user " << screenName << ", now parsing"; + regex.exactMatch( text ); + if ( text.startsWith( '@' ) && regex.captureCount() >= 2 && regex.cap( 1 ) != QString( '@' + myScreenName ) ) + { + qDebug() << "TwitterPlugin skipping mention because it's directed @someone that isn't us"; + return; + } + + QString node; + for ( int i = 0; i < regex.captureCount(); ++i ) + { + if ( regex.cap( i ) == QString( "Got Tomahawk?" ) ) + { + QString nodeCap = regex.cap( i + 1 ); + nodeCap.chop( 1 ); + node = nodeCap.mid( 1 ); + } + } + if ( node.isEmpty() ) + { + qDebug() << "TwitterPlugin could not parse node out of the tweet"; + return; + } + else + qDebug() << "TwitterPlugin parsed node " << node << " out of the tweet"; + + if ( screenName == myScreenName && node == Database::instance()->dbid() ) + { + qDebug() << "My screen name and my dbid found; ignoring"; + return; + } + + QHash< QString, QVariant > peerData; + if( m_cachedPeers.contains( screenName ) ) + { + peerData = m_cachedPeers[screenName].toHash(); + //force a re-send of info but no need to re-register + peerData["resend"] = QVariant::fromValue< bool >( true ); + } + peerData["node"] = QVariant::fromValue< QString >( node ); + QMetaObject::invokeMethod( this, "registerOffer", Q_ARG( QString, screenName ), QGenericArgument( "QHash< QString, QVariant >", (const void*)&peerData ) ); +} + void TwitterPlugin::friendsTimelineStatuses( const QList< QTweetStatus > &statuses ) { qDebug() << Q_FUNC_INFO; - QRegExp regex( QString( "^(@[a-zA-Z0-9]+ )?(Got Tomahawk\\?) (\\{[a-fA-F0-9\\-]+\\}) (.*)$" ), Qt::CaseSensitive, QRegExp::RegExp2 ); + QRegExp regex( s_gotTomahawkRegex, Qt::CaseSensitive, QRegExp::RegExp2 ); QString myScreenName = TomahawkSettings::instance()->twitterScreenName(); QHash< QString, QTweetStatus > latestHash; foreach ( QTweetStatus status, statuses ) { + if ( !regex.exactMatch( status.text() ) ) + continue; + if ( !latestHash.contains( status.user().screenName() ) ) latestHash[status.user().screenName()] = status; else @@ -237,46 +363,8 @@ TwitterPlugin::friendsTimelineStatuses( const QList< QTweetStatus > &statuses ) if ( status.id() > m_cachedFriendsSinceId ) m_cachedFriendsSinceId = status.id(); - if ( regex.exactMatch( status.text() ) ) - { - qDebug() << "TwitterPlugin found an exact tweet from friend " << status.user().screenName(); - if ( status.text().startsWith( '@' ) && regex.captureCount() >= 2 && regex.cap( 1 ) != QString( '@' + myScreenName ) ) - { - qDebug() << "TwitterPlugin skipping tweet because it's directed @someone that isn't us"; - continue; - } - - QString node; - for ( int i = 0; i < regex.captureCount(); ++i ) - { - if ( regex.cap( i ) == QString( "Got Tomahawk?" ) ) - { - QString nodeCap = regex.cap( i + 1 ); - nodeCap.chop( 1 ); - node = nodeCap.mid( 1 ); - } - } - if ( node.isEmpty() ) - { - qDebug() << "TwitterPlugin could not parse node out of the tweet"; - continue; - } - else - qDebug() << "TwitterPlugin parsed node " << node << " out of the tweet"; - - if ( status.user().screenName() == myScreenName && node == Database::instance()->dbid() ) - continue; - - QHash< QString, QVariant > peerData; - if( m_cachedPeers.contains( status.user().screenName() ) ) - { - peerData = m_cachedPeers[status.user().screenName()].toHash(); - //force a re-send of info but no need to re-register - peerData["resend"] = QVariant::fromValue< bool >( true ); - } - peerData["node"] = QVariant::fromValue< QString >( node ); - QMetaObject::invokeMethod( this, "registerOffer", Q_ARG( QString, status.user().screenName() ), QGenericArgument( "QHash< QString, QVariant >", (const void*)&peerData ) ); - } + qDebug() << "TwitterPlugin checking mention from " << status.user().screenName() << " with content " << status.text(); + parseGotTomahawk( regex, status.user().screenName(), status.text() ); } TomahawkSettings::instance()->setTwitterCachedFriendsSinceId( m_cachedFriendsSinceId ); @@ -289,12 +377,14 @@ void TwitterPlugin::mentionsStatuses( const QList< QTweetStatus > &statuses ) { qDebug() << Q_FUNC_INFO; - QRegExp regex( QString( "^(@[a-zA-Z0-9]+ )?(Got Tomahawk\\?) (\\{[a-fA-F0-9\\-]+\\}) (.*)$" ), Qt::CaseSensitive, QRegExp::RegExp2 ); - QString myScreenName = TomahawkSettings::instance()->twitterScreenName(); + QRegExp regex( s_gotTomahawkRegex, Qt::CaseSensitive, QRegExp::RegExp2 ); QHash< QString, QTweetStatus > latestHash; foreach ( QTweetStatus status, statuses ) { + if ( !regex.exactMatch( status.text() ) ) + continue; + if ( !latestHash.contains( status.user().screenName() ) ) latestHash[status.user().screenName()] = status; else @@ -308,47 +398,9 @@ TwitterPlugin::mentionsStatuses( const QList< QTweetStatus > &statuses ) { if ( status.id() > m_cachedMentionsSinceId ) m_cachedMentionsSinceId = status.id(); - - if ( regex.exactMatch( status.text() ) ) - { - qDebug() << "TwitterPlugin found an exact matching mention from user " << status.user().screenName(); - if ( status.text().startsWith( '@' ) && regex.captureCount() >= 2 && regex.cap( 1 ) != QString( '@' + myScreenName ) ) - { - qDebug() << "TwitterPlugin skipping mention because it's directed @someone that isn't us"; - continue; - } - - QString node; - for ( int i = 0; i < regex.captureCount(); ++i ) - { - if ( regex.cap( i ) == QString( "Got Tomahawk?" ) ) - { - QString nodeCap = regex.cap( i + 1 ); - nodeCap.chop( 1 ); - node = nodeCap.mid( 1 ); - } - } - if ( node.isEmpty() ) - { - qDebug() << "TwitterPlugin could not parse node out of the tweet"; - continue; - } - else - qDebug() << "TwitterPlugin parsed node " << node << " out of the tweet"; - - if ( status.user().screenName() == myScreenName && node == Database::instance()->dbid() ) - continue; - - QHash< QString, QVariant > peerData; - if( m_cachedPeers.contains( status.user().screenName() ) ) - { - peerData = m_cachedPeers[status.user().screenName()].toHash(); - //force a re-send of info but no need to re-register - peerData["resend"] = QVariant::fromValue< bool >( true ); - } - peerData["node"] = QVariant::fromValue< QString >( node ); - QMetaObject::invokeMethod( this, "registerOffer", Q_ARG( QString, status.user().screenName() ), QGenericArgument( "QHash< QString, QVariant >", (const void*)&peerData ) ); - } + + qDebug() << "TwitterPlugin checking mention from " << status.user().screenName() << " with content " << status.text(); + parseGotTomahawk( regex, status.user().screenName(), status.text() ); } TomahawkSettings::instance()->setTwitterCachedMentionsSinceId( m_cachedMentionsSinceId ); @@ -383,9 +435,26 @@ TwitterPlugin::directMessages( const QList< QTweetDMStatus > &messages ) { qDebug() << Q_FUNC_INFO; + QRegExp regex( s_gotTomahawkRegex, Qt::CaseSensitive, QRegExp::RegExp2 ); + QString myScreenName = TomahawkSettings::instance()->twitterScreenName(); + QHash< QString, QTweetDMStatus > latestHash; foreach ( QTweetDMStatus status, messages ) { + if ( !regex.exactMatch( status.text() ) ) + { + QStringList splitList = status.text().split(':'); + if ( splitList.length() != 5 ) + continue; + if ( splitList[0] != "TOMAHAWKPEER" ) + continue; + if ( !splitList[1].startsWith( "Host=" ) || !splitList[2].startsWith( "Port=" ) || !splitList[3].startsWith( "Node=" ) || !splitList[4].startsWith( "PKey=" ) ) + continue; + int port = splitList[2].mid( 5 ).toInt(); + if ( port == 0 ) + continue; + } + if ( !latestHash.contains( status.senderScreenName() ) ) latestHash[status.senderScreenName()] = status; else @@ -400,43 +469,48 @@ TwitterPlugin::directMessages( const QList< QTweetDMStatus > &messages ) qDebug() << "TwitterPlugin checking direct message from " << status.senderScreenName() << " with content " << status.text(); if ( status.id() > m_cachedDirectMessagesSinceId ) m_cachedDirectMessagesSinceId = status.id(); - QStringList splitList = status.text().split(':'); - qDebug() << "TwitterPlugin found " << splitList.length() << " parts to the message; the parts are:"; - foreach( QString part, splitList ) - qDebug() << part; - if ( splitList.length() != 5 ) - continue; - if ( splitList[0] != "TOMAHAWKPEER" ) - continue; - if ( !splitList[1].startsWith( "Host=" ) || !splitList[2].startsWith( "Port=" ) || !splitList[3].startsWith( "Node=" ) || !splitList[4].startsWith( "PKey=" ) ) - continue; - int port = splitList[2].mid( 5 ).toInt(); - if ( port == 0 ) - continue; - QString host = splitList[1].mid( 5 ); - QString node = splitList[3].mid( 5 ); - QString pkey = splitList[4].mid( 5 ); - qDebug() << "TwitterPlugin found a peerstart message from " << status.senderScreenName() << " with host " << host << " and port " << port << " and pkey " << pkey << " destined for node " << node; - if ( node != Database::instance()->dbid() ) + if ( regex.exactMatch( status.text() ) ) + parseGotTomahawk( regex, status.sender().screenName(), status.text() ); + else { - qDebug() << "Not destined for this node; leaving it alone and not answering"; - continue; - } + QStringList splitList = status.text().split(':'); + qDebug() << "TwitterPlugin found " << splitList.length() << " parts to the message; the parts are:"; + foreach( QString part, splitList ) + qDebug() << part; + //validity is checked above + int port = splitList[2].mid( 5 ).toInt(); + QString host = splitList[1].mid( 5 ); + QString node = splitList[3].mid( 5 ); + QString pkey = splitList[4].mid( 5 ); + QStringList splitNode = node.split('*'); + if ( splitNode.length() != 2 ) + { + qDebug() << "Old-style node info found, ignoring"; + continue; + } + qDebug() << "TwitterPlugin found a peerstart message from " << status.senderScreenName() << " with host " << host << " and port " << port << " and pkey " << pkey << " and node " << splitNode[0] << " destined for node " << splitNode[1]; + - QHash< QString, QVariant > peerData = ( m_cachedPeers.contains( status.senderScreenName() ) ) ? - m_cachedPeers[status.senderScreenName()].toHash() : - QHash< QString, QVariant >(); - - peerData["host"] = QVariant::fromValue< QString >( host ); - peerData["port"] = QVariant::fromValue< int >( port ); - peerData["pkey"] = QVariant::fromValue< QString >( pkey ); - peerData["dirty"] = QVariant::fromValue< bool >( true ); + QHash< QString, QVariant > peerData = ( m_cachedPeers.contains( status.senderScreenName() ) ) ? + m_cachedPeers[status.senderScreenName()].toHash() : + QHash< QString, QVariant >(); + + peerData["host"] = QVariant::fromValue< QString >( host ); + peerData["port"] = QVariant::fromValue< int >( port ); + peerData["pkey"] = QVariant::fromValue< QString >( pkey ); + peerData["node"] = QVariant::fromValue< QString >( splitNode[0] ); + peerData["dirty"] = QVariant::fromValue< bool >( true ); - QMetaObject::invokeMethod( this, "registerOffer", Q_ARG( QString, status.senderScreenName() ), QGenericArgument( "QHash< QString, QVariant >", (const void*)&peerData ) ); - - if ( !m_directMessageDestroy.isNull() ) - m_directMessageDestroy.data()->destroyMessage( status.id() ); + QMetaObject::invokeMethod( this, "registerOffer", Q_ARG( QString, status.senderScreenName() ), QGenericArgument( "QHash< QString, QVariant >", (const void*)&peerData ) ); + + if ( Database::instance()->dbid().startsWith( splitNode[1] ) ) + { + qDebug() << "TwitterPlugin found message destined for this node; destroying it"; + if ( !m_directMessageDestroy.isNull() ) + m_directMessageDestroy.data()->destroyMessage( status.id() ); + } + } } TomahawkSettings::instance()->setTwitterCachedDirectMessagesSinceId( m_cachedDirectMessagesSinceId ); @@ -468,11 +542,14 @@ TwitterPlugin::registerOffer( const QString &screenName, const QHash< QString, Q _peerData.remove( "resend" ); } - if ( !_peerData.contains( "okey" ) ) + if ( !_peerData.contains( "okey" ) || + !_peerData.contains( "onod" ) || + ( _peerData.contains( "onod" ) && _peerData["onod"] != Database::instance()->dbid() ) ) { QString okey = QUuid::createUuid().toString().split( '-' ).last(); okey.chop( 1 ); _peerData["okey"] = QVariant::fromValue< QString >( okey ); + _peerData["onod"] = QVariant::fromValue< QString >( Database::instance()->dbid() ); peersChanged = true; needToAddToCache = true; needToSend = true; @@ -505,24 +582,27 @@ TwitterPlugin::registerOffer( const QString &screenName, const QHash< QString, Q qDebug() << "TwitterPlugin did not send offer because external address is " << Servent::instance()->externalAddress() << " and external port is " << Servent::instance()->externalPort(); } - if ( m_isOnline && _peerData.contains( "host" ) && _peerData.contains( "port" ) && _peerData.contains( "pkey" ) ) - QMetaObject::invokeMethod( this, "makeConnection", Q_ARG( QString, screenName ), QGenericArgument( "QHash< QString, QVariant >", (const void*)&_peerData ) ); - if ( peersChanged ) { + _peerData["lastseen"] = QString::number( QDateTime::currentMSecsSinceEpoch() ); m_cachedPeers[screenName] = QVariant::fromValue< QHash< QString, QVariant > >( _peerData ); TomahawkSettings::instance()->setTwitterCachedPeers( m_cachedPeers ); } + + if ( m_isOnline && _peerData.contains( "host" ) && _peerData.contains( "port" ) && _peerData.contains( "pkey" ) ) + QMetaObject::invokeMethod( this, "makeConnection", Q_ARG( QString, screenName ), QGenericArgument( "QHash< QString, QVariant >", (const void*)&_peerData ) ); + } void TwitterPlugin::sendOffer( const QString &screenName, const QHash< QString, QVariant > &peerData ) { qDebug() << Q_FUNC_INFO; - QString offerString = QString( "TOMAHAWKPEER:Host=%1:Port=%2:Node=%3:PKey=%4" ).arg( peerData["ohst"].toString() ) - .arg( peerData["oprt"].toString() ) - .arg( peerData["node"].toString() ) - .arg( peerData["okey"].toString() ); + QString offerString = QString( "TOMAHAWKPEER:Host=%1:Port=%2:Node=%3*%4:PKey=%5" ).arg( peerData["ohst"].toString() ) + .arg( peerData["oprt"].toString() ) + .arg( Database::instance()->dbid() ) + .arg( peerData["node"].toString().left( 8 ) ) + .arg( peerData["okey"].toString() ); qDebug() << "TwitterPlugin sending message to " << screenName << ": " << offerString; if( !m_directMessageNew.isNull() ) m_directMessageNew.data()->post( screenName, offerString ); @@ -532,9 +612,10 @@ void TwitterPlugin::makeConnection( const QString &screenName, const QHash< QString, QVariant > &peerData ) { qDebug() << Q_FUNC_INFO; - if ( !peerData.contains( "host" ) || !peerData.contains( "port" ) || !peerData.contains( "pkey" ) || !peerData.contains( "node" ) ) + if ( !peerData.contains( "host" ) || !peerData.contains( "port" ) || !peerData.contains( "pkey" ) || !peerData.contains( "node" ) || + peerData["host"].toString().isEmpty() || peerData["port"].toString().isEmpty() || peerData["pkey"].toString().isEmpty() || peerData["node"].toString().isEmpty() ) { - qDebug() << "TwitterPlugin could not find host and/or port and/or pkey for peer " << screenName; + qDebug() << "TwitterPlugin could not find host and/or port and/or pkey and/or node for peer " << screenName; return; } QString friendlyName = QString( '@' + screenName ); @@ -557,6 +638,8 @@ TwitterPlugin::directMessagePosted( const QTweetDMStatus& message ) void TwitterPlugin::directMessagePostError( QTweetNetBase::ErrorCode errorCode, const QString &message ) { + Q_UNUSED( errorCode ); + Q_UNUSED( message ); qDebug() << Q_FUNC_INFO; qDebug() << "TwitterPlugin received an error posting direct message: " << m_directMessageNew.data()->lastErrorMessage(); } @@ -568,4 +651,11 @@ TwitterPlugin::directMessageDestroyed( const QTweetDMStatus& message ) qDebug() << "TwitterPlugin destroyed message " << message.text(); } +void +TwitterPlugin::checkSettings() +{ + disconnectPlugin(); + connectPlugin( false ); +} + Q_EXPORT_PLUGIN2( sip, TwitterPlugin ) diff --git a/src/sip/twitter/twitter.h b/src/sip/twitter/twitter.h index 05a64d35f..418cc1f07 100644 --- a/src/sip/twitter/twitter.h +++ b/src/sip/twitter/twitter.h @@ -1,3 +1,21 @@ +/* === 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 TWITTER_H #define TWITTER_H @@ -41,22 +59,28 @@ public: public slots: virtual bool connectPlugin( bool startup ); - void disconnectPlugin(); + void checkSettings(); void sendMsg( const QString& to, const QString& msg ) { + Q_UNUSED( to ); + Q_UNUSED( msg ); } void broadcastMsg( const QString &msg ) { + Q_UNUSED( msg ); } void addContact( const QString &jid, const QString& msg = QString() ) { + Q_UNUSED( jid ); + Q_UNUSED( msg ); } private slots: + void configDialogAuthedSignalSlot( bool authed ); void connectAuthVerifyReply( const QTweetUser &user ); void checkTimerFired(); void connectTimerFired(); @@ -72,6 +96,9 @@ private slots: void makeConnection( const QString &screenName, const QHash< QString, QVariant > &peerdata ); private: + bool refreshTwitterAuth(); + void parseGotTomahawk( const QRegExp ®ex, const QString &screenName, const QString &text ); + QWeakPointer< TomahawkOAuthTwitter > m_twitterAuth; QWeakPointer< QTweetFriendsTimeline > m_friendsTimeline; QWeakPointer< QTweetMentions > m_mentions; diff --git a/src/sip/twitter/twitterconfigwidget.cpp b/src/sip/twitter/twitterconfigwidget.cpp index 6b193aa8a..f728b4107 100644 --- a/src/sip/twitter/twitterconfigwidget.cpp +++ b/src/sip/twitter/twitterconfigwidget.cpp @@ -1,3 +1,21 @@ +/* === 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 "twitterconfigwidget.h" #include "ui_twitterconfigwidget.h" @@ -8,34 +26,52 @@ #include "tomahawkoauthtwitter.h" #include #include +#include #include -TwitterConfigWidget::TwitterConfigWidget(SipPlugin* plugin, QWidget *parent) : - QWidget(parent), - ui(new Ui::TwitterConfigWidget), - m_plugin(plugin) +TwitterConfigWidget::TwitterConfigWidget( SipPlugin* plugin, QWidget *parent ) : + QWidget( parent ), + ui( new Ui::TwitterConfigWidget ), + m_plugin( plugin ) { - ui->setupUi(this); - - connect(ui->twitterAuthenticateButton, SIGNAL(pressed()), - this, SLOT(authenticateTwitter())); - connect(ui->twitterTweetGotTomahawkButton, SIGNAL(pressed()), - this, SLOT(startPostGotTomahawkStatus())); + ui->setupUi( this ); + connect( ui->twitterAuthenticateButton, SIGNAL( pressed() ), + this, SLOT( authDeauthTwitter() ) ); + connect( ui->twitterTweetGotTomahawkButton, SIGNAL( pressed() ), + this, SLOT( startPostGotTomahawkStatus() ) ); + connect( ui->twitterTweetComboBox, SIGNAL( currentIndexChanged( int ) ), + this, SLOT( tweetComboBoxIndexChanged( int ) ) ); + ui->twitterTweetComboBox->setCurrentIndex( 0 ); + ui->twitterUserTweetLineEdit->setReadOnly( true ); + ui->twitterUserTweetLineEdit->setEnabled( false ); + TomahawkSettings* s = TomahawkSettings::instance(); - if ( s->twitterOAuthToken().isEmpty() || s->twitterOAuthTokenSecret().isEmpty() ) + if ( s->twitterOAuthToken().isEmpty() || s->twitterOAuthTokenSecret().isEmpty() || s->twitterScreenName().isEmpty() ) { - ui->twitterStatusLabel->setText("Status: No saved credentials"); - ui->twitterAuthenticateButton->setText( "Authenticate" ); - ui->twitterInstructionsBox->setVisible( false ); + ui->twitterStatusLabel->setText( tr( "Status: No saved credentials" ) ); + ui->twitterAuthenticateButton->setText( tr( "Authenticate" ) ); + ui->twitterInstructionsInfoLabel->setVisible( false ); + ui->twitterGlobalTweetLabel->setVisible( false ); + ui->twitterTweetGotTomahawkButton->setVisible( false ); + ui->twitterUserTweetLineEdit->setVisible( false ); + ui->twitterTweetComboBox->setVisible( false ); + + emit twitterAuthed( false ); } else { - ui->twitterStatusLabel->setText("Status: Credentials saved"); - ui->twitterAuthenticateButton->setText( "Re-authenticate" ); - ui->twitterInstructionsBox->setVisible( true ); + ui->twitterStatusLabel->setText( tr( "Status: Credentials saved for %1" ).arg( s->twitterScreenName() ) ); + ui->twitterAuthenticateButton->setText( tr( "De-authenticate" ) ); + ui->twitterInstructionsInfoLabel->setVisible( true ); + ui->twitterGlobalTweetLabel->setVisible( true ); + ui->twitterTweetGotTomahawkButton->setVisible( true ); + ui->twitterUserTweetLineEdit->setVisible( true ); + ui->twitterTweetComboBox->setVisible( true ); + + emit twitterAuthed( true ); } } @@ -45,6 +81,15 @@ TwitterConfigWidget::~TwitterConfigWidget() delete ui; } +void +TwitterConfigWidget::authDeauthTwitter() +{ + if ( ui->twitterAuthenticateButton->text() == tr( "Authenticate" ) ) //FIXME: don't rely on UI strings here! + authenticateTwitter(); + else + deauthenticateTwitter(); +} + void TwitterConfigWidget::authenticateTwitter() { @@ -52,40 +97,114 @@ TwitterConfigWidget::authenticateTwitter() TomahawkOAuthTwitter *twitAuth = new TomahawkOAuthTwitter( this ); twitAuth->setNetworkAccessManager( TomahawkUtils::nam() ); twitAuth->authorizePin(); - if ( !twitAuth->oauthToken().isEmpty() && !twitAuth->oauthTokenSecret().isEmpty() ) - { - TomahawkSettings* s = TomahawkSettings::instance(); - s->setTwitterOAuthToken( twitAuth->oauthToken() ); - s->setTwitterOAuthTokenSecret( twitAuth->oauthTokenSecret() ); - ui->twitterStatusLabel->setText("Status: Credentials saved"); - ui->twitterAuthenticateButton->setText( "Re-authenticate" ); - ui->twitterInstructionsBox->setVisible( true ); - TomahawkSettings::instance()->setTwitterCachedFriendsSinceId( 0 ); - TomahawkSettings::instance()->setTwitterCachedMentionsSinceId( 0 ); - m_plugin->connectPlugin( false ); + + TomahawkSettings* s = TomahawkSettings::instance(); + s->setTwitterOAuthToken( twitAuth->oauthToken() ); + s->setTwitterOAuthTokenSecret( twitAuth->oauthTokenSecret() ); + + QTweetAccountVerifyCredentials *credVerifier = new QTweetAccountVerifyCredentials( twitAuth, this ); + connect( credVerifier, SIGNAL( parsedUser( const QTweetUser & ) ), SLOT( authenticateVerifyReply( const QTweetUser & ) ) ); + connect( credVerifier, SIGNAL( error( QTweetNetBase::ErrorCode, QString ) ), SLOT( authenticateVerifyError( QTweetNetBase::ErrorCode, QString ) ) ); + credVerifier->verify(); +} +void +TwitterConfigWidget::authenticateVerifyReply( const QTweetUser &user ) +{ + qDebug() << Q_FUNC_INFO; + if ( user.id() == 0 ) + { + QMessageBox::critical( this, tr("Tweetin' Error"), tr("The credentials could not be verified.\nYou may wish to try re-authenticating.") ); + emit twitterAuthed( false ); + return; + } + + TomahawkSettings* s = TomahawkSettings::instance(); + s->setTwitterScreenName( user.screenName() ); + s->setTwitterCachedFriendsSinceId( 0 ); + s->setTwitterCachedMentionsSinceId( 0 ); + + ui->twitterStatusLabel->setText( tr( "Status: Credentials saved for %1" ).arg( s->twitterScreenName() ) ); + ui->twitterAuthenticateButton->setText( tr( "De-authenticate" ) ); + ui->twitterInstructionsInfoLabel->setVisible( true ); + ui->twitterGlobalTweetLabel->setVisible( true ); + ui->twitterTweetGotTomahawkButton->setVisible( true ); + ui->twitterUserTweetLineEdit->setVisible( true ); + ui->twitterTweetComboBox->setVisible( true ); + + m_plugin->connectPlugin( false ); + + emit twitterAuthed( true ); +} + +void +TwitterConfigWidget::authenticateVerifyError( QTweetNetBase::ErrorCode code, const QString &errorMsg ) +{ + qDebug() << Q_FUNC_INFO; + qDebug() << "Error validating credentials, error code is " << code << ", error message is " << errorMsg; + ui->twitterStatusLabel->setText(tr("Status: Error validating credentials")); + emit twitterAuthed( false ); + return; +} + +void +TwitterConfigWidget::deauthenticateTwitter() +{ + qDebug() << Q_FUNC_INFO; + TomahawkSettings* s = TomahawkSettings::instance(); + s->setTwitterOAuthToken( QString() ); + s->setTwitterOAuthTokenSecret( QString() ); + s->setTwitterScreenName( QString() ); + + ui->twitterStatusLabel->setText(tr("Status: No saved credentials")); + ui->twitterAuthenticateButton->setText( tr( "Authenticate" ) ); + ui->twitterInstructionsInfoLabel->setVisible( false ); + ui->twitterGlobalTweetLabel->setVisible( false ); + ui->twitterTweetGotTomahawkButton->setVisible( false ); + ui->twitterUserTweetLineEdit->setVisible( false ); + ui->twitterTweetComboBox->setVisible( false ); + + emit twitterAuthed( false ); +} + +void +TwitterConfigWidget::tweetComboBoxIndexChanged( int index ) +{ + Q_UNUSED( index ); + if( ui->twitterTweetComboBox->currentText() == tr( "Global Tweet" ) ) //FIXME: use data! + { + ui->twitterUserTweetLineEdit->setReadOnly( true ); + ui->twitterUserTweetLineEdit->setEnabled( false ); } else { - TomahawkSettings* s = TomahawkSettings::instance(); - s->setTwitterOAuthToken( QString() ); - s->setTwitterOAuthTokenSecret( QString() ); - ui->twitterStatusLabel->setText("Status: No saved credentials"); - ui->twitterAuthenticateButton->setText( "Authenticate" ); - ui->twitterInstructionsBox->setVisible( false ); - QMessageBox::critical( 0, QString("Tweetin' Error"), QString("There was an error validating your authentication") ); + ui->twitterUserTweetLineEdit->setReadOnly( false ); + ui->twitterUserTweetLineEdit->setEnabled( true ); } + + if( ui->twitterTweetComboBox->currentText() == tr( "Direct Message" ) ) //FIXME: use data! + ui->twitterTweetGotTomahawkButton->setText( tr( "Send Message!" ) ); + else + ui->twitterTweetGotTomahawkButton->setText( tr( "Tweet!" ) ); } - void TwitterConfigWidget::startPostGotTomahawkStatus() { + m_postGTtype = ui->twitterTweetComboBox->currentText(); + + if ( m_postGTtype != "Global Tweet" && ( ui->twitterUserTweetLineEdit->text().isEmpty() || ui->twitterUserTweetLineEdit->text() == "@" ) ) + { + QMessageBox::critical( this, tr("Tweetin' Error"), tr("You must enter a user name for this type of tweet.") ); + return; + } + qDebug() << "Posting Got Tomahawk status"; TomahawkSettings* s = TomahawkSettings::instance(); - if ( s->twitterOAuthToken().isEmpty() || s->twitterOAuthTokenSecret().isEmpty() ) + if ( s->twitterOAuthToken().isEmpty() || s->twitterOAuthTokenSecret().isEmpty() || s->twitterScreenName().isEmpty() ) { - QMessageBox::critical( 0, QString("Tweetin' Error"), QString("Your saved credentials could not be loaded.\nYou may wish to try re-authenticating.") ); + QMessageBox::critical( this, tr("Tweetin' Error"), tr("Your saved credentials could not be loaded.\nYou may wish to try re-authenticating.") ); + emit twitterAuthed( false ); return; } TomahawkOAuthTwitter *twitAuth = new TomahawkOAuthTwitter( this ); @@ -97,13 +216,13 @@ TwitterConfigWidget::startPostGotTomahawkStatus() credVerifier->verify(); } - void TwitterConfigWidget::postGotTomahawkStatusAuthVerifyReply( const QTweetUser &user ) { if ( user.id() == 0 ) { - QMessageBox::critical( 0, QString("Tweetin' Error"), QString("Your saved credentials could not be verified.\nYou may wish to try re-authenticating.") ); + QMessageBox::critical( this, tr("Tweetin' Error"), tr("Your saved credentials could not be verified.\nYou may wish to try re-authenticating.") ); + emit twitterAuthed( false ); return; } TomahawkSettings* s = TomahawkSettings::instance(); @@ -112,28 +231,58 @@ TwitterConfigWidget::postGotTomahawkStatusAuthVerifyReply( const QTweetUser &use twitAuth->setNetworkAccessManager( TomahawkUtils::nam() ); twitAuth->setOAuthToken( s->twitterOAuthToken().toLatin1() ); twitAuth->setOAuthTokenSecret( s->twitterOAuthTokenSecret().toLatin1() ); - QTweetStatusUpdate *statUpdate = new QTweetStatusUpdate( twitAuth, this ); - connect( statUpdate, SIGNAL( postedStatus(const QTweetStatus &) ), SLOT( postGotTomahawkStatusUpdateReply(const QTweetStatus &) ) ); - connect( statUpdate, SIGNAL( error(QTweetNetBase::ErrorCode, const QString&) ), SLOT( postGotTomahawkStatusUpdateError(QTweetNetBase::ErrorCode, const QString &) ) ); - QString uuid = QUuid::createUuid(); - statUpdate->post( QString( "Got Tomahawk? {" ) + Database::instance()->dbid() + QString( "} (" ) + uuid.mid( 1, 8 ) + QString( ")" ) ); + if ( m_postGTtype != "Direct Message" ) + { + QTweetStatusUpdate *statUpdate = new QTweetStatusUpdate( twitAuth, this ); + connect( statUpdate, SIGNAL( postedStatus(const QTweetStatus &) ), SLOT( postGotTomahawkStatusUpdateReply(const QTweetStatus &) ) ); + connect( statUpdate, SIGNAL( error(QTweetNetBase::ErrorCode, const QString&) ), SLOT( postGotTomahawkStatusUpdateError(QTweetNetBase::ErrorCode, const QString &) ) ); + QString uuid = QUuid::createUuid(); + QString message = QString( "Got Tomahawk? {" ) + Database::instance()->dbid() + QString( "} (" ) + uuid.mid( 1, 8 ) + QString( ")" ) + QString( " http://gettomahawk.com" ); + if ( m_postGTtype == "@Mention" ) + { + QString user = ui->twitterUserTweetLineEdit->text(); + if ( user.startsWith( "@" ) ) + user.remove( 0, 1 ); + message = QString( "@" ) + user + QString( " " ) + message; + } + statUpdate->post( message ); + } + else + { + QTweetDirectMessageNew *statUpdate = new QTweetDirectMessageNew( twitAuth, this ); + connect( statUpdate, SIGNAL( parsedDirectMessage(const QTweetDMStatus &)), SLOT( postGotTomahawkDirectMessageReply(const QTweetDMStatus &) ) ); + connect( statUpdate, SIGNAL( error(QTweetNetBase::ErrorCode, const QString&) ), SLOT( postGotTomahawkStatusUpdateError(QTweetNetBase::ErrorCode, const QString &) ) ); + QString uuid = QUuid::createUuid(); + QString message = QString( "Got Tomahawk? {" ) + Database::instance()->dbid() + QString( "} (" ) + uuid.mid( 1, 8 ) + QString( ")" ) + QString( " http://gettomahawk.com" ); + QString user = ui->twitterUserTweetLineEdit->text(); + if ( user.startsWith( "@" ) ) + user.remove( 0, 1 ); + statUpdate->post( user, message ); + } } - void TwitterConfigWidget::postGotTomahawkStatusUpdateReply( const QTweetStatus& status ) { if ( status.id() == 0 ) - QMessageBox::critical( 0, QString("Tweetin' Error"), QString("There was an error posting your status -- sorry!") ); + QMessageBox::critical( this, tr("Tweetin' Error"), tr("There was an error posting your status -- sorry!") ); else - QMessageBox::information( 0, QString("Tweeted!"), QString("Your tweet has been posted!") ); + QMessageBox::information( this, tr("Tweeted!"), tr("Your tweet has been posted!") ); } +void +TwitterConfigWidget::postGotTomahawkDirectMessageReply( const QTweetDMStatus& status ) +{ + if ( status.id() == 0 ) + QMessageBox::critical( this, tr("Tweetin' Error"), tr("There was an error posting your direct message -- sorry!") ); + else + QMessageBox::information( this, tr("Tweeted!"), tr("Your message has been posted!") ); +} void TwitterConfigWidget::postGotTomahawkStatusUpdateError( QTweetNetBase::ErrorCode code, const QString& errorMsg ) { qDebug() << Q_FUNC_INFO; qDebug() << "Error posting Got Tomahawk message, error code is " << code << ", error message is " << errorMsg; - QMessageBox::critical( 0, QString("Tweetin' Error"), QString("There was an error posting your status -- sorry!") ); + QMessageBox::critical( this, tr("Tweetin' Error"), tr("There was an error posting your status -- sorry!") ); } diff --git a/src/sip/twitter/twitterconfigwidget.h b/src/sip/twitter/twitterconfigwidget.h index 355c45777..85fabce90 100644 --- a/src/sip/twitter/twitterconfigwidget.h +++ b/src/sip/twitter/twitterconfigwidget.h @@ -1,9 +1,28 @@ +/* === 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 TWITTERCONFIGWIDGET_H #define TWITTERCONFIGWIDGET_H #include "sip/SipPlugin.h" #include +#include #include #include @@ -19,19 +38,30 @@ class TwitterConfigWidget : public QWidget Q_OBJECT public: - explicit TwitterConfigWidget(SipPlugin* plugin = 0, QWidget *parent = 0); + explicit TwitterConfigWidget( SipPlugin* plugin = 0, QWidget *parent = 0 ); ~TwitterConfigWidget(); +signals: + void twitterAuthed( bool authed ); + private slots: - void authenticateTwitter(); + void authDeauthTwitter(); void startPostGotTomahawkStatus(); + void authenticateVerifyReply( const QTweetUser &user ); + void authenticateVerifyError( QTweetNetBase::ErrorCode code, const QString &errorMsg ); void postGotTomahawkStatusAuthVerifyReply( const QTweetUser &user ); void postGotTomahawkStatusUpdateReply( const QTweetStatus &status ); + void postGotTomahawkDirectMessageReply( const QTweetDMStatus &status ); void postGotTomahawkStatusUpdateError( QTweetNetBase::ErrorCode, const QString &errorMsg ); + void tweetComboBoxIndexChanged( int index ); private: + void authenticateTwitter(); + void deauthenticateTwitter(); + Ui::TwitterConfigWidget *ui; SipPlugin *m_plugin; + QString m_postGTtype; }; #endif // TWITTERCONFIGWIDGET_H diff --git a/src/sip/twitter/twitterconfigwidget.ui b/src/sip/twitter/twitterconfigwidget.ui index ad35c9d55..1bb728fd0 100644 --- a/src/sip/twitter/twitterconfigwidget.ui +++ b/src/sip/twitter/twitterconfigwidget.ui @@ -1,106 +1,188 @@ + - - - TwitterConfigWidget - - Twitter - - + + + 0 + 0 + 795 + 509 + + + + + + + + + + 0 + 0 + + + + Authenticating with Twitter allows you to discover and play music from your Twitter friends running Tomahawk. + + + true + + + + + + + This feature works best when you have set a static host name in the "Network" settings tab under Advanced Settings, but may work even if you do not. Tomahawk uses Direct Messages and this will only work when both Twitter users have followed each other. + + + true + + + + + - - - - - - 0 - 0 - - - - Authenticating with Twitter allows you to discover and play music from your Twitter friends running Tomahawk. - -This feature works best when you have set a static host name in the "Network" settings tab under Advanced Settings, but may work even if you do not. Please note: this discovery uses Direct Messages and will only work when both Twitter users have followed each other. Tomahawk will attempt to "clean up" after itself to keep your Direct Message inbox tidy, but may miss some. - -When you press the button your web browser will launch and take you to Twitter.com to authenticate. You must copy and paste the PIN number into the dialog box that appears. - - - true - - - - - - - - - Status: No saved credentials - - - Qt::AutoText - - - - - - - Authenticate with Twitter - - - - - - - - - - - Instructions + + + Status: No saved credentials + + + Qt::AutoText - - - - - - - How it works is simple: just press the button below to tweet "Got Tomahawk?" and some necessary information. Then be (very) patient. Twitter is an asynchronous protocol so it can take a bit! - -If connections to peers seem to have been lost, just press the button again to re-post a tweet for resynchronization. - - - - true - - - - - - - Press here to have Tomahawk post a tweet - - - - - - - - - Qt::Vertical + + + Authenticate with Twitter - - - 20 - 40 - - - + - - + + + + + + + Here's how it works: just press one of the buttons below to tweet "Got Tomahawk?" and some necessary information. Then be (very) patient. Twitter is an asynchronous protocol so it can take a bit! + +If connections to peers seem to have been lost, just press the appropriate button again to re-post a tweet for resynchronization. + + + true + + + + + + + Qt::Vertical + + + QSizePolicy::Fixed + + + + 20 + 10 + + + + + + + + + + + 0 + 0 + + + + Select the kind of tweet you would like, then press the button to post it: + + + + + + + + + + 0 + 0 + + + + + Global Tweet + + + + + @Mention + + + + + Direct Message + + + + + + + + + 0 + 0 + + + + + 250 + 0 + + + + e.g. @tomahawkplayer + + + + + + + + 0 + 0 + + + + Tweet! + + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + - \ No newline at end of file + diff --git a/src/sip/zeroconf/CMakeLists.txt b/src/sip/zeroconf/CMakeLists.txt index e203901cf..199704dff 100644 --- a/src/sip/zeroconf/CMakeLists.txt +++ b/src/sip/zeroconf/CMakeLists.txt @@ -20,7 +20,7 @@ include_directories( . ${CMAKE_CURRENT_BINARY_DIR} .. ) qt4_wrap_cpp( zeroconfMoc ${zeroconfHeaders} ) -add_library( sip_zeroconf SHARED ${zeroconfSources} ${zeroconfMoc} ) +add_library( tomahawk_sipzeroconf SHARED ${zeroconfSources} ${zeroconfMoc} ) IF( WIN32 ) SET( OS_SPECIFIC_LINK_LIBRARIES @@ -31,7 +31,7 @@ SET( OS_SPECIFIC_LINK_LIBRARIES ) ENDIF( WIN32 ) -target_link_libraries( sip_zeroconf +target_link_libraries( tomahawk_sipzeroconf ${QT_LIBRARIES} ${OS_SPECIFIC_LINK_LIBRARIES} tomahawklib @@ -41,4 +41,4 @@ IF( APPLE ) # SET( CMAKE_SHARED_LINKER_FLAGS ${CMAKE_SHARED_LINKER_FLAGS} "-undefined dynamic_lookup" ) ENDIF( APPLE ) -install( TARGETS sip_zeroconf DESTINATION lib ) +install( TARGETS tomahawk_sipzeroconf DESTINATION lib${LIB_SUFFIX} ) diff --git a/src/sip/zeroconf/tomahawkzeroconf.h b/src/sip/zeroconf/tomahawkzeroconf.h index d80f1103a..44dd84a87 100644 --- a/src/sip/zeroconf/tomahawkzeroconf.h +++ b/src/sip/zeroconf/tomahawkzeroconf.h @@ -1,3 +1,21 @@ +/* === 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 TOMAHAWKZCONF #define TOMAHAWKZCONF @@ -7,6 +25,7 @@ #include #include #include +#include #include #include @@ -62,6 +81,7 @@ public: : QObject( parent ), m_sock( this ), m_port( port ) { qDebug() << Q_FUNC_INFO; + m_sock.setProxy( QNetworkProxy::NoProxy ); m_sock.bind( ZCONF_PORT, QUdpSocket::ShareAddress ); connect( &m_sock, SIGNAL( readyRead() ), this, SLOT( readPacket() ) ); } diff --git a/src/sip/zeroconf/zeroconf.cpp b/src/sip/zeroconf/zeroconf.cpp index 3a567ea48..7e6bbf955 100644 --- a/src/sip/zeroconf/zeroconf.cpp +++ b/src/sip/zeroconf/zeroconf.cpp @@ -1,3 +1,21 @@ +/* === 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 "zeroconf.h" #include @@ -31,15 +49,12 @@ ZeroconfPlugin::connectPlugin( bool /*startup*/ ) m_zeroconf->advertise(); m_isOnline = true; - foreach( QStringList *currNode, m_cachedNodes ) + foreach( const QStringList& nodeSet, m_cachedNodes ) { - QStringList nodeSet = *currNode; if ( !Servent::instance()->connectedToSession( nodeSet[3] ) ) Servent::instance()->connectToPeer( nodeSet[0], nodeSet[1].toInt(), "whitelist", nodeSet[2], nodeSet[3] ); - - delete currNode; } - + m_cachedNodes.clear(); return true; } @@ -63,9 +78,9 @@ ZeroconfPlugin::lanHostFound( const QString& host, int port, const QString& name if ( !m_isOnline ) { qDebug() << "Not online, so not connecting."; - QStringList *nodeSet = new QStringList(); - *nodeSet << host << QString::number( port ) << name << nodeid; - m_cachedNodes.insert( nodeSet ); + QStringList nodeSet; + nodeSet << host << QString::number( port ) << name << nodeid; + m_cachedNodes.append( nodeSet ); return; } diff --git a/src/sip/zeroconf/zeroconf.h b/src/sip/zeroconf/zeroconf.h index aa4926908..059f4727a 100644 --- a/src/sip/zeroconf/zeroconf.h +++ b/src/sip/zeroconf/zeroconf.h @@ -1,3 +1,21 @@ +/* === 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 ZEROCONF_H #define ZEROCONF_H @@ -34,19 +52,24 @@ public: public slots: virtual bool connectPlugin( bool startup ); - void disconnectPlugin(); + void checkSettings() {} void sendMsg( const QString& to, const QString& msg ) { + Q_UNUSED( to ); + Q_UNUSED( msg ); } void broadcastMsg( const QString &msg ) { + Q_UNUSED( msg ); } void addContact( const QString &jid, const QString& msg = QString() ) { + Q_UNUSED( jid ); + Q_UNUSED( msg ); } private slots: @@ -55,7 +78,7 @@ private slots: private: TomahawkZeroconf* m_zeroconf; bool m_isOnline; - QSet< QStringList* > m_cachedNodes; + QVector m_cachedNodes; }; #endif diff --git a/src/sourcetree/sourcesmodel.cpp b/src/sourcetree/sourcesmodel.cpp index e2a95de0c..76f1d233d 100644 --- a/src/sourcetree/sourcesmodel.cpp +++ b/src/sourcetree/sourcesmodel.cpp @@ -1,3 +1,21 @@ +/* === 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 "sourcesmodel.h" #include @@ -334,6 +352,7 @@ SourcesModel::collectionToIndex( const Tomahawk::collection_ptr& collection ) bool SourcesModel::setData( const QModelIndex& index, const QVariant& value, int role ) { + Q_UNUSED( role ); qDebug() << Q_FUNC_INFO; if ( !index.isValid() ) diff --git a/src/sourcetree/sourcesmodel.h b/src/sourcetree/sourcesmodel.h index e258e73ff..a9ad5d74b 100644 --- a/src/sourcetree/sourcesmodel.h +++ b/src/sourcetree/sourcesmodel.h @@ -1,3 +1,21 @@ +/* === 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 SOURCESMODEL_H #define SOURCESMODEL_H diff --git a/src/sourcetree/sourcesproxymodel.cpp b/src/sourcetree/sourcesproxymodel.cpp index e4bcea7c3..d93cf098e 100644 --- a/src/sourcetree/sourcesproxymodel.cpp +++ b/src/sourcetree/sourcesproxymodel.cpp @@ -1,3 +1,21 @@ +/* === 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 "sourcesproxymodel.h" #include diff --git a/src/sourcetree/sourcesproxymodel.h b/src/sourcetree/sourcesproxymodel.h index 5961b4a37..add57b257 100644 --- a/src/sourcetree/sourcesproxymodel.h +++ b/src/sourcetree/sourcesproxymodel.h @@ -1,3 +1,21 @@ +/* === 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 SOURCESPROXYMODEL_H #define SOURCESPROXYMODEL_H diff --git a/src/sourcetree/sourcetreeitem.cpp b/src/sourcetree/sourcetreeitem.cpp index 4fd22a4d3..c9ec16101 100644 --- a/src/sourcetree/sourcetreeitem.cpp +++ b/src/sourcetree/sourcetreeitem.cpp @@ -1,3 +1,21 @@ +/* === 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 "sourcetreeitem.h" #include @@ -25,7 +43,18 @@ SourceTreeItem::SourceTreeItem( const source_ptr& source, QObject* parent ) : QObject( parent ) , m_source( source ) { - QStandardItem* item = new QStandardItem( source.isNull() ? "Super Collection" : source->friendlyName() ); + QString name; + if( source.isNull() ) + name = tr( "Super Collection" ); + else + { + if( TomahawkApp::instance()->scrubFriendlyName() && source->friendlyName().contains( '@' ) ) + name = source->friendlyName().left( source->friendlyName().indexOf( '@' ) ); + else + name = source->friendlyName(); + } + + QStandardItem* item = new QStandardItem( name ); item->setIcon( QIcon( RESPATH "images/user-avatar.png" ) ); item->setEditable( false ); item->setData( SourcesModel::CollectionSource, Type ); @@ -121,8 +150,9 @@ SourceTreeItem::onPlaylistsDeleted( const QList& playlists ) if ( type == SourcesModel::PlaylistSource && ptr == qlonglong( pl->data() ) ) { - m_playlists.removeAll( p ); item->removeRow( i ); + m_playlists.removeAll( p ); + break; } } } @@ -147,6 +177,7 @@ SourceTreeItem::onPlaylistLoaded( Tomahawk::PlaylistRevision revision ) { pi->setEnabled( true ); m_current_revisions.insert( pl->data()->guid(), revision.revisionguid ); + break; } } } @@ -170,7 +201,10 @@ SourceTreeItem::onPlaylistChanged() playlist_ptr* pl = reinterpret_cast(piptr); if ( ptr == qlonglong( pl->data() ) ) + { pi->setText( pl->data()->title() ); + break; + } } if ( type == SourcesModel::DynamicPlaylistSource ) { @@ -178,7 +212,10 @@ SourceTreeItem::onPlaylistChanged() dynplaylist_ptr* pl = reinterpret_cast(piptr); if ( ptr == qlonglong( pl->data() ) ) + { pi->setText( pl->data()->title() ); + break; + } } } } @@ -226,8 +263,9 @@ SourceTreeItem::onDynamicPlaylistsDeleted( const QList< dynplaylist_ptr >& playl //qDebug() << "Deleting dynamic playlist:" << pl->isNull(); if ( type == SourcesModel::DynamicPlaylistSource && ptr == qlonglong( pl->data() ) ) { - m_dynplaylists.removeAll( p ); item->removeRow( i ); + m_dynplaylists.removeAll( p ); + break; } } } @@ -252,6 +290,7 @@ SourceTreeItem::onDynamicPlaylistLoaded( DynamicPlaylistRevision revision ) { pi->setEnabled( true ); m_current_dynamic_revisions.insert( pl->data()->guid(), revision.revisionguid ); + break; } } } diff --git a/src/sourcetree/sourcetreeitem.h b/src/sourcetree/sourcetreeitem.h index caedb01a3..36f07d61b 100644 --- a/src/sourcetree/sourcetreeitem.h +++ b/src/sourcetree/sourcetreeitem.h @@ -1,3 +1,21 @@ +/* === 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 SOURCETREEITEM_H #define SOURCETREEITEM_H diff --git a/src/sourcetree/sourcetreeitemwidget.cpp b/src/sourcetree/sourcetreeitemwidget.cpp index fe37abbf8..9f8bc64a2 100644 --- a/src/sourcetree/sourcetreeitemwidget.cpp +++ b/src/sourcetree/sourcetreeitemwidget.cpp @@ -1,3 +1,21 @@ +/* === 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 "sourcetreeitemwidget.h" #include "ui_sourcetreeitemwidget.h" diff --git a/src/sourcetree/sourcetreeitemwidget.h b/src/sourcetree/sourcetreeitemwidget.h index 6414521b5..515b2de27 100644 --- a/src/sourcetree/sourcetreeitemwidget.h +++ b/src/sourcetree/sourcetreeitemwidget.h @@ -1,3 +1,21 @@ +/* === 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 SOURCETREEITEMWIDGET_H #define SOURCETREEITEMWIDGET_H diff --git a/src/sourcetree/sourcetreeview.cpp b/src/sourcetree/sourcetreeview.cpp index 173a6a957..4935b8d0e 100644 --- a/src/sourcetree/sourcetreeview.cpp +++ b/src/sourcetree/sourcetreeview.cpp @@ -1,7 +1,24 @@ +/* === 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 "sourcetreeview.h" #include "playlist.h" -#include "playlist/collectionmodel.h" #include "playlist/playlistmanager.h" #include "sourcetreeitem.h" #include "sourcesmodel.h" @@ -43,7 +60,6 @@ private: SourceTreeView::SourceTreeView( QWidget* parent ) : QTreeView( parent ) - , m_collectionModel( new CollectionModel( this ) ) , m_dragging( false ) { setFrameShape( QFrame::NoFrame ); @@ -114,10 +130,10 @@ SourceTreeView::setupMenus() if ( type == SourcesModel::PlaylistSource || type == SourcesModel::DynamicPlaylistSource ) { playlist_ptr playlist = SourcesModel::indexToDynamicPlaylist( m_contextMenuIndex ); - if( playlist.isNull() ) + if ( playlist.isNull() ) { playlist = SourcesModel::indexToPlaylist( m_contextMenuIndex ); - } + } if ( !playlist.isNull() ) { readonly = !playlist->author()->isLocal(); @@ -150,6 +166,7 @@ SourceTreeView::hideOfflineSources() void SourceTreeView::onSourceOffline( Tomahawk::source_ptr src ) { + Q_UNUSED( src ); qDebug() << Q_FUNC_INFO; } @@ -245,7 +262,7 @@ SourceTreeView::onItemActivated( const QModelIndex& index ) if ( !playlist.isNull() ) { qDebug() << "Dynamic Playlist activated:" << playlist->title(); - + PlaylistManager::instance()->show( playlist ); } } @@ -280,13 +297,16 @@ SourceTreeView::deletePlaylist() playlist_ptr playlist = SourcesModel::indexToPlaylist( idx ); if ( !playlist.isNull() ) { - qDebug() << "Playlist about to be deleted:" << playlist->title(); Playlist::remove( playlist ); } - } else if( type == SourcesModel::DynamicPlaylistSource ) { - dynplaylist_ptr playlist = SourcesModel::indexToDynamicPlaylist( idx ); + } + else if ( type == SourcesModel::DynamicPlaylistSource ) + { + dynplaylist_ptr playlist = SourcesModel::indexToDynamicPlaylist( idx ); if( !playlist.isNull() ) + { DynamicPlaylist::remove( playlist ); + } } } @@ -479,9 +499,14 @@ void SourceDelegate::paint( QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index ) const { QStyleOptionViewItem o = option; - QStyleOptionViewItem o2 = option; - o2.rect.setX( 0 ); - o2.state = option.state; + +#ifdef Q_WS_MAC + QFont savedFont = painter->font(); + QFont smaller = savedFont; + smaller.setPointSize( smaller.pointSize() - 2 ); + painter->setFont( smaller ); + o.font = smaller; +#endif if ( ( option.state & QStyle::State_Enabled ) == QStyle::State_Enabled ) { @@ -492,13 +517,13 @@ SourceDelegate::paint( QPainter* painter, const QStyleOptionViewItem& option, co o.palette.setColor( QPalette::Text, o.palette.color( QPalette::HighlightedText ) ); } } - + QStyleOptionViewItemV4 o3 = option; if ( index.data( SourceTreeItem::Type ) != SourcesModel::CollectionSource ) o3.rect.setX( 0 ); - + QApplication::style()->drawControl( QStyle::CE_ItemViewItem, &o3, painter ); - + if ( index.data( SourceTreeItem::Type ) == SourcesModel::CollectionSource ) { painter->save(); @@ -506,7 +531,7 @@ SourceDelegate::paint( QPainter* painter, const QStyleOptionViewItem& option, co QFont normal = painter->font(); QFont bold = painter->font(); bold.setBold( true ); - + SourceTreeItem* sti = SourcesModel::indexToTreeItem( index ); bool status = !( !sti || sti->source().isNull() || !sti->source()->isOnline() ); QString tracks; @@ -519,13 +544,13 @@ SourceDelegate::paint( QPainter* painter, const QStyleOptionViewItem& option, co } QRect iconRect = option.rect.adjusted( 4, 6, -option.rect.width() + option.rect.height() - 12 + 4, -6 ); - painter->drawPixmap( iconRect, QPixmap( RESPATH "images/user-avatar.png" ) ); + painter->drawPixmap( iconRect, QPixmap( RESPATH "images/user-avatar.png" ).scaledToHeight( iconRect.height(), Qt::SmoothTransformation ) ); if ( ( option.state & QStyle::State_Selected ) == QStyle::State_Selected ) { painter->setPen( o.palette.color( QPalette::HighlightedText ) ); } - + QRect textRect = option.rect.adjusted( iconRect.width() + 8, 6, -figWidth - 24, 0 ); if ( status || sti->source().isNull() ) painter->setFont( bold ); @@ -585,4 +610,8 @@ SourceDelegate::paint( QPainter* painter, const QStyleOptionViewItem& option, co { QStyledItemDelegate::paint( painter, o, index ); } + +#ifdef Q_WS_MAC + painter->setFont( savedFont ); +#endif } diff --git a/src/sourcetree/sourcetreeview.h b/src/sourcetree/sourcetreeview.h index 077fb2937..f2d8581e3 100644 --- a/src/sourcetree/sourcetreeview.h +++ b/src/sourcetree/sourcetreeview.h @@ -1,3 +1,21 @@ +/* === 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 SOURCETREEVIEW_H #define SOURCETREEVIEW_H @@ -21,7 +39,7 @@ public: public slots: void showOfflineSources(); void hideOfflineSources(); - + signals: void onOnline( const QModelIndex& index ); void onOffline( const QModelIndex& index ); @@ -32,14 +50,14 @@ private slots: void onCollectionActivated( const Tomahawk::collection_ptr& collection ); void onSuperCollectionActivated(); void onTempPageActivated(); - + void onItemActivated( const QModelIndex& index ); void onSelectionChanged(); void loadPlaylist(); void deletePlaylist(); void renamePlaylist(); - + void onCustomContextMenu( const QPoint& pos ); void onSourceOffline( Tomahawk::source_ptr ); @@ -50,14 +68,13 @@ protected: virtual void paintEvent( QPaintEvent* event ); virtual void dragEnterEvent( QDragEnterEvent* event ); - virtual void dragLeaveEvent( QDragLeaveEvent* event ) { m_dragging = false; setDirtyRegion( m_dropRect ); } + virtual void dragLeaveEvent( QDragLeaveEvent* event ) { Q_UNUSED( event ); m_dragging = false; setDirtyRegion( m_dropRect ); } virtual void dragMoveEvent( QDragMoveEvent* event ); virtual void dropEvent( QDropEvent* event ); private: void setupMenus(); - CollectionModel* m_collectionModel; SourcesModel* m_model; SourcesProxyModel* m_proxyModel; QModelIndex m_contextMenuIndex; diff --git a/src/tomahawkapp.cpp b/src/tomahawkapp.cpp index 4aaafc324..ea5311844 100644 --- a/src/tomahawkapp.cpp +++ b/src/tomahawkapp.cpp @@ -1,3 +1,21 @@ +/* === 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 "tomahawk/tomahawkapp.h" #include "config.h" @@ -14,7 +32,7 @@ #include "artist.h" #include "album.h" #include "collection.h" -#include "tomahawk/infosystem.h" +#include "infosystem/infosystem.h" #include "database/database.h" #include "database/databasecollection.h" #include "database/databasecommand_collectionstats.h" @@ -34,6 +52,9 @@ #include "audio/audioengine.h" #include "utils/xspfloader.h" +#include +#include "config.h" + #ifndef TOMAHAWK_HEADLESS #include "tomahawkwindow.h" #include "settingsdialog.h" @@ -60,6 +81,9 @@ ofstream logfile; void TomahawkLogHandler( QtMsgType type, const char *msg ) { + static QMutex s_mutex; + + QMutexLocker locker( &s_mutex ); switch( type ) { case QtDebugMsg: @@ -121,46 +145,44 @@ using namespace Tomahawk; TomahawkApp::TomahawkApp( int& argc, char *argv[] ) : TOMAHAWK_APPLICATION( argc, argv ) , m_database( 0 ) + , m_scanManager( 0 ) , m_audioEngine( 0 ) , m_sipHandler( 0 ) , m_servent( 0 ) , m_shortcutHandler( 0 ) + , m_scrubFriendlyName( false ) , m_mainwindow( 0 ) - , m_infoSystem( 0 ) { - qsrand( QTime( 0, 0, 0 ).secsTo( QTime::currentTime() ) ); - - // send the first arg to an already running instance, but don't open twice no matter what - if( ( argc > 1 && sendMessage( argv[ 1 ] ) ) || sendMessage( "" ) ) { - qDebug() << "Sent message, already exists"; - throw runtime_error( "Already Running" ); - } - - connect( this, SIGNAL( messageReceived( QString ) ), this, SLOT( messageReceived( QString ) ) ); - -#ifdef TOMAHAWK_HEADLESS - m_headless = true; -#else - m_mainwindow = 0; - m_headless = arguments().contains( "--headless" ); - setWindowIcon( QIcon( RESPATH "icons/tomahawk-icon-128x128.png" ) ); -#endif - qDebug() << "TomahawkApp thread:" << this->thread(); setOrganizationName( QLatin1String( ORGANIZATION_NAME ) ); setOrganizationDomain( QLatin1String( ORGANIZATION_DOMAIN ) ); setApplicationName( QLatin1String( APPLICATION_NAME ) ); setApplicationVersion( QLatin1String( VERSION ) ); - registerMetaTypes(); setupLogfile(); - +} + +void +TomahawkApp::init() +{ + qsrand( QTime( 0, 0, 0 ).secsTo( QTime::currentTime() ) ); + + #ifdef TOMAHAWK_HEADLESS + m_headless = true; + #else + m_mainwindow = 0; + m_headless = arguments().contains( "--headless" ); + setWindowIcon( QIcon( RESPATH "icons/tomahawk-icon-128x128.png" ) ); + #endif + + registerMetaTypes(); + Echonest::Config::instance()->setAPIKey( "JRIHWEP6GPOER2QQ6" ); - + new TomahawkSettings( this ); m_audioEngine = new AudioEngine; - new ScanManager( this ); + m_scanManager = new ScanManager( this ); new Pipeline( this ); - + m_servent = new Servent( this ); connect( m_servent, SIGNAL( ready() ), SLOT( setupSIP() ) ); @@ -169,18 +191,15 @@ TomahawkApp::TomahawkApp( int& argc, char *argv[] ) qDebug() << "Init Echonest Factory."; GeneratorFactory::registerFactory( "echonest", new EchonestFactory ); - + + m_scrubFriendlyName = arguments().contains( "--demo" ); // Register shortcut handler for this platform -#ifdef Q_WS_MAC + #ifdef Q_WS_MAC m_shortcutHandler = new MacShortcutHandler( this ); Tomahawk::setShortcutHandler( static_cast( m_shortcutHandler) ); Tomahawk::setApplicationHandler( this ); - - QFont f( QApplication::font() ); - f.setPointSize( f.pointSize() - 2 ); - QApplication::setFont( f ); -#endif + #endif // Connect up shortcuts if ( m_shortcutHandler ) @@ -195,29 +214,22 @@ TomahawkApp::TomahawkApp( int& argc, char *argv[] ) connect( m_shortcutHandler, SIGNAL( mute() ), m_audioEngine, SLOT( mute() ) ); } + qDebug() << "Init InfoSystem."; + m_infoSystem = new Tomahawk::InfoSystem::InfoSystem( this ); + #ifdef LIBLASTFM_FOUND qDebug() << "Init Scrobbler."; m_scrobbler = new Scrobbler( this ); qDebug() << "Setting NAM."; - TomahawkUtils::setNam( new lastfm::NetworkAccessManager( this ) ); + TomahawkUtils::setNam( lastfm::nam() ); - connect( m_audioEngine, SIGNAL( started( const Tomahawk::result_ptr& ) ), - m_scrobbler, SLOT( trackStarted( const Tomahawk::result_ptr& ) ), Qt::QueuedConnection ); - - connect( m_audioEngine, SIGNAL( paused() ), - m_scrobbler, SLOT( trackPaused() ), Qt::QueuedConnection ); - - connect( m_audioEngine, SIGNAL( resumed() ), - m_scrobbler, SLOT( trackResumed() ), Qt::QueuedConnection ); - - connect( m_audioEngine, SIGNAL( stopped() ), - m_scrobbler, SLOT( trackStopped() ), Qt::QueuedConnection ); -#else + #else qDebug() << "Setting NAM."; TomahawkUtils::setNam( new QNetworkAccessManager ); -#endif + #endif // Set up proxy + //FIXME: This overrides the lastfm proxy above? if( TomahawkSettings::instance()->proxyType() != QNetworkProxy::NoProxy && !TomahawkSettings::instance()->proxyHost().isEmpty() ) { @@ -226,16 +238,19 @@ TomahawkApp::TomahawkApp( int& argc, char *argv[] ) qDebug() << "Proxy type =" << QString::number( static_cast(TomahawkUtils::proxy()->type()) ); qDebug() << "Proxy host =" << TomahawkUtils::proxy()->hostName(); TomahawkUtils::nam()->setProxy( *TomahawkUtils::proxy() ); + lastfm::nam()->setProxy( *TomahawkUtils::proxy() ); } else TomahawkUtils::setProxy( new QNetworkProxy( QNetworkProxy::NoProxy ) ); + + Echonest::Config::instance()->setAPIKey( "JRIHWEP6GPOER2QQ6" ); + Echonest::Config::instance()->setNetworkAccessManager( TomahawkUtils::nam() ); + QNetworkProxy::setApplicationProxy( *TomahawkUtils::proxy() ); qDebug() << "Init SIP system."; m_sipHandler = new SipHandler( this ); - qDebug() << "Init InfoSystem."; - m_infoSystem = new Tomahawk::InfoSystem::InfoSystem( this ); #ifndef TOMAHAWK_HEADLESS if ( !m_headless ) @@ -245,7 +260,7 @@ TomahawkApp::TomahawkApp( int& argc, char *argv[] ) m_mainwindow->setWindowTitle( "Tomahawk" ); m_mainwindow->show(); } -#endif + #endif qDebug() << "Init Local Collection."; initLocalCollection(); @@ -253,7 +268,6 @@ TomahawkApp::TomahawkApp( int& argc, char *argv[] ) setupPipeline(); qDebug() << "Init Servent."; startServent(); - //loadPlugins(); if( arguments().contains( "--http" ) || TomahawkSettings::instance()->value( "network/http", true ).toBool() ) { @@ -262,7 +276,7 @@ TomahawkApp::TomahawkApp( int& argc, char *argv[] ) } #ifndef TOMAHAWK_HEADLESS - if ( !TomahawkSettings::instance()->hasScannerPath() ) + if ( !TomahawkSettings::instance()->hasScannerPaths() ) { m_mainwindow->showSettingsDialog(); } @@ -274,14 +288,21 @@ TomahawkApp::~TomahawkApp() { qDebug() << Q_FUNC_INFO; + // stop script resolvers + foreach( Tomahawk::ExternalResolver* r, m_scriptResolvers.values() ) + { + delete r; + } + m_scriptResolvers.clear(); + delete m_sipHandler; delete m_servent; - + delete m_scanManager; #ifndef TOMAHAWK_HEADLESS delete m_mainwindow; delete m_audioEngine; #endif - + delete m_infoSystem; delete m_database; } @@ -320,7 +341,7 @@ TomahawkApp::registerMetaTypes() qRegisterMetaType< QMap >("QMap"); qRegisterMetaType< QMap< QString, plentry_ptr > >("QMap< QString, plentry_ptr >"); qRegisterMetaType< QHash< QString, QMap > >("QHash< QString, QMap >"); - + qRegisterMetaType< GeneratorMode>("GeneratorMode"); qRegisterMetaType("Tomahawk::GeneratorMode"); // Extra definition for namespaced-versions of signals/slots required @@ -330,6 +351,8 @@ TomahawkApp::registerMetaTypes() qRegisterMetaType< Tomahawk::query_ptr >("Tomahawk::query_ptr"); qRegisterMetaType< Tomahawk::source_ptr >("Tomahawk::source_ptr"); qRegisterMetaType< Tomahawk::dyncontrol_ptr >("Tomahawk::dyncontrol_ptr"); + qRegisterMetaType< Tomahawk::playlist_ptr >("Tomahawk::playlist_ptr"); + qRegisterMetaType< Tomahawk::dynplaylist_ptr >("Tomahawk::dynplaylist_ptr"); qRegisterMetaType< Tomahawk::geninterface_ptr >("Tomahawk::geninterface_ptr"); qRegisterMetaType< QList >("QList"); qRegisterMetaType< QList >("QList"); @@ -392,37 +415,41 @@ TomahawkApp::setupPipeline() Pipeline::instance()->addResolver( new DatabaseResolver( 100 ) ); // load script resolvers - foreach( QString resolver, TomahawkSettings::instance()->scriptResolvers() ) - addScriptResolver( resolver ); + foreach( QString resolver, TomahawkSettings::instance()->enabledScriptResolvers() ) + enableScriptResolver( resolver ); } void -TomahawkApp::addScriptResolver( const QString& path ) +TomahawkApp::enableScriptResolver( const QString& path ) { const QFileInfo fi( path ); if ( fi.suffix() == "js" || fi.suffix() == "script" ) - m_scriptResolvers << new QtScriptResolver( path ); + m_scriptResolvers.insert( path, new QtScriptResolver( path ) ); else - m_scriptResolvers << new ScriptResolver( path ); + m_scriptResolvers.insert( path, new ScriptResolver( path ) ); } void -TomahawkApp::removeScriptResolver( const QString& path ) +TomahawkApp::disableScriptResolver( const QString& path ) { - foreach( Tomahawk::ExternalResolver* r, m_scriptResolvers ) + if( m_scriptResolvers.contains( path ) ) { - if( r->filePath() == path ) - { - m_scriptResolvers.removeAll( r ); - connect( r, SIGNAL( finished() ), r, SLOT( deleteLater() ) ); - r->stop(); - return; - } + Tomahawk::ExternalResolver* r = m_scriptResolvers.take( path ); + + connect( r, SIGNAL( finished() ), r, SLOT( deleteLater() ) ); + r->stop(); + return; } } +Tomahawk::ExternalResolver* +TomahawkApp::resolverForPath( const QString& scriptPath ) +{ + return m_scriptResolvers.value( scriptPath, 0 ); +} + void TomahawkApp::initLocalCollection() @@ -446,52 +473,15 @@ TomahawkApp::initLocalCollection() void TomahawkApp::startServent() { - bool upnp = !arguments().contains( "--noupnp" ) && TomahawkSettings::instance()->value( "network/upnp", true ).toBool(); - if ( !Servent::instance()->startListening( QHostAddress( QHostAddress::Any ), upnp ) ) + bool upnp = !arguments().contains( "--noupnp" ) && TomahawkSettings::instance()->value( "network/upnp", true ).toBool() && !TomahawkSettings::instance()->preferStaticHostPort(); + int port = TomahawkSettings::instance()->externalPort(); + if ( !Servent::instance()->startListening( QHostAddress( QHostAddress::Any ), upnp, port ) ) { qDebug() << "Failed to start listening with servent"; exit( 1 ); } } - -void -TomahawkApp::loadPlugins() -{ - // look in same dir as executable for plugins - QDir dir( TomahawkApp::instance()->applicationDirPath() ); - QStringList filters; - filters << "*.so" << "*.dll" << "*.dylib"; - - QStringList files = dir.entryList( filters ); - foreach( const QString& filename, files ) - { - qDebug() << "Attempting to load" << QString( "%1/%2" ).arg( dir.absolutePath() ).arg( filename ); - - QPluginLoader loader( dir.absoluteFilePath( filename ) ); - if ( QObject* inst = loader.instance() ) - { - TomahawkPlugin* pluginst = qobject_cast(inst); - if ( !pluginst ) - continue; - - PluginAPI* api = new PluginAPI( Pipeline::instance() ); - TomahawkPlugin* plugin = pluginst->factory( api ); - qDebug() << "Loaded Plugin:" << plugin->name(); - qDebug() << plugin->description(); - m_plugins.append( plugin ); - - // plugins responsibility to register itself as a resolver/collection - // all we need to do is create an instance of it. - } - else - { - qDebug() << "PluginLoader failed to create instance:" << filename << " Err:" << loader.errorString(); - } - } -} - - void TomahawkApp::setupSIP() { @@ -526,8 +516,8 @@ TomahawkApp::loadUrl( const QString& url ) if( url.contains( "tomahawk://" ) ) { QString cmd = url.mid( 11 ); qDebug() << "tomahawk!s" << cmd; - if( cmd.startsWith( "load/" ) ) { - cmd = cmd.mid( 5 ); + if( cmd.startsWith( "load/?" ) ) { + cmd = cmd.mid( 6 ); qDebug() << "loading.." << cmd; if( cmd.startsWith( "xspf=" ) ) { XSPFLoader* l = new XSPFLoader( true, this ); @@ -541,21 +531,23 @@ TomahawkApp::loadUrl( const QString& url ) if( f.exists() && info.suffix() == "xspf" ) { XSPFLoader* l = new XSPFLoader( true, this ); qDebug() << "Loading spiff:" << url; - l->load( QUrl( url ) ); + l->load( QUrl::fromUserInput( url ) ); } } return true; } -void -TomahawkApp::messageReceived( const QString& msg ) +void +TomahawkApp::instanceStarted( KDSingleApplicationGuard::Instance instance ) { - qDebug() << "MESSAGE RECEIVED" << msg; - if( msg.isEmpty() ) { + qDebug() << "INSTANCE STARTED!" << instance.pid << instance.arguments; + + if( instance.arguments.size() < 2 ) + { return; } - - loadUrl( msg ); + + loadUrl( instance.arguments.at( 1 ) ); } diff --git a/src/tomahawkapp_macdelegate.h b/src/tomahawkapp_macdelegate.h index a5884e225..01ef7a401 100644 --- a/src/tomahawkapp_macdelegate.h +++ b/src/tomahawkapp_macdelegate.h @@ -1,3 +1,21 @@ +/* === 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 . + */ + #import #include "config.h" diff --git a/src/tomahawktrayicon.cpp b/src/tomahawktrayicon.cpp index f364c4a53..e49a813ef 100644 --- a/src/tomahawktrayicon.cpp +++ b/src/tomahawktrayicon.cpp @@ -1,3 +1,21 @@ +/* === 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 "tomahawktrayicon.h" #include diff --git a/src/tomahawktrayicon.h b/src/tomahawktrayicon.h index c73a85781..2cef11872 100644 --- a/src/tomahawktrayicon.h +++ b/src/tomahawktrayicon.h @@ -1,3 +1,21 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2010-2011, Christian Muehlhaeuser + * + * Tomahawk is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Tomahawk is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Tomahawk. If not, see . + */ + #ifndef TOMAHAWK_TRAYICON_H #define TOMAHAWK_TRAYICON_H diff --git a/src/tomahawkwindow.cpp b/src/tomahawkwindow.cpp index 4f04f84a6..62a442946 100644 --- a/src/tomahawkwindow.cpp +++ b/src/tomahawkwindow.cpp @@ -1,3 +1,21 @@ +/* === 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 "tomahawkwindow.h" #include "ui_tomahawkwindow.h" @@ -90,7 +108,7 @@ TomahawkWindow::TomahawkWindow( QWidget* parent ) connect( ui->actionHideOfflineSources, SIGNAL( triggered() ), stv, SLOT( hideOfflineSources() ) ); connect( ui->actionShowOfflineSources, SIGNAL( triggered() ), stv, SLOT( showOfflineSources() ) ); - + sidebar->addWidget( stv ); sidebar->addWidget( transferView ); sidebar->hide( 1, false ); @@ -127,24 +145,25 @@ TomahawkWindow::TomahawkWindow( QWidget* parent ) toolbar->setIconSize( QSize( 32, 32 ) ); toolbar->setToolButtonStyle( Qt::ToolButtonFollowStyle ); toolbar->installEventFilter( new WidgetDragFilter( toolbar ) ); - + #if defined( Q_OS_DARWIN ) && defined( HAVE_SPARKLE ) - QAction* checkForUpdates = ui->menu_Help->addAction( tr( "Check for updates...") ); + QAction* checkForUpdates = ui->menu_Help->addAction( tr( "Check For Updates...") ); checkForUpdates->setMenuRole( QAction::ApplicationSpecificRole ); connect(checkForUpdates, SIGNAL( triggered( bool ) ), SLOT( checkForUpdates() ) ); #elif defined( WIN32 ) QUrl updaterUrl; - #ifdef DEBUG_BUILD + + if ( qApp->arguments().contains( "--debug" ) ) updaterUrl.setUrl( "http://download.tomahawk-player.org/sparklewin-debug" ); - #else + else updaterUrl.setUrl( "http://download.tomahawk-player.org/sparklewin" ); - #endif + qtsparkle::Updater* updater = new qtsparkle::Updater( updaterUrl, this ); updater->SetNetworkAccessManager( TomahawkUtils::nam() ); updater->SetVersion( VERSION ); - + ui->menu_Help->addSeparator(); - QAction* checkForUpdates = ui->menu_Help->addAction( tr( "Check for updates...") ); + QAction* checkForUpdates = ui->menu_Help->addAction( tr( "Check For Updates...") ); connect( checkForUpdates, SIGNAL( triggered() ), updater, SLOT( CheckNow() ) ); #endif @@ -236,7 +255,7 @@ TomahawkWindow::setupSignals() // connect( ui->actionPreferences, SIGNAL( triggered() ), SLOT( showSettingsDialog() ) ); connect( ui->actionToggleConnect, SIGNAL( triggered() ), APP->sipHandler(), SLOT( toggleConnect() ) ); - connect( ui->actionAddPeerManually, SIGNAL( triggered() ), SLOT( addPeerManually() ) ); +// connect( ui->actionAddPeerManually, SIGNAL( triggered() ), SLOT( addPeerManually() ) ); connect( ui->actionRescanCollection, SIGNAL( triggered() ), SLOT( updateCollectionManually() ) ); connect( ui->actionLoadXSPF, SIGNAL( triggered() ), SLOT( loadSpiff() )); connect( ui->actionCreatePlaylist, SIGNAL( triggered() ), SLOT( createPlaylist() )); @@ -300,8 +319,8 @@ TomahawkWindow::showSettingsDialog() void TomahawkWindow::updateCollectionManually() { - if ( TomahawkSettings::instance()->hasScannerPath() ) - ScanManager::instance()->runManualScan( TomahawkSettings::instance()->scannerPath() ); + if ( TomahawkSettings::instance()->hasScannerPaths() ) + ScanManager::instance()->runManualScan( TomahawkSettings::instance()->scannerPaths() ); } @@ -335,13 +354,15 @@ TomahawkWindow::addPeerManually() Servent::instance()->connectToPeer( addr, port, key ); } -void + +void TomahawkWindow::pluginMenuAdded( QMenu* menu ) { ui->menuNetwork->addMenu( menu ); } -void + +void TomahawkWindow::pluginMenuRemoved( QMenu* menu ) { foreach( QAction* action, ui->menuNetwork->actions() ) @@ -359,25 +380,23 @@ void TomahawkWindow::loadSpiff() { bool ok; - QString urlstr = QInputDialog::getText( this, "Load XSPF", "Path:", QLineEdit::Normal, "http://ws.audioscrobbler.com/1.0/tag/metal/toptracks.xspf", &ok ); + QString urlstr = QInputDialog::getText( this, tr( "Load XSPF" ), tr( "Path:" ), QLineEdit::Normal, "http://ws.audioscrobbler.com/1.0/tag/metal/toptracks.xspf", &ok ); if ( !ok || urlstr.isEmpty() ) return; - QUrl url( urlstr ); - XSPFLoader* loader = new XSPFLoader; - loader->load( url ); + loader->load( QUrl::fromUserInput( urlstr ) ); } -void +void TomahawkWindow::createAutomaticPlaylist() { bool ok; - QString name = QInputDialog::getText( this, "Create New Automatic Playlist", "Name:", QLineEdit::Normal, "New Automatic Playlist", &ok ); + QString name = QInputDialog::getText( this, tr( "Create New Automatic Playlist" ), tr( "Name:" ), QLineEdit::Normal, tr( "New Automatic Playlist" ), &ok ); if ( !ok || name.isEmpty() ) return; - + source_ptr author = SourceList::instance()->getLocal(); QString id = uuid(); QString info = ""; // FIXME @@ -393,10 +412,10 @@ void TomahawkWindow::createStation() { bool ok; - QString name = QInputDialog::getText( this, "Create New Station", "Name:", QLineEdit::Normal, "New Station", &ok ); + QString name = QInputDialog::getText( this, tr( "Create New Station" ), tr( "Name:" ), QLineEdit::Normal, tr( "New Station" ), &ok ); if ( !ok || name.isEmpty() ) return; - + source_ptr author = SourceList::instance()->getLocal(); QString id = uuid(); QString info = ""; // FIXME @@ -472,8 +491,8 @@ TomahawkWindow::setWindowTitle( const QString& title ) QMainWindow::setWindowTitle( title ); else { - QString s = m_currentTrack->track() + " " + tr( "by" ) + " " + m_currentTrack->artist()->name(); - QMainWindow::setWindowTitle( s + " - " + title ); + QString s = tr( "%1 by %2", "track, artist name" ).arg( m_currentTrack->track(), m_currentTrack->artist()->name() ); + QMainWindow::setWindowTitle( tr( "%1 - %2", "current track, some window title" ).arg( s, title ) ); } } @@ -481,7 +500,7 @@ TomahawkWindow::setWindowTitle( const QString& title ) void TomahawkWindow::showAboutTomahawk() { - QMessageBox::about( this, "About Tomahawk", + QMessageBox::about( this, tr( "About Tomahawk" ), tr( "

Tomahawk %1

Copyright 2010, 2011
Christian Muehlhaeuser <muesli@tomahawk-player.org>

" "Thanks to: Leo Franchi, Jeff Mitchell, Dominik Schmidt, Jason Herskowitz, Alejandro Wainzinger, Harald Sitter and Steve Robertson" ) .arg( qApp->applicationVersion() ) ); diff --git a/src/tomahawkwindow.h b/src/tomahawkwindow.h index 4d729186c..e8211fbb6 100644 --- a/src/tomahawkwindow.h +++ b/src/tomahawkwindow.h @@ -1,9 +1,26 @@ +/* === 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 TOMAHAWKWINDOW_H #define TOMAHAWKWINDOW_H #include #include -#include #include #include #include @@ -70,7 +87,6 @@ private: Ui::TomahawkWindow* ui; AudioControls* m_audioControls; TomahawkTrayIcon* m_trayIcon; - QNetworkAccessManager m_nam; QPushButton* m_statusButton; QAction* m_backAvailable; diff --git a/src/tomahawkwindow.ui b/src/tomahawkwindow.ui index 94a6cecd1..6b142c616 100644 --- a/src/tomahawkwindow.ui +++ b/src/tomahawkwindow.ui @@ -71,7 +71,6 @@ - @@ -102,11 +101,6 @@ Go &online - - - Add &Peer Manually... - - Add &Friend... diff --git a/src/transferview.cpp b/src/transferview.cpp index 75231cc81..3d0e771d1 100644 --- a/src/transferview.cpp +++ b/src/transferview.cpp @@ -1,3 +1,21 @@ +/* === 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 "transferview.h" #include @@ -5,7 +23,8 @@ #include "tomahawk/tomahawkapp.h" #include "artist.h" -#include "network/filetransferconnection.h" +#include "source.h" +#include "network/streamconnection.h" #include "network/servent.h" @@ -20,8 +39,8 @@ TransferView::TransferView( AnimatedSplitter* parent ) layout()->setMargin( 0 ); layout()->addWidget( m_tree ); - connect( Servent::instance(), SIGNAL( fileTransferStarted( FileTransferConnection* ) ), SLOT( fileTransferRegistered( FileTransferConnection* ) ) ); - connect( Servent::instance(), SIGNAL( fileTransferFinished( FileTransferConnection* ) ), SLOT( fileTransferFinished( FileTransferConnection* ) ) ); + connect( Servent::instance(), SIGNAL( streamStarted( StreamConnection* ) ), SLOT( streamRegistered( StreamConnection* ) ) ); + connect( Servent::instance(), SIGNAL( streamFinished( StreamConnection* ) ), SLOT( streamFinished( StreamConnection* ) ) ); QStringList headers; headers << tr( "Peer" ) << tr( "Rate" ) << tr( "Track" ); @@ -42,20 +61,20 @@ TransferView::TransferView( AnimatedSplitter* parent ) void -TransferView::fileTransferRegistered( FileTransferConnection* ftc ) +TransferView::streamRegistered( StreamConnection* sc ) { qDebug() << Q_FUNC_INFO; - connect( ftc, SIGNAL( updated() ), SLOT( onTransferUpdate() ) ); + connect( sc, SIGNAL( updated() ), SLOT( onTransferUpdate() ) ); } void -TransferView::fileTransferFinished( FileTransferConnection* ftc ) +TransferView::streamFinished( StreamConnection* sc ) { - if ( !m_index.contains( ftc ) ) + if ( !m_index.contains( sc ) ) return; - QPersistentModelIndex i = m_index.take( ftc ); + QPersistentModelIndex i = m_index.take( sc ); delete m_tree->invisibleRootItem()->takeChild( i.row() ); if ( m_tree->invisibleRootItem()->childCount() > 0 ) @@ -63,9 +82,9 @@ TransferView::fileTransferFinished( FileTransferConnection* ftc ) else emit hideWidget(); -/* if ( m_index.contains( ftc ) ) +/* if ( m_index.contains( sc ) ) { - int i = m_index.value( ftc ); + int i = m_index.value( sc ); m_tree->invisibleRootItem()->child( i )->setText( 1, tr( "Finished" ) ); }*/ } @@ -74,32 +93,32 @@ TransferView::fileTransferFinished( FileTransferConnection* ftc ) void TransferView::onTransferUpdate() { - FileTransferConnection* ftc = (FileTransferConnection*)sender(); -// qDebug() << Q_FUNC_INFO << ftc->track().isNull() << ftc->source().isNull(); + StreamConnection* sc = (StreamConnection*)sender(); +// qDebug() << Q_FUNC_INFO << sc->track().isNull() << sc->source().isNull(); - if ( ftc->track().isNull() || ftc->source().isNull() ) + if ( sc->track().isNull() || sc->source().isNull() ) return; QTreeWidgetItem* ti = 0; - if ( m_index.contains( ftc ) ) + if ( m_index.contains( sc ) ) { - QPersistentModelIndex i = m_index.value( ftc ); + QPersistentModelIndex i = m_index.value( sc ); ti = m_tree->invisibleRootItem()->child( i.row() ); } else { ti = new QTreeWidgetItem( m_tree ); - m_index.insert( ftc, QPersistentModelIndex( m_tree->model()->index( m_tree->invisibleRootItem()->childCount() - 1, 0 ) ) ); + m_index.insert( sc, QPersistentModelIndex( m_tree->model()->index( m_tree->invisibleRootItem()->childCount() - 1, 0 ) ) ); emit showWidget(); } if ( !ti ) return; - ti->setText( 0, ftc->source()->friendlyName() ); - ti->setText( 1, QString( "%1 kb/s" ).arg( ftc->transferRate() / 1024 ) ); - ti->setText( 2, QString( "%1 - %2" ).arg( ftc->track()->artist()->name() ).arg( ftc->track()->track() ) ); + ti->setText( 0, sc->source()->friendlyName() ); + ti->setText( 1, QString( "%1 kb/s" ).arg( sc->transferRate() / 1024 ) ); + ti->setText( 2, QString( "%1 - %2" ).arg( sc->track()->artist()->name() ).arg( sc->track()->track() ) ); if ( isHidden() ) emit showWidget(); diff --git a/src/transferview.h b/src/transferview.h index 3520c03be..560088bbf 100644 --- a/src/transferview.h +++ b/src/transferview.h @@ -1,3 +1,21 @@ +/* === 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 TRANSFERVIEW_H #define TRANSFERVIEW_H @@ -7,7 +25,7 @@ #include "typedefs.h" #include "utils/animatedsplitter.h" -class FileTransferConnection; +class StreamConnection; class TransferView : public AnimatedWidget { @@ -25,13 +43,13 @@ public: signals: private slots: - void fileTransferRegistered( FileTransferConnection* ftc ); - void fileTransferFinished( FileTransferConnection* ftc ); + void streamRegistered( StreamConnection* sc ); + void streamFinished( StreamConnection* sc ); void onTransferUpdate(); private: - QHash< FileTransferConnection*, QPersistentModelIndex > m_index; + QHash< StreamConnection*, QPersistentModelIndex > m_index; QTreeWidget* m_tree; AnimatedSplitter* m_parent; }; diff --git a/src/web/api_v1.cpp b/src/web/api_v1.cpp index 2e32de572..d0a3b0475 100644 --- a/src/web/api_v1.cpp +++ b/src/web/api_v1.cpp @@ -1,3 +1,21 @@ +/* === 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 "api_v1.h" #include @@ -127,6 +145,7 @@ Api_v1::api( QxtWebRequestEvent* event ) void Api_v1::sid( QxtWebRequestEvent* event, QString unused ) { + Q_UNUSED( unused ); using namespace Tomahawk; RID rid = event->url.path().mid( 5 ); qDebug() << "Request for sid " << rid; @@ -188,6 +207,8 @@ Api_v1::stat( QxtWebRequestEvent* event ) void Api_v1::statResult( const QString& clientToken, const QString& name, bool valid ) { + Q_UNUSED( clientToken ) + Q_UNUSED( name ) QVariantMap m; m.insert( "name", "playdar" ); m.insert( "version", "0.1.1" ); // TODO (needs to be >=0.1.1 for JS to work) diff --git a/src/web/api_v1.h b/src/web/api_v1.h index 243f52449..cc065a41a 100644 --- a/src/web/api_v1.h +++ b/src/web/api_v1.h @@ -1,3 +1,21 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2010-2011, Christian Muehlhaeuser + * + * Tomahawk is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Tomahawk is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Tomahawk. If not, see . + */ + #ifndef TOMAHAWK_WEBAPI_V1 #define TOMAHAWK_WEBAPI_V1 @@ -20,8 +38,7 @@ #include #include "network/servent.h" -#include "tomahawkutils.h" -#include "tomahawk/tomahawkapp.h" +#include "utils/tomahawkutils.h" #include #include #include diff --git a/src/xmppbot/xmppbot.cpp b/src/xmppbot/xmppbot.cpp index 579aab92a..404d64fd2 100644 --- a/src/xmppbot/xmppbot.cpp +++ b/src/xmppbot/xmppbot.cpp @@ -1,7 +1,24 @@ +/* === 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 "xmppbot.h" -#include "tomahawk/tomahawkapp.h" -#include "tomahawk/infosystem.h" +#include "infosystem/infosystem.h" #include "album.h" #include "typedefs.h" #include "tomahawksettings.h" @@ -18,7 +35,7 @@ using namespace gloox; using namespace Tomahawk::InfoSystem; -static QString s_infoIdentifier = QString("XMPPBot"); +static QString s_botInfoIdentifier = QString( "XMPPBot" ); XMPPBot::XMPPBot(QObject *parent) : QObject(parent) @@ -32,7 +49,7 @@ XMPPBot::XMPPBot(QObject *parent) int port = settings->xmppBotPort(); if (jidstring.isEmpty() || password.isEmpty()) return; - + JID jid(jidstring.toStdString()); jid.setResource( QString( "tomahawkbot%1" ).arg( qrand() ).toStdString() ); @@ -48,11 +65,11 @@ XMPPBot::XMPPBot(QObject *parent) connect(AudioEngine::instance(), SIGNAL(started(const Tomahawk::result_ptr &)), SLOT(newTrackSlot(const Tomahawk::result_ptr &))); - connect(TomahawkApp::instance()->infoSystem(), - SIGNAL(info(QString, Tomahawk::InfoSystem::InfoType, QVariant, QVariant, Tomahawk::InfoSystem::InfoCustomDataHash)), - SLOT(infoReturnedSlot(QString, Tomahawk::InfoSystem::InfoType, QVariant, QVariant, Tomahawk::InfoSystem::InfoCustomDataHash))); - - connect(TomahawkApp::instance()->infoSystem(), SIGNAL(finished(QString)), SLOT(infoFinishedSlot(QString))); + connect(InfoSystem::instance(), + SIGNAL(info(QString, Tomahawk::InfoSystem::InfoType, QVariant, QVariant, Tomahawk::InfoSystem::InfoCustomData)), + SLOT(infoReturnedSlot(QString, Tomahawk::InfoSystem::InfoType, QVariant, QVariant, Tomahawk::InfoSystem::InfoCustomData))); + + connect(InfoSystem::instance(), SIGNAL(finished(QString)), SLOT(infoFinishedSlot(QString))); bool success = m_client.data()->gloox::Client::connect(false); if (success) @@ -131,7 +148,7 @@ void XMPPBot::handleMessage(const Message& msg, MessageSession* session) //TODO: implement "properly" with MessageSessions, if the bot is to be multi-user if (msg.subtype() != Message::Chat || msg.from().full().empty() || msg.to().full().empty()) return; - + QString body = QString::fromStdString( msg.body() ).toLower().trimmed(); QString originatingJid = QString::fromStdString( msg.from().full() ); @@ -174,7 +191,7 @@ void XMPPBot::handleMessage(const Message& msg, MessageSession* session) qDebug() << "jid from:" << QString::fromStdString(msg.from().full()) << ", jid to:" << QString::fromStdString(msg.to().full()); qDebug() << "Operating on tokens:" << tokens; - + if (m_currTrack.isNull() || m_currTrack->artist()->name().isEmpty() || m_currTrack->track().isEmpty()) { qDebug() << "XMPPBot can't figure out track"; @@ -183,8 +200,8 @@ void XMPPBot::handleMessage(const Message& msg, MessageSession* session) m_client.data()->send(retMsg); return; } - - InfoMap infoMap; + + InfoMap infoMap; Q_FOREACH(QString token, tokens) { if (token == "biography") @@ -197,13 +214,13 @@ void XMPPBot::handleMessage(const Message& msg, MessageSession* session) infoMap[InfoArtistFamiliarity] = m_currTrack.data()->artist()->name(); if (token == "lyrics") { - MusixMatchHash myhash; - myhash["trackName"] = m_currTrack.data()->track(); - myhash["artistName"] = m_currTrack.data()->artist()->name(); - infoMap[InfoTrackLyrics] = QVariant::fromValue(myhash); + InfoCustomData myhash; + myhash["trackName"] = QVariant::fromValue(m_currTrack.data()->track()); + myhash["artistName"] = QVariant::fromValue(m_currTrack.data()->artist()->name()); + infoMap[InfoTrackLyrics] = QVariant::fromValue(myhash); } } - + if (infoMap.isEmpty()) { qDebug() << "XMPPBot can't figure out track"; @@ -211,38 +228,38 @@ void XMPPBot::handleMessage(const Message& msg, MessageSession* session) Message retMsg(Message::Chat, JID(originatingJid.toStdString()), m_currReturnMessage.toStdString()); m_client.data()->send(retMsg); return; - } - + } + m_currInfoMap.unite(infoMap); QString waitMsg("Please wait..."); Message retMsg(Message::Chat, JID(originatingJid.toStdString()), waitMsg.toStdString()); m_client.data()->send(retMsg); - Tomahawk::InfoSystem::InfoCustomDataHash hash; + Tomahawk::InfoSystem::InfoCustomData hash; hash["XMPPBotSendToJID"] = originatingJid; - TomahawkApp::instance()->infoSystem()->getInfo(s_infoIdentifier, infoMap, hash); + InfoSystem::instance()->getInfo(s_botInfoIdentifier, infoMap, hash); } -void XMPPBot::infoReturnedSlot(QString caller, Tomahawk::InfoSystem::InfoType type, QVariant input, QVariant output, Tomahawk::InfoSystem::InfoCustomDataHash customData) +void XMPPBot::infoReturnedSlot(QString caller, Tomahawk::InfoSystem::InfoType type, QVariant input, QVariant output, Tomahawk::InfoSystem::InfoCustomData customData) { qDebug() << Q_FUNC_INFO; - - if (caller != s_infoIdentifier || + + if (caller != s_botInfoIdentifier || input.isNull() || !input.isValid() || !customData.contains("XMPPBotSendToJID") ) { qDebug() << "Not the right object, custom data is null, or don't have a set JID"; return; - } - + } + if (!m_currInfoMap.contains(type)) { qDebug() << "not in currInfoMap"; return; } - else + else m_currInfoMap.remove(type); - + QString jid = customData["XMPPBotSendToJID"].toString(); if (!m_currReturnJid.isEmpty() && m_currReturnJid != jid && !m_currReturnMessage.isEmpty()) { @@ -251,7 +268,7 @@ void XMPPBot::infoReturnedSlot(QString caller, Tomahawk::InfoSystem::InfoType ty m_currReturnMessage = QString("\n"); } m_currReturnJid = jid; - + switch(type) { case InfoArtistBiography: @@ -269,7 +286,7 @@ void XMPPBot::infoReturnedSlot(QString caller, Tomahawk::InfoSystem::InfoType ty m_currReturnMessage += QString("\nBiographies for %1\n").arg(artist); Q_FOREACH(QString source, bmap.keys()) { - m_currReturnMessage += (bmap[source]["attribution"].isEmpty() ? + m_currReturnMessage += (bmap[source]["attribution"].isEmpty() ? QString("From %1:\n").arg(bmap[source]["site"]) : QString("From %1 at %2:\n").arg(bmap[source]["attribution"]).arg(bmap[source]["site"])); m_currReturnMessage += bmap[source]["text"] + QString("\n"); @@ -288,9 +305,9 @@ void XMPPBot::infoReturnedSlot(QString caller, Tomahawk::InfoSystem::InfoType ty } InfoGenericMap tmap = output.value(); QString artist = input.toString(); - m_currReturnMessage += QString("\nTerms for %1:\n").arg(artist); + m_currReturnMessage += tr("\nTerms for %1:\n").arg(artist); if (tmap.isEmpty()) - m_currReturnMessage += QString("No terms found, sorry."); + m_currReturnMessage += tr("No terms found, sorry."); else { bool first = true; @@ -323,7 +340,7 @@ void XMPPBot::infoReturnedSlot(QString caller, Tomahawk::InfoSystem::InfoType ty QString artist = input.toString(); qreal retVal = output.toReal(); QString retValString = (retVal == 0.0 ? "(none)" : QString::number(retVal)); - m_currReturnMessage += QString("\nHotttness for %1: %2\n").arg(artist).arg(retValString); + m_currReturnMessage += tr("\nHotttness for %1: %2\n").arg(artist, retValString); break; } case InfoArtistFamiliarity: @@ -339,39 +356,39 @@ void XMPPBot::infoReturnedSlot(QString caller, Tomahawk::InfoSystem::InfoType ty QString artist = input.toString(); qreal retVal = output.toReal(); QString retValString = (retVal == 0.0 ? "(none)" : QString::number(retVal)); - m_currReturnMessage += QString("\nFamiliartiy for %1: %2\n").arg(artist).arg(retValString); + m_currReturnMessage += tr("\nFamiliarity for %1: %2\n").arg(artist, retValString); break; } case InfoTrackLyrics: { qDebug() << "Lyrics requested"; if (!output.canConvert() || - !input.canConvert() + !input.canConvert() ) { qDebug() << "Variants failed to be valid"; break; } - MusixMatchHash inHash = input.value(); - QString artist = inHash["artistName"]; - QString track = inHash["trackName"]; + InfoCustomData inHash = input.value(); + QString artist = inHash["artistName"].toString(); + QString track = inHash["trackName"].toString(); QString lyrics = output.toString(); qDebug() << "lyrics = " << lyrics; - m_currReturnMessage += QString("\nLyrics for \"%1\" by %2:\n\n%3\n").arg(track).arg(artist).arg(lyrics); + m_currReturnMessage += tr("\nLyrics for \"%1\" by %2:\n\n%3\n").arg(track, artist, lyrics); break; } default: break; } - + if (m_currReturnMessage.isEmpty()) { qDebug() << "Empty message, not sending anything back"; return; } - + qDebug() << "Going to send message: " << m_currReturnMessage << " to " << jid; - + //gloox::Message msg(Message::Chat, JID(jid.toStdString()), m_currReturnMessage.toStdString()); //m_client.data()->send(msg); } @@ -380,10 +397,10 @@ void XMPPBot::infoFinishedSlot(QString caller) { qDebug() << Q_FUNC_INFO; qDebug() << "current return message is" << m_currReturnMessage; - qDebug() << "id is" << caller << "and our id is" << s_infoIdentifier; - if (m_currReturnMessage.isEmpty() || caller != s_infoIdentifier) + qDebug() << "id is" << caller << "and our id is" << s_botInfoIdentifier; + if (m_currReturnMessage.isEmpty() || caller != s_botInfoIdentifier) return; - + qDebug() << "Sending message to JID" << m_currReturnJid; gloox::Message msg(Message::Chat, JID(m_currReturnJid.toStdString()), m_currReturnMessage.toStdString()); m_client.data()->send(msg); diff --git a/src/xmppbot/xmppbot.h b/src/xmppbot/xmppbot.h index d1034d08e..e22a564ae 100644 --- a/src/xmppbot/xmppbot.h +++ b/src/xmppbot/xmppbot.h @@ -1,8 +1,26 @@ +/* === 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 XMPPBOT_H #define XMPPBOT_H #include -#include +#include #include #include @@ -24,12 +42,12 @@ class XMPPBotClient public: XMPPBotClient(QObject* parent, gloox::JID &jid, std::string password, int port); virtual ~XMPPBotClient(); - + void run(); - + private slots: void recvSlot(); - + private: QTimer m_timer; }; @@ -41,25 +59,25 @@ class XMPPBot , public gloox::MessageHandler { Q_OBJECT - + public: XMPPBot(QObject *parent); virtual ~XMPPBot(); public slots: virtual void newTrackSlot(const Tomahawk::result_ptr &track); - virtual void infoReturnedSlot(QString caller, Tomahawk::InfoSystem::InfoType type, QVariant input, QVariant output, Tomahawk::InfoSystem::InfoCustomDataHash customData); + virtual void infoReturnedSlot(QString caller, Tomahawk::InfoSystem::InfoType type, QVariant input, QVariant output, Tomahawk::InfoSystem::InfoCustomData customData); virtual void infoFinishedSlot(QString caller); - + protected: // ConnectionListener virtual void onConnect(); virtual void onDisconnect(gloox::ConnectionError e); virtual bool onTLSConnect(const gloox::CertInfo &info); - + // SubscriptionHandler virtual void handleSubscription(const gloox::Subscription &subscription); - + // MessageHandler virtual void handleMessage(const gloox::Message &msg, gloox::MessageSession *session = 0); diff --git a/thirdparty/CMakeLists.txt b/thirdparty/CMakeLists.txt index 2eda28a6c..ca840ac1f 100644 --- a/thirdparty/CMakeLists.txt +++ b/thirdparty/CMakeLists.txt @@ -2,3 +2,4 @@ add_subdirectory( jdns ) add_subdirectory( qtweetlib ) ADD_SUBDIRECTORY( libportfwd ) ADD_SUBDIRECTORY( qxt ) +ADD_SUBDIRECTORY( liblastfm2 ) diff --git a/thirdparty/jdns/CMakeLists.txt b/thirdparty/jdns/CMakeLists.txt index 59f820b93..70df58ed4 100644 --- a/thirdparty/jdns/CMakeLists.txt +++ b/thirdparty/jdns/CMakeLists.txt @@ -9,6 +9,8 @@ INCLUDE( ${QT_USE_FILE} ) add_definitions( ${QT_DEFINITIONS} ) add_definitions( -DQT_SHARED ) +SET( CMAKE_C_FLAGS ${CLEAN_C_FLAGS} ) + if(WIN32) set(PLATFORM_SPECIFIC_LIBS "ws2_32.dll" "advapi32.dll" ) endif(WIN32) @@ -49,4 +51,4 @@ target_link_libraries(tomahawk_jdns SET_TARGET_PROPERTIES( tomahawk_jdns PROPERTIES DEFINE_SYMBOL MAKE_JDNS_LIB ) -INSTALL(TARGETS tomahawk_jdns DESTINATION lib) +INSTALL(TARGETS tomahawk_jdns DESTINATION lib${LIB_SUFFIX}) diff --git a/thirdparty/jreen b/thirdparty/jreen index 040ca3f3c..126ef9d96 160000 --- a/thirdparty/jreen +++ b/thirdparty/jreen @@ -1 +1 @@ -Subproject commit 040ca3f3cb9b30b4845fc23054c833fda4717460 +Subproject commit 126ef9d96bf774b9808a16dd8c94001af408528b diff --git a/thirdparty/liblastfm2/CMakeLists.txt b/thirdparty/liblastfm2/CMakeLists.txt new file mode 100644 index 000000000..b0f71bd5d --- /dev/null +++ b/thirdparty/liblastfm2/CMakeLists.txt @@ -0,0 +1,27 @@ +cmake_minimum_required(VERSION 2.6) + +set( CMAKE_MODULE_PATH + ${CMAKE_MODULE_PATH} + ${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules + ) + +if(${CMAKE_BUILD_TYPE} MATCHES "Release") + add_definitions(-DNDEBUG) +endif(${CMAKE_BUILD_TYPE} MATCHES "Release") + +# Set up definitions and paths +add_definitions(${QT_DEFINITIONS}) +include(${QT_USE_FILE}) + +# Main Library +add_subdirectory(src) + +# Optionally build the fingerprint library +option(BUILD_FINGERPRINT "Build the lastfm-fingerprint library" OFF) +find_package(LibSamplerate) +find_package(LibFFTW3) + +if (BUILD_FINGERPRINT AND LIBFFTW3_FOUND AND LIBSAMPLERATE_FOUND) + add_subdirectory(src/fingerprint) +endif (BUILD_FINGERPRINT AND LIBFFTW3_FOUND AND LIBSAMPLERATE_FOUND) + diff --git a/thirdparty/liblastfm2/COPYING b/thirdparty/liblastfm2/COPYING new file mode 100644 index 000000000..94a045322 --- /dev/null +++ b/thirdparty/liblastfm2/COPYING @@ -0,0 +1,621 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS diff --git a/thirdparty/liblastfm2/README b/thirdparty/liblastfm2/README new file mode 100644 index 000000000..d39417ecc --- /dev/null +++ b/thirdparty/liblastfm2/README @@ -0,0 +1,138 @@ +liblastfm +========= +liblastfm is a collection of libraries to help you integrate Last.fm services +into your rich desktop software. It is officially supported software developed +by Last.fm staff. + +Max Howell http://twitter.com/mxcl +Jono Cole http://twitter.com/jonocole +Doug Mansell http://twitter.com/dougma + +Fork it: http://github.com/mxcl/liblastfm + + +Dependencies +============ +liblastfm dynamically links to: + +* Qt 4.4 + http://www.qtsoftware.com +* FFTW 3.2 + Compiled with single precision + http://www.fftw.org +* Secret Rabbit code (aka libsamplerate) + http://www.mega-nerd.com/SRC + +Additionally, to build you will need Ruby and GNU make (or Microsoft nmake). + +Mac OS X +-------- + sudo port selfupdate + sudo port upgrade installed + sudo port install libsamplerate fftw-3 qt4-mac-devel + +qt4-mac-devel will take a long time to build. So you may want to install the +Trolltech binary package instead. + +MacPorts carries liblastfm now, so you may have downloaded this package simply +to perform this next line: + + sudo port install liblastfm + +Linux/*NIX +---------- +Do something like this: + + sudo apt-get install qt4-qmake pkg-config libsamplerate-dev libfftw3-dev ruby g++ libqt4-dev + +Additionally on Linux the configure process requires lsb_release. This is +usually already installed (correct me if I'm wrong). + +Please note, we have only tested on Linux, but we think it'll work on all +varieties of UNIX. If it doesn't, report the bug to mxcl on GitHub. + +Windows +------- +Install Ruby. Install Visual Studio 2005 or higher. Install Qt. Install the +Windows Server 2003 Platform SDK r2: + +http://www.microsoft.com/Downloads/details.aspx?FamilyID=484269e2-3b89-47e3-8eb7-1f2be6d7123a + +Set up your environment variables so all include paths and tools are +available. + +Build and install FFTW and Secret Rabbit Code. + +Open a plain Windows shell (Cygwin will work but we don't recommend it), and +see the next section. + + +Installing liblastfm +==================== + ruby configure --release --prefix /usr/local && make && sudo make install + +Packaging liblastfm +------------------- +DESTDIR is supported. + +liblastfm builds to two dynamic libraries (liblastfm.so and +liblastfm_fingerprint.so). liblastfm.so links only to Qt, but the +fingerprinting part has additional dependencies. So ideally, you would +distribute two packages. + + +Using liblastfm +=============== +We have copied the API at http://last.fm/api onto C++, so like you find +artist.getInfo there you will find an lastfm::Artist::getInfo function in our +C++ API. lastfm is a namespace, Artist a class and getInfo a function. + +Thus the API is quite easy to learn. We suggest installing and checking the +include/lastfm/* directory to find out all capabilities. + +The demos directory shows some further basic usage including Audioscrobbling +and getting metadata for music via our fingerprinting technology. + +You need an API key from http://last.fm/api to use the webservice API. + +Your link line needs to include the following: + + -llastfm -lQtCore -lQtNetwork -lQtXml + +Radio +----- +Please set an identifiable UserAgent on your HTTP requests for the actual MP3s, +in extreme cases we'll contact you directly and demand you do so :P + +HTTP & Networking +----------------- +You can specify your own QNetworkAccessManager derived class for liblastfm to +use with lastfm::setNetworkAccessManager(). Our default is pretty good +though, auto-determining proxy settings on Windows and OS X for instance. + + +Using liblastfm_fingerprint +=========================== +The liblastfm_fingerprint library does not decode audio files. We anticipate +that Phonon will soon do that work for us. In the meantime, sample *Source +files for MP3, Ogg Vorbis, FLAC, and AAC/MP4 are available in +src/fingerprint/contrib. If you want to fingerprint files or get metadata +suggestions, you either need to add the *Source files to your project, or +implement your own. + + +Development +=========== +Public Headers +-------------- +1. Header guards should be prefixed with LASTFM, eg. LASTFM_WS_REPLY_H +2. #includes should be to the system path eg. #include +3. Don't make a header public unless it is absolutely required! +4. To make the header public edit the headers.files line in the pro file + +Private Headers +--------------- +1. For consistency and to make it more obvious it is a private header, don't + prefix the header guard with LASTFM +2. #includes should be the full source tree path, eg. + #include "../core/UrlBuilder.h" diff --git a/thirdparty/liblastfm2/admin/lastfm.h.rb b/thirdparty/liblastfm2/admin/lastfm.h.rb new file mode 100755 index 000000000..1582b7086 --- /dev/null +++ b/thirdparty/liblastfm2/admin/lastfm.h.rb @@ -0,0 +1,5 @@ +#!/usr/bin/ruby +f = File.new(ARGV[0], "w") +Dir["_include/lastfm/*"].each do |h| + f.write %Q{#include "lastfm/#{File.basename h}"\n} +end \ No newline at end of file diff --git a/thirdparty/liblastfm2/admin/platform.rb b/thirdparty/liblastfm2/admin/platform.rb new file mode 100644 index 000000000..54eda1db1 --- /dev/null +++ b/thirdparty/liblastfm2/admin/platform.rb @@ -0,0 +1,101 @@ +# +# platform.rb: naive platform detection for Ruby +# author: Matt Mower +# + +# == Platform +# +# Platform is a simple module which parses the Ruby constant +# RUBY_PLATFORM and works out the OS, it's implementation, +# and the architecture it's running on. +# +# The motivation for writing this was coming across a case where +# +# +if RUBY_PLATFORM =~ /win/+ +# +# didn't behave as expected (i.e. on powerpc-darwin-8.1.0) +# +# It is hoped that providing a library for parsing the platform +# means that we can cover all the cases and have something which +# works reliably 99% of the time. +# +# Please report any anomalies or new combinations to the author(s). +# +# == Use +# +# require "platform" +# +# defines +# +# Platform::OS (:unix,:win32,:vms,:os2) +# Platform::IMPL (:macosx,:linux,:mswin) +# Platform::ARCH (:powerpc,:x86,:alpha) +# +# if an unknown configuration is encountered any (or all) of +# these constant may have the value :unknown. +# +# To display the combination for your setup run +# +# ruby platform.rb +# +module Platform + + if RUBY_PLATFORM =~ /darwin/i + OS = :unix + IMPL = :macosx + elsif RUBY_PLATFORM =~ /linux/i + OS = :unix + IMPL = :linux + elsif RUBY_PLATFORM =~ /freebsd/i + OS = :unix + IMPL = :freebsd + elsif RUBY_PLATFORM =~ /netbsd/i + OS = :unix + IMPL = :netbsd + elsif RUBY_PLATFORM =~ /mswin/i + OS = :win32 + IMPL = :mswin + elsif RUBY_PLATFORM =~ /cygwin/i + OS = :win32 + IMPL = :mswin + elsif RUBY_PLATFORM =~ /mingw/i + OS = :win32 + IMPL = :mingw + elsif RUBY_PLATFORM =~ /bccwin/i + OS = :win32 + IMPL = :bccwin + elsif RUBY_PLATFORM =~ /wince/i + OS = :win32 + IMPL = :wince + elsif RUBY_PLATFORM =~ /vms/i + OS = :vms + IMPL = :vms + elsif RUBY_PLATFORM =~ /os2/i + OS = :os2 + IMPL = :os2 # maybe there is some better choice here? + else + OS = :unknown + IMPL = :unknown + end + + # whither AIX, SOLARIS, and the other unixen? + + if RUBY_PLATFORM =~ /(i\d86)/i + ARCH = :x86 + elsif RUBY_PLATFORM =~ /ia64/i + ARCH = :ia64 + elsif RUBY_PLATFORM =~ /powerpc/i + ARCH = :powerpc + elsif RUBY_PLATFORM =~ /alpha/i + ARCH = :alpha + else + ARCH = :unknown + end + + # What about AMD, Turion, Motorola, etc..? + +end + +if __FILE__ == $0 + puts "Platform OS=#{Platform::OS}, IMPL=#{Platform::IMPL}, ARCH=#{Platform::ARCH}" +end diff --git a/thirdparty/liblastfm2/admin/qpp b/thirdparty/liblastfm2/admin/qpp new file mode 100755 index 000000000..a99c5ce34 --- /dev/null +++ b/thirdparty/liblastfm2/admin/qpp @@ -0,0 +1,79 @@ +#!/usr/bin/ruby +# Usage examples: +# qpp foo.pro => ./_file.qmake +# qpp foo/bar/ => ./_file.qmake +# +cwd = File.dirname( __FILE__ ) +require 'find' +require 'ftools' +require "#{cwd}/platform.rb" + +def find_sources() + Find.find( '.' ) do |path| + if File.directory?( path ) + excludes = ['.svn', 'tests', '_build', 'contrib'] + case Platform::IMPL + when :macosx then excludes << 'win' << 'linux' + when :mswin, :cygwin then excludes << 'mac' << 'linux' + when :linux, :freebsd then excludes << 'win' << 'mac' + else excludes << 'win' << 'mac' << 'linux' + end + Find.prune if excludes.include?( File.basename( path ) ) or (path != "." and not Dir["#{path}/*.pro"].empty? ) + elsif File.file?( path ) + case Platform::IMPL + when :macosx then next if /_(linux|win)\.(cpp|h)$/.match( path ) + when :mswin, :cygwin then next if /_(mac|linux)\.(cpp|h)$/.match( path ) + when :linux, :freebsd then next if /_(mac|win)\.(cpp|h)$/.match( path ) + end + yield( path, File.extname( path ) ) unless File.basename(path) == 'EXAMPLE.cpp' + end + end +end + +########################################################################### impl +sources = Array.new +headers = Array.new +forms = Array.new +resources = Array.new + +abort "usage: qpp file.pro.in" unless File.file? ARGV[0] + +File.open( ARGV[0] ).each_line do |line| + line.chomp! + + matches = /^\s*TEMPLATE += (.*)$/.match( line ) + if !matches.nil? + exit if matches[1].downcase == 'subdirs' + end + + matches = /^\s*VERSION += +((\d\.){0,2}\d)/.match( line ) + if !matches.nil? && !File.file?( "_version.h" ) + File.open( "_version.h", 'w' ) { |f| f.write( "#define VERSION \"#{matches[1]}\"\n" ) } + end +end + +Dir.chdir File.dirname(ARGV[0]) do + find_sources do |path, ext| + path.sub!( /^.\//, '' ) + case ext + when ".h" then headers << path + when ".ui" then forms << path + when ".qrc" then resources << path + when ".cpp" then sources << path + when ".mm" then sources << path if Platform::IMPL == :macosx + when ".m" then sources << path if Platform::IMPL == :macosx + end + end +end + +def write_section( section, array ) + return if array.empty? + print "#{section} +=" + array.each { |path| print " \\\n\t#{path}" } + puts +end + +write_section( "SOURCES", sources ) +write_section( "HEADERS", headers ) +write_section( "FORMS", forms ) +write_section( "RESOURCES", resources ) diff --git a/thirdparty/liblastfm2/admin/utils.rb b/thirdparty/liblastfm2/admin/utils.rb new file mode 100644 index 000000000..ddcb01917 --- /dev/null +++ b/thirdparty/liblastfm2/admin/utils.rb @@ -0,0 +1,40 @@ +cwd = File.dirname( __FILE__ ) +require "#{cwd}/platform.rb" + +def h(s, n) + case Platform::IMPL + when :mswin + puts '==> '+s + else + puts "\033[0;#{n}m==>\033[0;0;1m #{s} \033[0;0m" + end +end + +def h1 s + h(s, 34) +end + +def h2 s + h(s, 33) + yield +end + +def qmake_env(env, qenv) + env=Array.new(1,env) if env.instance_of? String + values=Array.new + env.each { |x| values << ENV[x] if ENV[x] } + if values.size > 0 + "#{qenv} = #{values.join(' ')}\n" + else + nil + end +end + +class PkgConfigNotFound < RuntimeError; end +class PkgNotFound < RuntimeError; end + +def pkgconfig pkg, prettyname + system "pkg-config --exists '#{pkg}'" + raise PkgConfigNotFound if $? == 127 + raise PkgNotFound.new(prettyname) if $? != 0 +end \ No newline at end of file diff --git a/thirdparty/liblastfm2/admin/which_qmake.rb b/thirdparty/liblastfm2/admin/which_qmake.rb new file mode 100644 index 000000000..f621de3e9 --- /dev/null +++ b/thirdparty/liblastfm2/admin/which_qmake.rb @@ -0,0 +1,38 @@ +require "#{File.dirname __FILE__}/platform.rb" + +class QMakeNotFound < RuntimeError; end +class QMakeTooOld < RuntimeError; end + +def which_qmake + args = '-v' + args += ' 2> /dev/null' unless Platform::IMPL == :mswin + + versions = Hash.new + ['qmake','qmake-qt4'].each do |qmake| + begin + /^Using Qt version (\d\.\d\.\d)(-(.+))?/.match( `#{qmake} #{args}` ) + rescue + end + versions[qmake] = $1 unless $1.nil? + end + + raise QMakeNotFound if versions.empty? + + versions.each do |key, v| + i = 1 + j = 0 + v.split( '.' ).reverse.each {|n| j += (n.to_i * i); i *= 100} + versions[key] = j + end + + versions.sort {|a,b| a[1]<=>b[1]} + + versions.each do |k, v| + if v >= 40400 + return k + end + raise QMakeTooOld + end +end + +puts which_qmake if __FILE__ == $0 \ No newline at end of file diff --git a/thirdparty/liblastfm2/cmake/modules/FindLibFFTW3.cmake b/thirdparty/liblastfm2/cmake/modules/FindLibFFTW3.cmake new file mode 100644 index 000000000..fa6419a2b --- /dev/null +++ b/thirdparty/liblastfm2/cmake/modules/FindLibFFTW3.cmake @@ -0,0 +1,45 @@ +# This file is copyrighted under the BSD-license for buildsystem files of KDE +# copyright 2010, Patrick von Reth +# +# +# - Try to find the LIBFFTW3 library +# Once done this will define +# +# LIBFFTW3_FOUND Set to TRUE if LIBFFTW3 librarys and include directory is found +# LIBFFTW3_INCLUDE_DIR The libfftw3 include directory +# LIBFFTW3_LIBRARY The libfftw3 librarys + +if(NOT LIBFFTW3_PRECISION) + message(STATUS "Searching for LIBFFTW3, using default precision float") + set(LIBFFTW3_PRECISION FLOAT) +endif(NOT LIBFFTW3_PRECISION) + +find_path(LIBFFTW3_INCLUDE_DIR fftw3.h) + +if(LIBFFTW3_PRECISION STREQUAL FLOAT) + set(LIBFFTW3_PRECISION_SUFFIX f) +endif(LIBFFTW3_PRECISION STREQUAL FLOAT) + +if(LIBFFTW3_PRECISION STREQUAL DOUBLE) + set(LIBFFTW3_PRECISION_SUFFIX "") +endif(LIBFFTW3_PRECISION STREQUAL DOUBLE) + +if(LIBFFTW3_PRECISION STREQUAL LDOUBLE) + set(LIBFFTW3_PRECISION_SUFFIX l) +endif(LIBFFTW3_PRECISION STREQUAL LDOUBLE) + +find_library(LIBFFTW3_LIBRARY NAMES fftw3${LIBFFTW3_PRECISION_SUFFIX} libfftw3${LIBFFTW3_PRECISION_SUFFIX}-3 fftw3${LIBFFTW3_PRECISION_SUFFIX}-3) + +if(FIND_LIBFFTW3_VERBOSE) + message(STATUS + "LIBFFTW3_PRECISION ${LIBFFTW3_PRECISION}, searched for fftw3${LIBFFTW3_PRECISION_SUFFIX} libfftw3${LIBFFTW3_PRECISION_SUFFIX}-3 fftw3${LIBFFTW3_PRECISION_SUFFIX}-3 + and found ${LIBFFTW3_LIBRARY}" + ) +endif(FIND_LIBFFTW3_VERBOSE) + +if(LIBFFTW3_LIBRARY AND LIBFFTW3_INCLUDE_DIR) + set(LIBFFTW3_FOUND TRUE) + message(STATUS "Found libfftw3 ${LIBFFTW3_LIBRARY}") +else(LIBFFTW3_LIBRARY AND LIBFFTW3_PLUGIN_PATH) + message(STATUS "Could not find libfftw3, get it http://www.fftw.org/") +endif(LIBFFTW3_LIBRARY AND LIBFFTW3_INCLUDE_DIR) diff --git a/thirdparty/liblastfm2/cmake/modules/FindLibSamplerate.cmake b/thirdparty/liblastfm2/cmake/modules/FindLibSamplerate.cmake new file mode 100644 index 000000000..d77536b2f --- /dev/null +++ b/thirdparty/liblastfm2/cmake/modules/FindLibSamplerate.cmake @@ -0,0 +1,22 @@ +# This file is copyrighted under the BSD-license for buildsystem files of KDE +# copyright 2010, Patrick von Reth +# +# +# - Try to find the libsamplerate library +# Once done this will define +# +# LIBSAMPLERATE_FOUND Set to TRUE if libsamplerate librarys and include directory is found +# LIBSAMPLERATE_LIBRARY The libsamplerate librarys +# LIBSAMPLERATE_INCLUDE_DIR The libsamplerate include directory + + +find_library(LIBSAMPLERATE_LIBRARY NAMES samplerate libsamplerate-0 samplerate-0) + +find_path(LIBSAMPLERATE_INCLUDE_DIR samplerate.h) + +if(LIBSAMPLERATE_LIBRARY AND LIBSAMPLERATE_INCLUDE_DIR) + set(LIBSAMPLERATE_FOUND TRUE) + message(STATUS "Found libsamplerate ${LIBSAMPLERATE_LIBRARY}") +else(LIBSAMPLERATE_LIBRARY AND LIBSAMPLERATE_PLUGIN_PATH) + message(STATUS "Could not find libsamplerate, get it http://www.mega-nerd.com/SRC/") +endif(LIBSAMPLERATE_LIBRARY AND LIBSAMPLERATE_INCLUDE_DIR) diff --git a/thirdparty/liblastfm2/configure b/thirdparty/liblastfm2/configure new file mode 100755 index 000000000..757edcbc1 --- /dev/null +++ b/thirdparty/liblastfm2/configure @@ -0,0 +1,127 @@ +#!/usr/bin/ruby +if ARGV.include? '--help' + puts "usage: ./configure [--prefix ] [--release] [--no-strip] [--skip-checks]" + exit +end + +cwd = File.dirname( __FILE__ ) +require "#{cwd}/admin/platform.rb" +require "#{cwd}/admin/which_qmake.rb" +require "#{cwd}/admin/utils.rb" + +begin + IO.read("#{cwd}/src/global.h") =~ /LASTFM_VERSION_STRING\s+"((\d\.)*\d)"/ + abort "Couldn't determine our version!" if $1.nil? + LFM_VERSION=$1 + ENV['LFM_VERSION']=LFM_VERSION + + h1 "Configuring liblastfm-#{LFM_VERSION}..." + + unless ARGV.include? '--skip-checks' + $qmake=which_qmake + pkgconfig 'samplerate', 'libsamplerate' + pkgconfig 'fftw3f', 'fftw' + puts 'Using '+`which #{$qmake}` unless Platform::IMPL == :mswin + else + $qmake='qmake' + end + + h2 'Determining installation prefix' do + if ARGV.include? '--prefix' + n=ARGV.index '--prefix' + ENV['LFM_PREFIX'] = ARGV[n+1] + end + ENV['LFM_PREFIX'] = '/usr/local' if ENV['LFM_PREFIX'].nil? + if File.exists? ENV['LFM_PREFIX'] and !File.directory? ENV['LFM_PREFIX'] + abort "Installation prefix exists but isn't a directory: "+ENV['LFM_PREFIX'] + end + puts "Will install to: "+ENV['LFM_PREFIX'] + end + + h1 'Generating Build System' + + h2 'Generating .qmake.env' do + f = File.new("#{cwd}/.qmake.env", 'w') + f.write qmake_env('CC', 'QMAKE_CC') + f.write qmake_env('CXX', 'QMAKE_CXX') + f.write qmake_env('LDFLAGS', 'QMAKE_LFLAGS_RELEASE') + f.write qmake_env(['CFLAGS', 'CPPFLAGS'], 'QMAKE_CFLAGS_RELEASE') + f.write qmake_env(['CXXFLAGS', 'CPPFLAGS'], 'QMAKE_CXXFLAGS_RELEASE') + f.close + end unless Platform::IMPL == :mswin + + h2 "Running qpp..." do + ['src/lastfm.pro','src/fingerprint/fingerprint.pro'].each do |p| + d="#{cwd}/#{File.dirname p}" + f=File.new "#{d}/_files.qmake", 'w' + f.write `ruby admin/qpp #{p}` + # on Windows VERSION produces lastfm0.dll, the 0 breaks the build + f.puts "VERSION = #{LFM_VERSION}" unless Platform::OS == :win32 + end + end + + h2 "Configuring qmake..." do + args=Array.new + args << '-spec macx-g++' if Platform::IMPL == :macosx + if ARGV.include? '--release' + args << '-config release' + args << '"CONFIG += app_bundle"' if Platform::IMPL == :macosx and ARGV.include? '--bundle' + else + args << '-config debug' + end + if ARGV.include? '--no-strip' + args << '"CONFIG += nostrip"' + end + ENV['LFM_QMAKE'] = "#{$qmake} #{args.join(' ')}" + end + + h2 "Generating Makefile..." do + hs = Array.new + hs << 'global.h' + hs << 'core/UrlBuilder.h' << 'core/XmlQuery.h' << 'core/misc.h' + hs << 'fingerprint/Fingerprint.h' << 'fingerprint/FingerprintableSource.h' + hs << 'radio/RadioStation.h' << 'radio/RadioTuner.h' + hs << 'scrobble/Audioscrobbler.h' << 'scrobble/ScrobblePoint.h' << 'scrobble/ScrobbleCache.h' + hs << 'types/AbstractType.h' << 'types/Track.h' << 'types/Mbid.h' << 'types/Artist.h' << 'types/Album.h' << 'types/FingerprintId.h' << 'types/Playlist.h' << 'types/Tag.h' << 'types/User.h' << 'types/Xspf.h' + hs << 'ws/ws.h' << 'ws/InternetConnectionMonitor.h' << 'ws/NetworkAccessManager.h' + + File.new("#{cwd}/Makefile", 'w').write `ruby admin/Makefile.rb #{hs.join(' ')}` + end + + case Platform::IMPL + when :mswin then make='nmake all' + else make='make' # NOTE only tested with GNU make, sorry :( + end + + puts + puts "Good, your configure is finished! Now type: #{make}" + +rescue QMakeTooOld + puts <<-sput + + Your version of Qt seems to be too old, we require Qt 4.4 or above. + + It is possible you have Qt3 and Qt4 both installed. Locate your Qt4 + installation and ensure it is placed first in the path, eg: + + PATH=/opt/qt4/bin:\$PATH ./configure + + sput + exit 1 +rescue QMakeNotFound + puts "Sorry, qmake was not found, is Qt4 installed?" + exit 2 +rescue PkgNotFound => e + puts <<-sput + + Sorry, we couldn't find #{e}. + You can try to compile anyway by forcing configure to finish: + + ./configure --skip-checks + + sput + exit 3 +rescue PkgConfigNotFound + puts "Sorry, pkg-config could not be found. You should install it!" + exit 4 +end diff --git a/thirdparty/liblastfm2/demos/demo1.cpp b/thirdparty/liblastfm2/demos/demo1.cpp new file mode 100644 index 000000000..b0fd8df99 --- /dev/null +++ b/thirdparty/liblastfm2/demos/demo1.cpp @@ -0,0 +1,95 @@ +/* + This software is in the public domain, furnished "as is", without technical + support, and with no warranty, express or implied, as to its usefulness for + any purpose. +*/ +#include // this includes everything in liblastfm, you may prefer +#include // to just include what you need with your project. Still +#include // we've given you the option. +#include +#include + +class ArtistList : public QListWidget +{ + Q_OBJECT + + QPointer reply; + QString artist; + +public: + ArtistList() + { + connect( this, + SIGNAL(itemActivated( QListWidgetItem* )), + SLOT(onItemActivated( QListWidgetItem* )) ); + } + + void getSimilar( const QString& artist ) + { + this->artist = artist; + setWindowTitle( "Loading " + artist + "..." ); + + // deleting a reply cancels the request and disconnects all signals + delete reply; + reply = lastfm::Artist( artist ).getSimilar(); + connect( reply, SIGNAL(finished()), SLOT(onGotSimilar()) ); + } + +private slots: + void onGotSimilar() + { + QNetworkReply* r = static_cast(sender()); + // always enclose retrieval functions in a try block, as they will + // throw if they can't parse the data + try + { + // you decode the response using the equivalent static function + QMap artists = lastfm::Artist::getSimilar( r ); + + clear(); + + // we iterate backwards because best match is last because the map + // sorts itself by key + QStringListIterator i( artists.values() ); + i.toBack(); + while (i.hasPrevious()) + addItem( i.previous() ); + + setWindowTitle( artist ); + } + catch (std::runtime_error& e) + { + // if getSimilar() failed to parse the QNetworkReply, then e will + // be of type lastfm::ws::ParseError, which derives + // std::runtime_error + qWarning() << e.what(); + } + } + + void onItemActivated( QListWidgetItem* item ) + { + getSimilar( item->text() ); + } +}; + + +int main( int argc, char** argv ) +{ + QApplication app( argc, argv ); + app.setApplicationName( "liblastfm" ); // used to generate UserAgent + + // all you need for non-authenticated webservices is your API key + // this one is a public one, it can only do artist.getSimilar calls, so + // I suggest you don't use it :P + lastfm::ws::ApiKey = "b25b959554ed76058ac220b7b2e0a026"; + + ArtistList artists; + artists.getSimilar( "nirvana" ); + artists.resize( 300, 400 ); // Qt picks truly asanine default sizes for its widgets + artists.show(); + + return app.exec(); +} + + +#include "demo1.moc" diff --git a/thirdparty/liblastfm2/demos/demo2.cpp b/thirdparty/liblastfm2/demos/demo2.cpp new file mode 100644 index 000000000..53c9a7ec9 --- /dev/null +++ b/thirdparty/liblastfm2/demos/demo2.cpp @@ -0,0 +1,114 @@ +/* + This software is in the public domain, furnished "as is", without technical + support, and with no warranty, express or implied, as to its usefulness for + any purpose. +*/ +#include +#include + + +struct MyCoreApp : QCoreApplication +{ + Q_OBJECT + +public: + MyCoreApp( int& argc, char**& argv ) : QCoreApplication( argc, argv ) + {} + +private slots: + void onWsError( lastfm::ws::Error e ) + { + // QNetworkReply will invoke this slot on application level errors + // mostly this is only stuff like Ws::InvalidSessionKey and + // Ws::InvalidApiKey + qWarning() << e; + } +}; + + +int main( int argc, char** argv ) +{ + MyCoreApp app( argc, argv ); + // this is used to generate the UserAgent for webservice requests + // please set it to something sensible in your application + app.setApplicationName( "liblastfm" ); + +////// you'll need to fill these in for this demo to work + lastfm::ws::Username = + lastfm::ws::ApiKey = + lastfm::ws::SharedSecret = + QString password = + +////// Usually you never have to construct an Last.fm WS API call manually + // eg. Track.getTopTags() just returns a QNetworkReply* but authentication is + // different. + // We're using getMobileSession here as we're a console app, but you + // should use getToken if you can as the user will be presented with a + // route that feels my trustworthy to them than entering their password + // into some random app they just downloaded... ;) + QMap params; + params["method"] = "auth.getMobileSession"; + params["username"] = lastfm::ws::Username; + params["authToken"] = lastfm::md5( (lastfm::ws::Username + lastfm::md5( password.toUtf8() )).toUtf8() ); + QNetworkReply* reply = lastfm::ws::post( params ); + + // never do this when an event loop is running it's a real HACK + QEventLoop loop; + loop.connect( reply, SIGNAL(finished()), SLOT(quit()) ); + loop.exec(); + + try + { + ////// Usually there is a convenience function to decode the output from + // ws calls too, but again, authentication is different. We think you + // need to handle it yourselves :P Also conveniently it means you + // can learn more about what our webservices return, eg. this service + // will return an XML document like this: + // + // + // + // mxcl + // d580d57f32848f5dcf574d1ce18d78b2 + // 1 + // + // + // + // If status is not "ok" then this function throws + lastfm::XmlQuery const lfm = lastfm::ws::parse( reply ); + + // replace username; because eg. perhaps the user typed their + // username with the wrong case + lastfm::ws::Username = lfm["session"]["name"].text(); + + // we now have a session key, you should save this, forever! Really. + // DO NOT AUTHENTICATE EVERY TIME THE APP STARTS! You only have to do + // this once. Or again if the user deletes your key on the site. If + // that happens you'll get notification to your onWsError() function, + // see above. + lastfm::ws::SessionKey = lfm["session"]["key"].text(); + + qDebug() << "sk:" << lastfm::ws::SessionKey; + + ////// because the SessionKey is now set, the AuthenticatedUser class will + // work. And we can call authenticated calls + QNetworkReply* reply = lastfm::AuthenticatedUser().getRecommendedArtists(); + + // again, you shouldn't do this.. ;) + QEventLoop loop; + loop.connect( reply, SIGNAL(finished()), SLOT(quit()) ); + loop.exec(); + + // yay, a list rec'd artists to stderr :) + qDebug() << lastfm::Artist::list( reply ); + } + catch (std::runtime_error& e) + { + // lastfm::ws::parse() can throw lastfm::ws::ParseError, this + // exception derives std::runtime_error + qWarning() << e.what(); + return 1; + } +} + + +#include "demo2.moc" diff --git a/thirdparty/liblastfm2/demos/demo3.cpp b/thirdparty/liblastfm2/demos/demo3.cpp new file mode 100644 index 000000000..2bf3c1db3 --- /dev/null +++ b/thirdparty/liblastfm2/demos/demo3.cpp @@ -0,0 +1,63 @@ +/* + This software is in the public domain, furnished "as is", without technical + support, and with no warranty, express or implied, as to its usefulness for + any purpose. +*/ +#include +#include +#include +#include "src/_version.h" + + +struct MyCoreApp : QCoreApplication +{ + Q_OBJECT + +public: + MyCoreApp( int& argc, char** argv ) : QCoreApplication( argc, argv ) + {} + +public slots: + void onStatus( int status ) + { + qDebug() << lastfm::Audioscrobbler::Status(status); + } +}; + + +int main( int argc, char** argv ) +{ + // all 6 of these lines are REQUIRED in order to scrobble + // this demo requires you to fill in the blanks as well... + lastfm::ws::Username = + lastfm::ws::ApiKey = + lastfm::ws::SharedSecret = + lastfm::ws::SessionKey = // you need to auth to get this... try demo2 + QCoreApplication::setApplicationName( "liblastfm" ); + QCoreApplication::setApplicationVersion( VERSION ); + + MyCoreApp app( argc, argv ); + + lastfm::MutableTrack t; + t.setArtist( "Max Howell" ); + t.setTitle( "I Told You Not To Trust Me With Your Daughter" ); + t.setDuration( 30 ); + t.stamp(); //sets track start time + + lastfm::Audioscrobbler as( "ass" ); + as.nowPlaying( t ); + // Audioscrobbler will submit whatever is in the cache when you call submit. + // And the cache is persistent between sessions. So you should cache at the + // scrobble point usually, not before + as.cache( t ); + + //FIXME I don't get it, but the timer never triggers! pls fork and fix! + QTimer::singleShot( 31*1000, &as, SLOT(submit()) ); + + app.connect( &as, SIGNAL(status(int)), SLOT(onStatus(int)) ); + + return app.exec(); +} + + +#include "demo3.moc" diff --git a/thirdparty/liblastfm2/demos/demos.pro b/thirdparty/liblastfm2/demos/demos.pro new file mode 100644 index 000000000..356edff74 --- /dev/null +++ b/thirdparty/liblastfm2/demos/demos.pro @@ -0,0 +1,3 @@ +QT = core gui network xml +LIBS += -llastfm -L$$DESTDIR +SOURCES = demo1.cpp # change to demo2.cpp (etc.) to compile that demo diff --git a/thirdparty/liblastfm2/src/CMakeLists.txt b/thirdparty/liblastfm2/src/CMakeLists.txt new file mode 100644 index 000000000..a9cd2622e --- /dev/null +++ b/thirdparty/liblastfm2/src/CMakeLists.txt @@ -0,0 +1,121 @@ +cmake_minimum_required(VERSION 2.6) + +# Macro to copy and rename headers +macro(copy_header from to) + configure_file( + ${CMAKE_CURRENT_SOURCE_DIR}/${from} + ${CMAKE_CURRENT_BINARY_DIR}/lastfm/${to} + COPY_ONLY + ) +endmacro(copy_header) + +# Copy headers +copy_header(core/misc.h misc.h) +copy_header(core/XmlQuery.h XmlQuery) +copy_header(core/UrlBuilder.h UrlBuilder) +copy_header(global.h global.h) +copy_header(radio/RadioTuner.h RadioTuner) +copy_header(radio/RadioStation.h RadioStation) +copy_header(scrobble/Audioscrobbler.h Audioscrobbler) +copy_header(scrobble/ScrobbleCache.h ScrobbleCache) +copy_header(scrobble/ScrobblePoint.h ScrobblePoint) +copy_header(types/AbstractType.h AbstractType) +copy_header(types/Album.h Album) +copy_header(types/Artist.h Artist) +copy_header(types/FingerprintId.h FingerprintId) +copy_header(types/Mbid.h Mbid) +copy_header(types/Playlist.h Playlist) +copy_header(types/Tag.h Tag) +copy_header(types/Track.h Track) +copy_header(types/User.h User) +copy_header(types/User.h UserList) +copy_header(types/Xspf.h Xspf) +copy_header(ws/ws.h ws.h) +copy_header(ws/InternetConnectionMonitor.h InternetConnectionMonitor) +copy_header(ws/NetworkAccessManager.h NetworkAccessManager) + +include_directories(${QT_INCLUDES} ${CMAKE_CURRENT_BINARY_DIR}) + +set(SOURCES + scrobble/ScrobbleCache.cpp + scrobble/Audioscrobbler.cpp + types/FingerprintId.cpp + types/Artist.cpp + types/Tag.cpp + types/Track.cpp + types/User.cpp + types/Xspf.cpp + types/Album.cpp + types/Playlist.cpp + types/Mbid.cpp + radio/RadioTuner.cpp + radio/RadioStation.cpp + core/UrlBuilder.cpp + core/misc.cpp + core/XmlQuery.cpp + ws/NetworkAccessManager.cpp + ws/ws.cpp + ws/InternetConnectionMonitor.cpp + ws/NetworkConnectionMonitor.cpp +) + +set(MOC_HEADERS + scrobble/Audioscrobbler.h + types/Track.h + radio/RadioTuner.h + ws/NetworkConnectionMonitor.h + ws/InternetConnectionMonitor.h + ws/NetworkAccessManager.h +) + +if(UNIX) + if(APPLE) + set(SOURCES ${SOURCES} ws/mac/MNetworkConnectionMonitor_mac.cpp) + set(MOC_HEADERS ${MOC_HEADERS} ws/mac/MNetworkConnectionMonitor.h) + else(APPLE) + set(SOURCES ${SOURCES} ws/linux/LNetworkConnectionMonitor_linux.cpp) + set(MOC_HEADERS ${MOC_HEADERS} ws/linux/LNetworkConnectionMonitor.h) + endif(APPLE) +endif(UNIX) +if(WIN32) + set(SOURCES ${SOURCES} ws/win/WNetworkConnectionMonitor_win.cpp) + set(MOC_HEADERS ${MOC_HEADERS} ws/win/WNetworkConnectionMonitor.h) +endif(WIN32) + +qt4_wrap_cpp(MOC_SOURCES ${MOC_HEADERS}) + +IF( WIN32 ) + add_library(tomahawk_lastfm2 SHARED + ${SOURCES} + ${MOC_SOURCES} + ) +ELSE() + add_definitions(-fPIC) + add_library(tomahawk_lastfm2 STATIC + ${SOURCES} + ${MOC_SOURCES} + ) +ENDIF() + +target_link_libraries(tomahawk_lastfm2 + ${QT_LIBRARIES} + ${QT_QTDBUS_LIBRARY} +) + +set_target_properties(tomahawk_lastfm2 PROPERTIES COMPILE_FLAGS "-DLASTFM_OHAI_QMAKE" ) + +if(APPLE) + target_link_libraries(tomahawk_lastfm2 + /System/Library/Frameworks/CoreFoundation.framework + /System/Library/Frameworks/SystemConfiguration.framework + ) +endif(APPLE) + + +IF( WIN32 ) + install(TARGETS tomahawk_lastfm2 + RUNTIME DESTINATION bin + LIBRARY DESTINATION lib${LIB_SUFFIX} + ARCHIVE DESTINATION lib${LIB_SUFFIX} + ) +ENDIF() diff --git a/thirdparty/liblastfm2/src/core/README b/thirdparty/liblastfm2/src/core/README new file mode 100644 index 000000000..b725eac20 --- /dev/null +++ b/thirdparty/liblastfm2/src/core/README @@ -0,0 +1,8 @@ +Files in lastfm-core are basically extensions to fundamental Qt classes. +They may be useful to you, but mainly they are here because they are useful to +liblastfm in general. + +A lot of the time they are convenience functions that hopefully at some point +Qt will make obsolete. + + diff --git a/thirdparty/liblastfm2/src/core/UrlBuilder.cpp b/thirdparty/liblastfm2/src/core/UrlBuilder.cpp new file mode 100644 index 000000000..0f451c7e5 --- /dev/null +++ b/thirdparty/liblastfm2/src/core/UrlBuilder.cpp @@ -0,0 +1,83 @@ +/* + Copyright 2009 Last.fm Ltd. + - Primarily authored by Max Howell, Jono Cole and Doug Mansell + + This file is part of liblastfm. + + liblastfm 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. + + liblastfm 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 liblastfm. If not, see . +*/ +#include "UrlBuilder.h" +#include +#include + + +QUrl +lastfm::UrlBuilder::url() const +{ + QUrl url; + url.setScheme( "http" ); + url.setHost( host() ); + url.setEncodedPath( path ); + return url; +} + + +QByteArray //static +lastfm::UrlBuilder::encode( QString s ) +{ + foreach (QChar c, QList() << '&' << '/' << ';' << '+' << '#' << '%') + if (s.contains( c )) + // the middle step may seem odd but this is what the site does + // eg. search for the exact string "Radiohead 2 + 2 = 5" + return QUrl::toPercentEncoding( s ).replace( "%20", "+" ).toPercentEncoding( "", "+" );; + + return QUrl::toPercentEncoding( s.replace( ' ', '+' ), "+" ); +} + + +QString //static +lastfm::UrlBuilder::host( const QLocale& locale ) +{ + switch (locale.language()) + { + case QLocale::Portuguese: return "www.lastfm.com.br"; + case QLocale::Turkish: return "www.lastfm.com.tr"; + case QLocale::French: return "www.lastfm.fr"; + case QLocale::Italian: return "www.lastfm.it"; + case QLocale::German: return "www.lastfm.de"; + case QLocale::Spanish: return "www.lastfm.es"; + case QLocale::Polish: return "www.lastfm.pl"; + case QLocale::Russian: return "www.lastfm.ru"; + case QLocale::Japanese: return "www.lastfm.jp"; + case QLocale::Swedish: return "www.lastfm.se"; + case QLocale::Chinese: return "cn.last.fm"; + default: return "www.last.fm"; + } +} + + +QUrl //static +lastfm::UrlBuilder::localize( QUrl url) +{ + url.setHost( url.host().replace( QRegExp("^(www.)?last.fm"), host() ) ); + return url; +} + + +QUrl //static +lastfm::UrlBuilder::mobilize( QUrl url ) +{ + url.setHost( url.host().replace( QRegExp("^(www.)?last"), "m.last" ) ); + return url; +} diff --git a/thirdparty/liblastfm2/src/core/UrlBuilder.h b/thirdparty/liblastfm2/src/core/UrlBuilder.h new file mode 100644 index 000000000..42014537c --- /dev/null +++ b/thirdparty/liblastfm2/src/core/UrlBuilder.h @@ -0,0 +1,69 @@ +/* + Copyright 2009 Last.fm Ltd. + - Primarily authored by Max Howell, Jono Cole and Doug Mansell + + This file is part of liblastfm. + + liblastfm 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. + + liblastfm 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 liblastfm. If not, see . +*/ +#ifndef LASTFM_URL_BUILDER_H +#define LASTFM_URL_BUILDER_H + +#include +#include +#include +#include + + +namespace lastfm +{ + /** For building www.last.fm urls. We have special rules for encoding and that */ + class LASTFM_DLLEXPORT UrlBuilder + { + QByteArray path; + + public: + /** Careful, the base is not encoded at all, we assume it is ASCII! + * If you need it encoded at all you must use the slash function. + * eg. UrlBuilder( "user" ).slash( "mxcl" ) ==> http://last.fm/user/mxcl + */ + UrlBuilder( const QString& base ) : path( '/' + base.toAscii() ) + {} + + UrlBuilder& slash( const QString& path ) { this->path += '/' + encode( path ); return *this; } + + QUrl url() const; + + /** www.last.fm becomes the local version, eg www.lastfm.de */ + static QUrl localize( QUrl ); + /** www.last.fm becomes m.last.fm, localisation is preserved */ + static QUrl mobilize( QUrl ); + + /** Use this to URL encode any database item (artist, track, album). It + * internally calls UrlEncodeSpecialChars to double encode some special + * symbols according to the same pattern as that used on the website. + * + * &, /, ;, +, # + * + * Use for any urls that go to www.last.fm + * Do not use for ws.audioscrobbler.com + */ + static QByteArray encode( QString ); + + /** returns eg. www.lastfm.de */ + static QString host( const QLocale& = QLocale() ); + }; +} + +#endif diff --git a/thirdparty/liblastfm2/src/core/XmlQuery.cpp b/thirdparty/liblastfm2/src/core/XmlQuery.cpp new file mode 100644 index 000000000..5c68aaa81 --- /dev/null +++ b/thirdparty/liblastfm2/src/core/XmlQuery.cpp @@ -0,0 +1,64 @@ +/* + Copyright 2009 Last.fm Ltd. + - Primarily authored by Max Howell, Jono Cole and Doug Mansell + + This file is part of liblastfm. + + liblastfm 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. + + liblastfm 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 liblastfm. If not, see . +*/ +#include "XmlQuery.h" +#include +using lastfm::XmlQuery; + + +XmlQuery::XmlQuery( const QByteArray& bytes ) +{ + domdoc.setContent(bytes); + e = domdoc.documentElement(); +} + + +XmlQuery +XmlQuery::operator[]( const QString& name ) const +{ + QStringList parts = name.split( ' ' ); + if (parts.size() >= 2) + { + QString tagName = parts[0]; + parts = parts[1].split( '=' ); + QString attributeName = parts.value( 0 ); + QString attributeValue = parts.value( 1 ); + + foreach (XmlQuery e, children( tagName )) + if (e.e.attribute( attributeName ) == attributeValue) + return e; + } + XmlQuery xq( e.firstChildElement( name ), name.toUtf8().data() ); + xq.domdoc = this->domdoc; + return xq; +} + + +QList +XmlQuery::children( const QString& named ) const +{ + QList elements; + QDomNodeList nodes = e.elementsByTagName( named ); + for (int x = 0; x < nodes.count(); ++x) { + XmlQuery xq( nodes.at( x ).toElement() ); + xq.domdoc = this->domdoc; + elements += xq; + } + return elements; +} diff --git a/thirdparty/liblastfm2/src/core/XmlQuery.h b/thirdparty/liblastfm2/src/core/XmlQuery.h new file mode 100644 index 000000000..a3c22d5c2 --- /dev/null +++ b/thirdparty/liblastfm2/src/core/XmlQuery.h @@ -0,0 +1,77 @@ +/* + Copyright 2009 Last.fm Ltd. + - Primarily authored by Max Howell, Jono Cole and Doug Mansell + + This file is part of liblastfm. + + liblastfm 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. + + liblastfm 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 liblastfm. If not, see . +*/ +#ifndef LASTFM_XMLQUERY_H +#define LASTFM_XMLQUERY_H + +#include +#include +#include + +namespace lastfm +{ + /** Qt's XmlQuery implementation is totally unimpressive, so this is a + * hack that feels like jQuery */ + class LASTFM_DLLEXPORT XmlQuery + { + QDomDocument domdoc; + QDomElement e; + + public: + /** we assume the bytearray is an XML document, this object will then + * represent the documentElement of that document, eg. if this is a + * Last.fm webservice response: + * + * XmlQuery xq = lastfm::ws::parse(response); + * qDebug() << xq["artist"].text() + * + * Notice the lfm node is not referenced, that is because it is the + * document-element of the XML document. + */ + XmlQuery( const QByteArray& ); + + XmlQuery( const QDomElement& e, const char* name = "" ) : e( e ) + { + if (e.isNull()) qWarning() << "Expected node absent:" << name; + } + + /** Selects a DIRECT child element, you can specify attributes like so: + * + * e["element"]["element attribute=value"].text(); + */ + XmlQuery operator[]( const QString& name ) const; + QString text() const { return e.text(); } + QString attribute( const QString& name ) const{ return e.attribute( name ); } + + /** selects all children with specified name, recursively */ + QList children( const QString& named ) const; + + operator QDomElement() const { return e; } + }; +} + +inline QDebug operator<<( QDebug d, const lastfm::XmlQuery& xq ) +{ + QString s; + QTextStream t( &s, QIODevice::WriteOnly ); + QDomElement(xq).save( t, 2 ); + return d << s; +} + +#endif diff --git a/thirdparty/liblastfm2/src/core/misc.cpp b/thirdparty/liblastfm2/src/core/misc.cpp new file mode 100644 index 000000000..006089925 --- /dev/null +++ b/thirdparty/liblastfm2/src/core/misc.cpp @@ -0,0 +1,174 @@ +/* + Copyright 2009 Last.fm Ltd. + - Primarily authored by Max Howell, Jono Cole and Doug Mansell + + This file is part of liblastfm. + + liblastfm 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. + + liblastfm 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 liblastfm. If not, see . +*/ +#include "misc.h" +#include +#ifdef WIN32 + #include +#endif +#ifdef Q_WS_MAC + #include +#endif + + +#ifdef Q_WS_MAC +QDir +lastfm::dir::bundle() +{ + // Trolltech provided example + CFURLRef appUrlRef = CFBundleCopyBundleURL( CFBundleGetMainBundle() ); + CFStringRef macPath = CFURLCopyFileSystemPath( appUrlRef, kCFURLPOSIXPathStyle ); + QString path = CFStringToQString( macPath ); + CFRelease(appUrlRef); + CFRelease(macPath); + return QDir( path ); +} +#endif + + +static QDir dataDotDot() +{ +#ifdef WIN32 + if ((QSysInfo::WindowsVersion & QSysInfo::WV_DOS_based) == 0) + { + // Use this for non-DOS-based Windowses + char path[MAX_PATH]; + HRESULT h = SHGetFolderPathA( NULL, + CSIDL_LOCAL_APPDATA | CSIDL_FLAG_CREATE, + NULL, + 0, + path ); + if (h == S_OK) + return QString::fromLocal8Bit( path ); + } + return QDir::home(); + +#elif defined(Q_WS_MAC) + return QDir::home().filePath( "Library/Application Support" ); +#elif defined(Q_WS_X11) + return QDir::home().filePath( ".local/share" ); + +#else + return QDir::home(); +#endif +} + + +QDir +lastfm::dir::runtimeData() +{ + return dataDotDot().filePath( "Last.fm" ); +} + + +QDir +lastfm::dir::logs() +{ +#ifdef Q_WS_MAC + return QDir::home().filePath( "Library/Logs/Last.fm" ); +#else + return runtimeData(); +#endif +} + + +QDir +lastfm::dir::cache() +{ +#ifdef Q_WS_MAC + return QDir::home().filePath( "Library/Caches/Last.fm" ); +#else + return runtimeData().filePath( "cache" ); +#endif +} + + +#ifdef WIN32 +QDir +lastfm::dir::programFiles() +{ + char path[MAX_PATH]; + + // TODO: this call is dependant on a specific version of shell32.dll. + // Need to degrade gracefully. Need to bundle SHFolder.exe with installer + // and execute it on install for this to work on Win98. + HRESULT h = SHGetFolderPathA( NULL, + CSIDL_PROGRAM_FILES, + NULL, + 0, // current path + path ); + + if (h != S_OK) + { + qCritical() << "Couldn't get Program Files dir. Possibly Win9x?"; + return QDir(); + } + + return QString::fromLocal8Bit( path ); +} +#endif + +#ifdef Q_WS_MAC +CFStringRef +lastfm::QStringToCFString( const QString &s ) +{ + return CFStringCreateWithCharacters( 0, (UniChar*)s.unicode(), s.length() ); +} + +QByteArray +lastfm::CFStringToUtf8( CFStringRef s ) +{ + QByteArray result; + + if (s != NULL) + { + CFIndex length; + length = CFStringGetLength( s ); + length = CFStringGetMaximumSizeForEncoding( length, kCFStringEncodingUTF8 ) + 1; + char* buffer = new char[length]; + + if (CFStringGetCString( s, buffer, length, kCFStringEncodingUTF8 )) + result = QByteArray( buffer ); + else + qWarning() << "CFString conversion failed."; + + delete[] buffer; + } + + return result; +} +#endif + +#if 0 +// this is a Qt implementation I found +QString cfstring2qstring(CFStringRef str) +{ + if(!str) + return QString(); + + CFIndex length = CFStringGetLength(str); + if(const UniChar *chars = CFStringGetCharactersPtr(str)) + return QString((QChar *)chars, length); + UniChar *buffer = (UniChar*)malloc(length * sizeof(UniChar)); + CFStringGetCharacters(str, CFRangeMake(0, length), buffer); + QString ret((QChar *)buffer, length); + free(buffer); + return ret; +} +#endif diff --git a/thirdparty/liblastfm2/src/core/misc.h b/thirdparty/liblastfm2/src/core/misc.h new file mode 100644 index 000000000..3f41ec534 --- /dev/null +++ b/thirdparty/liblastfm2/src/core/misc.h @@ -0,0 +1,111 @@ +/* + Copyright 2009 Last.fm Ltd. + - Primarily authored by Max Howell, Jono Cole and Doug Mansell + + This file is part of liblastfm. + + liblastfm 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. + + liblastfm 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 liblastfm. If not, see . +*/ +#ifndef LASTFM_MISC_H +#define LASTFM_MISC_H + +#include +#include +#include +#include + +#ifdef Q_WS_MAC +typedef const struct __CFString* CFStringRef; +#endif + +namespace lastfm +{ + namespace dir + { + #ifdef Q_WS_WIN + LASTFM_DLLEXPORT QDir programFiles(); + #endif + #ifdef Q_WS_MAC + LASTFM_DLLEXPORT QDir bundle(); + #endif + LASTFM_DLLEXPORT QDir runtimeData(); + LASTFM_DLLEXPORT QDir cache(); + LASTFM_DLLEXPORT QDir logs(); + } + +#ifdef Q_WS_MAC + LASTFM_DLLEXPORT QByteArray CFStringToUtf8( CFStringRef ); + LASTFM_DLLEXPORT CFStringRef QStringToCFString( const QString& ); + inline QString CFStringToQString( CFStringRef s ); +#endif + + inline const char* platform() + { + #ifdef Q_WS_WIN + switch (QSysInfo::WindowsVersion) + { + case QSysInfo::WV_32s: return "Windows 3.1 with Win32s"; + case QSysInfo::WV_95: return "Windows 95"; + case QSysInfo::WV_98: return "Windows 98"; + case QSysInfo::WV_Me: return "Windows Me"; + case QSysInfo::WV_DOS_based: return "MS-DOS-based Windows"; + + case QSysInfo::WV_NT: return "Windows NT"; + case QSysInfo::WV_2000: return "Windows 2000"; + case QSysInfo::WV_XP: return "Windows XP"; + case QSysInfo::WV_2003: return "Windows Server 2003"; + case QSysInfo::WV_VISTA: return "Windows Vista"; + case QSysInfo::WV_NT_based: return "NT-based Windows"; + + case QSysInfo::WV_CE: return "Windows CE"; + case QSysInfo::WV_CENET: return "Windows CE.NET"; + case QSysInfo::WV_CE_based: return "CE-based Windows"; + + default: return "Unknown"; + } + #elif defined Q_WS_MAC + switch (QSysInfo::MacintoshVersion) + { + case QSysInfo::MV_Unknown: return "Unknown Mac"; + case QSysInfo::MV_9: return "Mac OS 9"; + case QSysInfo::MV_10_0: return "Mac OS X 10.0"; + case QSysInfo::MV_10_1: return "Mac OS X 10.1"; + case QSysInfo::MV_10_2: return "Mac OS X 10.2"; + case QSysInfo::MV_10_3: return "Mac OS X 10.3"; + case QSysInfo::MV_10_4: return "Mac OS X 10.4"; + case QSysInfo::MV_10_5: return "Mac OS X 10.5"; + + default: return "Unknown"; + } + #elif defined Q_WS_X11 + return "UNIX X11"; + #else + return "Unknown"; + #endif + } + + inline QString md5( const QByteArray& src ) + { + QByteArray const digest = QCryptographicHash::hash( src, QCryptographicHash::Md5 ); + return QString::fromLatin1( digest.toHex() ).rightJustified( 32, '0' ).toLower(); + } +} + +#ifdef Q_WS_MAC +inline QString lastfm::CFStringToQString( CFStringRef s ) +{ + return QString::fromUtf8( CFStringToUtf8( s ) ); +} +#endif +#endif //LASTFM_MISC_H diff --git a/thirdparty/liblastfm2/src/fingerprint/CMakeLists.txt b/thirdparty/liblastfm2/src/fingerprint/CMakeLists.txt new file mode 100644 index 000000000..eae8397a8 --- /dev/null +++ b/thirdparty/liblastfm2/src/fingerprint/CMakeLists.txt @@ -0,0 +1,32 @@ +cmake_minimum_required(VERSION 2.6) + +include_directories(${LIBFFTW3_INCLUDE_DIRS}) +include_directories(${LIBSAMPLERATE_INCLUDE_DIRS}) +include_directories(${QT_INCLUDES}) +include_directories(${CMAKE_CURRENT_BINARY_DIR}/..) + +link_directories(${LIBFFTW3_LIBRARY_DIRS}) +link_directories(${LIBSAMPLERATE_LIBRARY_DIRS}) + +set(SOURCES + Collection.cpp + Fingerprint.cpp + Sha256.cpp + fplib/Filter.cpp + fplib/FingerprintExtractor.cpp + fplib/OptFFT.cpp +) + +add_library(tomahawk_lastfm2_fingerprint SHARED + ${SOURCES} +) + +target_link_libraries(tomahawk_lastfm2_fingerprint + ${QT_LIBRARIES} + ${QT_QTSQL_LIBRARY} + ${LIBFFTW3_LIBRARY} + ${LIBSAMPLERATE_LIBRARY} + tomahawk_lastfm2 +) + +set_target_properties(tomahawk_lastfm2_fingerprint PROPERTIES COMPILE_FLAGS "-DLASTFM_FINGERPRINT_OHAI_QMAKE" ) diff --git a/thirdparty/liblastfm2/src/fingerprint/Collection.cpp b/thirdparty/liblastfm2/src/fingerprint/Collection.cpp new file mode 100644 index 000000000..214e264e2 --- /dev/null +++ b/thirdparty/liblastfm2/src/fingerprint/Collection.cpp @@ -0,0 +1,267 @@ +/* + Copyright 2009 Last.fm Ltd. + - Primarily authored by Max Howell, Jono Cole and Doug Mansell + + This file is part of liblastfm. + + liblastfm 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. + + liblastfm 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 liblastfm. If not, see . +*/ + +#include "Collection.h" +#include "../core/misc.h" +#include +#include +#include +#include +#include +#include +#include + +static const int k_collectionDbVersion = 1; + +// Singleton instance needs to be initialised +Collection* Collection::s_instance = NULL; + + +Collection::Collection() +{ + m_db = QSqlDatabase::addDatabase( "QSQLITE", "collection" ); + m_db.setDatabaseName( lastfm::dir::runtimeData().filePath( "collection.db" ) ); + + if (!m_db.open()) { + qDebug() << m_db.lastError(); + return; + } + + if (!m_db.isValid()) { + qWarning() << "collection.db connection is not valid"; + return; + } + + if (!m_db.tables().contains( "files" )) + { + qDebug() << "Creating Collection database"; + + query( "CREATE TABLE artists (" + "id INTEGER PRIMARY KEY AUTOINCREMENT," + "serverUid INTEGER," + "lcName TEXT NOT NULL," + "displayName TEXT NOT NULL );" ); + + query( "CREATE TABLE albums (" + "id INTEGER PRIMARY KEY AUTOINCREMENT," + "serverUid INTEGER," + "lcName TEXT NOT NULL," + "displayName TEXT NOT NULL," + "primaryArtist INTEGER NOT NULL );" ); + + query( "CREATE UNIQUE INDEX album_names_idx ON albums ( primaryArtist, lcName );" ); + + query( "CREATE TABLE tracks (" + "id INTEGER PRIMARY KEY AUTOINCREMENT," + "lcName TEXT NOT NULL," + "displayName TEXT NOT NULL," + "primaryArtist INTEGER NOT NULL," + "primaryAlbum INTEGER );" ); + + query( "CREATE UNIQUE INDEX track_names_idx ON tracks ( primaryArtist, lcName );" ); + + query( "CREATE TABLE files (" + "id INTEGER PRIMARY KEY AUTOINCREMENT," + "uri TEXT NOT NULL," + "track INTEGER NOT NULL," + "bitrate INTEGER," + "samplerate INTEGER," + "duration INTEGER," + "filesize INTEGER," + "source INTEGER," + "modificationDate INTEGER," + "lastPlayDate INTEGER," + "playCounter INTEGER," + "mbId VARCHAR( 36 )," + "fpId INTEGER );" ); + + query( "CREATE UNIQUE INDEX files_uri_idx ON files ( uri );" ); + query( "CREATE INDEX files_track_idx ON files ( track );" ); + query( "CREATE INDEX files_fpId_idx ON files ( fpId );" ); + query( "CREATE INDEX files_source_idx ON files ( source );" ); + + query( "CREATE TABLE sources (" + "id INTEGER PRIMARY KEY AUTOINCREMENT," + "name TEXT UNIQUE," + "available INTEGER," + "host TEXT," + "cost INTEGER );" ); + + query( "CREATE TABLE genres (" + "id INTEGER PRIMARY KEY AUTOINCREMENT," + "name TEXT UNIQUE );" ); + + query( "CREATE TABLE labels (" + "id INTEGER PRIMARY KEY AUTOINCREMENT," + "serverUid INTEGER UNIQUE," + "name TEXT );" ); + } + + int const v = version(); + if ( v < k_collectionDbVersion ) + { + qDebug() << "Upgrading Collection::db from" << v << "to" << k_collectionDbVersion; + + /********************************************** + * README!!!!!!! * + * Ensure you use v < x * + * Ensure you do upgrades in ascending order! * + **********************************************/ + + if ( v < 1 ) + { + // Norman discovered that he stored some fpId's wrong prior to 17th December 2007 + // So we have to wipe the fpIds for databases without the metadata table + // we didn't store version information before that, which was a bad decision wasn't it? + + // this will trigger refingerprinting of every track + query( "UPDATE files SET fpId = NULL;" ); + + query( "CREATE TABLE metadata (" + "key TEXT UNIQUE NOT NULL," + "value TEXT );" ); + + query( "INSERT INTO metadata (key, value) VALUES ('version', '1');" ); + } + + + // do last, update DB version number + query( "UPDATE metadata set key='version', value='" + + QString::number( k_collectionDbVersion ) + "';" ); + } +} + + +Collection& //static +Collection::instance() +{ + static QMutex mutex; + QMutexLocker locker( &mutex ); + + if ( !s_instance ) + { + s_instance = new Collection; + qAddPostRoutine(destroy); + } + + return *s_instance; +} + + +void //static +Collection::destroy() +{ + delete s_instance; + QSqlDatabase::removeDatabase( "collection" ); +} + + +int +Collection::version() const +{ + QSqlQuery sql( m_db ); + sql.exec( "SELECT value FROM metadata WHERE key='version';" ); + + if ( sql.next() ) + { + return sql.value( 0 ).toInt(); + } + + return 0; +} + + +bool +Collection::query( const QString& queryToken ) +{ + QSqlQuery query( m_db ); + query.exec( queryToken ); + + if ( query.lastError().isValid() ) + { + qDebug() << "SQL query failed:" << query.lastQuery() << endl + << "SQL error was:" << query.lastError().databaseText() << endl + << "SQL error type:" << query.lastError().type(); + + return false; + } + + return true; +} + + +QString +Collection::fileURI( const QString& filePath ) +{ + QString prefix( "file:/" ); + +#ifdef WIN32 + prefix = "file://"; +#endif + + return prefix + QFileInfo( filePath ).absoluteFilePath(); +} + + +QString +Collection::getFingerprintId( const QString& filePath ) +{ + QSqlQuery query( m_db ); + query.prepare( "SELECT fpId FROM files WHERE uri = :uri" ); + query.bindValue( ":uri", fileURI( filePath ) ); + + query.exec(); + if ( query.lastError().isValid() ) + { + qDebug() << "SQL query failed:" << query.lastQuery() << endl + << "SQL error was:" << query.lastError().databaseText() << endl + << "SQL error type:" << query.lastError().type(); + } + else if (query.next()) + return query.value( 0 ).toString(); + + return ""; +} + + +bool +Collection::setFingerprintId( const QString& filePath, QString fpId ) +{ + bool isNumeric; + int intFpId = fpId.toInt( &isNumeric ); + Q_ASSERT( isNumeric ); + + QSqlQuery query( m_db ); + query.prepare( "REPLACE INTO files ( uri, track, fpId ) VALUES ( :uri, 0, :fpId )" ); + query.bindValue( ":uri", fileURI( filePath ) ); + query.bindValue( ":fpId", intFpId ); + query.exec(); + + if ( query.lastError().isValid() ) + { + qDebug() << "SQL query failed:" << query.lastQuery() << endl + << "SQL error was:" << query.lastError().databaseText() << endl + << "SQL error type:" << query.lastError().type(); + + return false; + } + + return true; +} diff --git a/thirdparty/liblastfm2/src/fingerprint/Collection.h b/thirdparty/liblastfm2/src/fingerprint/Collection.h new file mode 100644 index 000000000..9a1f3bd17 --- /dev/null +++ b/thirdparty/liblastfm2/src/fingerprint/Collection.h @@ -0,0 +1,59 @@ +/* + Copyright 2009 Last.fm Ltd. + - Primarily authored by Max Howell, Jono Cole and Doug Mansell + + This file is part of liblastfm. + + liblastfm 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. + + liblastfm 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 liblastfm. If not, see . +*/ + +/** Class that we use to store fingerprints, basically + */ + +#ifndef COLLECTION_H +#define COLLECTION_H + +#include +#include + + +/** @author: */ +class Collection +{ +public: + static Collection& instance(); + + /** \brief Temp method: Gets a fingerprint id. Returns "" if none found. */ + QString getFingerprintId( const QString& filePath ); + + /** \brief Temp method: Sets a fingerprint id. */ + bool setFingerprintId( const QString& filePath, QString fpId ); + +private: + Collection(); + + /** the database version + * version 0: up until 1.4.1 + * version 1: from 1.4.2 */ + int version() const; + bool query( const QString& queryToken ); + QString fileURI( const QString& filePath ); + + static void destroy(); + + static Collection* s_instance; + QSqlDatabase m_db; +}; + +#endif // COLLECTION_H diff --git a/thirdparty/liblastfm2/src/fingerprint/EXAMPLE.cpp b/thirdparty/liblastfm2/src/fingerprint/EXAMPLE.cpp new file mode 100644 index 000000000..f86d4bbaf --- /dev/null +++ b/thirdparty/liblastfm2/src/fingerprint/EXAMPLE.cpp @@ -0,0 +1,76 @@ +/* + Copyright 2009 Last.fm Ltd. + - Primarily authored by Max Howell, Jono Cole and Doug Mansell + + This file is part of liblastfm. + + liblastfm 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. + + liblastfm 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 liblastfm. If not, see . +*/ +#include +#include +#include +#include + +using namespace lastfm; + + +static void finish( QNetworkReply* reply ) +{ + QEventLoop loop; + loop.connect( reply, SIGNAL(finished()), SLOT(quit()) ); + loop.exec(); +} + + +int main( int argc, char** argv ) +{ + QCoreApplication app( argc, argv ); + + // these fields are required + MutableTrack t; + t.setArtist( "Air" ); + t.setTitle( "Redhead Girl" ); + t.setAlbum( "Pocket Symphony" ); + t.setUrl( QUrl::fromLocalFile( "/Users/mxcl/Music/iTunes/iTunes Music/Air/Pocket Symphony/1-11 Redhead Girl.mp3") ); + + try + { + Fingerprint fp( t ); + + // we cache FingerprintIds in an sqlite3 db, as the generate() function + // is expensive + if (fp.id().isNull()) + { + // this generates the full fingerprint hash, which is about 20kB + fp.generate(); + + // this asks Last.fm for a FingerprintId + // the finish function is a Qt hack to allow syncronous HTTP + finish( fp.submit() ); + + // the decode step sets the FingerprintId + // the FingerprintId is required to obtain suggestions + // id will now be valid, or this function throws + fp.decode( reply ); + } + + finish( fp.id().getSuggestions() ); + + qDebug() << FingerprintId::getSuggestions( reply ); + } + catch (Fingerprint::Error e) + { + qWarning() << e; //TODO enum debug thing + } +} diff --git a/thirdparty/liblastfm2/src/fingerprint/Fingerprint.cpp b/thirdparty/liblastfm2/src/fingerprint/Fingerprint.cpp new file mode 100644 index 000000000..b23805c7d --- /dev/null +++ b/thirdparty/liblastfm2/src/fingerprint/Fingerprint.cpp @@ -0,0 +1,300 @@ +/* + Copyright 2009 Last.fm Ltd. + - Primarily authored by Max Howell, Jono Cole and Doug Mansell + + This file is part of liblastfm. + + liblastfm 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. + + liblastfm 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 liblastfm. If not, see . +*/ + +#include "Fingerprint.h" +#include "FingerprintableSource.h" +#include "Collection.h" +#include "Sha256.h" +#include "fplib/FingerprintExtractor.h" +#include "../ws/ws.h" +#include +#include +#include +#include +#include +#include + +using lastfm::Track; + +static const uint k_bufferSize = 1024 * 8; +static const int k_minTrackDuration = 30; + + +lastfm::Fingerprint::Fingerprint( const Track& t ) + : m_track( t ) + , m_id( -1 ), m_duration( 0 ) + , m_complete( false ) +{ + QString id = Collection::instance().getFingerprintId( t.url().toLocalFile() ); + if (id.size()) { + bool b; + m_id = id.toInt( &b ); + if (!b) m_id = -1; + } +} + + +void +lastfm::Fingerprint::generate( FingerprintableSource* ms ) throw( Error ) +{ + //TODO throw if we can't get required metadata from the track object + +//TODO if (!QFileInfo( path ).isReadable()) +//TODO throw ReadError; + + int sampleRate, bitrate, numChannels; + + if ( !ms ) + throw ReadError; + + try + { + ms->init( m_track.url().toLocalFile() ); + ms->getInfo( m_duration, sampleRate, bitrate, numChannels ); + } + catch (std::exception& e) + { + qWarning() << e.what(); + throw HeadersError; + } + + + if (m_duration < k_minTrackDuration) + throw TrackTooShortError; + + ms->skipSilence(); + + bool fpDone = false; + fingerprint::FingerprintExtractor* extractor; + try + { + extractor = new fingerprint::FingerprintExtractor; + + if (m_complete) + { + extractor->initForFullSubmit( sampleRate, numChannels ); + } + else + { + extractor->initForQuery( sampleRate, numChannels, m_duration ); + + // Skippety skip for as long as the skipper sez (optimisation) + ms->skip( extractor->getToSkipMs() ); + float secsToSkip = extractor->getToSkipMs() / 1000.0f; + fpDone = extractor->process( 0, + (size_t) sampleRate * numChannels * secsToSkip, + false ); + } + } + catch (std::exception& e) + { + qWarning() << e.what(); + throw DecodeError; + } + + const size_t PCMBufSize = 131072; + short* pPCMBuffer = new short[PCMBufSize]; + + while (!fpDone) + { + size_t readData = ms->updateBuffer( pPCMBuffer, PCMBufSize ); + if (readData == 0) + break; + + try + { + fpDone = extractor->process( pPCMBuffer, readData, ms->eof() ); + } + catch ( const std::exception& e ) + { + qWarning() << e.what(); + delete ms; + delete[] pPCMBuffer; + throw InternalError; + } + } + + delete[] pPCMBuffer; + + if (!fpDone) + throw InternalError; + + // We succeeded + std::pair fpData = extractor->getFingerprint(); + + if (fpData.first == NULL || fpData.second == 0) + throw InternalError; + + // Make a deep copy before extractor gets deleted + m_data = QByteArray( fpData.first, fpData.second ); + delete extractor; +} + + +static QString sha256( const QString& path ) +{ + // no clue why this is static, there was no comment when I refactored it + // initially --mxcl + static uint8_t pBuffer[SHA_BUFFER_SIZE+7]; + + unsigned char hash[SHA256_HASH_SIZE]; + + { + QByteArray path8 = QFile::encodeName( path ); + std::ifstream inFile( path8.data(), std::ios::binary); + + SHA256Context sha256; + SHA256Init( &sha256 ); + + uint8_t* pMovableBuffer = pBuffer; + + // Ensure it is on a 64-bit boundary. + INTPTR offs; + if ((offs = reinterpret_cast(pBuffer) & 7L)) + pMovableBuffer += 8 - offs; + + unsigned int len; + + for (;;) + { + inFile.read( reinterpret_cast(pMovableBuffer), SHA_BUFFER_SIZE ); + len = inFile.gcount(); + + if (len == 0) + break; + + SHA256Update( &sha256, pMovableBuffer, len ); + } + + SHA256Final( &sha256, hash ); + } + + QString sha; + for (int i = 0; i < SHA256_HASH_SIZE; ++i) + { + QString hex = QString("%1").arg(uchar(hash[i]), 2, 16, + QChar('0')); + sha.append(hex); + } + + return sha; +} + + +static QByteArray number( uint n ) +{ + return n ? QByteArray::number( n ) : ""; +} + +QNetworkReply* +lastfm::Fingerprint::submit() const +{ + if (m_data.isEmpty()) + return 0; + + //Parameters understood by the server according to the MIR team: + //{ "trackid", "recordingid", "artist", "album", "track", "duration", + // "tracknum", "username", "sha256", "ip", "fpversion", "mbid", + // "filename", "genre", "year", "samplerate", "noupdate", "fulldump" } + + Track const t = m_track; + QString const path = t.url().toLocalFile(); + QFileInfo const fi( path ); + + #define e( x ) QUrl::toPercentEncoding( x ) + QUrl url( "http://www.last.fm/fingerprint/query/" ); + url.addEncodedQueryItem( "artist", e(t.artist()) ); + url.addEncodedQueryItem( "album", e(t.album()) ); + url.addEncodedQueryItem( "track", e(t.title()) ); + url.addEncodedQueryItem( "duration", number( m_duration > 0 ? m_duration : t.duration() ) ); + url.addEncodedQueryItem( "mbid", e(t.mbid()) ); + url.addEncodedQueryItem( "filename", e(fi.completeBaseName()) ); + url.addEncodedQueryItem( "fileextension", e(fi.completeSuffix()) ); + url.addEncodedQueryItem( "tracknum", number( t.trackNumber() ) ); + url.addEncodedQueryItem( "sha256", sha256( path ).toAscii() ); + url.addEncodedQueryItem( "time", number(QDateTime::currentDateTime().toTime_t()) ); + url.addEncodedQueryItem( "fpversion", QByteArray::number((int)fingerprint::FingerprintExtractor::getVersion()) ); + url.addEncodedQueryItem( "fulldump", m_complete ? "true" : "false" ); + url.addEncodedQueryItem( "noupdate", "false" ); + #undef e + + //FIXME: talk to mir about submitting fplibversion + + QNetworkRequest request( url ); + request.setHeader( QNetworkRequest::ContentTypeHeader, "multipart/form-data; boundary=----------------------------8e61d618ca16" ); + + QByteArray bytes; + bytes += "------------------------------8e61d618ca16\r\n"; + bytes += "Content-Disposition: "; + bytes += "form-data; name=\"fpdata\""; + bytes += "\r\n\r\n"; + bytes += m_data; + bytes += "\r\n"; + bytes += "------------------------------8e61d618ca16--\r\n"; + + qDebug() << url; + qDebug() << "Fingerprint size:" << bytes.size() << "bytes"; + + return lastfm::nam()->post( request, bytes ); +} + + +void +lastfm::Fingerprint::decode( QNetworkReply* reply, bool* complete_fingerprint_requested ) throw( Error ) +{ + // The response data will consist of a number and a string. + // The number is the fpid and the string is either FOUND or NEW + // (or NOT FOUND when noupdate was used). NEW means we should + // schedule a full fingerprint. + // + // In the case of an error, there will be no initial number, just + // an error string. + + QString const response( reply->readAll() ); + QStringList const list = response.split( ' ' ); + + QString const fpid = list.value( 0 ); + QString const status = list.value( 1 ); + + if (response.isEmpty() || list.count() < 2 || response == "No response to client error") + goto bad_response; + if (list.count() != 2) + qWarning() << "Response looks bad but continuing anyway:" << response; + + { + // so variables go out of scope before jump to label + // otherwise compiler error on GCC 4.2 + bool b; + uint fpid_as_uint = fpid.toUInt( &b ); + if (!b) goto bad_response; + + Collection::instance().setFingerprintId( m_track.url().toLocalFile(), fpid ); + + if (complete_fingerprint_requested) + *complete_fingerprint_requested = (status == "NEW"); + + m_id = (int)fpid_as_uint; + return; + } + +bad_response: + qWarning() << "Response is bad:" << response; + throw BadResponseError; +} diff --git a/thirdparty/liblastfm2/src/fingerprint/Fingerprint.h b/thirdparty/liblastfm2/src/fingerprint/Fingerprint.h new file mode 100644 index 000000000..b793c4bb9 --- /dev/null +++ b/thirdparty/liblastfm2/src/fingerprint/Fingerprint.h @@ -0,0 +1,116 @@ +/* + Copyright 2009 Last.fm Ltd. + - Primarily authored by Max Howell, Jono Cole and Doug Mansell + + This file is part of liblastfm. + + liblastfm 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. + + liblastfm 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 liblastfm. If not, see . +*/ +#ifndef LASTFM_FINGERPRINT_H +#define LASTFM_FINGERPRINT_H + +#include +#include + + +namespace lastfm +{ + class LASTFM_FINGERPRINT_DLLEXPORT Fingerprint + { + lastfm::Track m_track; + QByteArray m_data; + int m_id; + int m_duration; + + protected: + bool m_complete; + + public: + /** represents a partial fingerprint of 20 seconds of music, this is + * considered 99.9999...9999% unique and so we use it for most stuff as + * it is much quicker than a complete fingerprint, still though, you + * should do the generate step in a thread. */ + Fingerprint( const lastfm::Track& ); + + /** if the id isNull(), then you'll need to do generate, submit and decode */ + FingerprintId id() const { return m_id; } + + /** The actual data that is the fingerprint, this is about 70kB or so, + * there isn't anything in it until you call generate. */ + QByteArray data() const { return m_data; } + + enum Error + { + ReadError = 0, + + /** failed to extract samplerate, bitrate, channels, duration etc */ + HeadersError, + + DecodeError, + + /** there is a minimum track duration for fingerprinting */ + TrackTooShortError, + + /** the fingerprint service went wrong, or we submitted bad data, + * or myabe the request failed, whatever, we couldn't parse the + * result */ + BadResponseError, + + /** sorry, liblastfm sucks, report bug with log! */ + InternalError + }; + + /** This is CPU intensive, do it in a thread in your GUI application */ + void generate( FingerprintableSource* ) throw( Error ); + + /** Submits the fingerprint data to Last.fm in order to get a FingerprintId + * back. You need to wait for the QNetworkReply to finish before you can + * pass it to decode clearly. */ + QNetworkReply* submit() const; + + /** Pass a finished reply from submit(), if the response is sound, id() + * will be valid. Otherwise we will throw. You always get a valid id + * or a throw. + */ + void decode( QNetworkReply*, bool* lastfm_needs_a_complete_fingerprint = 0 ) throw( Error ); + }; + + + class CompleteFingerprint : public Fingerprint + { + public: + CompleteFingerprint( const lastfm::Track& t ) : Fingerprint( t ) + { + m_complete = true; + } + }; +} + + +inline QDebug operator<<( QDebug d, lastfm::Fingerprint::Error e ) +{ + #define CASE(x) case lastfm::Fingerprint::x: return d << #x; + switch (e) + { + CASE(ReadError) + CASE(HeadersError) + CASE(DecodeError) + CASE(TrackTooShortError) + CASE(BadResponseError) + CASE(InternalError) + } + #undef CASE +} + +#endif diff --git a/thirdparty/liblastfm2/src/fingerprint/FingerprintableSource.h b/thirdparty/liblastfm2/src/fingerprint/FingerprintableSource.h new file mode 100644 index 000000000..9954fd368 --- /dev/null +++ b/thirdparty/liblastfm2/src/fingerprint/FingerprintableSource.h @@ -0,0 +1,48 @@ +/* + Copyright 2009 Last.fm Ltd. + Copyright 2009 John Stamp + + This file is part of liblastfm. + + liblastfm 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. + + liblastfm 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 liblastfm. If not, see . +*/ + +#ifndef LASTFM_FINGERPRINTABLE_SOURCE_H +#define LASTFM_FINGERPRINTABLE_SOURCE_H + +#include +#include + +namespace lastfm +{ + class LASTFM_FINGERPRINT_DLLEXPORT FingerprintableSource + { + public: + /** do all initialisation here and throw if there is problems */ + virtual void init( const QString& path ) = 0; + + virtual void getInfo( int& lengthSecs, int& samplerate, int& bitrate, int& nchannels ) = 0; + + /** put a chunk of PCM data in pBuffer, don't exceed size, return the + * number of bytes you put in the buffer */ + virtual int updateBuffer( signed short* buffer, size_t bufferSize ) = 0; + + virtual void skip( const int mSecs ) = 0; + virtual void skipSilence( double silenceThreshold = 0.0001 ) = 0; + + virtual bool eof() const = 0; + }; +} + +#endif diff --git a/thirdparty/liblastfm2/src/fingerprint/Sha256.cpp b/thirdparty/liblastfm2/src/fingerprint/Sha256.cpp new file mode 100644 index 000000000..9be3c18ac --- /dev/null +++ b/thirdparty/liblastfm2/src/fingerprint/Sha256.cpp @@ -0,0 +1,480 @@ +/*- + * Copyright (c) 2001-2003 Allan Saddi + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY ALLAN SADDI AND HIS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL ALLAN SADDI OR HIS CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * $Id: sha256.c 680 2003-07-25 21:57:49Z asaddi $ + */ + +/* + * Define WORDS_BIGENDIAN if compiling on a big-endian architecture. + * + * Define SHA256_TEST to test the implementation using the NIST's + * sample messages. The output should be: + * + * ba7816bf 8f01cfea 414140de 5dae2223 b00361a3 96177a9c b410ff61 f20015ad + * 248d6a61 d20638b8 e5c02693 0c3e6039 a33ce459 64ff2167 f6ecedd4 19db06c1 + * cdc76e5c 9914fb92 81a1c7e2 84d73e67 f1809a48 a497200e 046d39cc c7112cd0 + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /* HAVE_CONFIG_H */ + +#if HAVE_INTTYPES_H +# include +#else +# if HAVE_STDINT_H +# include +# endif +#endif + +#include + +#include "Sha256.h" + +#ifndef lint +static const char rcsid[] = + "$Id: sha256.c 680 2003-07-25 21:57:49Z asaddi $"; +#endif /* !lint */ + +#define ROTL(x, n) (((x) << (n)) | ((x) >> (32 - (n)))) +#define ROTR(x, n) (((x) >> (n)) | ((x) << (32 - (n)))) + +#define Ch(x, y, z) ((z) ^ ((x) & ((y) ^ (z)))) +#define Maj(x, y, z) (((x) & ((y) | (z))) | ((y) & (z))) +#define SIGMA0(x) (ROTR((x), 2) ^ ROTR((x), 13) ^ ROTR((x), 22)) +#define SIGMA1(x) (ROTR((x), 6) ^ ROTR((x), 11) ^ ROTR((x), 25)) +#define sigma0(x) (ROTR((x), 7) ^ ROTR((x), 18) ^ ((x) >> 3)) +#define sigma1(x) (ROTR((x), 17) ^ ROTR((x), 19) ^ ((x) >> 10)) + +#define DO_ROUND() { \ + t1 = h + SIGMA1(e) + Ch(e, f, g) + *(Kp++) + *(W++); \ + t2 = SIGMA0(a) + Maj(a, b, c); \ + h = g; \ + g = f; \ + f = e; \ + e = d + t1; \ + d = c; \ + c = b; \ + b = a; \ + a = t1 + t2; \ +} + +static const uint32_t K[64] = { + 0x428a2f98L, 0x71374491L, 0xb5c0fbcfL, 0xe9b5dba5L, + 0x3956c25bL, 0x59f111f1L, 0x923f82a4L, 0xab1c5ed5L, + 0xd807aa98L, 0x12835b01L, 0x243185beL, 0x550c7dc3L, + 0x72be5d74L, 0x80deb1feL, 0x9bdc06a7L, 0xc19bf174L, + 0xe49b69c1L, 0xefbe4786L, 0x0fc19dc6L, 0x240ca1ccL, + 0x2de92c6fL, 0x4a7484aaL, 0x5cb0a9dcL, 0x76f988daL, + 0x983e5152L, 0xa831c66dL, 0xb00327c8L, 0xbf597fc7L, + 0xc6e00bf3L, 0xd5a79147L, 0x06ca6351L, 0x14292967L, + 0x27b70a85L, 0x2e1b2138L, 0x4d2c6dfcL, 0x53380d13L, + 0x650a7354L, 0x766a0abbL, 0x81c2c92eL, 0x92722c85L, + 0xa2bfe8a1L, 0xa81a664bL, 0xc24b8b70L, 0xc76c51a3L, + 0xd192e819L, 0xd6990624L, 0xf40e3585L, 0x106aa070L, + 0x19a4c116L, 0x1e376c08L, 0x2748774cL, 0x34b0bcb5L, + 0x391c0cb3L, 0x4ed8aa4aL, 0x5b9cca4fL, 0x682e6ff3L, + 0x748f82eeL, 0x78a5636fL, 0x84c87814L, 0x8cc70208L, + 0x90befffaL, 0xa4506cebL, 0xbef9a3f7L, 0xc67178f2L +}; + +#ifndef RUNTIME_ENDIAN + +#ifdef WORDS_BIGENDIAN + +#define BYTESWAP(x) (x) +#define BYTESWAP64(x) (x) + +#else /* WORDS_BIGENDIAN */ + +#define BYTESWAP(x) ((ROTR((x), 8) & 0xff00ff00L) | \ + (ROTL((x), 8) & 0x00ff00ffL)) +#define BYTESWAP64(x) _byteswap64(x) + +static inline uint64_t _byteswap64(uint64_t x) +{ + uint32_t a = x >> 32; + uint32_t b = (uint32_t) x; + return ((uint64_t) BYTESWAP(b) << 32) | (uint64_t) BYTESWAP(a); +} + +#endif /* WORDS_BIGENDIAN */ + +#else /* !RUNTIME_ENDIAN */ + +#define BYTESWAP(x) _byteswap(sc->littleEndian, x) +#define BYTESWAP64(x) _byteswap64(sc->littleEndian, x) + +#define _BYTESWAP(x) ((ROTR((x), 8) & 0xff00ff00L) | \ + (ROTL((x), 8) & 0x00ff00ffL)) +#define _BYTESWAP64(x) __byteswap64(x) + +static inline uint64_t __byteswap64(uint64_t x) +{ + uint32_t a = x >> 32; + uint32_t b = (uint32_t) x; + return ((uint64_t) _BYTESWAP(b) << 32) | (uint64_t) _BYTESWAP(a); +} + +static inline uint32_t _byteswap(int littleEndian, uint32_t x) +{ + if (!littleEndian) + return x; + else + return _BYTESWAP(x); +} + +static inline uint64_t _byteswap64(int littleEndian, uint64_t x) +{ + if (!littleEndian) + return x; + else + return _BYTESWAP64(x); +} + +static inline void setEndian(int *littleEndianp) +{ + union { + uint32_t w; + uint8_t b[4]; + } endian; + + endian.w = 1L; + *littleEndianp = endian.b[0] != 0; +} + +#endif /* !RUNTIME_ENDIAN */ + +static const uint8_t padding[64] = { + 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +void +SHA256Init (SHA256Context *sc) +{ +#ifdef RUNTIME_ENDIAN + setEndian (&sc->littleEndian); +#endif /* RUNTIME_ENDIAN */ + + sc->totalLength = 0LL; + sc->hash[0] = 0x6a09e667L; + sc->hash[1] = 0xbb67ae85L; + sc->hash[2] = 0x3c6ef372L; + sc->hash[3] = 0xa54ff53aL; + sc->hash[4] = 0x510e527fL; + sc->hash[5] = 0x9b05688cL; + sc->hash[6] = 0x1f83d9abL; + sc->hash[7] = 0x5be0cd19L; + sc->bufferLength = 0L; +} + +static void +burnStack (int size) +{ + char buf[128]; + + memset (buf, 0, sizeof (buf)); + size -= sizeof (buf); + if (size > 0) + burnStack (size); +} + +static void +SHA256Guts (SHA256Context *sc, const uint32_t *cbuf) +{ + uint32_t buf[64]; + uint32_t *W, *W2, *W7, *W15, *W16; + uint32_t a, b, c, d, e, f, g, h; + uint32_t t1, t2; + const uint32_t *Kp; + int i; + + W = buf; + + for (i = 15; i >= 0; i--) { + *(W++) = BYTESWAP(*cbuf); + cbuf++; + } + + W16 = &buf[0]; + W15 = &buf[1]; + W7 = &buf[9]; + W2 = &buf[14]; + + for (i = 47; i >= 0; i--) { + *(W++) = sigma1(*W2) + *(W7++) + sigma0(*W15) + *(W16++); + W2++; + W15++; + } + + a = sc->hash[0]; + b = sc->hash[1]; + c = sc->hash[2]; + d = sc->hash[3]; + e = sc->hash[4]; + f = sc->hash[5]; + g = sc->hash[6]; + h = sc->hash[7]; + + Kp = K; + W = buf; + +#ifndef SHA256_UNROLL +#define SHA256_UNROLL 1 +#endif /* !SHA256_UNROLL */ + +#if SHA256_UNROLL == 1 + for (i = 63; i >= 0; i--) + DO_ROUND(); +#elif SHA256_UNROLL == 2 + for (i = 31; i >= 0; i--) { + DO_ROUND(); DO_ROUND(); + } +#elif SHA256_UNROLL == 4 + for (i = 15; i >= 0; i--) { + DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND(); + } +#elif SHA256_UNROLL == 8 + for (i = 7; i >= 0; i--) { + DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND(); + DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND(); + } +#elif SHA256_UNROLL == 16 + for (i = 3; i >= 0; i--) { + DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND(); + DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND(); + DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND(); + DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND(); + } +#elif SHA256_UNROLL == 32 + for (i = 1; i >= 0; i--) { + DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND(); + DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND(); + DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND(); + DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND(); + DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND(); + DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND(); + DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND(); + DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND(); + } +#elif SHA256_UNROLL == 64 + DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND(); + DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND(); + DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND(); + DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND(); + DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND(); + DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND(); + DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND(); + DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND(); + DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND(); + DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND(); + DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND(); + DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND(); + DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND(); + DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND(); + DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND(); + DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND(); +#else +#error "SHA256_UNROLL must be 1, 2, 4, 8, 16, 32, or 64!" +#endif + + sc->hash[0] += a; + sc->hash[1] += b; + sc->hash[2] += c; + sc->hash[3] += d; + sc->hash[4] += e; + sc->hash[5] += f; + sc->hash[6] += g; + sc->hash[7] += h; +} + +void +SHA256Update (SHA256Context *sc, const void *vdata, uint32_t len) +{ + const uint8_t *data = (const uint8_t*)vdata; + uint32_t bufferBytesLeft; + uint32_t bytesToCopy; + int needBurn = 0; + +#ifdef SHA256_FAST_COPY + if (sc->bufferLength) { + bufferBytesLeft = 64L - sc->bufferLength; + + bytesToCopy = bufferBytesLeft; + if (bytesToCopy > len) + bytesToCopy = len; + + memcpy (&sc->buffer.bytes[sc->bufferLength], data, bytesToCopy); + + sc->totalLength += bytesToCopy * 8L; + + sc->bufferLength += bytesToCopy; + data += bytesToCopy; + len -= bytesToCopy; + + if (sc->bufferLength == 64L) { + SHA256Guts (sc, sc->buffer.words); + needBurn = 1; + sc->bufferLength = 0L; + } + } + + while (len > 63L) { + sc->totalLength += 512L; + + SHA256Guts (sc, data); + needBurn = 1; + + data += 64L; + len -= 64L; + } + + if (len) { + memcpy (&sc->buffer.bytes[sc->bufferLength], data, len); + + sc->totalLength += len * 8L; + + sc->bufferLength += len; + } +#else /* SHA256_FAST_COPY */ + while (len) { + bufferBytesLeft = 64L - sc->bufferLength; + + bytesToCopy = bufferBytesLeft; + if (bytesToCopy > len) + bytesToCopy = len; + + memcpy (&sc->buffer.bytes[sc->bufferLength], data, bytesToCopy); + + sc->totalLength += bytesToCopy * 8L; + + sc->bufferLength += bytesToCopy; + data += bytesToCopy; + len -= bytesToCopy; + + if (sc->bufferLength == 64L) { + SHA256Guts (sc, sc->buffer.words); + needBurn = 1; + sc->bufferLength = 0L; + } + } +#endif /* SHA256_FAST_COPY */ + + if (needBurn) + burnStack (sizeof (uint32_t[74]) + sizeof (uint32_t *[6]) + sizeof (int)); +} + +void +SHA256Final (SHA256Context *sc, uint8_t hash[SHA256_HASH_SIZE]) +{ + uint32_t bytesToPad; + uint64_t lengthPad; + int i; + + bytesToPad = 120L - sc->bufferLength; + if (bytesToPad > 64L) + bytesToPad -= 64L; + + lengthPad = BYTESWAP64(sc->totalLength); + + SHA256Update (sc, padding, bytesToPad); + SHA256Update (sc, &lengthPad, 8L); + + if (hash) { + for (i = 0; i < SHA256_HASH_WORDS; i++) { +#ifdef SHA256_FAST_COPY + *((uint32_t *) hash) = BYTESWAP(sc->hash[i]); +#else /* SHA256_FAST_COPY */ + hash[0] = (uint8_t) (sc->hash[i] >> 24); + hash[1] = (uint8_t) (sc->hash[i] >> 16); + hash[2] = (uint8_t) (sc->hash[i] >> 8); + hash[3] = (uint8_t) sc->hash[i]; +#endif /* SHA256_FAST_COPY */ + hash += 4; + } + } +} + +#ifdef SHA256_TEST + +#include +#include + +int +main (int argc, char *argv[]) +{ + SHA256Context foo; + uint8_t hash[SHA256_HASH_SIZE]; + char buf[1000]; + int i; + + SHA256Init (&foo); + SHA256Update (&foo, "abc", 3); + SHA256Final (&foo, hash); + + for (i = 0; i < SHA256_HASH_SIZE;) { + printf ("%02x", hash[i++]); + if (!(i % 4)) + printf (" "); + } + printf ("\n"); + + SHA256Init (&foo); + SHA256Update (&foo, + "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", + 56); + SHA256Final (&foo, hash); + + for (i = 0; i < SHA256_HASH_SIZE;) { + printf ("%02x", hash[i++]); + if (!(i % 4)) + printf (" "); + } + printf ("\n"); + + SHA256Init (&foo); + memset (buf, 'a', sizeof (buf)); + for (i = 0; i < 1000; i++) + SHA256Update (&foo, buf, sizeof (buf)); + SHA256Final (&foo, hash); + + for (i = 0; i < SHA256_HASH_SIZE;) { + printf ("%02x", hash[i++]); + if (!(i % 4)) + printf (" "); + } + printf ("\n"); + + exit (0); +} + +#endif /* SHA256_TEST */ diff --git a/thirdparty/liblastfm2/src/fingerprint/Sha256.h b/thirdparty/liblastfm2/src/fingerprint/Sha256.h new file mode 100644 index 000000000..433c8f9a6 --- /dev/null +++ b/thirdparty/liblastfm2/src/fingerprint/Sha256.h @@ -0,0 +1,180 @@ +/*- + * Copyright (c) 2001-2003 Allan Saddi + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY ALLAN SADDI AND HIS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL ALLAN SADDI OR HIS CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * $Id: sha256.h 348 2003-02-23 22:12:06Z asaddi $ + */ +// +/////////// EXAMPLE ///////////////////////////////// +// +// SHA256Context sha256; +// SHA256Init (&sha256); +// +// uint8_t* pBuffer = new uint8_t[SHA_BUFFER_SIZE + 7]; +// // Ensure it is on a 64-bit boundary. +// INTPTR offs; +// if ((offs = reinterpret_cast(pBuffer) & 7L)) +// pBuffer += 8 - offs; +// +// unsigned int len; +// +// ifstream inFile("test.txt", ios::binary); +// +// for (;;) +// { +// inFile.read( reinterpret_cast(pBuffer), SHA_BUFFER_SIZE ); +// len = inFile.gcount(); +// +// if ( len == 0) +// break; +// +// SHA256Update (&sha256, pBuffer, len); +// } +// +// uint8_t hash[SHA256_HASH_SIZE]; +// SHA256Final (&sha256, hash); +// +// cout << "Hash: "; +// for (int i = 0; i < SHA256_HASH_SIZE; ++i) +// printf ("%02x", hash[i]); +// cout << endl; + + +#ifndef _SHA256_H +#define _SHA256_H + +// ---------------------------------------------------------------------------- +// ---------------------------------------------------------------------------- + +/* Define to 1 if you have the header file. */ +#ifndef WIN32 +#define HAVE_INTTYPES_H 1 +#endif + +/* Define to 1 if you have the header file. */ +#define HAVE_MEMORY_H 1 + +/* Define to 1 if you have the header file. */ +#ifndef WIN32 +#define HAVE_STDINT_H 1 +#endif + +/* Define to 1 if you have the header file. */ +#define HAVE_STDLIB_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRINGS_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRING_H 1 + +/* Define to 1 if you have the `strerror' function. */ +#ifndef WIN32 +#define HAVE_STRERROR 1 +#endif + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_STAT_H 1 + +/* Define to 1 if you have the header file. */ +#ifndef WIN32 +#define HAVE_SYS_TYPES_H 1 +#endif + +/* Define to 1 if you have the header file. */ +#ifndef WIN32 +#define HAVE_UNISTD_H 1 +#endif + +/* Define to 1 if you have the ANSI C header files. */ +#define STDC_HEADERS 1 + +/* Define as `__inline' if that's what the C compiler calls it, or to nothing + if it is not supported. */ +#ifdef WIN32 +#define inline __inline +#endif + +/* Define to `unsigned' if does not define. */ +/* #undef size_t */ + +#ifdef WIN32 +#define uint64_t unsigned __int64 +#define uint32_t unsigned int +#define uint8_t unsigned char +#endif // WIN32 + +#ifdef WIN32 +#define INTPTR intptr_t +#else +#define INTPTR long +#endif + +#define SHA_BUFFER_SIZE 65536 + +// ---------------------------------------------------------------------------- +// ---------------------------------------------------------------------------- + + +#if HAVE_INTTYPES_H +# include +#else +# if HAVE_STDINT_H +# include +# endif +#endif + +#define SHA256_HASH_SIZE 32 + +/* Hash size in 32-bit words */ +#define SHA256_HASH_WORDS 8 + +struct _SHA256Context { + uint64_t totalLength; + uint32_t hash[SHA256_HASH_WORDS]; + uint32_t bufferLength; + union { + uint32_t words[16]; + uint8_t bytes[64]; + } buffer; +#ifdef RUNTIME_ENDIAN + int littleEndian; +#endif /* RUNTIME_ENDIAN */ +}; + +typedef struct _SHA256Context SHA256Context; + +#ifdef __cplusplus +extern "C" { +#endif + +void SHA256Init (SHA256Context *sc); +void SHA256Update (SHA256Context *sc, const void *data, uint32_t len); +void SHA256Final (SHA256Context *sc, uint8_t hash[SHA256_HASH_SIZE]); + +#ifdef __cplusplus +} +#endif + +#endif /* !_SHA256_H */ diff --git a/thirdparty/liblastfm2/src/fingerprint/contrib/AacSource.cpp b/thirdparty/liblastfm2/src/fingerprint/contrib/AacSource.cpp new file mode 100644 index 000000000..77bacd343 --- /dev/null +++ b/thirdparty/liblastfm2/src/fingerprint/contrib/AacSource.cpp @@ -0,0 +1,953 @@ +/* + Copyright 2009 Last.fm Ltd. + Copyright 2009 John Stamp + Portions Copyright 2003-2005 M. Bakker, Nero AG, http://www.nero.com + - Adapted from main.c found in the FAAD2 source tarball. + + This file is part of liblastfm. + + liblastfm 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. + + liblastfm 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 liblastfm. If not, see . +*/ +#include "AacSource.h" +#include "AacSource_p.h" + +#include +#include +#include +#include +#include +#include +#include +#include + + +//////////////////////////////////////////////////////////////////////// +// +// AAC_File +// +//////////////////////////////////////////////////////////////////////// +AAC_File::AAC_File(const QString& fileName, int headerType) + : m_fileName(fileName) + , m_inBuf(NULL) + , m_inBufSize(0) + , m_decoder(0) + , m_overflow(static_cast(malloc( sizeof(unsigned char) * 1024 ))) + , m_overflowSize(0) + , m_header(headerType) +{ +} + + +AAC_File::~AAC_File() +{ + // common + if ( m_decoder ) + { + NeAACDecClose( m_decoder ); + m_decoder = NULL; + } + if ( m_inBuf ) + { + free( m_inBuf ); + m_inBufSize = 0; + m_inBuf = NULL; + } + if ( m_overflow ) + { + free( m_overflow ); + m_overflowSize = 0; + m_overflow = NULL; + } +} + + + +//////////////////////////////////////////////////////////////////////// +// +// AAC with ADTS or ADIF headers +// +//////////////////////////////////////////////////////////////////////// + + +#define MAX_CHANNELS 6 // Output will get mixed down to 2 channels +#define ADTS_HEADER_SIZE 8 + +static int adts_sample_rates[] = +{ + 96000, + 88200, + 64000, + 48000, + 44100, + 32000, + 24000, + 22050, + 16000, + 12000, + 11025, + 8000, + 7350, + 0, + 0, + 0 +}; + +AAC_ADTS_File::AAC_ADTS_File( const QString& fileName, int headerType ) : AAC_File(fileName, headerType) + , m_file( NULL ) + , m_adifSamplerate( 0 ) + , m_adifChannels( 0 ) +{ +} + + +AAC_ADTS_File::~AAC_ADTS_File() +{ + if ( m_file ) + { + fclose( m_file ); + } +} + + +void AAC_ADTS_File::fillBuffer( FILE*& fp, unsigned char*& buf, size_t& bufSize, const size_t bytesConsumed ) +{ + size_t bread; + + if ( bytesConsumed > 0 ) + { + if ( bufSize ) + memmove( (void*)buf, (void*)(buf + bytesConsumed), bufSize*sizeof(unsigned char) ); + + bread = fread( (void*)(buf + bufSize), 1, bytesConsumed, fp ); + bufSize += bread; + + if ( bufSize > 3 ) + { + if ( memcmp( buf, "TAG", 3 ) == 0 ) + bufSize = 0; + } + if ( bufSize > 11 ) + { + if ( memcmp( buf, "LYRICSBEGIN", 11 ) == 0 ) + bufSize = 0; + } + if ( bufSize > 8 ) + { + if ( memcmp( buf, "APETAGEX", 8 ) == 0 ) + bufSize = 0; + } + } +} + + +void AAC_ADTS_File::parse( FILE*& fp, unsigned char*& buf, size_t& bufSize, int &bitrate, double &length ) +{ + unsigned int frames, frame_length = 0; + int t_framelength = 0; + int samplerate = 0; + double frames_per_sec, bytes_per_frame; + + // Read all frames to ensure correct time and bitrate + for ( frames = 0; /* */; frames++ ) + { + fillBuffer( fp, buf, bufSize, frame_length ); + + if ( bufSize > 7 ) + { + /* check syncword */ + if ( !( (buf[0] == 0xFF) && ((buf[1] & 0xF6) == 0xF0) ) ) + break; + + if ( frames == 0 ) + samplerate = adts_sample_rates[ (buf[2] & 0x3c) >> 2 ]; + + frame_length = ( ((buf[3] & 0x3) << 11) + | ((buf[4]) << 3) + | (buf[5] >> 5) ); + + t_framelength += frame_length - ADTS_HEADER_SIZE; + + if ( frame_length > bufSize ) + break; + + bufSize -= frame_length; + } + else + { + break; + } + } + + frames_per_sec = samplerate / 1024.0; + + if ( frames != 0 ) + bytes_per_frame = t_framelength / frames; + else + bytes_per_frame = 0; + + bitrate = static_cast(8 * bytes_per_frame * frames_per_sec + 0.5); + + if ( frames_per_sec != 0 ) + length = frames / frames_per_sec; + else + length = 1; +} + + +int32_t AAC_ADTS_File::commonSetup( FILE*& fp, NeAACDecHandle& decoder, unsigned char*& buf, size_t& bufSize, uint32_t& samplerate, uint8_t& channels ) +{ + samplerate = 0; + channels = 0; + + fp = fopen(QFile::encodeName(m_fileName), "rb" ); + if( !fp ) + { + std::cerr << "ERROR: Failed to open " << strerror( errno ) << std::endl; + return -1; + } + + if ( !(buf = static_cast( malloc(FAAD_MIN_STREAMSIZE*MAX_CHANNELS)) ) ) + { + std::cerr << "Memory allocation error" << std::endl; + fclose ( fp ); + return -1; + } + + memset( buf, 0, FAAD_MIN_STREAMSIZE*MAX_CHANNELS ); + + bufSize = fread( buf, 1, FAAD_MIN_STREAMSIZE * MAX_CHANNELS, fp ); + + int tagsize = 0; + if ( !memcmp( buf, "ID3", 3 ) ) + { + /* high bit is not used */ + tagsize = (buf[6] << 21) | (buf[7] << 14) | + (buf[8] << 7) | (buf[9] << 0); + + tagsize += 10; + bufSize -= tagsize; + fillBuffer( fp, buf, bufSize, tagsize ); + } + + decoder = NeAACDecOpen(); + + /* Set configuration */ + NeAACDecConfigurationPtr config; + config = NeAACDecGetCurrentConfiguration(decoder); + config->outputFormat = FAAD_FMT_16BIT; + config->downMatrix = 1; // Turn 5.1 channels into 2 + NeAACDecSetConfiguration( decoder, config); + + int32_t initval = 0; + if ((initval = NeAACDecInit(decoder, buf, + FAAD_MIN_STREAMSIZE*MAX_CHANNELS, &samplerate, &channels)) < 0) + { + std::cerr << "Error: could not set up AAC decoder" << std::endl; + if ( buf ) + free( buf ); + buf = NULL; + NeAACDecClose( decoder ); + decoder = NULL; + fclose( fp ); + fp = NULL; + } + return initval; +} + + +bool AAC_ADTS_File::init() +{ + uint32_t initSamplerate = 0; + uint8_t initChannels = 0; + int32_t initval = commonSetup( m_file, m_decoder, m_inBuf, m_inBufSize, initSamplerate, initChannels ); + + if ( initval >= 0 ) + { + m_inBufSize -= initval; + fillBuffer( m_file, m_inBuf, m_inBufSize, initval ); + + // These two only needed for skipping AAC ADIF files + m_adifSamplerate = initSamplerate; + m_adifChannels = initChannels; + + return true; + } + + throw std::runtime_error( "ERROR: Could not initialize AAC file reader!" ); + return false; +} + + +/*QString AAC_ADTS_File::getMbid() +{ + char out[MBID_BUFFER_SIZE]; + int const r = getMP3_MBID(QFile::encodeName(m_fileName), out); + if ( r == 0 ) + return QString::fromLatin1( out ); + return QString(); +}*/ + +void AAC_ADTS_File::getInfo( int& lengthSecs, int& samplerate, int& bitrate, int& nchannels ) +{ + long fileread; + uint32_t initSamplerate; + uint8_t initChannels; + double initLength = 0; + unsigned char *tempBuf = NULL; + size_t tempBufSize; + FILE *fp = NULL; + NeAACDecHandle decoder = NULL; + commonSetup( fp, decoder, tempBuf, tempBufSize, initSamplerate, initChannels ); + + long origpos = ftell( fp ); + fseek( fp, 0, SEEK_END ); + fileread = ftell( fp ); + fseek( fp, origpos, SEEK_SET ); + + if ( (tempBuf[0] == 0xFF) && ((tempBuf[1] & 0xF6) == 0xF0) ) + { + parse( fp, tempBuf, tempBufSize, bitrate, initLength ); + } + else if (memcmp(tempBuf, "ADIF", 4) == 0) + { + int skip_size = (tempBuf[4] & 0x80) ? 9 : 0; + bitrate = ((tempBuf[4 + skip_size] & 0x0F)<<19) | + (tempBuf[5 + skip_size]<<11) | + (tempBuf[6 + skip_size]<<3) | + (tempBuf[7 + skip_size] & 0xE0); + + if ( fileread != 0) + { + initLength = static_cast(fileread) * 8 / bitrate + 0.5; + } + } + + lengthSecs = static_cast(initLength); + nchannels = initChannels; + samplerate = initSamplerate; + + if ( decoder ) + NeAACDecClose( decoder ); + if ( fp ) + fclose( fp ); + if ( tempBuf ) + free( tempBuf ); +} + + +void AAC_ADTS_File::skip( const int mSecs ) +{ + if ( m_header == AAC_ADTS ) + { + // As AAC is VBR we need to check all ADTS headers to enable seeking... + // There is no other solution + unsigned char header[8]; + unsigned int frameCount, frameLength; + double seconds = 0; + + // We need to find the ATDS syncword so rewind to the beginning + // of the unprocessed data. + if ( m_inBufSize > 0 ) + { + fseek ( m_file, -m_inBufSize, SEEK_CUR ); + m_inBufSize = 0; + } + + for( frameCount = 1; seconds * 1000 < mSecs; frameCount++ ) + { + if ( fread( header, 1, ADTS_HEADER_SIZE, m_file ) != ADTS_HEADER_SIZE ) + { + break; + } + if ( !strncmp( (char*)header, "ID3", 3 ) ) + { + // high bit is not used + unsigned char rest[2]; + fread( rest, 1, 2, m_file ); + int tagsize = (header[6] << 21) | (header[7] << 14) | + (rest[0] << 7) | (rest[1] << 0); + + fseek( m_file, tagsize, SEEK_CUR ); + fread( header, 1, ADTS_HEADER_SIZE, m_file ); + } + if ( !((header[0] == 0xFF) && ((header[1] & 0xF6) == 0xF0)) ) + { + std::cerr << "Error: Bad frame header; file may be corrupt!" << std::endl; + break; + } + + int samplerate = adts_sample_rates[ (header[2] & 0x3c) >> 2 ]; + frameLength = ( ( header[3] & 0x3 ) << 11 ) + | ( header[4] << 3 ) + | ( header[5] >> 5 ); + + if ( samplerate > 0 ) + seconds += 1024.0 / samplerate; + else + { + std::cerr << "Error: Bad frame header; file may be corrupt!" << std::endl; + break; + } + + if ( fseek( m_file, frameLength - ADTS_HEADER_SIZE, SEEK_CUR ) == -1 ) + break; + } + m_inBufSize = fread( m_inBuf, 1, FAAD_MIN_STREAMSIZE * MAX_CHANNELS, m_file ); + } + else if ( m_header == AAC_ADIF ) + { + // AAC ADIF is even worse. There's only the one header at the + // beginning of the file. If you want to skip forward, you have to + // decode block by block and check how far along you are. Lovely, eh? + + unsigned long totalSamples = 0; + void *sampleBuffer = NULL; + + do + { + NeAACDecFrameInfo frameInfo; + sampleBuffer = NeAACDecDecode(m_decoder, &frameInfo, m_inBuf, static_cast(m_inBufSize) ); + totalSamples += frameInfo.samples; + if ( frameInfo.bytesconsumed > 0 ) + { + m_inBufSize -= frameInfo.bytesconsumed; + fillBuffer( m_file, m_inBuf, m_inBufSize, frameInfo.bytesconsumed ); + } + if ( totalSamples >= ( mSecs * m_adifSamplerate * m_adifChannels / 1000 ) ) + break; + } while ( sampleBuffer != NULL ); + } +} + + +void AAC_ADTS_File::postDecode(unsigned long bytesConsumed) +{ + m_inBufSize -= bytesConsumed; + fillBuffer( m_file, m_inBuf, m_inBufSize, bytesConsumed ); +} + + +//////////////////////////////////////////////////////////////////////// +// +// AAC in an MP4 wrapper +// +//////////////////////////////////////////////////////////////////////// + + +uint32_t read_callback( void *user_data, void *buffer, uint32_t length ) +{ + return static_cast(fread( buffer, 1, length, static_cast(user_data) )); +} + + +uint32_t seek_callback( void *user_data, uint64_t position ) +{ + return fseek( static_cast(user_data), static_cast(position), SEEK_SET ); +} + +AAC_MP4_File::AAC_MP4_File( const QString& fileName, int headerType ) : AAC_File(fileName, headerType) + , m_mp4AudioTrack( -1 ) + , m_mp4SampleId( 0 ) + , m_mp4File ( NULL ) + , m_mp4cb ( NULL ) +{ +} + +int32_t AAC_MP4_File::readSample() +{ + unsigned int bsize; + int32_t rc = mp4ff_read_sample( m_mp4File, m_mp4AudioTrack, m_mp4SampleId, &m_inBuf, &bsize ); + m_inBufSize = bsize; + // Not necessarily an error. Could just mean end of file. + //if ( rc == 0 ) + // std::cerr << "Reading samples failed." << std::endl; + return rc; +} + + +int32_t AAC_MP4_File::getTrack( const mp4ff_t *f ) +{ + // find AAC track + int32_t numTracks = mp4ff_total_tracks( f ); + + for ( int32_t i = 0; i < numTracks; i++ ) + { + unsigned char *buff = NULL; + unsigned int buff_size = 0; + mp4AudioSpecificConfig mp4ASC; + + mp4ff_get_decoder_config( f, i, &buff, &buff_size ); + + if ( buff ) + { + int8_t rc = NeAACDecAudioSpecificConfig( buff, buff_size, &mp4ASC ); + free( buff ); + + if ( rc < 0 ) + continue; + return i; + } + } + + // can't decode this, probably DRM + return -1; +} + + +bool AAC_MP4_File::commonSetup( NeAACDecHandle& decoder, mp4ff_callback_t*& cb, FILE*& fp, mp4ff_t*& mp4, int32_t& audioTrack ) +{ + fp = fopen(QFile::encodeName(m_fileName), "rb"); + if ( !fp ) + { + throw std::runtime_error( "Error: failed to open AAC file!" ); + return false; + } + + decoder = NeAACDecOpen(); + + // Set configuration + NeAACDecConfigurationPtr config; + config = NeAACDecGetCurrentConfiguration( decoder ); + config->outputFormat = FAAD_FMT_16BIT; + config->downMatrix = 1; // Turn 5.1 channels into 2 + NeAACDecSetConfiguration( decoder, config ); + + // initialise the callback structure + cb = static_cast( malloc( sizeof(mp4ff_callback_t) ) ); + + cb->read = read_callback; + cb->seek = seek_callback; + cb->user_data = fp; + + mp4 = mp4ff_open_read( cb ); + + if ( !mp4 ) + { + // unable to open file + free( cb ); + cb = NULL; + NeAACDecClose( decoder ); + decoder = NULL; + fclose( fp ); + fp = NULL; + throw std::runtime_error( "Error: failed to set up AAC decoder!" ); + return false; + } + + if ( ( audioTrack = getTrack( mp4 )) < 0 ) + { + free( cb ); + cb = NULL; + NeAACDecClose( decoder ); + decoder = NULL; + fclose( fp ); + fp = NULL; + mp4ff_close( mp4 ); + mp4 = NULL; + audioTrack = 0; + throw std::runtime_error( "Error: Unable to find an audio track. Is the file DRM protected?" ); + return false; + } + return true; +} + + +/*QString AAC_MP4_File::getMbid() +{ + int j = mp4ff_meta_get_num_items( m_mp4File ); + if ( j > 0 ) + { + int k; + for ( k = 0; k < j; k++ ) + { + char *tag = NULL, *item = NULL; + if ( mp4ff_meta_get_by_index( m_mp4File, k, &item, &tag ) ) + { + if ( item != NULL && tag != NULL ) + { + QString key(item); + if ( key.toLower() == "musicbrainz track id" ) + { + QString ret(tag); + free( item ); + free( tag ); + return ret; + } + free( item ); + free( tag ); + } + } + } + } + return QString(); +}*/ + + +void AAC_MP4_File::getInfo( int& lengthSecs, int& samplerate, int& bitrate, int& nchannels ) +{ + FILE* fp = NULL; + mp4ff_callback_t *cb = NULL; + NeAACDecHandle decoder = NULL; + mp4ff_t* mp4 = NULL; + int32_t audioTrack; + + bool success = commonSetup( decoder, cb, fp, mp4, audioTrack ); + + if ( success ) + { + // get basic file info + mp4AudioSpecificConfig mp4ASC; + unsigned char* buffer = NULL; + unsigned int buffer_size = 0; + double f = 1024.0; + unsigned int framesize = 1024; + + int32_t samples = mp4ff_num_samples( mp4, audioTrack ); + + if ( buffer ) + { + if ( NeAACDecAudioSpecificConfig(buffer, buffer_size, &mp4ASC) >= 0 ) + { + if ( mp4ASC.frameLengthFlag == 1 ) + framesize = 960; + if ( mp4ASC.sbr_present_flag == 1 ) + framesize *= 2; + if ( mp4ASC.sbr_present_flag == 1 ) + f = f * 2.0; + } + free( buffer ); + } + + samplerate = mp4ff_get_sample_rate( mp4, audioTrack ); + if ( samplerate > 0 ) + lengthSecs = static_cast(samples * f / samplerate + 0.5); + bitrate = mp4ff_get_avg_bitrate( mp4, audioTrack ); + nchannels = mp4ff_get_channel_count( mp4, audioTrack ); + + mp4ff_close( mp4 ); + NeAACDecClose( decoder ); + free( cb ); + fclose( fp ); + } +} + + +bool AAC_MP4_File::init() +{ + FILE* fp = NULL; + + bool success = commonSetup( m_decoder, m_mp4cb, fp, m_mp4File, m_mp4AudioTrack ); + if ( !success ) + return false; + + unsigned char* buffer = NULL; + unsigned int buffer_size = 0; + uint32_t samplerate; + uint8_t channels; + + mp4ff_get_decoder_config( m_mp4File, m_mp4AudioTrack, &buffer, &buffer_size ); + + if( NeAACDecInit2( m_decoder, buffer, buffer_size, &samplerate, &channels) < 0 ) + { + // If some error initializing occured, skip the file + if ( fp ) + fclose( fp ); + throw std::runtime_error( "Error: unable to initialize AAC decoder library!" ); + return false; + } + + if ( buffer ) + free( buffer ); + + return true; +} + + +void AAC_MP4_File::postDecode(unsigned long) +{ + free( m_inBuf ); + m_inBuf = NULL; + m_mp4SampleId++; +} + +void AAC_MP4_File::skip( const int mSecs ) +{ + double dur = 0.0; + int f = 1; + unsigned char *buff = NULL; + unsigned int buff_size = 0; + uint32_t totalSamples = mp4ff_num_samples( m_mp4File, m_mp4AudioTrack ); + mp4AudioSpecificConfig mp4ASC; + + mp4ff_get_decoder_config( m_mp4File, m_mp4AudioTrack, &buff, &buff_size ); + + if ( buff ) + { + int8_t rc = NeAACDecAudioSpecificConfig( buff, buff_size, &mp4ASC ); + free( buff ); + if ( rc >= 0 && mp4ASC.sbr_present_flag == 1 ) + f = 2; + + // I think the f multiplier is needed here. + while ( dur * 1000.0 * f / static_cast(mp4ASC.samplingFrequency) < mSecs && m_mp4SampleId < totalSamples ) + { + dur += mp4ff_get_sample_duration( m_mp4File, m_mp4AudioTrack, m_mp4SampleId ); + m_mp4SampleId++; + } + } + else + std::cerr << "Error: could not skip " << mSecs << " milliseconds" << std::endl; +} + + +AAC_MP4_File::~AAC_MP4_File() +{ + if ( m_mp4File ) + mp4ff_close( m_mp4File ); + if ( m_mp4cb ) + { + free( m_mp4cb ); + } +} + + +//////////////////////////////////////////////////////////////////////// +// +// AacSource +// +//////////////////////////////////////////////////////////////////////// + +AacSource::AacSource() + : m_eof( false ) + , m_aacFile( NULL ) +{} + + +AacSource::~AacSource() +{ + delete m_aacFile; +} + + +int AacSource::checkHeader() +{ + FILE *fp = NULL; + unsigned char header[10]; + + // check for mp4 file + fp = fopen(QFile::encodeName(m_fileName), "rb"); + if ( !fp ) + { + std::cerr << "Error: failed to open " << strerror( errno ) << std::endl; + return AAC_File::AAC_UNKNOWN; + } + + fread( header, 1, 10, fp ); + + // MP4 headers + if ( !memcmp( &header[4], "ftyp", 4 ) ) + { + fclose( fp ); + return AAC_File::AAC_MP4; + } + + // Skip id3 tags + int tagsize = 0; + if ( !memcmp( header, "ID3", 3 ) ) + { + /* high bit is not used */ + tagsize = (header[6] << 21) | (header[7] << 14) | + (header[8] << 7) | (header[9] << 0); + + tagsize += 10; + fseek( fp, tagsize, SEEK_SET ); + fread( header, 1, 10, fp ); + } + + // Check for ADTS OR ADIF headers + if ( (header[0] == 0xFF) && ((header[1] & 0xF6) == 0xF0) ) + { + fclose( fp ); + return AAC_File::AAC_ADTS; + } + else if (memcmp(header, "ADIF", 4) == 0) + { + fclose( fp ); + return AAC_File::AAC_ADIF; + } + + fclose( fp ); + return AAC_File::AAC_UNKNOWN; +} + + +void AacSource::getInfo( int& lengthSecs, int& samplerate, int& bitrate, int& nchannels ) +{ + // get the header plus some other stuff.. + + m_aacFile->getInfo( lengthSecs, samplerate, bitrate, nchannels ); +} + + +void AacSource::init(const QString& fileName) +{ + m_fileName = fileName; + + int headerType = checkHeader(); + if ( headerType != AAC_File::AAC_UNKNOWN ) + { + if ( headerType == AAC_File::AAC_MP4 ) + m_aacFile = new AAC_MP4_File(m_fileName, headerType); + else + m_aacFile = new AAC_ADTS_File( m_fileName, headerType ); + } + + if ( m_aacFile ) + m_aacFile->init(); + else + throw std::runtime_error( "ERROR: No suitable AAC decoder found!" ); +} + + +/*QString AacSource::getMbid() +{ + QString mbid = m_aacFile->getMbid(); + return mbid; +}*/ + + +void AacSource::skip( const int mSecs ) +{ + if ( mSecs < 0 || !m_aacFile->m_decoder ) + return; + + m_aacFile->skip( mSecs ); +} + + +void AacSource::skipSilence(double silenceThreshold /* = 0.0001 */) +{ + if ( !m_aacFile->m_decoder ) + return; + + silenceThreshold *= static_cast( std::numeric_limits::max() ); + + for (;;) + { + if ( m_aacFile->m_header == AAC_File::AAC_MP4 ) + { + if ( !static_cast(m_aacFile)->readSample() ) + break; + } + NeAACDecFrameInfo frameInfo; + + void* sampleBuffer = NeAACDecDecode(m_aacFile->m_decoder, &frameInfo, m_aacFile->m_inBuf, static_cast(m_aacFile->m_inBufSize) ); + + m_aacFile->postDecode( frameInfo.bytesconsumed ); + + if ( frameInfo.error > 0 ) + { + break; + } + else if ( frameInfo.samples > 0 ) + { + double sum = 0; + int16_t *buf = static_cast(sampleBuffer); + switch ( frameInfo.channels ) + { + case 1: + for (size_t j = 0; j < frameInfo.samples; ++j) + sum += abs( buf[j] ); + break; + case 2: + for (size_t j = 0; j < frameInfo.samples; j+=2) + sum += abs( (buf[j] >> 1) + (buf[j+1] >> 1) ); + break; + } + if ( (sum >= silenceThreshold * static_cast(frameInfo.samples/frameInfo.channels) ) ) + break; + } + } +} + + +int AacSource::updateBuffer( signed short *pBuffer, size_t bufferSize ) +{ + size_t nwrit = 0; //number of samples written to the output buffer + + if ( m_aacFile->m_overflowSize > 0 ) + { + size_t samples_to_use = bufferSize < m_aacFile->m_overflowSize ? bufferSize : m_aacFile->m_overflowSize; + memcpy( pBuffer, m_aacFile->m_overflow, samples_to_use * sizeof(signed short) ); + nwrit += samples_to_use; + m_aacFile->m_overflowSize -= samples_to_use; + memmove( (void*)(m_aacFile->m_overflow), (void*)(m_aacFile->m_overflow + samples_to_use*sizeof(signed short)), samples_to_use*sizeof(signed short) ); + } + + if ( !m_aacFile->m_decoder ) + return 0; + + for (;;) + { + signed short* pBufferIt = pBuffer + nwrit; + void* sampleBuffer; + + assert( nwrit <= bufferSize ); + + if ( m_aacFile->m_header == AAC_File::AAC_MP4 ) + { + if ( !static_cast(m_aacFile)->readSample() ) + { + m_eof = true; + return static_cast(nwrit); + } + } + NeAACDecFrameInfo frameInfo; + + sampleBuffer = NeAACDecDecode(m_aacFile->m_decoder, &frameInfo, m_aacFile->m_inBuf, static_cast(m_aacFile->m_inBufSize) ); + size_t samples_to_use = (bufferSize - nwrit) < frameInfo.samples ? bufferSize-nwrit : frameInfo.samples; + + if ( samples_to_use > 0 && sampleBuffer != NULL ) + { + memcpy( pBufferIt, sampleBuffer, samples_to_use * sizeof(signed short) ); + nwrit += samples_to_use; + } + + if ( samples_to_use < frameInfo.samples ) + { + m_aacFile->m_overflow = static_cast(realloc( m_aacFile->m_overflow, (frameInfo.samples - samples_to_use) * sizeof(signed short) ) ); + memcpy( m_aacFile->m_overflow, static_cast(sampleBuffer) + samples_to_use, (frameInfo.samples - samples_to_use) * sizeof(signed short) ); + m_aacFile->m_overflowSize = frameInfo.samples - samples_to_use; + } + + m_aacFile->postDecode( frameInfo.bytesconsumed ); + + if ( sampleBuffer == NULL ) + { + m_eof = true; + break; + } + + if ( frameInfo.error > 0 ) + { + std::cerr << "Error: " << NeAACDecGetErrorMessage(frameInfo.error) << std::endl; + break; + } + + if ( nwrit == bufferSize ) + break; + } + + return static_cast(nwrit); +} diff --git a/thirdparty/liblastfm2/src/fingerprint/contrib/AacSource.h b/thirdparty/liblastfm2/src/fingerprint/contrib/AacSource.h new file mode 100644 index 000000000..0fff6f585 --- /dev/null +++ b/thirdparty/liblastfm2/src/fingerprint/contrib/AacSource.h @@ -0,0 +1,46 @@ +/* + Copyright 2009 Last.fm Ltd. + Copyright 2009 John Stamp + + This file is part of liblastfm. + + liblastfm 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. + + liblastfm 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 liblastfm. If not, see . +*/ +#ifndef __AAC_SOURCE_H__ +#define __AAC_SOURCE_H__ + +#include + +class AacSource : public lastfm::FingerprintableSource +{ +public: + AacSource(); + ~AacSource(); + + virtual void getInfo(int& lengthSecs, int& samplerate, int& bitrate, int& nchannels); + virtual void init(const QString& fileName); + virtual int updateBuffer(signed short* pBuffer, size_t bufferSize); + virtual void skip(const int mSecs); + virtual void skipSilence(double silenceThreshold = 0.0001); + virtual bool eof() const { return m_eof; } + +private: + int checkHeader(); + QString m_fileName; + bool m_eof; + class AAC_File *m_aacFile; +}; + +#endif + diff --git a/thirdparty/liblastfm2/src/fingerprint/contrib/AacSource_p.h b/thirdparty/liblastfm2/src/fingerprint/contrib/AacSource_p.h new file mode 100644 index 000000000..870482c1f --- /dev/null +++ b/thirdparty/liblastfm2/src/fingerprint/contrib/AacSource_p.h @@ -0,0 +1,94 @@ +/* + Copyright 2009 Last.fm Ltd. + Copyright 2009 John Stamp + + This file is part of liblastfm. + + liblastfm 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. + + liblastfm 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 liblastfm. If not, see . +*/ +#include +#include + +class AAC_File +{ +public: + AAC_File(const QString&, int headerType); + virtual ~AAC_File(); + virtual void getInfo( int& lengthSecs, int& samplerate, int& bitrate, int& nchannels ) = 0; + virtual bool init() = 0; + //virtual QString getMbid() = 0; + virtual void skip( const int mSecs ) = 0; + virtual void postDecode(unsigned long) = 0; + + enum HeaderType + { + AAC_UNKNOWN = 0, + AAC_ADIF, + AAC_ADTS, + AAC_MP4 + }; + + QString m_fileName; + unsigned char *m_inBuf; + size_t m_inBufSize; + NeAACDecHandle m_decoder; + unsigned char *m_overflow; + size_t m_overflowSize; + int m_header; +}; + + +class AAC_MP4_File : public AAC_File +{ +public: + AAC_MP4_File(const QString&, int headerType = AAC_MP4 ); + ~AAC_MP4_File(); + virtual void getInfo( int& lengthSecs, int& samplerate, int& bitrate, int& nchannels ); + virtual bool init(); + //virtual QString getMbid(); + virtual void skip( const int mSecs ); + virtual void postDecode(unsigned long); + int32_t readSample(); + +private: + bool commonSetup( NeAACDecHandle& handle, mp4ff_callback_t*& cb, FILE*& fp, mp4ff_t*& mp4, int32_t& audioTrack ); + virtual int32_t getTrack( const mp4ff_t* f ); + int m_mp4AudioTrack; + uint32_t m_mp4SampleId; + mp4ff_t *m_mp4File; + mp4ff_callback_t *m_mp4cb; +}; + + +class AAC_ADTS_File : public AAC_File +{ +public: + AAC_ADTS_File( const QString& fileName, int headerType ); + ~AAC_ADTS_File(); + virtual void getInfo( int& lengthSecs, int& samplerate, int& bitrate, int& nchannels ); + virtual bool init(); + //virtual QString getMbid(); + virtual void skip( const int mSecs ); + virtual void postDecode(unsigned long bytesconsumed ); + +private: + int32_t commonSetup( FILE*& fp, NeAACDecHandle& decoder, unsigned char*& buf, size_t& bufSize, uint32_t& samplerate, uint8_t& channels ); + void parse( FILE*& fp, unsigned char*& buf, size_t& bufSize, int &bitrate, double &length ); + void fillBuffer( FILE*& fp, unsigned char*& buf, size_t& bufSize, const size_t m_bytesConsumed ); + + FILE* m_file; + // These two only needed for skipping AAC ADIF files + uint32_t m_adifSamplerate; + int m_adifChannels; +}; diff --git a/thirdparty/liblastfm2/src/fingerprint/contrib/FlacSource.cpp b/thirdparty/liblastfm2/src/fingerprint/contrib/FlacSource.cpp new file mode 100644 index 000000000..1e1e29900 --- /dev/null +++ b/thirdparty/liblastfm2/src/fingerprint/contrib/FlacSource.cpp @@ -0,0 +1,339 @@ +/* + Copyright 2009 Last.fm Ltd. + Copyright 2009 John Stamp + + This file is part of liblastfm. + + liblastfm 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. + + liblastfm 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 liblastfm. If not, see . +*/ +#include "FlacSource.h" +#include +#include +#include +#include +#include +#include +#include + +#include + + +FLAC__StreamDecoderWriteStatus FlacSource::_write_callback(const FLAC__StreamDecoder *, const FLAC__Frame *frame, const FLAC__int32 * const buffer[], void *client_data) +{ + assert(client_data != NULL); + FlacSource *instance = reinterpret_cast(client_data); + assert(instance != NULL); + return instance->write_callback(frame, buffer); +} + +FLAC__StreamDecoderWriteStatus FlacSource::write_callback(const FLAC__Frame *frame, const FLAC__int32 * const buffer[]) +{ + m_outBufLen = 0; + + if ( m_outBuf ) + { + size_t i; + for(i = 0; i < frame->header.blocksize; i++) + { + switch ( m_channels ) + { + case 1: + m_outBuf[m_outBufLen] = (FLAC__int16)buffer[0][i]; // mono + m_outBufLen++; + break; + case 2: + m_outBuf[m_outBufLen] = (FLAC__int16)buffer[0][i]; // left channel + m_outBuf[m_outBufLen+1] = (FLAC__int16)buffer[1][i]; // right channel + m_outBufLen += 2; + break; + } + } + m_samplePos += frame->header.blocksize; + } + + return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE; +} + +// --------------------------------------------------------------------- + +void FlacSource::_metadata_callback(const FLAC__StreamDecoder *, const FLAC__StreamMetadata *metadata, void *client_data) +{ + assert(client_data != NULL); + FlacSource *instance = reinterpret_cast(client_data); + assert(instance != NULL); + instance->metadata_callback(metadata); +} + +void FlacSource::metadata_callback( const FLAC__StreamMetadata *metadata ) +{ + switch ( metadata->type ) + { + case FLAC__METADATA_TYPE_STREAMINFO: + m_channels = metadata->data.stream_info.channels; + m_totalSamples = metadata->data.stream_info.total_samples; + m_samplerate = metadata->data.stream_info.sample_rate; + m_bps = metadata->data.stream_info.bits_per_sample; + m_maxFrameSize = metadata->data.stream_info.max_framesize; + break; + case FLAC__METADATA_TYPE_VORBIS_COMMENT: + m_commentData = FLAC__metadata_object_clone(metadata); + break; + default: + break; + } +} + +// --------------------------------------------------------------------- + +void FlacSource::_error_callback(const FLAC__StreamDecoder *, FLAC__StreamDecoderErrorStatus status, void *client_data) +{ + assert(client_data != NULL); + FlacSource *instance = reinterpret_cast(client_data); + assert(instance != NULL); + instance->error_callback(status); +} + +void FlacSource::error_callback(FLAC__StreamDecoderErrorStatus status) +{ + std::cerr << "Got FLAC error: " << FLAC__StreamDecoderErrorStatusString[status] << std::endl; +} + +// --------------------------------------------------------------------- + +FlacSource::FlacSource() + : m_decoder( 0 ) + , m_fileSize( 0 ) + , m_outBuf( 0 ) + , m_outBufLen( 0 ) + , m_outBufPos( 0 ) + , m_samplePos( 0 ) + , m_maxFrameSize( 0 ) + , m_commentData( 0 ) + , m_bps( 0 ) + , m_channels( 0 ) + , m_samplerate( 0 ) + , m_totalSamples( 0 ) + , m_eof( false ) +{ +} + +// --------------------------------------------------------------------- + +FlacSource::~FlacSource() +{ + if ( m_decoder ) + { + FLAC__stream_decoder_finish( m_decoder ); + FLAC__stream_decoder_delete( m_decoder ); + } + if ( m_commentData ) + FLAC__metadata_object_delete( m_commentData ); + if ( m_outBuf ) + free( m_outBuf ); +} + +// --------------------------------------------------------------------- + +void FlacSource::init(const QString& fileName) +{ + m_fileName = fileName; + + if ( !m_decoder ) + { + FILE *f = fopen(QFile::encodeName(m_fileName), "rb" ); + if ( f ) + { + // Need to check which init call to use; flac doesn't do that for us + unsigned char header[35]; + bool isOgg = false; + fread( header, 1, 35, f ); + if ( memcmp(header, "OggS", 4) == 0 && + memcmp(&header[29], "FLAC", 4) == 0 ) + isOgg = true; + + // getInfo() will need this to calculate bitrate + fseek( f, 0, SEEK_END ); + m_fileSize = ftell(f); + + rewind( f ); + + m_decoder = FLAC__stream_decoder_new(); + FLAC__stream_decoder_set_metadata_respond(m_decoder, FLAC__METADATA_TYPE_STREAMINFO); + FLAC__stream_decoder_set_metadata_respond(m_decoder, FLAC__METADATA_TYPE_VORBIS_COMMENT); + + int init_status; + if ( FLAC_API_SUPPORTS_OGG_FLAC && isOgg ) + init_status = FLAC__stream_decoder_init_ogg_FILE( m_decoder, f, _write_callback, _metadata_callback, _error_callback, this ); + else + init_status = FLAC__stream_decoder_init_FILE( m_decoder, f, _write_callback, _metadata_callback, _error_callback, this ); + + if(init_status != FLAC__STREAM_DECODER_INIT_STATUS_OK) + return; + + FLAC__stream_decoder_process_until_end_of_metadata( m_decoder ); + m_outBuf = static_cast(malloc( sizeof(signed short)*m_maxFrameSize)); + + if ( m_bps != 16 ) + { + FLAC__stream_decoder_finish( m_decoder ); + FLAC__stream_decoder_delete( m_decoder ); + FLAC__metadata_object_delete( m_commentData ); + m_decoder = 0; + m_commentData = 0; + throw std::runtime_error( "ERROR: only 16 bit FLAC files are currently supported!" ); + } + } + else + throw std::runtime_error( "ERROR: cannot load FLAC file!" ); + } +} + +// --------------------------------------------------------------------- + +/*QString FlacSource::getMbid() +{ + if ( m_commentData ) + { + FLAC__StreamMetadata_VorbisComment *vc; + vc = &m_commentData->data.vorbis_comment; + for ( unsigned int i = 0; i < vc->num_comments; ++i ) + { + QByteArray key( (char*)(vc->comments[i].entry), vc->comments[i].length ); + if ( key.left(20).toLower() == "musicbrainz_trackid=" ) + { + QString val = key.mid(20); + return val; + } + } + } + + return QString(); +}*/ + +// --------------------------------------------------------------------- + +void FlacSource::getInfo(int& lengthSecs, int& samplerate, int& bitrate, int& nchannels) +{ + lengthSecs = 0; + samplerate = 0; + bitrate = 0; + nchannels = 0; + + if ( m_decoder ) + { + samplerate = m_samplerate; + nchannels = m_channels; + if ( samplerate > 0 ) + lengthSecs = static_cast( static_cast(m_totalSamples)/m_samplerate + 0.5); + + // Calcuate bitrate + if ( lengthSecs > 0 ) + { + FLAC__Metadata_SimpleIterator *it = FLAC__metadata_simple_iterator_new(); + FLAC__metadata_simple_iterator_init( it, QFile::encodeName(m_fileName), true, true ); + while( !FLAC__metadata_simple_iterator_is_last( it ) ) + { + FLAC__metadata_simple_iterator_next( it ); + } + off_t audioOffset = FLAC__metadata_simple_iterator_get_block_offset( it ) + + FLAC__metadata_simple_iterator_get_block_length( it ); + FLAC__metadata_simple_iterator_delete( it ); + bitrate = static_cast( static_cast(m_fileSize - audioOffset) * 8 / lengthSecs + 0.5 ); + } + } +} + +// --------------------------------------------------------------------- + +void FlacSource::skip( const int mSecs ) +{ + FLAC__uint64 absSample = mSecs * m_samplerate / 1000 + m_samplePos; + if ( !FLAC__stream_decoder_seek_absolute(m_decoder, absSample) ) + FLAC__stream_decoder_reset( m_decoder ); + m_outBufLen = 0; +} + +// --------------------------------------------------------------------- + +void FlacSource::skipSilence(double silenceThreshold /* = 0.0001 */) +{ + silenceThreshold *= static_cast( std::numeric_limits::max() ); + for ( ;; ) + { + double sum = 0; + bool result = FLAC__stream_decoder_process_single( m_decoder ); + // there was a fatal read + if ( !result ) + break; + + switch ( m_channels ) + { + case 1: + for (size_t j = 0; j < m_outBufLen; ++j) + sum += abs( m_outBuf[j] ); + break; + case 2: + for ( size_t j = 0; j < m_outBufLen; j+=2 ) + sum += abs( (m_outBuf[j] >> 1) + + (m_outBuf[j+1] >> 1) ); + break; + } + if ( (sum >= silenceThreshold * static_cast(m_outBufLen) ) ) + break; + } + m_outBufLen = 0; +} + +// --------------------------------------------------------------------- + +int FlacSource::updateBuffer( signed short *pBuffer, size_t bufferSize ) +{ + size_t nwrit = 0; + + for ( ;; ) + { + size_t samples_to_use = std::min (bufferSize - nwrit, m_outBufLen - m_outBufPos); + signed short* pBufferIt = pBuffer + nwrit; + + nwrit += samples_to_use; + assert( nwrit <= bufferSize ); + memcpy( pBufferIt, m_outBuf + m_outBufPos, sizeof(signed short)*samples_to_use ); + + if ( samples_to_use < m_outBufLen - m_outBufPos ) + m_outBufPos = samples_to_use; + else + { + m_outBufPos = 0; + bool result = FLAC__stream_decoder_process_single( m_decoder ); + // there was a fatal read + if ( !result ) + { + std::cerr << "Fatal error decoding FLAC" << std::endl; + return 0; + } + else if ( FLAC__stream_decoder_get_state( m_decoder ) == FLAC__STREAM_DECODER_END_OF_STREAM ) + { + m_eof = true; + break; + } + } + + if ( nwrit == bufferSize ) + return static_cast(nwrit); + } + return static_cast(nwrit); +} + +// ----------------------------------------------------------------------------- + diff --git a/thirdparty/liblastfm2/src/fingerprint/contrib/FlacSource.h b/thirdparty/liblastfm2/src/fingerprint/contrib/FlacSource.h new file mode 100644 index 000000000..24dd97f11 --- /dev/null +++ b/thirdparty/liblastfm2/src/fingerprint/contrib/FlacSource.h @@ -0,0 +1,74 @@ +/* + Copyright 2009 Last.fm Ltd. + Copyright 2009 John Stamp + + This file is part of liblastfm. + + liblastfm 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. + + liblastfm 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 liblastfm. If not, see . +*/ +#ifndef __FLAC_SOURCE_H__ +#define __FLAC_SOURCE_H__ + +#include +#include +#include + + +class FlacSource : public lastfm::FingerprintableSource +{ +public: + FlacSource(); + virtual ~FlacSource(); + + virtual void getInfo(int& lengthSecs, int& samplerate, int& bitrate, int& nchannels); + virtual void init(const QString& fileName); + + // return a chunk of PCM data from the FLAC file + virtual int updateBuffer(signed short* pBuffer, size_t bufferSize); + + virtual void skip(const int mSecs); + virtual void skipSilence(double silenceThreshold = 0.0001); + + //QString getMbid(); + + bool eof() const { return m_eof; } + +private: + static FLAC__StreamDecoderWriteStatus _write_callback(const FLAC__StreamDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 * const buffer[], void *client_data); + static void _metadata_callback(const FLAC__StreamDecoder *decoder, const FLAC__StreamMetadata *metadata, void *client_data); + static void _error_callback(const ::FLAC__StreamDecoder *decoder, ::FLAC__StreamDecoderErrorStatus status, void *client_data); + + FLAC__StreamDecoderWriteStatus write_callback(const FLAC__Frame *frame, const FLAC__int32 * const buffer[]); + void metadata_callback( const FLAC__StreamMetadata *metadata ); + void error_callback(FLAC__StreamDecoderErrorStatus status); + + FLAC__StreamDecoder *m_decoder; + QString m_fileName; + size_t m_fileSize; + short *m_outBuf; + size_t m_outBufLen; + size_t m_outBufPos; + FLAC__uint64 m_samplePos; + unsigned m_maxFrameSize; + FLAC__StreamMetadata* m_commentData; + unsigned m_bps; + unsigned m_channels; + unsigned m_samplerate; + FLAC__uint64 m_totalSamples; + + bool m_eof; +}; + +#endif + diff --git a/thirdparty/liblastfm2/src/fingerprint/contrib/MadSource.cpp b/thirdparty/liblastfm2/src/fingerprint/contrib/MadSource.cpp new file mode 100644 index 000000000..00e725ae6 --- /dev/null +++ b/thirdparty/liblastfm2/src/fingerprint/contrib/MadSource.cpp @@ -0,0 +1,514 @@ +/* + Copyright 2009 Last.fm Ltd. + Copyright 2009 John Stamp + + This file is part of liblastfm. + + liblastfm 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. + + liblastfm 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 liblastfm. If not, see . +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "MadSource.h" + +#undef max // was definded in mad + +using namespace std; + + +// ----------------------------------------------------------- + +MadSource::MadSource() + : m_pMP3_Buffer ( new unsigned char[m_MP3_BufferSize+MAD_BUFFER_GUARD] ) +{} + +// ----------------------------------------------------------- + +MadSource::~MadSource() +{ + if ( m_inputFile.isOpen() ) + { + m_inputFile.close(); + mad_synth_finish(&m_mad_synth); + mad_frame_finish(&m_mad_frame); + mad_stream_finish(&m_mad_stream); + } + if (m_pMP3_Buffer) delete[] m_pMP3_Buffer; +} + +// --------------------------------------------------------------------- + +inline short f2s(mad_fixed_t f) +{ + /* A fixed point number is formed of the following bit pattern: + * + * SWWWFFFFFFFFFFFFFFFFFFFFFFFFFFFF + * MSB LSB + * S ==> Sign (0 is positive, 1 is negative) + * W ==> Whole part bits + * F ==> Fractional part bits + * + * This pattern contains MAD_F_FRACBITS fractional bits, one + * should alway use this macro when working on the bits of a fixed + * point number. It is not guaranteed to be constant over the + * different platforms supported by libmad. + * + * The signed short value is formed, after clipping, by the least + * significant whole part bit, followed by the 15 most significant + * fractional part bits. Warning: this is a quick and dirty way to + * compute the 16-bit number, madplay includes much better + * algorithms. + */ + + /* Clipping */ + if(f >= MAD_F_ONE) + return(SHRT_MAX); + if(f <= -MAD_F_ONE) + return(-SHRT_MAX); + + /* Conversion. */ + f = f >> (MAD_F_FRACBITS-15); + return (signed short)f; +} + +// --------------------------------------------------------------------- + +string MadSource::MadErrorString(const mad_error& error) +{ + switch(error) + { + /* Generic unrecoverable errors. */ + case MAD_ERROR_BUFLEN: + return("input buffer too small (or EOF)"); + case MAD_ERROR_BUFPTR: + return("invalid (null) buffer pointer"); + case MAD_ERROR_NOMEM: + return("not enough memory"); + + /* Frame header related unrecoverable errors. */ + case MAD_ERROR_LOSTSYNC: + return("lost synchronization"); + case MAD_ERROR_BADLAYER: + return("reserved header layer value"); + case MAD_ERROR_BADBITRATE: + return("forbidden bitrate value"); + case MAD_ERROR_BADSAMPLERATE: + return("reserved sample frequency value"); + case MAD_ERROR_BADEMPHASIS: + return("reserved emphasis value"); + + /* Recoverable errors */ + case MAD_ERROR_BADCRC: + return("CRC check failed"); + case MAD_ERROR_BADBITALLOC: + return("forbidden bit allocation value"); + case MAD_ERROR_BADSCALEFACTOR: + return("bad scalefactor index"); + case MAD_ERROR_BADFRAMELEN: + return("bad frame length"); + case MAD_ERROR_BADBIGVALUES: + return("bad big_values count"); + case MAD_ERROR_BADBLOCKTYPE: + return("reserved block_type"); + case MAD_ERROR_BADSCFSI: + return("bad scalefactor selection info"); + case MAD_ERROR_BADDATAPTR: + return("bad main_data_begin pointer"); + case MAD_ERROR_BADPART3LEN: + return("bad audio data length"); + case MAD_ERROR_BADHUFFTABLE: + return("bad Huffman table select"); + case MAD_ERROR_BADHUFFDATA: + return("Huffman data overrun"); + case MAD_ERROR_BADSTEREO: + return("incompatible block_type for JS"); + + /* Unknown error. This switch may be out of sync with libmad's + * defined error codes. + */ + default: + return("Unknown error code"); + } +} + + +// ----------------------------------------------------------------------------- + +bool MadSource::isRecoverable(const mad_error& error, bool log) +{ + if (MAD_RECOVERABLE (error)) + { + /* Do not print a message if the error is a loss of + * synchronization and this loss is due to the end of + * stream guard bytes. (See the comments marked {3} + * supra for more informations about guard bytes.) + */ + if (error != MAD_ERROR_LOSTSYNC /*|| mad_stream.this_frame != pGuard */ && log) + { + cerr << "Recoverable frame level error: " + << MadErrorString(error) << endl; + } + + return true; + } + else + { + if (error == MAD_ERROR_BUFLEN) + return true; + else + { + stringstream ss; + + ss << "Unrecoverable frame level error: " + << MadErrorString (error) << endl; + throw ss.str(); + } + } + + return false; +} + +// ----------------------------------------------------------- + +void MadSource::init(const QString& fileName) +{ + m_inputFile.setFileName( m_fileName = fileName ); + bool fine = m_inputFile.open( QIODevice::ReadOnly ); + + if ( !fine ) + { + throw std::runtime_error ("Cannot load mp3 file!"); + } + + mad_stream_init(&m_mad_stream); + mad_frame_init (&m_mad_frame); + mad_synth_init (&m_mad_synth); + mad_timer_reset(&m_mad_timer); + + m_pcmpos = m_mad_synth.pcm.length; +} + +// ----------------------------------------------------------------------------- + +/*QString MadSource::getMbid() +{ + char out[MBID_BUFFER_SIZE]; + int const r = getMP3_MBID( QFile::encodeName( m_fileName ), out ); + if (r == 0) + return QString::fromLatin1( out ); + return QString(); +}*/ + +void MadSource::getInfo(int& lengthSecs, int& samplerate, int& bitrate, int& nchannels ) +{ + // get the header plus some other stuff.. + QFile inputFile(m_fileName); + bool fine = inputFile.open( QIODevice::ReadOnly ); + + if ( !fine ) + { + throw std::runtime_error ("ERROR: Cannot load file for getInfo!"); + return; + } + + unsigned char* pMP3_Buffer = new unsigned char[m_MP3_BufferSize+MAD_BUFFER_GUARD]; + + mad_stream madStream; + mad_header madHeader; + mad_timer_t madTimer; + + mad_stream_init(&madStream); + mad_timer_reset(&madTimer); + + double avgSamplerate = 0; + double avgBitrate = 0; + double avgNChannels = 0; + int nFrames = 0; + + while ( fetchData( inputFile, pMP3_Buffer, m_MP3_BufferSize, madStream) ) + { + if ( mad_header_decode(&madHeader, &madStream) != 0 ) + { + if ( isRecoverable(madStream.error) ) + continue; + else + break; + } + + mad_timer_add(&madTimer, madHeader.duration); + + avgSamplerate += madHeader.samplerate; + avgBitrate += madHeader.bitrate; + + if ( madHeader.mode == MAD_MODE_SINGLE_CHANNEL ) + ++avgNChannels; + else + avgNChannels += 2; + + ++nFrames; + } + + inputFile.close(); + mad_stream_finish(&madStream); + mad_header_finish(&madHeader); + delete[] pMP3_Buffer; + + + lengthSecs = static_cast(madTimer.seconds); + samplerate = static_cast( (avgSamplerate/nFrames) + 0.5 ); + bitrate = static_cast( (avgBitrate/nFrames) + 0.5 ); + nchannels = static_cast( (avgNChannels/nFrames) + 0.5 ); +} + +// ----------------------------------------------------------- + + +bool MadSource::fetchData( QFile& mp3File, + unsigned char* pMP3_Buffer, + const int MP3_BufferSize, + mad_stream& madStream ) +{ + unsigned char *pReadStart = NULL; + unsigned char *pGuard = NULL; + + if ( madStream.buffer == NULL || + madStream.error == MAD_ERROR_BUFLEN ) + { + + size_t readSize; + size_t remaining; + + /* {2} libmad may not consume all bytes of the input + * buffer. If the last frame in the buffer is not wholly + * contained by it, then that frame's start is pointed by + * the next_frame member of the Stream structure. This + * common situation occurs when mad_frame_decode() fails, + * sets the stream error code to MAD_ERROR_BUFLEN, and + * sets the next_frame pointer to a non NULL value. (See + * also the comment marked {4} bellow.) + * + * When this occurs, the remaining unused bytes must be + * put back at the beginning of the buffer and taken in + * account before refilling the buffer. This means that + * the input buffer must be large enough to hold a whole + * frame at the highest observable bit-rate (currently 448 + * kb/s). XXX=XXX Is 2016 bytes the size of the largest + * frame? (448000*(1152/32000))/8 + */ + if (madStream.next_frame != NULL) + { + remaining = madStream.bufend - madStream.next_frame; + memmove (pMP3_Buffer, madStream.next_frame, remaining); + + pReadStart = pMP3_Buffer + remaining; + readSize = MP3_BufferSize - remaining; + } + else + { + readSize = MP3_BufferSize; + pReadStart = pMP3_Buffer; + remaining = 0; + } + + readSize = mp3File.read( reinterpret_cast(pReadStart), readSize ); + + // nothing else to read! + if (readSize <= 0) + return false; + + if ( mp3File.atEnd() ) + { + pGuard = pReadStart + readSize; + + memset (pGuard, 0, MAD_BUFFER_GUARD); + readSize += MAD_BUFFER_GUARD; + } + + // Pipe the new buffer content to libmad's stream decoder facility. + mad_stream_buffer( &madStream, pMP3_Buffer, + static_cast(readSize + remaining)); + + madStream.error = MAD_ERROR_NONE; + } + + return true; +} + +// ----------------------------------------------------------------------------- + +void MadSource::skipSilence(double silenceThreshold /* = 0.0001 */) +{ + mad_frame madFrame; + mad_synth madSynth; + + mad_frame_init(&madFrame); + mad_synth_init (&madSynth); + + silenceThreshold *= static_cast( numeric_limits::max() ); + + for (;;) + { + if ( !fetchData( m_inputFile, m_pMP3_Buffer, m_MP3_BufferSize, m_mad_stream) ) + break; + + if ( mad_frame_decode(&madFrame, &m_mad_stream) != 0 ) + { + if ( isRecoverable(m_mad_stream.error) ) + continue; + else + break; + } + + mad_synth_frame (&madSynth, &madFrame); + + double sum = 0; + + switch (madSynth.pcm.channels) + { + case 1: + for (size_t j = 0; j < madSynth.pcm.length; ++j) + sum += abs(f2s(madSynth.pcm.samples[0][j])); + break; + case 2: + for (size_t j = 0; j < madSynth.pcm.length; ++j) + sum += abs(f2s( + (madSynth.pcm.samples[0][j] >> 1) + + (madSynth.pcm.samples[1][j] >> 1))); + break; + } + + if ( (sum >= silenceThreshold * madSynth.pcm.length) ) + break; + } + + mad_frame_finish(&madFrame); +} + +// ----------------------------------------------------------------------------- + +void MadSource::skip(const int mSecs) +{ + if ( mSecs <= 0 ) + return; + + mad_header madHeader; + mad_header_init(&madHeader); + + for (;;) + { + if (!fetchData( m_inputFile, m_pMP3_Buffer, m_MP3_BufferSize, m_mad_stream)) + break; + + if ( mad_header_decode(&madHeader, &m_mad_stream) != 0 ) + { + if ( isRecoverable(m_mad_stream.error) ) + continue; + else + break; + } + + mad_timer_add(&m_mad_timer, madHeader.duration); + + if ( mad_timer_count(m_mad_timer, MAD_UNITS_MILLISECONDS) >= mSecs ) + break; + } + + mad_header_finish(&madHeader); +} + +// ----------------------------------------------------------- + +int MadSource::updateBuffer(signed short* pBuffer, size_t bufferSize) +{ + size_t nwrit = 0; //number of samples written to the output buffer + + for (;;) + { + // get a (valid) frame + // m_pcmpos == 0 could mean two things + // - we have completely decoded a frame, but the output buffer is still + // not full (it would make more sense for pcmpos == pcm.length(), but + // the loop assigns pcmpos = 0 at the end and does it this way! + // - we are starting a stream + if ( m_pcmpos == m_mad_synth.pcm.length ) + { + if ( !fetchData( m_inputFile, m_pMP3_Buffer, m_MP3_BufferSize, m_mad_stream) ) + { + break; // nothing else to read + } + + // decode the frame + if (mad_frame_decode (&m_mad_frame, &m_mad_stream)) + { + if ( isRecoverable(m_mad_stream.error) ) + continue; + else + break; + } // if (mad_frame_decode (&madFrame, &madStream)) + + mad_timer_add (&m_mad_timer, m_mad_frame.header.duration); + mad_synth_frame (&m_mad_synth, &m_mad_frame); + + m_pcmpos = 0; + } + + size_t samples_for_mp3 = m_mad_synth.pcm.length - m_pcmpos; + size_t samples_for_buf = bufferSize - nwrit; + signed short* pBufferIt = pBuffer + nwrit; + size_t i = 0, j = 0; + + switch( m_mad_synth.pcm.channels ) + { + case 1: + { + size_t samples_to_use = min (samples_for_mp3, samples_for_buf); + for (i = 0; i < samples_to_use; ++i ) + pBufferIt[i] = f2s( m_mad_synth.pcm.samples[0][i+m_pcmpos] ); + } + j = i; + break; + + case 2: + for (; i < samples_for_mp3 && j < samples_for_buf ; ++i, j+=2 ) + { + pBufferIt[j] = f2s( m_mad_synth.pcm.samples[0][i+m_pcmpos] ); + pBufferIt[j+1] = f2s( m_mad_synth.pcm.samples[1][i+m_pcmpos] ); + } + break; + + default: + cerr << "wtf kind of mp3 has " << m_mad_synth.pcm.channels << " channels??\n"; + break; + } + + m_pcmpos += i; + nwrit += j; + + assert( nwrit <= bufferSize ); + + if (nwrit == bufferSize) + return static_cast(nwrit); + } + + return static_cast(nwrit); +} + +// ----------------------------------------------------------------------------- + diff --git a/thirdparty/liblastfm2/src/fingerprint/contrib/MadSource.h b/thirdparty/liblastfm2/src/fingerprint/contrib/MadSource.h new file mode 100644 index 000000000..c22cb6d5d --- /dev/null +++ b/thirdparty/liblastfm2/src/fingerprint/contrib/MadSource.h @@ -0,0 +1,69 @@ +/* + Copyright 2009 Last.fm Ltd. + Copyright 2009 John Stamp + + This file is part of liblastfm. + + liblastfm 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. + + liblastfm 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 liblastfm. If not, see . +*/ + +#ifndef __MP3_SOURCE_H__ +#define __MP3_SOURCE_H__ + +#include +#include +#include +#include +#include +#include + + +class MadSource : public lastfm::FingerprintableSource +{ +public: + MadSource(); + ~MadSource(); + + virtual void getInfo(int& lengthSecs, int& samplerate, int& bitrate, int& nchannels); + virtual void init(const QString& fileName); + virtual int updateBuffer(signed short* pBuffer, size_t bufferSize); + virtual void skip(const int mSecs); + virtual void skipSilence(double silenceThreshold = 0.0001); + virtual bool eof() const { return m_inputFile.atEnd(); } + +private: + static bool fetchData( QFile& mp3File, + unsigned char* pMP3_Buffer, + const int MP3_BufferSize, + mad_stream& madStream ); + + static bool isRecoverable(const mad_error& error, bool log = false); + + static std::string MadErrorString(const mad_error& error); + + struct mad_stream m_mad_stream; + struct mad_frame m_mad_frame; + mad_timer_t m_mad_timer; + struct mad_synth m_mad_synth; + + QFile m_inputFile; + + unsigned char* m_pMP3_Buffer; + static const int m_MP3_BufferSize = (5*8192); + QString m_fileName; + + size_t m_pcmpos; +}; + +#endif diff --git a/thirdparty/liblastfm2/src/fingerprint/contrib/VorbisSource.cpp b/thirdparty/liblastfm2/src/fingerprint/contrib/VorbisSource.cpp new file mode 100644 index 000000000..fd4defb17 --- /dev/null +++ b/thirdparty/liblastfm2/src/fingerprint/contrib/VorbisSource.cpp @@ -0,0 +1,204 @@ +/* + Copyright 2009 Last.fm Ltd. + Copyright 2009 John Stamp + + This file is part of liblastfm. + + liblastfm 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. + + liblastfm 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 liblastfm. If not, see . +*/ +#include "VorbisSource.h" +#include +#include +#include +#include +#include +#include +#include + +// These specify the output format +static const int wordSize = 2; // 16 bit output +static const int isSigned = 1; +#if __BIG_ENDIAN__ +static const int isBigEndian = 1; +#else +static const int isBigEndian = 0; +#endif + + +VorbisSource::VorbisSource() + : m_channels( 0 ) + , m_samplerate( 0 ) + , m_eof( false ) +{ + memset( &m_vf, 0, sizeof(m_vf) ); +} + +// --------------------------------------------------------------------- + +VorbisSource::~VorbisSource() +{ + // ov_clear() also closes the file + ov_clear( &m_vf ); +} + +// --------------------------------------------------------------------- + +void VorbisSource::init(const QString& fileName) +{ + m_fileName = fileName; + + if ( m_vf.datasource ) + { + std::cerr << "Warning: file already appears to be open"; + return; + } + + FILE *fp = fopen(QFile::encodeName(m_fileName), "rb" ); + if( !fp ) + throw std::runtime_error( "ERROR: Cannot open ogg file!" ); + + // See the warning about calling ov_open on Windows + if ( ov_test_callbacks( fp, &m_vf, NULL, 0, OV_CALLBACKS_DEFAULT ) < 0 ) + { + fclose( fp ); + throw std::runtime_error( "ERROR: This is not an ogg vorbis file!" ); + } + + ov_test_open( &m_vf ); + + // Don't fingerprint files with more than one logical bitstream + // They most likely contain more than one track + if ( ov_streams( &m_vf ) != 1 ) + throw std::runtime_error( "ERROR: ogg file contains multiple bitstreams" ); + + m_channels = ov_info( &m_vf, 0 )->channels; + m_samplerate = static_cast(ov_info( &m_vf, 0 )->rate); + m_eof = false; +} + +void VorbisSource::getInfo( int& lengthSecs, int& samplerate, int& bitrate, int& nchannels) +{ + // stream info + nchannels = ov_info( &m_vf, -1 )->channels; + samplerate = static_cast(ov_info( &m_vf, -1 )->rate); + lengthSecs = static_cast(ov_time_total( &m_vf, -1 ) + 0.5); + bitrate = static_cast(ov_bitrate( &m_vf, -1 )); +} + +// --------------------------------------------------------------------- + +void VorbisSource::skip( const int mSecs ) +{ + if ( mSecs < 0 ) + return; + + double ts = mSecs / 1000.0 + ov_time_tell( &m_vf ); + ov_time_seek( &m_vf, ts ); +} + +// --------------------------------------------------------------------- + +void VorbisSource::skipSilence(double silenceThreshold /* = 0.0001 */) +{ + silenceThreshold *= static_cast( std::numeric_limits::max() ); + + char sampleBuffer[4096]; + int bs = 0; + for (;;) + { + long charReadBytes = ov_read( &m_vf, sampleBuffer, 4096, isBigEndian, wordSize, isSigned, &bs ); + + // eof + if ( !charReadBytes ) + { + m_eof = true; + break; + } + if ( charReadBytes < 0 ) + { + // a bad bit of data: OV_HOLE || OV_EBADLINK + continue; + } + else if ( charReadBytes > 0 ) + { + double sum = 0; + int16_t *buf = reinterpret_cast(sampleBuffer); + switch ( m_channels ) + { + case 1: + for (long j = 0; j < charReadBytes/wordSize; j++) + sum += abs( buf[j] ); + break; + case 2: + for (long j = 0; j < charReadBytes/wordSize; j+=2) + sum += abs( (buf[j] >> 1) + (buf[j+1] >> 1) ); + break; + } + if ( sum >= silenceThreshold * static_cast(charReadBytes/wordSize/m_channels) ) + break; + } + } +} + +// --------------------------------------------------------------------- + +int VorbisSource::updateBuffer( signed short *pBuffer, size_t bufferSize ) +{ + char buf[ bufferSize * wordSize ]; + int bs = 0; + size_t charwrit = 0; //number of samples written to the output buffer + + for (;;) + { + long charReadBytes = ov_read( &m_vf, buf, static_cast(bufferSize * wordSize - charwrit), + isBigEndian, wordSize, isSigned, &bs ); + if ( !charReadBytes ) + { + m_eof = true; + break; // nothing else to read + } + + // Don't really need this though since we're excluding files that have + // more than one logical bitstream + if ( bs != 0 ) + { + vorbis_info *vi = ov_info( &m_vf, -1 ); + if ( m_channels != vi->channels || m_samplerate != vi->rate ) + { + std::cerr << "Files that change channel parameters or samplerate are currently not supported" << std::endl; + return 0; + } + } + + if( charReadBytes < 0 ) + { + std::cerr << "Warning: corrupt section of data, attempting to continue..." << std::endl; + continue; + } + + char* pBufferIt = reinterpret_cast(pBuffer) + charwrit; + charwrit += charReadBytes; + + assert( charwrit <= bufferSize * wordSize ); + memcpy( pBufferIt, buf, charReadBytes ); + + if (charwrit == bufferSize * wordSize) + return static_cast(charwrit/wordSize); + } + + return static_cast(charwrit/wordSize); +} + +// ----------------------------------------------------------------------------- + diff --git a/thirdparty/liblastfm2/src/fingerprint/contrib/VorbisSource.h b/thirdparty/liblastfm2/src/fingerprint/contrib/VorbisSource.h new file mode 100644 index 000000000..988ce6239 --- /dev/null +++ b/thirdparty/liblastfm2/src/fingerprint/contrib/VorbisSource.h @@ -0,0 +1,47 @@ +/* + Copyright 2009 Last.fm Ltd. + Copyright 2009 John Stamp + + This file is part of liblastfm. + + liblastfm 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. + + liblastfm 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 liblastfm. If not, see . +*/ +#ifndef __VORBIS_SOURCE_H__ +#define __VORBIS_SOURCE_H__ + +#include +#include + + +class VorbisSource : public lastfm::FingerprintableSource +{ +public: + VorbisSource(); + ~VorbisSource(); + virtual void getInfo(int& lengthSecs, int& samplerate, int& bitrate, int& nchannels); + virtual void init(const QString& fileName); + virtual int updateBuffer(signed short* pBuffer, size_t bufferSize); + virtual void skip(const int mSecs); + virtual void skipSilence(double silenceThreshold = 0.0001); + virtual bool eof() const { return m_eof; } + +private: + OggVorbis_File m_vf; + QString m_fileName; + int m_channels; + int m_samplerate; + bool m_eof; +}; + +#endif diff --git a/thirdparty/liblastfm2/src/fingerprint/contrib/lastfm-fingerprint.pro b/thirdparty/liblastfm2/src/fingerprint/contrib/lastfm-fingerprint.pro new file mode 100644 index 000000000..bd615e723 --- /dev/null +++ b/thirdparty/liblastfm2/src/fingerprint/contrib/lastfm-fingerprint.pro @@ -0,0 +1,13 @@ +QT = core xml network +LIBS += -L$$DESTDIR -llastfm -llastfm_fingerprint +LIBS += -lvorbisfile -lFLAC -lfaad -lmp4ff -lmad +SOURCES = AacSource.cpp FlacSource.cpp MadSource.cpp VorbisSource.cpp main.cpp + +mac { + INCLUDEPATH += /opt/local/include + LIBS += -L/opt/local/lib + + DEFINES += MACPORTS_SUCKS + SOURCES -= AacSource.cpp + LIBS -= -lmp4ff +} diff --git a/thirdparty/liblastfm2/src/fingerprint/contrib/main.cpp b/thirdparty/liblastfm2/src/fingerprint/contrib/main.cpp new file mode 100644 index 000000000..3b035f2d8 --- /dev/null +++ b/thirdparty/liblastfm2/src/fingerprint/contrib/main.cpp @@ -0,0 +1,173 @@ +/* + Copyright 2009 Last.fm Ltd. + Copyright 2009 John Stamp + + This file is part of liblastfm. + + liblastfm 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. + + liblastfm 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 liblastfm. If not, see . +*/ + +// ubuntu 9.04: sudo apt-get install libmad0-dev libvorbis-dev libflac-dev libfaac-dev +// macports: sudo port install libmad libvorbis libflac +// Windows: lol + +#include "MadSource.h" +#include "VorbisSource.h" +#include "FlacSource.h" +#include "AacSource.h" +#include +#include +#include +#include +#include +int typeOf(const QString& path); +lastfm::FingerprintableSource* factory(int type); +enum { MP3, OGG, FLAC, AAC, UNKNOWN }; +namespace lastfm { Track taglib(const QString& path); } + + +int main(int argc, char** argv) try +{ + if (argc < 2) { + std::cerr << "usage: " << argv[0] << " path" << std::endl; + return 1; + } + + QCoreApplication app(argc, argv); + QEventLoop loop; + + QString const path = QFile::decodeName(argv[1]); + + lastfm::Track t = lastfm::taglib(path); //see contrib //TODO mbid + lastfm::Fingerprint fp(t); + if (fp.id().isNull()) { + lastfm::FingerprintableSource* src = factory(typeOf(path)); + fp.generate(src); + QNetworkReply* reply = fp.submit(); + loop.connect(reply, SIGNAL(finished()), SLOT(quit())); + fp.decode(reply); + } + + QNetworkReply* reply = fp.id().getSuggestions(); + loop.connect(reply, SIGNAL(finished()), SLOT(quit())); + + std::cout << reply->readAll().data() << std::endl; //returns XML + return 0; +} +catch (std::exception& e) +{ + std::cerr << e.what() << std::endl; +} + +lastfm::FingerprintableSource* factory(int type) +{ + switch (type) { + case MP3: return new MadSource; + case OGG: return new VorbisSource; + case FLAC: return new FlacSource; + #ifndef MACPORTS_SUCKS + case AAC: return new AacSource; + #endif + default: throw std::runtime_error("Cannot handle filetype"); + } +} + +int typeOf(const QString& fileName) +{ + QStringList parts = fileName.split( "." ); + QString extension; + if ( parts.size() > 1 ) + extension = parts.last(); + + // Let's be trusting about extensions + if ( extension.toLower() == "mp3" ) + return MP3; + else if ( extension.toLower() == "ogg" ) + return OGG; + else if ( extension.toLower() == "oga" ) + return FLAC; + else if ( extension.toLower() == "flac" ) + return FLAC; + else if ( extension.toLower() == "aac" ) + return AAC; + else if ( extension.toLower() == "m4a" ) + return AAC; + + // So much for relying on extensions. Let's try file magic instead. + FILE *fp = NULL; + unsigned char header[35]; + + fp = fopen(QFile::encodeName(fileName), "rb"); + if ( !fp ) + { + return UNKNOWN; + } + int fType = UNKNOWN; + fread( header, 1, 35, fp ); + + // Some formats can have ID3 tags (or not), so let's just + // get them out of the way first before we check what we have. + if ( memcmp( header, "ID3", 3) == 0 ) + { + int tagsize = 0; + /* high bit is not used */ + tagsize = (header[6] << 21) | (header[7] << 14) | + (header[8] << 7) | (header[9] << 0); + + tagsize += 10; + fseek( fp, tagsize, SEEK_SET ); + fread( header, 1, 35, fp ); + } + + if ( (header[0] == 0xFF) && ((header[1] & 0xFE) == 0xFA ) ) + { + fType = MP3; + } + else if ( memcmp(header, "OggS", 4) == 0 ) + { + if ( memcmp(&header[29], "vorbis", 6) == 0 ) + { + // ogg vorbis (.ogg) + fType = OGG; + } + else if ( memcmp(&header[29], "FLAC", 4) == 0 ) + { + // ogg flac (.oga) + fType = FLAC; + } + } + else if ( memcmp(header, "fLaC", 4 ) == 0 ) + { + // flac file + fType = FLAC; + } + else if ( (header[0] == 0xFF) && ((header[1] & 0xF6) == 0xF0) ) + { + // aac adts + fType = AAC; + } + else if (memcmp(header, "ADIF", 4) == 0) + { + // aac adif + fType = AAC; + } + else if ( memcmp( &header[4], "ftyp", 4 ) == 0 ) + { + // mp4 header: aac + fType = AAC; + } + + fclose(fp); + return fType; +} diff --git a/thirdparty/liblastfm2/src/fingerprint/fingerprint.pro b/thirdparty/liblastfm2/src/fingerprint/fingerprint.pro new file mode 100644 index 000000000..043ad7bfb --- /dev/null +++ b/thirdparty/liblastfm2/src/fingerprint/fingerprint.pro @@ -0,0 +1,25 @@ +TEMPLATE = lib +TARGET = lastfm_fingerprint +LIBS += -L$$DESTDIR -llastfm +QT = core xml network sql +include( _files.qmake ) +DEFINES += LASTFM_FINGERPRINT_LIB + +INSTALLS = target +target.path = /lib + +mac:CONFIG( app_bundle ) { + LIBS += libfftw3f.a libsamplerate.a -L/opt/local/include + INCLUDEPATH += /opt/local/include:/opt/qt/qt-current/lib/QtSql.framework/Include/ +}else{ + INCLUDEPATH += /opt/qt/qt-current/lib/QtSql.framework/Include/ + CONFIG += link_pkgconfig + PKGCONFIG += samplerate + win32 { + CONFIG += link_pkgconfig + DEFINES += __NO_THREAD_CHECK + QMAKE_LFLAGS_DEBUG += /NODEFAULTLIB:msvcrt.lib /NODEFAULTLIB:libcmt.lib + } + PKGCONFIG += fftw3f + +} diff --git a/thirdparty/liblastfm2/src/fingerprint/fplib/CircularArray.h b/thirdparty/liblastfm2/src/fingerprint/fplib/CircularArray.h new file mode 100644 index 000000000..bfec5a8fd --- /dev/null +++ b/thirdparty/liblastfm2/src/fingerprint/fplib/CircularArray.h @@ -0,0 +1,292 @@ +/* + Copyright 2005-2009 Last.fm Ltd. + + This file is part of liblastfm. + + liblastfm 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. + + liblastfm 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 liblastfm. If not, see . +*/ +#ifndef __CIRCULAR_ARRAY_H +#define __CIRCULAR_ARRAY_H + +#include +#include +#include +#include +#include // for memset +#include // for max + +#ifndef NULL +#define NULL 0 +#endif + +template< typename T > +class CircularArray +{ + +public: + + typedef size_t size_type; + + ///////////////////////////////////////////////////////////// + + // IMPORTANT: The distance must be redefined!! + // See declaration of iterator from stl_iterator_base_types.h: + // template + // struct iterator { ... + + // ---------- Forward declarations + + class iterator : + public std::iterator + { + // it should be by default because is an inner class, but I put it just to be sure.. + friend class CircularArray; + + private: + iterator( size_type idx, T* pData, size_type size ) : _idx(idx), _pData(pData), _size(size) {} + + public: + + //typedef random_access_iterator_tag iterator_category; + + iterator() : _idx(0), _pData(NULL) {} + + iterator& operator++() + { // preincrement + _idx = (_idx + 1) % _size; + return (*this); + } + + iterator operator++(int) + { // postincrement + iterator _Tmp = *this; + _idx = (_idx + 1) % _size; + return (_Tmp); + } + + void operator+=(size_type offs) + { + this->_idx = (_idx + offs) % _size; + } + + iterator operator+(size_type offs) const + { + size_type newIdx = (_idx + offs) % _size; + iterator _Tmp(newIdx, _pData, _size); + return _Tmp; + } + + // return the distance between this iterator and it + size_t operator-(const iterator& it) const + { + if ( this->_idx > it._idx ) + return this->_idx - it._idx; + else + return this->_idx + (_size - it._idx); + } + + iterator operator-(size_type offs) const + { + size_type newIdx; + + if ( offs <= _idx ) + newIdx = _idx - offs; + else + newIdx = _size - ((_idx - offs) % _size); // note: should be ok, but to be checked better + + iterator _Tmp(newIdx, _pData, _size); + return _Tmp; + } + + iterator& operator--() + { // predecrement + if (_idx == 0) + _idx = _size - 1; + else + --_idx; + return (*this); + } + + iterator operator--(int) + { // postdecrement + iterator _Tmp = *this; + if (_idx == 0) + _idx = _size - 1; + else + --_idx; + return (_Tmp); + } + + T& operator*() const + { // return designated object + return _pData[_idx]; + } + + T* operator->() const + { // return pointer to class object + return &_pData[_idx]; + } + + /* T& operator=(const T& right) + { // assign reference right to _val + return ( this->_idx = right._idx ); + }*/ + + bool operator==(const iterator& right) const + { // test for iterator equality + return ( this->_idx == right._idx ); + } + + bool operator!=(const iterator& right) const + { // test for iterator inequality + return ( this->_idx != right._idx ); + } + + protected: + size_type _idx; + T* _pData; + size_type _size; + }; + + ///////////////////////////////////////////////////////////// + + + CircularArray() + : _headIdx(0), _pData(NULL), _size(0) + { } + + CircularArray( size_type size ) + : _headIdx(0), _pData(NULL) + { + this->resize(size); + } + + CircularArray( size_type size, const T& init ) + : _headIdx(0), _pData(NULL) + { + this->resize(size, init); + } + + ~CircularArray() + { + this->clear(); + } + + // remember: it is not working (yet!) with negative numbers! + T& operator[](size_type offset) + { + return _pData[ (_headIdx + offset) % _size ]; + } + + void resize( size_type size ) + { + _headIdx = 0; + if ( size == _size ) + return; + + this->clear(); + _pData = new T[size]; + _size = size; + } + + void resize( size_type size, const T& init ) + { + this->resize(size, false); + this->fill(init); + } + + void fill( const T& val ) + { + for (size_type i=0; i<_size; ++i) + _pData[i] = val; + } + + void zero_fill() + { + memset( _pData, 0, _size * sizeof(T) ); + } + + bool empty() const + { + return ( _pData == NULL ); + } + + void clear() + { + if (_pData) + delete [] _pData; + _pData = NULL; + _headIdx = 0; + _size = 0; + } + + iterator head() const + { + if (_pData == NULL) + std::cerr << "WARNING: iterator in CircularArray points to an empty CircularArray" << std::endl; + return iterator(_headIdx, _pData, _size); + } + + void shift_head( int offset ) + { + if ( offset < 0) + { + int mod = (-offset) % (int)_size; + mod -= (int)_headIdx; + _headIdx = _size - mod; + } + else + _headIdx = (_headIdx + offset) % _size; + } + + size_type size() const + { + return _size; + } + + //// to be changed to an input forward iterator + //template + //void get_data( TIterator toFillIt, size_type size = 0 ) + //{ + // if ( size == 0 ) + // size = _size; + // iterator it = head(); + // + // for (size_type i = 0; i < size; ++i) + // *(toFillIt++) = *(it++); + //} + + // IMPORTANT! Destination buffer MUST be the same size! + void copy_buffer( T* pDest ) + { + memcpy( pDest, _pData, sizeof(T)*_size ); + } + + // returns the buffer + T* get_buffer() const + { + return _pData; + } + + +private: + + size_type _headIdx; // index + T* _pData; // array of data + size_type _size; // size of data + +}; + +#endif // __CIRCULAR_ARRAY_H diff --git a/thirdparty/liblastfm2/src/fingerprint/fplib/Filter.cpp b/thirdparty/liblastfm2/src/fingerprint/fplib/Filter.cpp new file mode 100644 index 000000000..eed4ea3a3 --- /dev/null +++ b/thirdparty/liblastfm2/src/fingerprint/fplib/Filter.cpp @@ -0,0 +1,128 @@ +/* + Copyright 2005-2009 Last.fm Ltd. + + This file is part of liblastfm. + + liblastfm 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. + + liblastfm 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 liblastfm. If not, see . +*/ +#include +#include // for max +#include + +#include "Filter.h" +#include "fp_helper_fun.h" + +using namespace std; + +namespace fingerprint +{ + +Filter::Filter(unsigned int id, float threshold, float weight) +: id(id), threshold(threshold), weight(weight) +{ + float time_rate = 1.5; + + unsigned int t = 1; + vector time_lengths; + + while (t < KEYWIDTH) + { + time_lengths.push_back(t); + t = max( static_cast( round__(time_rate*t) ) + + static_cast( round__(time_rate*t) % 2), + t+1 ); + } + + unsigned int filter_count = 0; + + for (wt = 1; wt <= time_lengths.size(); wt++) + { + for (wb = 1; wb <= NBANDS; wb++) + { + for (first_band = 1; first_band <= NBANDS - wb + 1; + first_band++) + { + unsigned int time = time_lengths[wt-1]; + filter_count++; + + if (filter_count == id) + { + wt = time_lengths[wt-1]; + filter_type = 1; + return; + } + + if (time > 1) + { + filter_count++; + if (filter_count == id) + { + wt = time_lengths[wt-1]; + filter_type = 2; + return; + } + } + + if (wb > 1) + { + filter_count++; + if (filter_count == id) + { + wt = time_lengths[wt-1]; + filter_type = 3; + return; + } + } + + if (time > 1 && wb > 1) + { + filter_count++; + if (filter_count == id) + { + wt = time_lengths[wt-1]; + filter_type = 4; + return; + } + } + + if (time > 3) + { + filter_count++; + if (filter_count == id) + { + wt = time_lengths[wt-1]; + filter_type = 5; + return; + } + } + + if (wb > 3) + { + filter_count++; + if (filter_count == id) + { + wt = time_lengths[wt-1]; + filter_type = 6; + return; + } + } + + } // for first_band + } // for wb + } // for wt +} + +} // end of namespace fingerprint + +// ----------------------------------------------------------------------------- diff --git a/thirdparty/liblastfm2/src/fingerprint/fplib/Filter.h b/thirdparty/liblastfm2/src/fingerprint/fplib/Filter.h new file mode 100644 index 000000000..04185eec2 --- /dev/null +++ b/thirdparty/liblastfm2/src/fingerprint/fplib/Filter.h @@ -0,0 +1,47 @@ +/* + Copyright 2005-2009 Last.fm Ltd. + + This file is part of liblastfm. + + liblastfm 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. + + liblastfm 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 liblastfm. If not, see . +*/ +#ifndef __FILTER_H +#define __FILTER_H + +namespace fingerprint +{ + +struct Filter +{ + /// Constructs a new filter with id. + Filter(unsigned int id, float threshold, float weight); + + unsigned int id; //< filter id + unsigned int wt; //< time width + unsigned int first_band; //< first band + unsigned int wb; //< band width + unsigned int filter_type; //< filter type + + float threshold; //< threshold for filter + float weight; //< filter weight + + // number of frames in time + static const unsigned int KEYWIDTH = 100; + // number of bands to divide the signal (log step) + static const unsigned int NBANDS = 33; +}; + +} + +#endif // __FILTER_H diff --git a/thirdparty/liblastfm2/src/fingerprint/fplib/FingerprintExtractor.cpp b/thirdparty/liblastfm2/src/fingerprint/fplib/FingerprintExtractor.cpp new file mode 100644 index 000000000..305aad7b5 --- /dev/null +++ b/thirdparty/liblastfm2/src/fingerprint/fplib/FingerprintExtractor.cpp @@ -0,0 +1,786 @@ +/* + Copyright 2005-2009 Last.fm Ltd. + + This file is part of liblastfm. + + liblastfm 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. + + liblastfm 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 liblastfm. If not, see . +*/ +#include +#include +#include +#include +#include +#include +#include +#include + +#include // libsamplerate + +#include "FingerprintExtractor.h" +#include "fp_helper_fun.h" // for GroupData +#include "Filter.h" +#include "FloatingAverage.h" +#include "OptFFT.h" + +////////////////////////////////////////////////////////////////////////// + +namespace fingerprint +{ + +using namespace std; +static const int NUM_FRAMES_CLIENT = 32; // ~= 10 secs. + +enum eProcessType +{ + PT_UNKNOWN, + PT_FOR_QUERY, + PT_FOR_FULLSUBMIT +}; + +////////////////////////////////////////////////////////////////////////// + +class PimplData +{ + +public: + + PimplData() + : m_pDownsampledPCM(NULL), m_pDownsampledCurrIt(NULL), + m_normalizedWindowMs(static_cast(NORMALIZATION_SKIP_SECS * 1000 * 2)), + m_compensateBufferSize(FRAMESIZE-OVERLAPSAMPLES + Filter::KEYWIDTH * OVERLAPSAMPLES), + m_downsampledProcessSize(NUM_FRAMES_CLIENT*FRAMESIZE), + // notice that the buffer has extra space on either side for the normalization window + m_fullDownsampledBufferSize( m_downsampledProcessSize + // the actual processed part + m_compensateBufferSize + // a compensation buffer for the fft + ((m_normalizedWindowMs * DFREQ / 1000) / 2) ), // a compensation buffer for the normalization + m_normWindow(m_normalizedWindowMs * DFREQ / 1000), + m_pFFT(NULL), m_pDownsampleState(NULL), m_processType(PT_UNKNOWN) + { + m_pFFT = new OptFFT(m_downsampledProcessSize + m_compensateBufferSize); + m_pDownsampledPCM = new float[m_fullDownsampledBufferSize]; + + // the end of ||-------m_bufferSize-------|-cb-|---norm/2---|| + // ^-- pEndDownsampledBuf + m_pEndDownsampledBuf = m_pDownsampledPCM + m_fullDownsampledBufferSize; + + // loading filters + size_t numFilters = sizeof(rFilters) / sizeof(RawFilter) ; + for (size_t i = 0; i < numFilters; ++i) + m_filters.push_back( Filter( rFilters[i].ftid, rFilters[i].thresh, rFilters[i].weight ) ); + + } + + ~PimplData() + { + if ( m_pFFT ) + delete m_pFFT; + m_pFFT = NULL; + if ( m_pDownsampledPCM ) + delete [] m_pDownsampledPCM; + m_pDownsampledPCM = NULL; + + if ( m_pDownsampleState ) + src_delete(m_pDownsampleState) ; + + } + + float* m_pDownsampledPCM; + float* m_pDownsampledCurrIt; + + const unsigned int m_normalizedWindowMs; + const size_t m_compensateBufferSize; + const size_t m_downsampledProcessSize; + const size_t m_fullDownsampledBufferSize; + + FloatingAverage m_normWindow; + OptFFT* m_pFFT; + + ////////////////////////////////////////////////////////////////////////// + + // libsamplerate + SRC_STATE* m_pDownsampleState; + SRC_DATA m_downsampleData; + + vector m_floatInData; + + ////////////////////////////////////////////////////////////////////////// + + + bool m_groupsReady; + bool m_preBufferPassed; + + eProcessType m_processType; + + size_t m_toSkipSize; + size_t m_toSkipMs; + + size_t m_skippedSoFar; + bool m_skipPassed; + + float* m_pEndDownsampledBuf; + + int m_freq; + int m_nchannels; + + unsigned int m_lengthMs; + int m_minUniqueKeys; + unsigned int m_uniqueKeyWindowMs; + + unsigned int m_toProcessKeys; + unsigned int m_totalWindowKeys; + + vector m_filters; + + deque m_groupWindow; + vector m_groups; + unsigned int m_processedKeys; + + vector m_partialBits; // here just to avoid reallocation + +#if __BIG_ENDIAN__ + +#define reorderbits(X) ((((unsigned int)(X) & 0xff000000) >> 24) | \ + (((unsigned int)(X) & 0x00ff0000) >> 8) | \ + (((unsigned int)(X) & 0x0000ff00) << 8) | \ + (((unsigned int)(X) & 0x000000ff) << 24)) + + vector m_bigEndianGroups; +#endif +}; + +////////////////////////////////////////////////////////////////////////// + +void initCustom( PimplData& pd, + int freq, int nchannels, + unsigned int lengthMs, unsigned int skipMs, + int minUniqueKeys, unsigned int uniqueKeyWindowMs, int duration ); + +inline float getRMS( const FloatingAverage& signal ); +unsigned int processKeys( deque& groups, size_t size, PimplData& pd ); +void integralImage( float** ppFrames, unsigned int nFrames ); +void computeBits( vector& bits, + const vector& f, + float ** frames, unsigned int nframes ); + + +void src_short_to_float_and_mono_array(const short *in, float *out, int srclen, int nchannels); + +////////////////////////////////////////////////////////////////////////// + +// ----------------------------------------------------------------------------- + +FingerprintExtractor::FingerprintExtractor() +: m_pPimplData(NULL) +{ + m_pPimplData = new PimplData(); +} + +// ----------------------------------------------------------------------------- + +FingerprintExtractor::~FingerprintExtractor() +{ + if ( m_pPimplData ) + delete m_pPimplData; +} + +// ----------------------------------------------------------------------------- + +size_t FingerprintExtractor::getToSkipMs() +{ return m_pPimplData->m_toSkipMs; } + +// ----------------------------------------------------------------------------- + +size_t FingerprintExtractor::getMinimumDurationMs() +{ + return static_cast( (QUERY_SIZE_SECS + NORMALIZATION_SKIP_SECS * 2 + GUARD_SIZE_SECS) * 1000 ); +} + +// ----------------------------------------------------------------------------- + +size_t FingerprintExtractor::getVersion() +{ return FINGERPRINT_LIB_VERSION; } + +// ----------------------------------------------------------------------------- + +void FingerprintExtractor::initForQuery(int freq, int nchannels, int duration ) +{ + m_pPimplData->m_skipPassed = false; + m_pPimplData->m_processType = PT_FOR_QUERY; + + if ( !m_pPimplData ) + throw std::runtime_error("Not enough RAM to allocate the fingerprinter!"); + + initCustom( *m_pPimplData, + freq, nchannels, + static_cast(QUERY_SIZE_SECS * 1000), + static_cast(QUERY_START_SECS * 1000), + MIN_UNIQUE_KEYS, + static_cast(UPDATE_SIZE_SECS * 1000), duration ); +} + +// ----------------------------------------------------------------------------- + +void FingerprintExtractor::initForFullSubmit(int freq, int nchannels ) +{ + m_pPimplData->m_skipPassed = true; + m_pPimplData->m_processType = PT_FOR_FULLSUBMIT; + + if ( !m_pPimplData ) + throw std::runtime_error("Not enough RAM to allocate the fingerprinter!"); + + initCustom( *m_pPimplData, + freq, nchannels, + numeric_limits::max(), + 0, MIN_UNIQUE_KEYS, 0, -1 ); +} + +// ----------------------------------------------------------------------------- + +void initCustom( PimplData& pd, + int freq, int nchannels, + unsigned int lengthMs, + unsigned int skipMs, + int minUniqueKeys, + unsigned int uniqueKeyWindowMs, int duration ) +{ + ////////////////////////////////////////////////////////////////////////// + pd.m_freq = freq; + pd.m_nchannels = nchannels; + pd.m_lengthMs = lengthMs; + pd.m_minUniqueKeys = minUniqueKeys; + pd.m_uniqueKeyWindowMs = uniqueKeyWindowMs; + ////////////////////////////////////////////////////////////////////////// + + // *********************************************************************** + if ( pd.m_pDownsampleState ) + pd.m_pDownsampleState = src_delete(pd.m_pDownsampleState) ; + pd.m_pDownsampleState = src_new (SRC_SINC_FASTEST, 1, NULL) ; + pd.m_downsampleData.src_ratio = FDFREQ / freq; + // *********************************************************************** + + ////////////////////////////////////////////////////////////////////////// + if ( pd.m_processType == PT_FOR_FULLSUBMIT ) + skipMs = 0; // make sure + else if ( duration > 0 ) + { + // skip + size + right normalization window + FFT guard + // + int stdDurationMs = static_cast((QUERY_START_SECS + QUERY_SIZE_SECS + NORMALIZATION_SKIP_SECS + GUARD_SIZE_SECS) * 1000); + int actualDurationMs = duration * 1000; + // compute the actual skipMs depending on the duration + if ( actualDurationMs < stdDurationMs ) + skipMs -= max( stdDurationMs - actualDurationMs, 0 ); + } + + pd.m_toSkipMs = max( static_cast(skipMs) - static_cast((pd.m_normalizedWindowMs/2)), 0 ); + pd.m_toSkipSize = static_cast( freq * nchannels * + (pd.m_toSkipMs / 1000.0) ); // half the norm window in secs; + + //if ( pd.m_processType == PT_FOR_QUERY && skipMs > pd.m_normalizedWindowMs/2 ) + //{ + // pd.m_toSkipMs = skipMs - (pd.m_normalizedWindowMs/2); + // pd.m_toSkipSize = static_cast( freq * nchannels * + // (pd.m_toSkipMs / 1000.0) ); // half the norm window in secs + //} + //else + //{ + // pd.m_toSkipMs = 0; + // pd.m_toSkipSize = 0; // half of the normalization window will be skipped in ANY case + //} + + pd.m_skippedSoFar = 0; + pd.m_groupsReady = false; + pd.m_preBufferPassed = false; + + // prepare the position for pre-buffering + pd.m_pDownsampledCurrIt = pd.m_pDownsampledPCM + (pd.m_downsampledProcessSize - (pd.m_normWindow.size() / 2) ); + + pd.m_toProcessKeys = fingerprint::getTotalKeys(pd.m_lengthMs);// (m_lengthMs * DFREQ) / (1000 * OVERLAPSAMPLES) + 1; + pd.m_totalWindowKeys = fingerprint::getTotalKeys(pd.m_uniqueKeyWindowMs); //(m_uniqueKeyWindowMs * DFREQ) / (1000 * OVERLAPSAMPLES) + 1; + + if (pd.m_toProcessKeys == 1) + pd.m_toProcessKeys = 0; + if (pd.m_totalWindowKeys == 1) + pd.m_totalWindowKeys = 0; + + pd.m_processedKeys = 0; + + pd.m_groupWindow.clear(); + pd.m_processedKeys = 0; +} + +// ----------------------------------------------------------------------------- + + +// * cb = compensate buffer size +// * norm = floating normalization window size +// +// PREBUFFER: +// (-------m_bufferSize-------) +// || EMPTY |---norm/2---|-cb-|---norm/2---|| +// 1. {--------read frames-----------} +// 2. {--read normalize window--} +// 3. {----} normalize +// +// 1. read [norm + cb] frames to m_bufferSize - norm/2 +// 2. read [m_buffersize - norm/2...m_buffersize + norm/2] into normalize window +// 3. normalize [m_bufferSize..m_bufferSize+cb] +// +// PROCESS: +// +// ||-------m_bufferSize-------|-cb-|---norm/2---|| +// 1. <--------------------------{------copy-------} +// 2. {--------read frames-------} +// 3. {---------normalize--------} +// 4. {------fft/process/whatevs------} +// +// 1. copy [m_bufferSize..m_bufferSize + cb + norm/2] to beginning +// 2. read m_bufferSize frames to cb + norm/2 +// 3. normalize [cb..m_bufferSize+cb] +// 4. fft/process/whatevs [0...m_bufferSize+cb] +// +// repeat until enough blocks processed and enough groups! +// +bool FingerprintExtractor::process( const short* pPCM, size_t num_samples, bool end_of_stream ) +{ + if ( num_samples == 0 ) + return false; + + // easier read + PimplData& pd = *m_pPimplData; + + if ( pd.m_processType == PT_UNKNOWN ) + throw std::runtime_error("Please call initForQuery() or initForFullSubmit() before process()!"); + + const short* pSourcePCMIt = pPCM; + const short* pSourcePCMIt_end = pPCM + num_samples; + + if ( !pd.m_skipPassed ) + { + // needs to skip data? (reminder: the query needs to skip QUERY_START_SECS (- half of the normalization window) + if ( pd.m_skippedSoFar + num_samples > pd.m_toSkipSize ) + { + pSourcePCMIt = pPCM + (pd.m_toSkipSize - pd.m_skippedSoFar); + pd.m_skipPassed = true; + } + else + { + // need more data + pd.m_skippedSoFar += num_samples; + return false; + } + } + + pair readData(0,0); + pd.m_downsampleData.end_of_input = end_of_stream ? 1 : 0; + + ////////////////////////////////////////////////////////////////////////// + // PREBUFFER: + if ( !pd.m_preBufferPassed ) + { + // 1. downsample [norm + cb] frames to m_bufferSize - norm/2 + pd.m_floatInData.resize( (pSourcePCMIt_end - pSourcePCMIt) / pd.m_nchannels); + src_short_to_float_and_mono_array( pSourcePCMIt, + &(pd.m_floatInData[0]), static_cast(pSourcePCMIt_end - pSourcePCMIt), + pd.m_nchannels); + + pd.m_downsampleData.data_in = &(pd.m_floatInData[0]); + pd.m_downsampleData.input_frames = static_cast(pd.m_floatInData.size()); + + pd.m_downsampleData.data_out = pd.m_pDownsampledCurrIt; + pd.m_downsampleData.output_frames = static_cast(pd.m_pEndDownsampledBuf - pd.m_pDownsampledCurrIt); + + int err = src_process(pd.m_pDownsampleState, &(pd.m_downsampleData)); + if ( err ) + throw std::runtime_error( src_strerror(err) ); + + pd.m_pDownsampledCurrIt += pd.m_downsampleData.output_frames_gen; + + if ( pd.m_pDownsampledCurrIt != pd.m_pEndDownsampledBuf ) + return false; // NEED MORE DATA + + pSourcePCMIt += pd.m_downsampleData.input_frames_used * pd.m_nchannels; + + size_t pos = pd.m_downsampledProcessSize; + size_t window_pos = pd.m_downsampledProcessSize - pd.m_normWindow.size() / 2; + const size_t end_window_pos = window_pos + pd.m_normWindow.size(); + + // 2. read [m_buffersize - norm/2...m_buffersize + norm/2] into normalize window + for (; window_pos < end_window_pos ; ++window_pos) + pd.m_normWindow.add(pd.m_pDownsampledPCM[window_pos] * pd.m_pDownsampledPCM[window_pos]); + + // 3. normalize [m_bufferSize..m_bufferSize+cb] + for (; pos < pd.m_downsampledProcessSize + pd.m_compensateBufferSize; ++pos, ++window_pos) + { + pd.m_pDownsampledPCM[pos] /= getRMS(pd.m_normWindow); + pd.m_normWindow.add(pd.m_pDownsampledPCM[window_pos] * pd.m_pDownsampledPCM[window_pos]); + } + + pd.m_preBufferPassed = true; + } + + ////////////////////////////////////////////////////////////////////////// + // PROCESS: + + bool found_enough_unique_keys = false; + while (pd.m_toProcessKeys == 0 || pd.m_processedKeys < pd.m_toProcessKeys || !found_enough_unique_keys) + { + + // 1. copy [m_bufferSize..m_bufferSize + cb + norm/2] to beginning + if ( pd.m_pDownsampledCurrIt == pd.m_pEndDownsampledBuf ) + { + memcpy( pd.m_pDownsampledPCM, pd.m_pDownsampledPCM + pd.m_downsampledProcessSize, + (pd.m_compensateBufferSize + (pd.m_normWindow.size() / 2)) * sizeof(float)); + pd.m_pDownsampledCurrIt = pd.m_pDownsampledPCM + (pd.m_compensateBufferSize + (pd.m_normWindow.size() / 2)); + } + + // 2. read m_bufferSize frames to cb + norm/2 + pd.m_floatInData.resize( (pSourcePCMIt_end - pSourcePCMIt) / pd.m_nchannels); + + if ( pd.m_floatInData.empty() ) + return false; + + src_short_to_float_and_mono_array( pSourcePCMIt, + &(pd.m_floatInData[0]), static_cast(pSourcePCMIt_end - pSourcePCMIt), + pd.m_nchannels); + + pd.m_downsampleData.data_in = &(pd.m_floatInData[0]); + pd.m_downsampleData.input_frames = static_cast(pd.m_floatInData.size()); + + pd.m_downsampleData.data_out = pd.m_pDownsampledCurrIt; + pd.m_downsampleData.output_frames = static_cast(pd.m_pEndDownsampledBuf - pd.m_pDownsampledCurrIt); + + int err = src_process(pd.m_pDownsampleState, &(pd.m_downsampleData)); + if ( err ) + throw std::runtime_error( src_strerror(err) ); + + pd.m_pDownsampledCurrIt += pd.m_downsampleData.output_frames_gen; + + if ( pd.m_pDownsampledCurrIt != pd.m_pEndDownsampledBuf && !end_of_stream ) + return false; // NEED MORE DATA + + //pSourcePCMIt += readData.second; + pSourcePCMIt += pd.m_downsampleData.input_frames_used * pd.m_nchannels; + + // ******************************************************************** + + // 3. normalize [cb..m_bufferSize+cb] + size_t pos = static_cast(pd.m_compensateBufferSize); + size_t window_pos = static_cast(pd.m_compensateBufferSize + (pd.m_normWindow.size() / 2)); + + for(; pos < pd.m_downsampledProcessSize + pd.m_compensateBufferSize /* m_fullDownsampledBufferSize*/; ++pos, ++window_pos) + { + pd.m_pDownsampledPCM[pos] /= getRMS(pd.m_normWindow); + pd.m_normWindow.add(pd.m_pDownsampledPCM[window_pos] * pd.m_pDownsampledPCM[window_pos]); + } + + // 4. fft/process/whatevs [0...m_bufferSize+cb] + pd.m_processedKeys += processKeys(pd.m_groupWindow, pos, pd); + + // we have too many keys, now we have to chop either one end or the other + if (pd.m_toProcessKeys != 0 && pd.m_processedKeys > pd.m_toProcessKeys) + { + // set up window begin and end + deque::iterator itBeg = pd.m_groupWindow.begin(), itEnd = pd.m_groupWindow.end(); + unsigned int offset_left, offset_right; + + found_enough_unique_keys = + fingerprint::findSignificantGroups( itBeg, itEnd, offset_left, offset_right, pd.m_toProcessKeys, + pd.m_totalWindowKeys, pd.m_minUniqueKeys); + + // if we're happy with this set, snip the beginning and end of the grouped keys + if (found_enough_unique_keys) + { + itBeg->count -= offset_left; + if (offset_right > 0 && itEnd != pd.m_groupWindow.end()) + { + itEnd->count = offset_right; + ++itEnd; + } + } + + // chop the deque + copy(itBeg, itEnd, pd.m_groupWindow.begin()); + pd.m_groupWindow.resize(itEnd - itBeg); + + // recalc keys + pd.m_processedKeys = 0; + for (deque::const_iterator it = pd.m_groupWindow.begin(); it != pd.m_groupWindow.end(); ++it) + pd.m_processedKeys += it->count; + } + + if ( end_of_stream ) + break; + + } // while (totalKeys == 0 || keys < totalKeys || !found_enough_unique_keys) + + + if (pd.m_toProcessKeys != 0 && pd.m_processedKeys < pd.m_toProcessKeys) + throw std::runtime_error("Couldn't deliver the requested number of keys (it's the file too short?)"); + + if ((pd.m_toProcessKeys != 0 && !found_enough_unique_keys) || + (pd.m_toProcessKeys == 0 && !enoughUniqueGoodGroups(pd.m_groupWindow.begin(), pd.m_groupWindow.end(), pd.m_minUniqueKeys))) + { + throw std::runtime_error("Not enough unique keys (it's the file too short?)"); + } + + // copy to a vector so that they can be returned as contiguous data + pd.m_groups.resize(pd.m_groupWindow.size()); + copy(pd.m_groupWindow.begin(), pd.m_groupWindow.end(), pd.m_groups.begin()); + + pd.m_groupsReady = true; + pd.m_processType = PT_UNKNOWN; + return true; +} + +// ----------------------------------------------------------------------------- + +pair FingerprintExtractor::getFingerprint() +{ + // easier read + PimplData& pd = *m_pPimplData; + + if ( pd.m_groupsReady ) + { +#if __BIG_ENDIAN__ + pd.m_bigEndianGroups.resize(pd.m_groups.size()); + for ( size_t i = 0; i < pd.m_groups.size(); ++i ) + { + pd.m_bigEndianGroups[i].key = reorderbits(pd.m_groups[i].key); + pd.m_bigEndianGroups[i].count = reorderbits(pd.m_groups[i].count); + } + + return make_pair(reinterpret_cast(&pd.m_bigEndianGroups[0]), pd.m_bigEndianGroups.size() * sizeof(GroupData) ); + +#else + return make_pair(reinterpret_cast(&pd.m_groups[0]), pd.m_groups.size() * sizeof(GroupData) ); +#endif + } + else + return make_pair(reinterpret_cast(0), 0); // here's where null_ptr would become useful! +} + +// ----------------------------------------------------------------------------- +// ----------------------------------------------------------------------------- +// ----------------------------------------------------------------------------- + +float getRMS(const FloatingAverage& signal) +{ + // we don't want to normalize by the real rms, because excessive clipping will occur + float rms = sqrtf(static_cast(signal.getAverage())) * 10.0F; + + if (rms < 0.1F) + rms = 0.1F; + else if (rms > 3.0F) + rms = 3.0F; + + return rms; +} + +// ----------------------------------------------------------------------------- + +unsigned int processKeys( deque& groups, size_t size, PimplData& pd ) +{ + size_t read_size = min(size, pd.m_downsampledProcessSize + pd.m_compensateBufferSize); + + unsigned int numFrames = pd.m_pFFT->process(pd.m_pDownsampledPCM, read_size); + + if ( numFrames <= Filter::KEYWIDTH ) + return 0; // skip it when the number of frames is too small + + float** ppFrames = pd.m_pFFT->getFrames(); + + integralImage(ppFrames, numFrames); + computeBits(pd.m_partialBits, pd.m_filters, ppFrames, numFrames); + fingerprint::keys2GroupData(pd.m_partialBits, groups, false); + + return static_cast(pd.m_partialBits.size()); + +} + +// ----------------------------------------------------------------------------- + +void integralImage(float** ppFrames, unsigned int nFrames) +{ + for (unsigned int y = 1; y < nFrames; y++) + { + ppFrames[y][0] += ppFrames[y-1][0]; + } + + for (unsigned int x = 1; x < Filter::NBANDS; x++) + { + ppFrames[0][x] += ppFrames[0][x-1]; + } + + for (unsigned int y = 1; y < nFrames; y++) + { + for (unsigned int x = 1; x < Filter::NBANDS; x++) + { + ppFrames[y][x] += static_cast( static_cast(ppFrames[y-1][x]) + + static_cast(ppFrames[y][x-1]) - + static_cast(ppFrames[y-1][x-1]) ); + } + } +} + +// --------------------------------------------------------------------- +// +/// Convert bands to bits, using the supplied filters +void computeBits( vector& bits, + const vector& f, + float ** frames, unsigned int nframes ) +{ + unsigned int first_time = Filter::KEYWIDTH / 2 + 1; + unsigned int last_time = nframes - Filter::KEYWIDTH / 2; + + unsigned int numBits = last_time - first_time + 1; + bits.resize(numBits); + + const unsigned int fSize = static_cast(f.size()); + std::bitset<32> bt; + double X = 0; + + for (unsigned int t2 = first_time; t2 <= last_time; ++t2) + { + + for (unsigned int i = 0; i < fSize; ++i) + { + // we subtract 1 from t1 and b1 because we use integral images + + unsigned int t1 = (unsigned int) ((float) t2 - f[i].wt / 2.0 - 1); + unsigned int t3 = (unsigned int) ((float) t2 + f[i].wt / 2.0 - 1); + unsigned int b1 = f[i].first_band; + unsigned int b2 = (unsigned int) round__((float) b1 + f[i].wb / 2.0) - 1; + unsigned int b3 = b1 + f[i].wb - 1; + --b1; + + unsigned int t_1q = (t1 + t2) / 2; // one quarter time + unsigned int t_3q = t_1q + (t3 - t1 + 1) / 2; // three quarter time + unsigned int b_1q = (b1 + b2) / 2; // one quarter band + unsigned int b_3q = b_1q + (b3 - b1) / 2; // three quarter band + + X = 0; + + // we should check from t1 > 0, but in practice, this doesn't happen + // we subtract 1 from everything because this came from matlab where indices start from 1 + switch (f[i].filter_type) { + case 1: { // total energy + if (b1 > 0) + X = static_cast(frames[t3-1][b3-1]) - static_cast(frames[t3-1][b1-1]) + - static_cast(frames[t1-1][b3-1]) + static_cast(frames[t1-1][b1-1]); + else + X = static_cast(frames[t3-1][b3-1]) - static_cast(frames[t1-1][b3-1]); + break; + } + case 2: { // energy difference over time + if (b1 > 0) + X = static_cast(frames[t1-1][b1-1]) - 2*static_cast(frames[t2-2][b1-1]) + + static_cast(frames[t3-1][b1-1]) - static_cast(frames[t1-1][b3-1]) + + 2*static_cast(frames[t2-2][b3-1]) - static_cast(frames[t3-1][b3-1]); + else + X = - static_cast(frames[t1-1][b3-1]) + 2*static_cast(frames[t2-2][b3-1]) + - static_cast(frames[t3-1][b3-1]); + break; + + } + case 3: { // energy difference over bands + if (b1 > 0) + X = static_cast(frames[t1-1][b1-1]) - static_cast(frames[t3-1][b1-1]) + - 2*static_cast(frames[t1-1][b2-1]) + 2*static_cast(frames[t3-1][b2-1]) + + static_cast(frames[t1-1][b3-1]) - static_cast(frames[t3-1][b3-1]); + else + X = - 2*static_cast(frames[t1-1][b2-1]) + 2*static_cast(frames[t3-1][b2-1]) + + static_cast(frames[t1-1][b3-1]) - static_cast(frames[t3-1][b3-1]); + break; + } + case 4: { + // energy difference over time and bands + if (b1 > 0) + X = static_cast(frames[t1-1][b1-1]) - 2*static_cast(frames[t2-2][b1-1]) + + static_cast(frames[t3-1][b1-1]) - 2*static_cast(frames[t1-1][b2-1]) + + 4*static_cast(frames[t2-2][b2-1]) - 2*static_cast(frames[t3-1][b2-1]) + + static_cast(frames[t1-1][b3-1]) - 2*static_cast(frames[t2-2][b3-1]) + + static_cast(frames[t3-1][b3-1]); + else + X = - 2*static_cast(frames[t1-1][b2-1]) + 4*static_cast(frames[t2-2][b2-1]) + - 2*static_cast(frames[t3-1][b2-1]) + static_cast(frames[t1-1][b3-1]) + - 2*static_cast(frames[t2-2][b3-1]) + static_cast(frames[t3-1][b3-1]); + break; + } + case 5: { // time peak + if (b1 > 0) + X = - static_cast(frames[t1-1][b1-1]) + 2*static_cast(frames[t_1q-1][b1-1]) + - 2*static_cast(frames[t_3q-1][b1-1]) + static_cast(frames[t3-1][b1-1]) + + static_cast(frames[t1-1][b3-1]) - 2*static_cast(frames[t_1q-1][b3-1]) + + 2*static_cast(frames[t_3q-1][b3-1]) - static_cast(frames[t3-1][b3-1]); + else + X = static_cast(frames[t1-1][b3-1]) - 2*static_cast(frames[t_1q-1][b3-1]) + + 2*static_cast(frames[t_3q-1][b3-1]) - static_cast(frames[t3-1][b3-1]); + + break; + } + case 6: { // band beak + if (b1 > 0) + X = - static_cast(frames[t1-1][b1-1]) + static_cast(frames[t3-1][b1-1]) + + 2*static_cast(frames[t1-1][b_1q-1]) - 2*static_cast(frames[t3-1][b_1q-1]) + - 2*static_cast(frames[t1-1][b_3q-1]) + 2*static_cast(frames[t3-1][b_3q-1]) + + static_cast(frames[t1-1][b3-1]) - static_cast(frames[t3-1][b3-1]); + else + X = + 2*static_cast(frames[t1-1][b_1q-1]) - 2*static_cast(frames[t3-1][b_1q-1]) + - 2*static_cast(frames[t1-1][b_3q-1]) + 2*static_cast(frames[t3-1][b_3q-1]) + + static_cast(frames[t1-1][b3-1]) - static_cast(frames[t3-1][b3-1]); + + break; + } + } + + bt[i] = X > f[i].threshold; + } + + bits[t2 - first_time] = bt.to_ulong(); + } +} + +// ----------------------------------------------------------------------------- + +void src_short_to_float_and_mono_array( const short *in, float *out, int srclen, int nchannels ) +{ + switch ( nchannels ) + { + case 1: + src_short_to_float_array(in, out, srclen); + break; + case 2: + { + // this can be optimized + int j = 0; + const double div = numeric_limits::max() * nchannels; + for ( int i = 0; i < srclen; i += 2, ++j ) + { + out[j] = static_cast( static_cast(static_cast(in[i]) + static_cast(in[i+1])) / div ); + } + } + break; + + default: + throw( std::runtime_error("Unsupported number of channels!") ); + } + +} + +// ----------------------------------------------------------------------------- + +} // end of namespace + +// ----------------------------------------------------------------------------- diff --git a/thirdparty/liblastfm2/src/fingerprint/fplib/FingerprintExtractor.h b/thirdparty/liblastfm2/src/fingerprint/fplib/FingerprintExtractor.h new file mode 100644 index 000000000..fac9b5887 --- /dev/null +++ b/thirdparty/liblastfm2/src/fingerprint/fplib/FingerprintExtractor.h @@ -0,0 +1,77 @@ +/* + Copyright 2005-2009 Last.fm Ltd. + + This file is part of liblastfm. + + liblastfm 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. + + liblastfm 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 liblastfm. If not, see . +*/ +#ifndef __FINGERPRINT_EXTRACTOR_H +#define __FINGERPRINT_EXTRACTOR_H + +#include // for pair +#include // for size_t + +namespace fingerprint { + +// ----------------------------------------------------------------------------- + +class PimplData; + +class FingerprintExtractor +{ +public: + + FingerprintExtractor(); // ctor + ~FingerprintExtractor(); // dtor + + // duration (in seconds!) is optional, but if you want to submit tracks <34 secs + // it must be provided. + void initForQuery(int freq, int nchannels, int duration = -1); + void initForFullSubmit(int freq, int nchannels); + + // return false if it needs more data, otherwise true + // IMPORTANT: num_samples specify the size of the *short* array pPCM, that is + // the number of samples that are in the buffer. This includes + // the stereo samples, i.e. + // [L][R][L][R][L][R][L][R] would be num_samples=8 + bool process(const short* pPCM, size_t num_samples, bool end_of_stream = false); + + // returns pair if the data is not ready + std::pair getFingerprint(); + + ////////////////////////////////////////////////////////////////////////// + + // The FingerprintExtractor assumes that the file start from the beginning + // but since the first SkipMs are ignored, it's possible to feed it with NULL. + // In order to know how much must be skipped (in milliseconds) call this function. + // Remark: this is only for "advanced" users! + size_t getToSkipMs(); + + // Return the minimum duration of the file (in ms) + // Any file with a length smaller than this value will be discarded + static size_t getMinimumDurationMs(); + + // return the version of the fingerprint + static size_t getVersion(); + +private: + + PimplData* m_pPimplData; +}; + +// ----------------------------------------------------------------------------- + +} // end of namespace fingerprint + +#endif // __FINGERPRINT_EXTRACTOR_H diff --git a/thirdparty/liblastfm2/src/fingerprint/fplib/FloatingAverage.h b/thirdparty/liblastfm2/src/fingerprint/fplib/FloatingAverage.h new file mode 100644 index 000000000..1be665bd0 --- /dev/null +++ b/thirdparty/liblastfm2/src/fingerprint/fplib/FloatingAverage.h @@ -0,0 +1,106 @@ +/* + Copyright 2005-2009 Last.fm Ltd. + + This file is part of liblastfm. + + liblastfm 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. + + liblastfm 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 liblastfm. If not, see . +*/ +#ifndef __FLOAT_AVERAGE_H__ +#define __FLOAT_AVERAGE_H__ + +//#include +#include +#include "CircularArray.h" + +template +class FloatingAverage +{ +public: + FloatingAverage(size_t size) + { + m_values.resize(size); + m_valIt = m_values.head(); + m_sum = 0; + m_bufferFilled = false; + } + + void purge() + { + m_sum = 0; + const T* pCircularBuffer = m_values.get_buffer(); + const int size = m_values.size(); + + for ( int i = 0; i < size; ++i ) + m_sum += pCircularBuffer[i]; + } + + void add(const T& value) + { + m_sum += value; + + if ( m_bufferFilled ) + { + m_sum -= *m_valIt; + *m_valIt = value; + ++m_valIt; + } + else + { + *m_valIt = value; + ++m_valIt; + if ( m_valIt == m_values.head() ) + m_bufferFilled = true; + } + } + + T getAverage() const + { + if ( !m_bufferFilled ) + return m_sum / (m_valIt - m_values.head()); + else + return m_sum / m_values.size(); + } + + T getError() const + { + T real_sum = 0; + const T* pCircularBuffer = m_values.get_buffer(); + for ( int i = 0; i < size; ++i ) + real_sum += pCircularBuffer[i]; + return abs(real_sum - m_sum) / this->size(); + } + + size_t size() const + { + return m_values.size(); + } + + void clear() + { + m_bufferFilled = false; + m_values.zero_fill(); + m_valIt = m_values.head(); + m_sum = 0; + } + +private: + //std::deque m_values; + CircularArray m_values; + typename CircularArray::iterator m_valIt; + + bool m_bufferFilled; + T m_sum; +}; + +#endif // __FLOAT_AVERAGE_H__ diff --git a/thirdparty/liblastfm2/src/fingerprint/fplib/OptFFT.cpp b/thirdparty/liblastfm2/src/fingerprint/fplib/OptFFT.cpp new file mode 100644 index 000000000..3728c974c --- /dev/null +++ b/thirdparty/liblastfm2/src/fingerprint/fplib/OptFFT.cpp @@ -0,0 +1,411 @@ +/* + Copyright 2005-2009 Last.fm Ltd. + + This file is part of liblastfm. + + liblastfm 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. + + liblastfm 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 liblastfm. If not, see . +*/ +#include "OptFFT.h" +#include "fp_helper_fun.h" +#include "Filter.h" // for NBANDS + +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; +// ---------------------------------------------------------------------- + +namespace fingerprint +{ + + static const float hann[] = { + 0.000000f,0.000002f,0.000009f,0.000021f,0.000038f,0.000059f,0.000085f,0.000115f,0.000151f,0.000191f,0.000236f, + 0.000285f,0.000339f,0.000398f,0.000462f,0.000530f,0.000603f,0.000681f,0.000763f,0.000850f,0.000942f,0.001038f, + 0.001140f,0.001245f,0.001356f,0.001471f,0.001591f,0.001716f,0.001845f,0.001980f,0.002118f,0.002262f,0.002410f, + 0.002563f,0.002720f,0.002883f,0.003049f,0.003221f,0.003397f,0.003578f,0.003764f,0.003954f,0.004149f,0.004349f, + 0.004553f,0.004762f,0.004976f,0.005194f,0.005417f,0.005645f,0.005877f,0.006114f,0.006355f,0.006602f,0.006853f, + 0.007108f,0.007368f,0.007633f,0.007903f,0.008177f,0.008455f,0.008739f,0.009027f,0.009319f,0.009617f,0.009919f, + 0.010225f,0.010536f,0.010852f,0.011172f,0.011497f,0.011827f,0.012161f,0.012499f,0.012843f,0.013191f,0.013543f, + 0.013900f,0.014262f,0.014628f,0.014999f,0.015374f,0.015754f,0.016139f,0.016528f,0.016921f,0.017320f,0.017722f, + 0.018130f,0.018541f,0.018958f,0.019379f,0.019804f,0.020234f,0.020668f,0.021107f,0.021551f,0.021999f,0.022451f, + 0.022908f,0.023370f,0.023836f,0.024306f,0.024781f,0.025260f,0.025744f,0.026233f,0.026725f,0.027223f,0.027724f, + 0.028231f,0.028741f,0.029256f,0.029776f,0.030300f,0.030828f,0.031361f,0.031898f,0.032440f,0.032986f,0.033536f, + 0.034091f,0.034650f,0.035214f,0.035781f,0.036354f,0.036930f,0.037512f,0.038097f,0.038687f,0.039281f,0.039879f, + 0.040482f,0.041089f,0.041701f,0.042316f,0.042936f,0.043561f,0.044189f,0.044822f,0.045460f,0.046101f,0.046747f, + 0.047397f,0.048052f,0.048710f,0.049373f,0.050040f,0.050711f,0.051387f,0.052067f,0.052751f,0.053439f,0.054132f, + 0.054828f,0.055529f,0.056234f,0.056943f,0.057657f,0.058374f,0.059096f,0.059822f,0.060552f,0.061286f,0.062024f, + 0.062767f,0.063513f,0.064264f,0.065019f,0.065777f,0.066540f,0.067307f,0.068078f,0.068854f,0.069633f,0.070416f, + 0.071204f,0.071995f,0.072790f,0.073590f,0.074393f,0.075201f,0.076012f,0.076828f,0.077647f,0.078470f,0.079298f, + 0.080129f,0.080964f,0.081804f,0.082647f,0.083494f,0.084345f,0.085200f,0.086059f,0.086922f,0.087788f,0.088659f, + 0.089533f,0.090412f,0.091294f,0.092180f,0.093070f,0.093963f,0.094861f,0.095762f,0.096667f,0.097576f,0.098489f, + 0.099406f,0.100326f,0.101250f,0.102178f,0.103109f,0.104045f,0.104984f,0.105926f,0.106873f,0.107823f,0.108777f, + 0.109734f,0.110696f,0.111661f,0.112629f,0.113601f,0.114577f,0.115557f,0.116540f,0.117526f,0.118517f,0.119511f, + 0.120508f,0.121509f,0.122514f,0.123522f,0.124534f,0.125549f,0.126568f,0.127590f,0.128616f,0.129645f,0.130678f, + 0.131714f,0.132754f,0.133797f,0.134844f,0.135894f,0.136948f,0.138005f,0.139065f,0.140129f,0.141196f,0.142266f, + 0.143340f,0.144418f,0.145498f,0.146582f,0.147670f,0.148760f,0.149854f,0.150951f,0.152052f,0.153156f,0.154263f, + 0.155373f,0.156487f,0.157603f,0.158723f,0.159847f,0.160973f,0.162103f,0.163236f,0.164372f,0.165511f,0.166653f, + 0.167799f,0.168947f,0.170099f,0.171254f,0.172411f,0.173572f,0.174737f,0.175904f,0.177074f,0.178247f,0.179423f, + 0.180603f,0.181785f,0.182970f,0.184158f,0.185350f,0.186544f,0.187741f,0.188941f,0.190144f,0.191350f,0.192559f, + 0.193771f,0.194986f,0.196203f,0.197423f,0.198647f,0.199873f,0.201102f,0.202333f,0.203568f,0.204805f,0.206045f, + 0.207288f,0.208534f,0.209782f,0.211033f,0.212287f,0.213544f,0.214803f,0.216065f,0.217329f,0.218597f,0.219867f, + 0.221139f,0.222414f,0.223692f,0.224972f,0.226255f,0.227541f,0.228829f,0.230120f,0.231413f,0.232709f,0.234007f, + 0.235308f,0.236611f,0.237917f,0.239225f,0.240536f,0.241849f,0.243165f,0.244483f,0.245803f,0.247126f,0.248451f, + 0.249779f,0.251108f,0.252441f,0.253775f,0.255112f,0.256451f,0.257793f,0.259137f,0.260483f,0.261831f,0.263182f, + 0.264534f,0.265889f,0.267247f,0.268606f,0.269967f,0.271331f,0.272697f,0.274065f,0.275435f,0.276808f,0.278182f, + 0.279558f,0.280937f,0.282318f,0.283700f,0.285085f,0.286472f,0.287861f,0.289251f,0.290644f,0.292039f,0.293435f, + 0.294834f,0.296235f,0.297637f,0.299041f,0.300448f,0.301856f,0.303266f,0.304678f,0.306091f,0.307507f,0.308924f, + 0.310343f,0.311764f,0.313187f,0.314611f,0.316038f,0.317466f,0.318895f,0.320327f,0.321760f,0.323194f,0.324631f, + 0.326069f,0.327509f,0.328950f,0.330393f,0.331837f,0.333283f,0.334731f,0.336180f,0.337631f,0.339083f,0.340537f, + 0.341993f,0.343449f,0.344908f,0.346368f,0.347829f,0.349291f,0.350755f,0.352221f,0.353688f,0.355156f,0.356626f, + 0.358097f,0.359569f,0.361042f,0.362517f,0.363994f,0.365471f,0.366950f,0.368430f,0.369911f,0.371394f,0.372877f, + 0.374362f,0.375848f,0.377336f,0.378824f,0.380314f,0.381804f,0.383296f,0.384789f,0.386283f,0.387778f,0.389274f, + 0.390771f,0.392269f,0.393768f,0.395269f,0.396770f,0.398272f,0.399775f,0.401279f,0.402784f,0.404290f,0.405797f, + 0.407305f,0.408813f,0.410322f,0.411833f,0.413344f,0.414856f,0.416368f,0.417882f,0.419396f,0.420911f,0.422427f, + 0.423944f,0.425461f,0.426979f,0.428497f,0.430017f,0.431537f,0.433057f,0.434578f,0.436100f,0.437623f,0.439146f, + 0.440669f,0.442193f,0.443718f,0.445243f,0.446769f,0.448295f,0.449822f,0.451349f,0.452877f,0.454405f,0.455934f, + 0.457463f,0.458992f,0.460522f,0.462052f,0.463582f,0.465113f,0.466644f,0.468176f,0.469708f,0.471240f,0.472772f, + 0.474305f,0.475837f,0.477370f,0.478904f,0.480437f,0.481971f,0.483505f,0.485039f,0.486573f,0.488107f,0.489641f, + 0.491176f,0.492710f,0.494245f,0.495780f,0.497314f,0.498849f,0.500384f,0.501918f,0.503453f,0.504988f,0.506522f, + 0.508057f,0.509591f,0.511126f,0.512660f,0.514194f,0.515728f,0.517262f,0.518796f,0.520330f,0.521863f,0.523396f, + 0.524929f,0.526462f,0.527994f,0.529526f,0.531058f,0.532590f,0.534121f,0.535652f,0.537183f,0.538713f,0.540243f, + 0.541773f,0.543302f,0.544831f,0.546359f,0.547887f,0.549414f,0.550941f,0.552468f,0.553994f,0.555519f,0.557044f, + 0.558569f,0.560093f,0.561616f,0.563139f,0.564661f,0.566182f,0.567703f,0.569223f,0.570743f,0.572262f,0.573780f, + 0.575298f,0.576815f,0.578331f,0.579846f,0.581361f,0.582875f,0.584388f,0.585900f,0.587412f,0.588922f,0.590432f, + 0.591941f,0.593449f,0.594957f,0.596463f,0.597968f,0.599473f,0.600977f,0.602479f,0.603981f,0.605482f,0.606981f, + 0.608480f,0.609978f,0.611474f,0.612970f,0.614464f,0.615958f,0.617450f,0.618941f,0.620431f,0.621920f,0.623408f, + 0.624895f,0.626380f,0.627865f,0.629348f,0.630830f,0.632310f,0.633790f,0.635268f,0.636745f,0.638220f,0.639695f, + 0.641167f,0.642639f,0.644109f,0.645578f,0.647046f,0.648512f,0.649977f,0.651440f,0.652902f,0.654363f,0.655822f, + 0.657279f,0.658735f,0.660190f,0.661643f,0.663094f,0.664544f,0.665993f,0.667440f,0.668885f,0.670329f,0.671771f, + 0.673212f,0.674650f,0.676088f,0.677523f,0.678957f,0.680389f,0.681820f,0.683249f,0.684676f,0.686101f,0.687525f, + 0.688946f,0.690366f,0.691785f,0.693201f,0.694616f,0.696029f,0.697439f,0.698849f,0.700256f,0.701661f,0.703065f, + 0.704466f,0.705866f,0.707263f,0.708659f,0.710053f,0.711444f,0.712834f,0.714222f,0.715608f,0.716991f,0.718373f, + 0.719752f,0.721130f,0.722505f,0.723879f,0.725250f,0.726619f,0.727986f,0.729351f,0.730714f,0.732074f,0.733432f, + 0.734788f,0.736142f,0.737494f,0.738843f,0.740191f,0.741536f,0.742878f,0.744219f,0.745557f,0.746892f,0.748226f, + 0.749557f,0.750886f,0.752212f,0.753536f,0.754857f,0.756177f,0.757493f,0.758808f,0.760120f,0.761429f,0.762736f, + 0.764041f,0.765343f,0.766642f,0.767939f,0.769234f,0.770526f,0.771815f,0.773102f,0.774386f,0.775668f,0.776947f, + 0.778224f,0.779497f,0.780769f,0.782037f,0.783303f,0.784567f,0.785827f,0.787085f,0.788340f,0.789593f,0.790842f, + 0.792089f,0.793334f,0.794575f,0.795814f,0.797050f,0.798283f,0.799513f,0.800741f,0.801965f,0.803187f,0.804406f, + 0.805622f,0.806835f,0.808046f,0.809253f,0.810458f,0.811659f,0.812858f,0.814054f,0.815246f,0.816436f,0.817623f, + 0.818807f,0.819987f,0.821165f,0.822340f,0.823512f,0.824680f,0.825846f,0.827008f,0.828168f,0.829324f,0.830477f, + 0.831628f,0.832775f,0.833918f,0.835059f,0.836197f,0.837331f,0.838462f,0.839591f,0.840715f,0.841837f,0.842955f, + 0.844071f,0.845183f,0.846291f,0.847397f,0.848499f,0.849598f,0.850693f,0.851786f,0.852874f,0.853960f,0.855042f, + 0.856121f,0.857197f,0.858269f,0.859338f,0.860404f,0.861466f,0.862524f,0.863580f,0.864631f,0.865680f,0.866725f, + 0.867766f,0.868804f,0.869839f,0.870870f,0.871897f,0.872922f,0.873942f,0.874959f,0.875973f,0.876983f,0.877989f, + 0.878992f,0.879991f,0.880987f,0.881979f,0.882967f,0.883952f,0.884934f,0.885911f,0.886885f,0.887856f,0.888822f, + 0.889785f,0.890745f,0.891701f,0.892653f,0.893601f,0.894545f,0.895486f,0.896423f,0.897357f,0.898287f,0.899213f, + 0.900135f,0.901053f,0.901968f,0.902879f,0.903786f,0.904689f,0.905588f,0.906484f,0.907376f,0.908264f,0.909148f, + 0.910028f,0.910904f,0.911777f,0.912645f,0.913510f,0.914371f,0.915228f,0.916081f,0.916930f,0.917775f,0.918616f, + 0.919454f,0.920287f,0.921116f,0.921942f,0.922763f,0.923581f,0.924394f,0.925204f,0.926009f,0.926810f,0.927608f, + 0.928401f,0.929191f,0.929976f,0.930757f,0.931534f,0.932308f,0.933077f,0.933842f,0.934603f,0.935359f,0.936112f, + 0.936861f,0.937605f,0.938345f,0.939082f,0.939814f,0.940542f,0.941265f,0.941985f,0.942701f,0.943412f,0.944119f, + 0.944822f,0.945521f,0.946215f,0.946906f,0.947592f,0.948274f,0.948951f,0.949625f,0.950294f,0.950959f,0.951620f, + 0.952276f,0.952928f,0.953576f,0.954220f,0.954859f,0.955495f,0.956125f,0.956752f,0.957374f,0.957992f,0.958606f, + 0.959215f,0.959820f,0.960420f,0.961017f,0.961609f,0.962196f,0.962780f,0.963358f,0.963933f,0.964503f,0.965069f, + 0.965630f,0.966187f,0.966740f,0.967288f,0.967832f,0.968371f,0.968906f,0.969437f,0.969963f,0.970485f,0.971002f, + 0.971515f,0.972023f,0.972527f,0.973027f,0.973522f,0.974012f,0.974498f,0.974980f,0.975457f,0.975930f,0.976398f, + 0.976862f,0.977321f,0.977776f,0.978226f,0.978672f,0.979113f,0.979549f,0.979982f,0.980409f,0.980832f,0.981251f, + 0.981665f,0.982075f,0.982480f,0.982880f,0.983276f,0.983667f,0.984054f,0.984436f,0.984814f,0.985187f,0.985556f, + 0.985919f,0.986279f,0.986634f,0.986984f,0.987329f,0.987670f,0.988007f,0.988339f,0.988666f,0.988989f,0.989307f, + 0.989620f,0.989929f,0.990233f,0.990532f,0.990827f,0.991118f,0.991403f,0.991684f,0.991961f,0.992233f,0.992500f, + 0.992762f,0.993020f,0.993273f,0.993522f,0.993766f,0.994005f,0.994240f,0.994470f,0.994695f,0.994916f,0.995132f, + 0.995343f,0.995550f,0.995752f,0.995949f,0.996142f,0.996329f,0.996513f,0.996691f,0.996865f,0.997035f,0.997199f, + 0.997359f,0.997514f,0.997665f,0.997810f,0.997952f,0.998088f,0.998220f,0.998347f,0.998469f,0.998587f,0.998700f, + 0.998808f,0.998912f,0.999010f,0.999105f,0.999194f,0.999279f,0.999359f,0.999434f,0.999505f,0.999571f,0.999632f, + 0.999689f,0.999740f,0.999787f,0.999830f,0.999868f,0.999900f,0.999929f,0.999952f,0.999971f,0.999985f,0.999995f, + 0.999999f,0.999999f,0.999995f,0.999985f,0.999971f,0.999952f,0.999929f,0.999900f,0.999868f,0.999830f,0.999787f, + 0.999740f,0.999689f,0.999632f,0.999571f,0.999505f,0.999434f,0.999359f,0.999279f,0.999194f,0.999105f,0.999010f, + 0.998912f,0.998808f,0.998700f,0.998587f,0.998469f,0.998347f,0.998220f,0.998088f,0.997952f,0.997810f,0.997665f, + 0.997514f,0.997359f,0.997199f,0.997035f,0.996865f,0.996691f,0.996513f,0.996329f,0.996142f,0.995949f,0.995752f, + 0.995550f,0.995343f,0.995132f,0.994916f,0.994695f,0.994470f,0.994240f,0.994005f,0.993766f,0.993522f,0.993273f, + 0.993020f,0.992762f,0.992500f,0.992233f,0.991961f,0.991684f,0.991403f,0.991118f,0.990827f,0.990532f,0.990233f, + 0.989929f,0.989620f,0.989307f,0.988989f,0.988666f,0.988339f,0.988007f,0.987670f,0.987329f,0.986984f,0.986634f, + 0.986279f,0.985919f,0.985556f,0.985187f,0.984814f,0.984436f,0.984054f,0.983667f,0.983276f,0.982880f,0.982480f, + 0.982075f,0.981665f,0.981251f,0.980832f,0.980409f,0.979982f,0.979549f,0.979113f,0.978672f,0.978226f,0.977776f, + 0.977321f,0.976862f,0.976398f,0.975930f,0.975457f,0.974980f,0.974498f,0.974012f,0.973522f,0.973027f,0.972527f, + 0.972023f,0.971515f,0.971002f,0.970485f,0.969963f,0.969437f,0.968906f,0.968371f,0.967832f,0.967288f,0.966740f, + 0.966187f,0.965630f,0.965069f,0.964503f,0.963933f,0.963358f,0.962780f,0.962196f,0.961609f,0.961017f,0.960420f, + 0.959820f,0.959215f,0.958606f,0.957992f,0.957374f,0.956752f,0.956125f,0.955495f,0.954859f,0.954220f,0.953576f, + 0.952928f,0.952276f,0.951620f,0.950959f,0.950294f,0.949625f,0.948951f,0.948274f,0.947592f,0.946906f,0.946215f, + 0.945521f,0.944822f,0.944119f,0.943412f,0.942701f,0.941985f,0.941265f,0.940542f,0.939814f,0.939082f,0.938345f, + 0.937605f,0.936861f,0.936112f,0.935359f,0.934603f,0.933842f,0.933077f,0.932308f,0.931534f,0.930757f,0.929976f, + 0.929191f,0.928401f,0.927608f,0.926810f,0.926009f,0.925204f,0.924394f,0.923581f,0.922763f,0.921942f,0.921116f, + 0.920287f,0.919454f,0.918616f,0.917775f,0.916930f,0.916081f,0.915228f,0.914371f,0.913510f,0.912645f,0.911777f, + 0.910904f,0.910028f,0.909148f,0.908264f,0.907376f,0.906484f,0.905588f,0.904689f,0.903786f,0.902879f,0.901968f, + 0.901053f,0.900135f,0.899213f,0.898287f,0.897357f,0.896423f,0.895486f,0.894545f,0.893601f,0.892653f,0.891701f, + 0.890745f,0.889785f,0.888822f,0.887856f,0.886885f,0.885911f,0.884934f,0.883952f,0.882967f,0.881979f,0.880987f, + 0.879991f,0.878992f,0.877989f,0.876983f,0.875973f,0.874959f,0.873942f,0.872922f,0.871897f,0.870870f,0.869839f, + 0.868804f,0.867766f,0.866725f,0.865680f,0.864631f,0.863580f,0.862524f,0.861466f,0.860404f,0.859338f,0.858269f, + 0.857197f,0.856121f,0.855042f,0.853960f,0.852874f,0.851786f,0.850693f,0.849598f,0.848499f,0.847397f,0.846291f, + 0.845183f,0.844071f,0.842955f,0.841837f,0.840715f,0.839591f,0.838462f,0.837331f,0.836197f,0.835059f,0.833918f, + 0.832775f,0.831628f,0.830477f,0.829324f,0.828168f,0.827008f,0.825846f,0.824680f,0.823512f,0.822340f,0.821165f, + 0.819987f,0.818807f,0.817623f,0.816436f,0.815246f,0.814054f,0.812858f,0.811659f,0.810458f,0.809253f,0.808046f, + 0.806835f,0.805622f,0.804406f,0.803187f,0.801965f,0.800741f,0.799513f,0.798283f,0.797050f,0.795814f,0.794575f, + 0.793334f,0.792089f,0.790842f,0.789593f,0.788340f,0.787085f,0.785827f,0.784567f,0.783303f,0.782037f,0.780769f, + 0.779497f,0.778224f,0.776947f,0.775668f,0.774386f,0.773102f,0.771815f,0.770526f,0.769234f,0.767939f,0.766642f, + 0.765343f,0.764041f,0.762736f,0.761429f,0.760120f,0.758808f,0.757493f,0.756177f,0.754857f,0.753536f,0.752212f, + 0.750886f,0.749557f,0.748226f,0.746892f,0.745557f,0.744219f,0.742878f,0.741536f,0.740191f,0.738843f,0.737494f, + 0.736142f,0.734788f,0.733432f,0.732074f,0.730714f,0.729351f,0.727986f,0.726619f,0.725250f,0.723879f,0.722505f, + 0.721130f,0.719752f,0.718373f,0.716991f,0.715608f,0.714222f,0.712834f,0.711444f,0.710053f,0.708659f,0.707263f, + 0.705866f,0.704466f,0.703065f,0.701661f,0.700256f,0.698849f,0.697439f,0.696029f,0.694616f,0.693201f,0.691785f, + 0.690366f,0.688946f,0.687525f,0.686101f,0.684676f,0.683249f,0.681820f,0.680389f,0.678957f,0.677523f,0.676088f, + 0.674650f,0.673212f,0.671771f,0.670329f,0.668885f,0.667440f,0.665993f,0.664544f,0.663094f,0.661643f,0.660190f, + 0.658735f,0.657279f,0.655822f,0.654363f,0.652902f,0.651440f,0.649977f,0.648512f,0.647046f,0.645578f,0.644109f, + 0.642639f,0.641167f,0.639695f,0.638220f,0.636745f,0.635268f,0.633790f,0.632310f,0.630830f,0.629348f,0.627865f, + 0.626380f,0.624895f,0.623408f,0.621920f,0.620431f,0.618941f,0.617450f,0.615958f,0.614464f,0.612970f,0.611474f, + 0.609978f,0.608480f,0.606981f,0.605482f,0.603981f,0.602479f,0.600977f,0.599473f,0.597968f,0.596463f,0.594957f, + 0.593449f,0.591941f,0.590432f,0.588922f,0.587412f,0.585900f,0.584388f,0.582875f,0.581361f,0.579846f,0.578331f, + 0.576815f,0.575298f,0.573780f,0.572262f,0.570743f,0.569223f,0.567703f,0.566182f,0.564661f,0.563139f,0.561616f, + 0.560093f,0.558569f,0.557044f,0.555519f,0.553994f,0.552468f,0.550941f,0.549414f,0.547887f,0.546359f,0.544831f, + 0.543302f,0.541773f,0.540243f,0.538713f,0.537183f,0.535652f,0.534121f,0.532590f,0.531058f,0.529526f,0.527994f, + 0.526462f,0.524929f,0.523396f,0.521863f,0.520330f,0.518796f,0.517262f,0.515728f,0.514194f,0.512660f,0.511126f, + 0.509591f,0.508057f,0.506522f,0.504988f,0.503453f,0.501918f,0.500384f,0.498849f,0.497314f,0.495780f,0.494245f, + 0.492710f,0.491176f,0.489641f,0.488107f,0.486573f,0.485039f,0.483505f,0.481971f,0.480437f,0.478904f,0.477370f, + 0.475837f,0.474305f,0.472772f,0.471240f,0.469708f,0.468176f,0.466644f,0.465113f,0.463582f,0.462052f,0.460522f, + 0.458992f,0.457463f,0.455934f,0.454405f,0.452877f,0.451349f,0.449822f,0.448295f,0.446769f,0.445243f,0.443718f, + 0.442193f,0.440669f,0.439146f,0.437623f,0.436100f,0.434578f,0.433057f,0.431537f,0.430017f,0.428497f,0.426979f, + 0.425461f,0.423944f,0.422427f,0.420911f,0.419396f,0.417882f,0.416368f,0.414856f,0.413344f,0.411833f,0.410322f, + 0.408813f,0.407305f,0.405797f,0.404290f,0.402784f,0.401279f,0.399775f,0.398272f,0.396770f,0.395269f,0.393768f, + 0.392269f,0.390771f,0.389274f,0.387778f,0.386283f,0.384789f,0.383296f,0.381804f,0.380314f,0.378824f,0.377336f, + 0.375848f,0.374362f,0.372877f,0.371394f,0.369911f,0.368430f,0.366950f,0.365471f,0.363994f,0.362517f,0.361042f, + 0.359569f,0.358097f,0.356626f,0.355156f,0.353688f,0.352221f,0.350755f,0.349291f,0.347829f,0.346368f,0.344908f, + 0.343449f,0.341993f,0.340537f,0.339083f,0.337631f,0.336180f,0.334731f,0.333283f,0.331837f,0.330393f,0.328950f, + 0.327509f,0.326069f,0.324631f,0.323194f,0.321760f,0.320327f,0.318895f,0.317466f,0.316038f,0.314611f,0.313187f, + 0.311764f,0.310343f,0.308924f,0.307507f,0.306091f,0.304678f,0.303266f,0.301856f,0.300448f,0.299041f,0.297637f, + 0.296235f,0.294834f,0.293435f,0.292039f,0.290644f,0.289251f,0.287861f,0.286472f,0.285085f,0.283700f,0.282318f, + 0.280937f,0.279558f,0.278182f,0.276808f,0.275435f,0.274065f,0.272697f,0.271331f,0.269967f,0.268606f,0.267247f, + 0.265889f,0.264534f,0.263182f,0.261831f,0.260483f,0.259137f,0.257793f,0.256451f,0.255112f,0.253775f,0.252441f, + 0.251108f,0.249779f,0.248451f,0.247126f,0.245803f,0.244483f,0.243165f,0.241849f,0.240536f,0.239225f,0.237917f, + 0.236611f,0.235308f,0.234007f,0.232709f,0.231413f,0.230120f,0.228829f,0.227541f,0.226255f,0.224972f,0.223692f, + 0.222414f,0.221139f,0.219867f,0.218597f,0.217329f,0.216065f,0.214803f,0.213544f,0.212287f,0.211033f,0.209782f, + 0.208534f,0.207288f,0.206045f,0.204805f,0.203568f,0.202333f,0.201102f,0.199873f,0.198647f,0.197423f,0.196203f, + 0.194986f,0.193771f,0.192559f,0.191350f,0.190144f,0.188941f,0.187741f,0.186544f,0.185350f,0.184158f,0.182970f, + 0.181785f,0.180603f,0.179423f,0.178247f,0.177074f,0.175904f,0.174737f,0.173572f,0.172411f,0.171254f,0.170099f, + 0.168947f,0.167799f,0.166653f,0.165511f,0.164372f,0.163236f,0.162103f,0.160973f,0.159847f,0.158723f,0.157603f, + 0.156487f,0.155373f,0.154263f,0.153156f,0.152052f,0.150951f,0.149854f,0.148760f,0.147670f,0.146582f,0.145498f, + 0.144418f,0.143340f,0.142266f,0.141196f,0.140129f,0.139065f,0.138005f,0.136948f,0.135894f,0.134844f,0.133797f, + 0.132754f,0.131714f,0.130678f,0.129645f,0.128616f,0.127590f,0.126568f,0.125549f,0.124534f,0.123522f,0.122514f, + 0.121509f,0.120508f,0.119511f,0.118517f,0.117526f,0.116540f,0.115557f,0.114577f,0.113601f,0.112629f,0.111661f, + 0.110696f,0.109734f,0.108777f,0.107823f,0.106873f,0.105926f,0.104984f,0.104045f,0.103109f,0.102178f,0.101250f, + 0.100326f,0.099406f,0.098489f,0.097576f,0.096667f,0.095762f,0.094861f,0.093963f,0.093070f,0.092180f,0.091294f, + 0.090412f,0.089533f,0.088659f,0.087788f,0.086922f,0.086059f,0.085200f,0.084345f,0.083494f,0.082647f,0.081804f, + 0.080964f,0.080129f,0.079298f,0.078470f,0.077647f,0.076828f,0.076012f,0.075201f,0.074393f,0.073590f,0.072790f, + 0.071995f,0.071204f,0.070416f,0.069633f,0.068854f,0.068078f,0.067307f,0.066540f,0.065777f,0.065019f,0.064264f, + 0.063513f,0.062767f,0.062024f,0.061286f,0.060552f,0.059822f,0.059096f,0.058374f,0.057657f,0.056943f,0.056234f, + 0.055529f,0.054828f,0.054132f,0.053439f,0.052751f,0.052067f,0.051387f,0.050711f,0.050040f,0.049373f,0.048710f, + 0.048052f,0.047397f,0.046747f,0.046101f,0.045460f,0.044822f,0.044189f,0.043561f,0.042936f,0.042316f,0.041701f, + 0.041089f,0.040482f,0.039879f,0.039281f,0.038687f,0.038097f,0.037512f,0.036930f,0.036354f,0.035781f,0.035214f, + 0.034650f,0.034091f,0.033536f,0.032986f,0.032440f,0.031898f,0.031361f,0.030828f,0.030300f,0.029776f,0.029256f, + 0.028741f,0.028231f,0.027724f,0.027223f,0.026725f,0.026233f,0.025744f,0.025260f,0.024781f,0.024306f,0.023836f, + 0.023370f,0.022908f,0.022451f,0.021999f,0.021551f,0.021107f,0.020668f,0.020234f,0.019804f,0.019379f,0.018958f, + 0.018541f,0.018130f,0.017722f,0.017320f,0.016921f,0.016528f,0.016139f,0.015754f,0.015374f,0.014999f,0.014628f, + 0.014262f,0.013900f,0.013543f,0.013191f,0.012843f,0.012499f,0.012161f,0.011827f,0.011497f,0.011172f,0.010852f, + 0.010536f,0.010225f,0.009919f,0.009617f,0.009319f,0.009027f,0.008739f,0.008455f,0.008177f,0.007903f,0.007633f, + 0.007368f,0.007108f,0.006853f,0.006602f,0.006355f,0.006114f,0.005877f,0.005645f,0.005417f,0.005194f,0.004976f, + 0.004762f,0.004553f,0.004349f,0.004149f,0.003954f,0.003764f,0.003578f,0.003397f,0.003221f,0.003049f,0.002883f, + 0.002720f,0.002563f,0.002410f,0.002262f,0.002118f,0.001980f,0.001845f,0.001716f,0.001591f,0.001471f,0.001356f, + 0.001245f,0.001140f,0.001038f,0.000942f,0.000850f,0.000763f,0.000681f,0.000603f,0.000530f,0.000462f,0.000398f, + 0.000339f,0.000285f,0.000236f,0.000191f,0.000151f,0.000115f,0.000085f,0.000059f,0.000038f,0.000021f,0.000009f, + 0.000002f,0.000000f }; + +// ----------------------------------------------------------------------------- + +OptFFT::OptFFT(const size_t maxDataSize) +{ + assert( maxDataSize % OVERLAPSAMPLES == 0 ); + + // DOUBLE + //m_pIn = static_cast( fftw_malloc(sizeof(double) * FRAMESIZE) ); + //m_pOut = static_cast( fftw_malloc(sizeof(fftw_complex) * (FRAMESIZE/2 + 1)) ); + //m_p = fftw_plan_dft_r2c_1f(FRAMESIZE, m_pIn, m_pOut, FFTW_ESTIMATE); // FFTW_ESTIMATE or FFTW_MEASURE + + // FLOAT + // m_pIn = static_cast( fftwf_malloc(sizeof(float) * FRAMESIZE) ); + // m_pOut = static_cast( fftwf_malloc(sizeof(fftwf_complex) * (FRAMESIZE/2 + 1)) ); + + //// in destroyed when line executed + //m_p = fftwf_plan_dft_r2c_1d(FRAMESIZE, m_pIn, m_pOut, FFTW_ESTIMATE); // FFTW_ESTIMATE or FFTW_MEASURE + + //----------------------------------------------------------------- + + int numSamplesPerFrame = FRAMESIZE; + int numSamplesPerFrameOut = FRAMESIZE/2+1; + + m_maxFrames = static_cast ( (maxDataSize - FRAMESIZE) / OVERLAPSAMPLES + 1 ); + + m_pIn = static_cast ( fftwf_malloc(sizeof(float) * (numSamplesPerFrame * m_maxFrames) ) ); + if ( !m_pIn ) + { + ostringstream oss; + oss << "fftwf_malloc failed on m_pIn. Trying to allocate <" + << sizeof(float) * (numSamplesPerFrame * m_maxFrames) + << "> bytes"; + throw std::runtime_error(oss.str()); + } + + m_pOut = static_cast( fftwf_malloc(sizeof(fftwf_complex) * (numSamplesPerFrameOut* m_maxFrames) ) ); + if ( !m_pOut ) + { + ostringstream oss; + oss << "fftwf_malloc failed on m_pOut. Trying to allocate <" + << sizeof(fftwf_complex) * (numSamplesPerFrameOut* m_maxFrames) + << "> bytes"; + + throw std::runtime_error(oss.str()); + } + + // in destroyed when line executed + m_p = fftwf_plan_many_dft_r2c(1, &numSamplesPerFrame, m_maxFrames, + m_pIn, &numSamplesPerFrame, 1, numSamplesPerFrame, + m_pOut, &numSamplesPerFrameOut, + 1, numSamplesPerFrameOut, + FFTW_ESTIMATE | FFTW_DESTROY_INPUT); + + if ( !m_p ) + throw std::runtime_error ("fftwf_plan_many_dft_r2c failed"); + + double base = exp( log( static_cast(MAXFREQ) / static_cast(MINFREQ) ) / + static_cast(Filter::NBANDS) + ); + + m_powTable.resize( Filter::NBANDS+1 ); + for ( unsigned int i = 0; i < Filter::NBANDS + 1; ++i ) + m_powTable[i] = static_cast( (pow(base, static_cast(i)) - 1.0) * MINCOEF ); + + m_pFrames = new float*[m_maxFrames]; + + if ( !m_pFrames ) + { + ostringstream oss; + oss << "Allocation failed on m_pFrames. Trying to allocate <" + << sizeof(float*) * m_maxFrames + << "> bytes"; + + throw std::runtime_error(oss.str()); + } + + + for (int i = 0; i < m_maxFrames; ++i) + { + m_pFrames[i] = new float[Filter::NBANDS]; + if ( !m_pFrames[i] ) + throw std::runtime_error("Allocation failed on m_pFrames"); + } + +} + +// ---------------------------------------------------------------------- + +OptFFT::~OptFFT() +{ + fftwf_destroy_plan(m_p); + + fftwf_free(m_pIn); + fftwf_free(m_pOut); + + for (int i = 0; i < m_maxFrames; ++i) + delete [] m_pFrames[i]; + + delete [] m_pFrames; +} + +// ---------------------------------------------------------------------- + +int OptFFT::process(float* pInData, const size_t dataSize) +{ + // generally is the same of the one we used in the constructor (m_maxFrames) but + // might be less at the end of the stream + int nFrames = static_cast( (dataSize - FRAMESIZE) / OVERLAPSAMPLES + 1 ); + + float* pIn_It = m_pIn; + + for (int i = 0; i < nFrames; ++i) + { + memcpy( pIn_It, &pInData[i*OVERLAPSAMPLES], sizeof(float) * FRAMESIZE); + // apply hanning window + applyHann(pIn_It, FRAMESIZE); + + pIn_It += FRAMESIZE; + } + + // fill the rest with zeroes + if ( nFrames < m_maxFrames ) + memset( pIn_It, 0, sizeof(float) * (m_maxFrames-nFrames) * FRAMESIZE ); + + fftwf_execute(m_p); + + int totSamples = (FRAMESIZE/2+1) * // numSamplesPerFrameOut + nFrames; // the frames actually in the input + + // scaling (?) + float scalingFactor = static_cast(FRAMESIZE) / 2.0f; + for (int k = 0; k < totSamples; ++k) + { + m_pOut[k][0] /= scalingFactor; + m_pOut[k][1] /= scalingFactor; + } + + int frameStart; + unsigned int outBlocStart; + unsigned int outBlocEnd; + + for (int i = 0; i < nFrames; ++i) + { + frameStart = i * (FRAMESIZE/2+1); + + // compute bands + for (unsigned int j = 0; j < Filter::NBANDS; j++) + { + outBlocStart = m_powTable[j] + frameStart; + outBlocEnd = m_powTable[j+1] + frameStart; + + m_pFrames[i][j] = 0; + + // WARNING: We're double counting the last one here. + // this bug is to match matlab's implementation bug in power2band.m + unsigned int end_k = outBlocEnd + static_cast(MINCOEF); + for (unsigned int k = outBlocStart + static_cast(MINCOEF); k <= end_k; k++) + { + m_pFrames[i][j] += m_pOut[k][0] * m_pOut[k][0] + + m_pOut[k][1] * m_pOut[k][1]; + } + + // WARNING: if we change the k<=end to k(outBlocEnd - outBlocStart + 1); + } + } + + return nFrames; +} + +// ----------------------------------------------------------------------------- + +void OptFFT::applyHann( float* pInData, const size_t dataSize ) +{ + assert (dataSize == 2048); + + for ( size_t i = 0; i < dataSize; ++i ) + pInData[i] *= hann[i]; +} + +// ----------------------------------------------------------------------------- + +} // end of namespace + +// ---------------------------------------------------------------------- diff --git a/thirdparty/liblastfm2/src/fingerprint/fplib/OptFFT.h b/thirdparty/liblastfm2/src/fingerprint/fplib/OptFFT.h new file mode 100644 index 000000000..2a704ee73 --- /dev/null +++ b/thirdparty/liblastfm2/src/fingerprint/fplib/OptFFT.h @@ -0,0 +1,63 @@ +/* + Copyright 2005-2009 Last.fm Ltd. + + This file is part of liblastfm. + + liblastfm 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. + + liblastfm 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 liblastfm. If not, see . +*/ +#ifndef __OPT_FFT_H +#define __OPT_FFT_H + +#include +#include + +namespace fingerprint +{ + +class OptFFT +{ +public: + + OptFFT(const size_t maxDataSize); + ~OptFFT(); + + int + process(float* pInData, const size_t dataSize); + + float** + getFrames() { return m_pFrames; } + +private: + + void applyHann(float* pInData, const size_t dataSize); + + fftwf_plan m_p; + fftwf_complex * m_pOut; + float* m_pIn; + + //float m_base; + + int m_numSamples; + int m_numOutSamples; + + float** m_pFrames; + int m_maxFrames; + + std::vector m_powTable; + +}; + +} // end of namespace + +#endif // OPT_FFT diff --git a/thirdparty/liblastfm2/src/fingerprint/fplib/fp_helper_fun.h b/thirdparty/liblastfm2/src/fingerprint/fplib/fp_helper_fun.h new file mode 100644 index 000000000..947b63037 --- /dev/null +++ b/thirdparty/liblastfm2/src/fingerprint/fplib/fp_helper_fun.h @@ -0,0 +1,443 @@ +/* + Copyright 2005-2009 Last.fm Ltd. + + This file is part of liblastfm. + + liblastfm 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. + + liblastfm 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 liblastfm. If not, see . +*/ +#ifndef __FINGERPRINT_HELPER_FUNCTIONS_H +#define __FINGERPRINT_HELPER_FUNCTIONS_H + +#include +#include +#include + +namespace fingerprint +{ + +// ----------------------------------------------------------------------------- + +static const size_t FINGERPRINT_LIB_VERSION = 1; +static const float QUERY_START_SECS = 20; +static const float QUERY_SIZE_SECS = 14; +static const float UPDATE_SIZE_SECS = 10; +//FFT needs also a buffer that depends on the input freq. 3 secs should be enough up to 48Khz +static const float GUARD_SIZE_SECS = 3; +static const float NORMALIZATION_SKIP_SECS = 2.5; +static const int MIN_UNIQUE_KEYS = 75; +static const unsigned int MAX_GOOD_GROUP_SIZE = 200; +static const int SHA_SIZE = 32; + +///////////////////////////////////////////////////// +// For FFT. DO NOT TOUCH THEM! +// number of samples in a frame +static const int FRAMESIZE = 2048; +static const int OVERLAP = 32; +static const int OVERLAPSAMPLES = (FRAMESIZE/OVERLAP); // 64 + +// down-sampled frequency +static const int DFREQ = 5512; +static const float FDFREQ = 5512.5f; + +// ----------------------------------------------------------------------------- + +struct GroupData +{ + unsigned int key; // the key (or local descriptor) + unsigned int count; // the number of frames sharing this key +}; + +// ----------------------------------------------------------------------------- +// ----------------------------------------------------------------------------- + +inline +unsigned int getTotalKeys( + int mSecs ) +{ + return static_cast((static_cast(mSecs) / (1000.0 * OVERLAPSAMPLES) ) * DFREQ ) + 1; +} + +// ----------------------------------------------------------------------------- + +template +void simpleSkip( + GroupDataIt& begIt, const GroupDataIt& endIt, + unsigned int numSkipKeys ) +{ + if ( numSkipKeys <= 0 ) + return; + + unsigned int nKeys; + for ( nKeys = 0; nKeys < numSkipKeys && begIt != endIt; ++begIt ) + nKeys += begIt->count; + + // clear crop at the end + if ( nKeys > numSkipKeys ) + { + --begIt; + begIt->count = nKeys - numSkipKeys; + } + +} + +// ----------------------------------------------------------------------------- + +template +void cutGroups( + std::vector& groups, + const unsigned int startMS, + const unsigned int lengthMS ) +{ + typename std::vector::iterator itBeg = groups.begin(), itEnd = groups.begin(); + + unsigned int keys_begin, keys_end; + + for (keys_begin = getTotalKeys(startMS); + itBeg != groups.end() && keys_begin > itBeg->count; ++itBeg) + keys_begin -= itBeg->count; + + for (keys_end = getTotalKeys(startMS + lengthMS); + itEnd != groups.end() && keys_end > itEnd->count; ++itEnd) + keys_end -= itEnd->count; + + if (itBeg == groups.end()) // in the umpossible scenario that you try to cut past the size of the groups + { + groups.clear(); + return; + } + + itBeg->count -= keys_begin; + if (keys_end > 0 && itEnd != groups.end()) + { + itEnd->count = keys_end; + ++itEnd; + } + + copy(itBeg, itEnd, groups.begin()); + groups.resize(itEnd - itBeg); + + keys_begin = getTotalKeys(lengthMS); + for (typename std::vector::iterator it = groups.begin(); it != groups.end(); ++it) + keys_begin -= it->count; +} + +// ------------------------------------------------------------------------- + +template +void keys2GroupData( + const std::vector& keys, // in + std::vector& groupData, + bool clearDst = true ) // out +{ + if (clearDst) + groupData.clear(); + + if (keys.empty()) + return; + + TGroupData tmpGroup; + std::vector::const_iterator it = keys.begin(); + + if ( !groupData.empty() ) + { + // get the last group + tmpGroup = groupData.back(); + groupData.pop_back(); + } + else + { + // new group! + tmpGroup.key = *it; + tmpGroup.count = 1; + ++it; // move to the next key + } + + for (; it != keys.end(); ++it) + { + if ( *it != tmpGroup.key ) + { + // new group ready! + groupData.push_back( tmpGroup ); + tmpGroup.key = *it; + tmpGroup.count = 0; + } + + ++tmpGroup.count; + } + + // last group + groupData.push_back( tmpGroup ); +} + +// ------------------------------------------------------------------------- + +template +void keys2GroupData( + const std::vector& keys, // in + std::deque& groupData, + bool clearDst = true ) // out +{ + if (clearDst) + groupData.clear(); + + if (keys.empty()) + return; + + TGroupData tmpGroup; + std::vector::const_iterator it = keys.begin(); + + if ( !groupData.empty() ) + { + // get the last group + tmpGroup = groupData.back(); + groupData.pop_back(); + } + else + { + // new group! + tmpGroup.key = *it; + tmpGroup.count = 1; + ++it; // move to the next key + } + + for (; it != keys.end(); ++it) + { + if ( *it != tmpGroup.key ) + { + // new group ready! + groupData.push_back( tmpGroup ); + tmpGroup.key = *it; + tmpGroup.count = 0; + } + + ++tmpGroup.count; + } + + // last group + groupData.push_back( tmpGroup ); +} + +// ------------------------------------------------------------------------- + +template +inline +void groupData2Keys( + const std::vector& groupData, // in + std::vector& keys ) // out +{ + keys.clear(); + + typename std::vector::const_iterator it; + + for (it = groupData.begin(); it != groupData.end(); ++it) + { + for (unsigned int j = 0; j < it->count; ++j) + keys.push_back(it->key); + } +} + +// ------------------------------------------------------------------------- + +template +bool findSignificantGroups( + GroupDataIt& beg, GroupDataIt& end, unsigned int& offset_left, unsigned int& offset_right, + unsigned int windowKeySize, unsigned int subWindowKeySize, unsigned int minUniqueKeys) +{ + GroupDataIt itBeg = beg, itEnd = beg, itWindowBeg = beg, itWindowEnd = beg; + + offset_left = 0; + unsigned int window_offset_left; + unsigned int window_offset_right; + + // this amounts to around a 500 ms hop for, say, a 20 second sub-window + unsigned int key_hop_size = subWindowKeySize / 40; + + // trail out itEnd + for (offset_right = windowKeySize; itEnd != end && offset_right > itEnd->count; ++itEnd) + offset_right -= itEnd->count; + + // dang man, we don't even have enough groups to span the window size + if (itEnd == end && offset_right > 0) + return false; + + // 0 window size means just scan the whole range + if (windowKeySize == 0) + itEnd = end; + + // trail out itWindowBeg + for (window_offset_left = (windowKeySize - subWindowKeySize) / 2; + window_offset_left > itWindowBeg->count; ++itWindowBeg) + window_offset_left -= itWindowBeg->count; + + // trail out itWindowEnd + for (window_offset_right = (windowKeySize + subWindowKeySize) / 2; + window_offset_right > itWindowEnd->count; ++itWindowEnd) + window_offset_right -= itWindowEnd->count; + + while (itEnd != end) + { + if (enoughUniqueGoodGroups(itWindowBeg, itWindowEnd, minUniqueKeys)) + { + beg = itBeg; + end = itEnd; + return true; + } + + // okay, jump key_hop_size on end iterator + for (offset_right += key_hop_size; itEnd != end && offset_right > itEnd->count; ++itEnd) + offset_right -= itEnd->count; + + // if we didn't hop the full hop size, modify the hop size to only hop as far as we hopped + if (itEnd == end) + key_hop_size -= offset_right; + + for (offset_left += key_hop_size; offset_left > itBeg->count; ++itBeg) + offset_left -= itBeg->count; + for (window_offset_right += key_hop_size; window_offset_right > itWindowEnd->count; ++itWindowEnd) + window_offset_right -= itWindowEnd->count; + for (window_offset_left += key_hop_size; window_offset_left > itWindowBeg->count; ++itWindowBeg) + window_offset_left -= itWindowBeg->count; + } + + beg = itBeg; + end = itEnd; + + return enoughUniqueGoodGroups(itWindowBeg, itWindowEnd, minUniqueKeys); +} + +// ----------------------------------------------------------------------------- + +template +bool +reduceGroups( + std::vector& groups, unsigned int startKeySize, + unsigned int windowKeySize, unsigned int subWindowKeySize, unsigned int minUniqueKeys ) +{ + unsigned int offset_left = 0; + unsigned int offset_right = 0; + + typename std::vector::iterator begIt = groups.begin(); + typename std::vector::iterator endIt = groups.end(); + + simpleSkip(begIt, endIt, startKeySize); + bool result = findSignificantGroups( begIt, endIt, + offset_left, offset_right, + windowKeySize, subWindowKeySize, minUniqueKeys ); + + if ( !result ) + { + groups.clear(); + return false; + } + + begIt->count -= offset_left; + if (offset_right > 0 && endIt != groups.end()) + { + endIt->count = offset_right; + ++endIt; + } + + std::vector resGrups(begIt, endIt); + groups.swap(resGrups); + + return true; +} + + +// ------------------------------------------------------------------------- + +template +inline bool enoughUniqueGoodGroups( + const GroupDataIt& beg, + const GroupDataIt& end, + unsigned int minUniqueKeys) +{ + std::set groupKeys; + + for (GroupDataIt it = beg; it != end && static_cast(groupKeys.size()) < minUniqueKeys; ++it) + { + if (it->count > MAX_GOOD_GROUP_SIZE) + return false; + + groupKeys.insert(it->key); + } + + return static_cast(groupKeys.size()) >= minUniqueKeys; +} + +// ----------------------------------------------------------------------------- +////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////// +// Used by the fingerprint keys operation + +// minimum and maximum frequency to consider +#define MINFREQ 300 +#define MAXFREQ 2000 + +// amount of time in a frame +#define FRAME_TLEN ((float) FRAMESIZE / (float) DFREQ) +#define MINCOEF (FRAME_TLEN * MINFREQ) + +#define round__(x) ((int)(x + .5)) + +struct RawFilter +{ + unsigned int ftid; + float thresh; + float weight; +}; + +const RawFilter rFilters[] = { + { 26752, -4.37515e-07f, 0.260836f }, // filterID, threshold, alpha (weight) + { 23871, -2.44615e-05f, 0.263986f }, + { 26777, -3.69244e-08f, 0.267763f }, + { 4635, -1.13672e-05f, 0.269428f }, + { 2937, 5.28804e-09f, 0.271896f }, + { 27405, -0.000126494f, 0.272362f }, + { 10782, 4.27478e-08f, 0.272609f }, + { 21033, -6.7912e-07f, 0.276099f }, + { 27117, 8.07178e-06f, 0.277762f }, + { 27072, 2.46044e-05f, 0.27883f }, + { 24228, 4.11255e-07f, 0.281743f }, + { 23838, 0.000228396f, 0.284479f }, + { 17165, -1.19495e-07f, 0.286304f }, + { 25263, 0.000398279f, 0.287066f }, + { 20721, 7.15095e-07f, 0.288913f }, + { 8502, -2.78361e-07f, 0.290424f }, + { 17175, -1.08429e-08f, 0.292219f }, + { 17811, -3.29527e-08f, 0.292554f }, + { 27495, -4.47575e-07f, 0.290119f }, + { 23538, -3.04273e-09f, 0.294539f }, + { 8205, 4.02691e-07f, 0.293525f }, + { 12177, 1.16873e-06f, 0.293832f }, + { 27051, -0.000902544f, 0.296453f }, + { 27111, -2.38425e-05f, 0.297428f }, + { 21779, -1.0669e-07f, 0.297302f }, + { 14817, -9.52849e-09f, 0.299f }, + { 27087, 1.22163e-05f, 0.296502f }, + { 27081, -2.8758e-09f, 0.300112f }, + { 20394, 1.28237e-06f, 0.298693f }, + { 28209, 0.000624447f, 0.29812f }, + { 23533, -2.19406e-06f, 0.299773f }, + { 23865, -1.28037e-08f, 0.300777f } // this is iteration 1 +}; + +// ----------------------------------------------------------------------------- + +} + +// ----------------------------------------------------------------------------- + +#endif // __FINGERPRINT_HELPER_FUNCTIONS_H + diff --git a/thirdparty/liblastfm2/src/global.h b/thirdparty/liblastfm2/src/global.h new file mode 100644 index 000000000..d297ae702 --- /dev/null +++ b/thirdparty/liblastfm2/src/global.h @@ -0,0 +1,136 @@ +/* + Copyright 2009 Last.fm Ltd. + - Primarily authored by Max Howell, Jono Cole and Doug Mansell + + This file is part of liblastfm. + + liblastfm 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. + + liblastfm 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 liblastfm. If not, see . +*/ + +#ifndef LASTFM_GLOBAL_H +#define LASTFM_GLOBAL_H + +#define LASTFM_VERSION 0x00000400 +#define LASTFM_VERSION_STRING "0.4.0" +#define LASTFM_MAJOR_VERSION 0 +#define LASTFM_MINOR_VERSION 4 +#define LASTFM_PATCH_VERSION 0 + + +#include + +#ifdef Q_CC_MSVC + #ifdef LASTFM_LIB + #define LASTFM_DLLEXPORT __declspec(dllexport) + #else + #define LASTFM_DLLEXPORT __declspec(dllimport) + #endif + #ifdef LASTFM_FINGERPRINT_LIB + #define LASTFM_FINGERPRINT_DLLEXPORT __declspec(dllexport) + #else + #define LASTFM_FINGERPRINT_DLLEXPORT __declspec(dllimport) + #endif +#elif __GNUC__ >= 4 + #define LASTFM_DLLEXPORT __attribute__ ((visibility("default"))) + #define LASTFM_FINGERPRINT_DLLEXPORT __attribute__ ((visibility("default"))) +#else + #define LASTFM_DLLEXPORT + #define LASTFM_FINGERPRINT_DLLEXPORT +#endif + + + +#include +#include + +namespace lastfm +{ + /** http://labs.trolltech.com/blogs/2008/10/09/coding-tip-pretty-printing-enum-values + * Tips for making this take a single parameter welcome! :) + * + * eg. lastfm::qMetaEnumString( error, "NetworkError" ); + */ + template static inline QString qMetaEnumString( int enum_value, const char* enum_name ) + { + QMetaObject meta = T::staticMetaObject; + for (int i=0; i < meta.enumeratorCount(); ++i) + { + QMetaEnum m = meta.enumerator(i); + if (m.name() == QLatin1String(enum_name)) + return QLatin1String(m.valueToKey(enum_value)); + } + return QString("Unknown enum value for \"%1\": %2").arg( enum_name ).arg( enum_value ); + } + + + enum ImageSize + { + Small, + Medium, + Large, /** seemingly 174x174 */ + ExtraLarge, + Mega + }; + + + //convenience + class Album; + class Artist; + class Audioscrobbler; + class AuthenticatedUser; + class Fingerprint; + class FingerprintableSource; + class FingerprintId; + class Mbid; + class MutableTrack; + class NetworkAccessManager; + class Playlist; + class User; + class RadioStation; + class Tag; + class Track; + class XmlQuery; + class Xspf; +} + + +#ifdef LASTFM_COLLAPSE_NAMESPACE +using lastfm::Album; +using lastfm::Artist; +using lastfm::Audioscrobbler; +using lastfm::AuthenticatedUser; +using lastfm::Fingerprint; +using lastfm::FingerprintId; +using lastfm::Mbid; +using lastfm::MutableTrack; +using lastfm::Playlist; +using lastfm::User; +using lastfm::RadioStation; +using lastfm::Tag; +using lastfm::Track; +using lastfm::XmlQuery; +using lastfm::Xspf; +#endif + + +//convenience +class QDomDocument; +class QNetworkAccessManager; +class QNetworkReply; + + +//convenience for development +#include + +#endif //LASTFM_GLOBAL_H diff --git a/thirdparty/liblastfm2/src/lastfm.pro b/thirdparty/liblastfm2/src/lastfm.pro new file mode 100644 index 000000000..c0b306f84 --- /dev/null +++ b/thirdparty/liblastfm2/src/lastfm.pro @@ -0,0 +1,20 @@ +TEMPLATE = lib +QT = core network xml +include( _files.qmake ) + +INSTALLS = target +target.path = /lib + +win32{ + DEFINES += LASTFM_LIB _ATL_DLL + LIBS += winhttp.lib wbemuuid.lib # ws configuration +} +mac{ + LIBS += -framework SystemConfiguration # ws configuration + #TODO we should only use these with the carbon version of Qt! + LIBS += -framework Carbon -framework CoreFoundation # various +} + +linux*{ + QT += dbus +} diff --git a/thirdparty/liblastfm2/src/radio/RadioStation.cpp b/thirdparty/liblastfm2/src/radio/RadioStation.cpp new file mode 100755 index 000000000..38e9c7d3c --- /dev/null +++ b/thirdparty/liblastfm2/src/radio/RadioStation.cpp @@ -0,0 +1,201 @@ +/* + Copyright 2009 Last.fm Ltd. + + This file is part of liblastfm. + + liblastfm 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. + + liblastfm 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 liblastfm. If not, see . +*/ + +#include +#include + +#include "RadioStation.h" +#include "../core/XmlQuery.h" + +QRegExp rxDisco("opt:discovery\\|(\\S+)", Qt::CaseSensitive, QRegExp::RegExp2); +QRegExp rxRep("opt:rep\\|([\\d\\.]+)", Qt::CaseSensitive, QRegExp::RegExp2); +QRegExp rxMainstr("opt:mainstr\\|([\\d\\.]+)", Qt::CaseSensitive, QRegExp::RegExp2); + + +const float k_defaultRep(0.5); +const float k_defaultMainstr(0.5); +const bool k_defaultDisco(false); + +//static +QList +lastfm::RadioStation::list( QNetworkReply* r ) +{ + QList result; + try { + foreach (XmlQuery xq, XmlQuery(ws::parse(r)).children("station")) { + lastfm::RadioStation rs( QUrl::fromPercentEncoding( xq["url"].text().toUtf8() ) ); + rs.setTitle(xq["name"].text()); + result.append(rs); + } + } + catch (ws::ParseError& e) + { + qWarning() << e.what(); + } + return result; +} + +void +lastfm::RadioStation::setString( const QString& string ) +{ + QString replaceString( string ); + QString decodedString = QUrl::fromPercentEncoding( replaceString.replace( QChar('+'), QChar(' ') ).toUtf8() ); + + QRegExp rxRql( "lastfm:\\/\\/rql\\/(.+)$" ); + QRegExp rxPersonal( "lastfm:\\/\\/user\\/(.+)\\/personal" ); + QRegExp rxRecommended( "lastfm://user/(.+)\\/recommended" ); + QRegExp rxNeighbours( "lastfm:\\/\\/user\\/(.+)\\/neighbours" ); + QRegExp rxLoved( "lastfm:\\/\\/user\\/(.+)\\/loved" ); + QRegExp rxGlobalTags( "lastfm:\\/\\/globaltags\\/(.+)" ); + QRegExp rxSimilarArtists( "lastfm:\\/\\/artist\\/(.+)\\/similarartists" ); + QRegExp rxUserTags( "lastfm:\\/\\/usertags\\/(.+)\\/(.+)" ); + QRegExp rxPlaylist( "lastfm:\\/\\/playlist/(.+)\\/shuffle" ); + + if (rxRql.indexIn(decodedString) == 0) + setRql( QByteArray::fromBase64( rxRql.capturedTexts()[1].toAscii() ) ); + else if (rxPersonal.indexIn(decodedString) == 0) + setRql( libraryStr( rxPersonal.capturedTexts()[1] ) ); + else if ( rxRecommended.indexIn(decodedString) == 0) + setRql( recommendationsStr( rxRecommended.capturedTexts()[1] ) ); + else if ( rxNeighbours.indexIn(decodedString) == 0) + setRql( neighbourhoodStr( rxNeighbours.capturedTexts()[1] ) ); + else if ( rxLoved.indexIn(decodedString) == 0) + setRql( lovedTracksStr( rxLoved.capturedTexts()[1] ) ); + else if ( rxGlobalTags.indexIn(decodedString) == 0) + setRql( globalTagStr( rxGlobalTags.capturedTexts()[1] ) ); + else if ( rxSimilarArtists.indexIn(decodedString) == 0) + setRql( similarStr( rxSimilarArtists.capturedTexts()[1] ) ); + else if ( rxUserTags.indexIn(decodedString) == 0) + setRql( userTagStr( rxUserTags.capturedTexts()[1], rxUserTags.capturedTexts()[2] ) ); + else if ( rxPlaylist.indexIn(decodedString) == 0) + setRql( playlistStr( rxPlaylist.capturedTexts()[1].toInt() ) ); + else + { + m_url = string; + } +} + +bool +lastfm::RadioStation::setRep(float rep) +{ + if ( m_rql.isEmpty() ) + return false; + + int indexIn = rxRep.indexIn(m_rql); + + if ( indexIn != -1 ) + { + if (rep != k_defaultRep) + m_rql.replace( indexIn, rxRep.capturedTexts()[0].length(), QString("opt:rep|%1").arg(rep) ); + else + m_rql.replace( indexIn, rxRep.capturedTexts()[0].length(), "" ); + } + else + { + // the rql doesn't have rep in it + // so append it to the end + if (rep != k_defaultRep) + m_rql.append( QString(" opt:rep|%1").arg(rep) ); + } + + setRql(m_rql); + + return true; +} + +bool +lastfm::RadioStation::setMainstr(float mainstr) +{ + if ( m_rql.isEmpty() ) + return false; + + int indexIn = rxMainstr.indexIn(m_rql); + + if ( indexIn != -1 ) + { + if (mainstr != k_defaultMainstr) + m_rql.replace( indexIn, rxMainstr.capturedTexts()[0].length(), QString("opt:mainstr|%1").arg(mainstr) ); + else + m_rql.replace( indexIn, rxMainstr.capturedTexts()[0].length(), "" ); + } + else + { + // the rql doesn't have rep in it + // so append it to the end + if ( mainstr != k_defaultMainstr ) + m_rql.append( QString(" opt:mainstr|%1").arg(mainstr) ); + } + + setRql(m_rql); + + return true; +} + +bool +lastfm::RadioStation::setDisco(bool disco) +{ + if ( m_rql.isEmpty() ) + return false; + + int indexIn = rxDisco.indexIn(m_rql); + + if ( indexIn != -1 ) + { + if (disco) + m_rql.replace( indexIn, rxDisco.capturedTexts()[0].length(), "opt:discovery|true" ); + else + m_rql.replace( indexIn, rxDisco.capturedTexts()[0].length(), "" ); + } + else + { + // the rql doesn't have disco in it + // so append it to the end if it is set + + if (disco) + m_rql.append( " opt:discovery|true" ); + } + + setRql(m_rql); + + return true; +} + +float lastfm::RadioStation::rep() const +{ + if ( rxRep.indexIn(m_rql) != -1 ) + return rxRep.capturedTexts()[1].toFloat(); + + return k_defaultRep; +} + +float lastfm::RadioStation::mainstr() const +{ + if ( rxMainstr.indexIn(m_rql) != -1 ) + return rxMainstr.capturedTexts()[1].toFloat(); + + return k_defaultMainstr; +} + +bool lastfm::RadioStation::disco() const +{ + if ( rxDisco.indexIn(m_rql) != -1 ) + return rxDisco.capturedTexts()[1] == "true"; + + return k_defaultDisco; +} diff --git a/thirdparty/liblastfm2/src/radio/RadioStation.h b/thirdparty/liblastfm2/src/radio/RadioStation.h new file mode 100644 index 000000000..09ee27743 --- /dev/null +++ b/thirdparty/liblastfm2/src/radio/RadioStation.h @@ -0,0 +1,123 @@ +/* + Copyright 2009 Last.fm Ltd. + - Primarily authored by Max Howell, Jono Cole and Doug Mansell + + This file is part of liblastfm. + + liblastfm 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. + + liblastfm 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 liblastfm. If not, see . +*/ +#ifndef LASTFM_RADIO_STATION_H +#define LASTFM_RADIO_STATION_H + +#include +#include +#include + +namespace lastfm +{ + /** @author + */ + class LASTFM_DLLEXPORT RadioStation + { + public: + RadioStation() + {} + RadioStation( const QString& s ) + { + setString( s ); + } + + static RadioStation library( const lastfm::User& user ) { return rql( libraryStr( user ) ); } + static RadioStation recommendations( const lastfm::User& user ) { return rql( recommendationsStr( user ) ); } + static RadioStation neighbourhood( const lastfm::User& user ) { return rql( neighbourhoodStr( user ) ); } + static RadioStation lovedTracks( const lastfm::User& user ) { return rql( lovedTracksStr( user ) ); } + static RadioStation globalTag( const lastfm::Tag& tag ) { return rql( globalTagStr( tag ) ); } + static RadioStation similar( const lastfm::Artist& artist ) { return rql( similarStr( artist ) ); } + static RadioStation userTag( const lastfm::User& user, const lastfm::Tag& tag) { return rql( userTagStr( user, tag ) ); } + static RadioStation playlist( int playlistId ) { return rql( playlistStr( playlistId ) ); } + static RadioStation adventure( const lastfm::User& user ) { return rql( adventureStr( user ) ); } + + static RadioStation rql( const QString& rql ) + { + RadioStation station; + station.setRql( rql ); + return station; + } + + /** eg. "mxcl's Loved Tracks" + * It is worth noting that the Radio doesn't set the title of RadioStation + * object until we have tuned to it, and then we only set the one we give + * you back. + */ + QString title() const { return m_title; } + /** the Last.fm url, eg. lastfm://user/mxcl/loved */ + QString url() const { return m_url; } + QString rql() const { return m_rql; } + + void setTitle( const QString& s ) { m_title = s; } + + bool setRep(float rep); + bool setMainstr(float mainstr); + bool setDisco(bool disco); + + float rep() const; + float mainstr() const; + bool disco() const; + + bool isLegacyPlaylist() const + { + return m_url.startsWith( "lastfm://play/" ) || + m_url.startsWith( "lastfm://preview/" ) || + m_url.startsWith( "lastfm://track/" ) || + m_url.startsWith( "lastfm://playlist/" ); + } + + // good for getRecentStations: + static QList list( QNetworkReply* ); + + private: + void setRql( const QString& rql ) + { + m_rql = rql; + m_url = "lastfm://rql/" + QString(rql.toUtf8().toBase64()); + } + + void setString( const QString& s ); + + static QString libraryStr( const lastfm::User& user ) { return "library:" + user ; } + static QString recommendationsStr( const lastfm::User& user ) { return "rec:" + user ; } + static QString neighbourhoodStr( const lastfm::User& user ) { return "neigh:" + user ; } + static QString lovedTracksStr( const lastfm::User& user ) { return "loved:" + user ; } + static QString globalTagStr( const lastfm::Tag& tag ) { return "tag:\"" + tag + "\"" ; } + static QString similarStr( const lastfm::Artist& artist ) { return "simart:\"" + artist + "\""; } + static QString userTagStr( const lastfm::User& user, const lastfm::Tag& tag) { return "ptag:\"" + tag + "\"|" + user ; } + static QString playlistStr( int playlistId ) { return "playlist:" + QString::number(playlistId) ; } + static QString adventureStr( const lastfm::User& user ) { return "adv:" + user ; } + private: + QString m_rql; + QString m_url; + QString m_title; + }; +} + + +Q_DECLARE_METATYPE( lastfm::RadioStation ) + + +inline QDebug operator<<( QDebug d, const lastfm::RadioStation& station ) +{ + return d << station.url(); +} + +#endif diff --git a/thirdparty/liblastfm2/src/radio/RadioTuner.cpp b/thirdparty/liblastfm2/src/radio/RadioTuner.cpp new file mode 100644 index 000000000..f09b560e1 --- /dev/null +++ b/thirdparty/liblastfm2/src/radio/RadioTuner.cpp @@ -0,0 +1,152 @@ +/* + Copyright 2009 Last.fm Ltd. + - Primarily authored by Max Howell, Jono Cole and Doug Mansell + + This file is part of liblastfm. + + liblastfm 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. + + liblastfm 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 liblastfm. If not, see . +*/ +#include "RadioTuner.h" +#include "../core/XmlQuery.h" +#include "../types/Xspf.h" +#include "../ws/ws.h" +using namespace lastfm; + +//TODO skips left +//TODO multiple locations for the same track +//TODO set rtp flag in getPlaylist (whether user is scrobbling this radio session or not) + +// limit the number of retries following empty playlists: +#define MAX_TUNING_ATTEMPTS 3 + + +RadioTuner::RadioTuner( const RadioStation& station ) + : m_retry_counter( 0 ) +{ + //Empty RadioStation implies that the radio + //should tune to the previous station. + if( station.url().isEmpty() ) { + fetchFiveMoreTracks(); + return; + } + + QMap map; + map["method"] = "radio.tune"; + map["station"] = station.url(); + map["additional_info"] = "1"; + QNetworkReply* reply = ws::post(map); + connect( reply, SIGNAL(finished()), SLOT(onTuneReturn()) ); +} + +void +RadioTuner::retune( const RadioStation& station) +{ + m_queue.clear(); + + QMap map; + map["method"] = "radio.tune"; + map["station"] = station.url(); + map["additional_info"] = "1"; + QNetworkReply* reply = ws::post(map); + connect( reply, SIGNAL(finished()), SLOT(onTuneReturn()) ); +} + + +void +RadioTuner::onTuneReturn() +{ + try { + XmlQuery lfm = ws::parse( (QNetworkReply*)sender() ); + // TODO: uncomment this is we are to get a radio station + // name when we tune to an rql radio station + //emit title( lfm["station"]["name"].text() ); + + qDebug() << lfm; + + emit supportsDisco( lfm["station"]["supportsdiscovery"].text() == "1" ); + fetchFiveMoreTracks(); + } + catch (ws::ParseError& e) + { + emit error( e.enumValue() ); + } +} + + +bool +RadioTuner::fetchFiveMoreTracks() +{ + //TODO check documentation, I figure this needs a session key + QMap map; + map["method"] = "radio.getPlaylist"; + map["additional_info"] = "1"; + map["rtp"] = "1"; // see above + QNetworkReply* reply = ws::post( map ); + connect( reply, SIGNAL(finished()), SLOT(onGetPlaylistReturn()) ); + return true; +} + + +bool +RadioTuner::tryAgain() +{ + qDebug() << "Bad response count" << m_retry_counter; + + if (++m_retry_counter > MAX_TUNING_ATTEMPTS) + return false; + fetchFiveMoreTracks(); + return true; +} + + +void +RadioTuner::onGetPlaylistReturn() +{ + try { + XmlQuery lfm = ws::parse( (QNetworkReply*)sender() ); + Xspf xspf( lfm["playlist"] ); + QList tracks( xspf.tracks() ); + if (tracks.isEmpty()) { + // give up after too many empty playlists :( + if (!tryAgain()) + emit error( ws::NotEnoughContent ); + } else { + m_retry_counter = 0; + foreach (Track t, tracks) + MutableTrack( t ).setSource( Track::LastFmRadio ); + m_queue += tracks; + emit trackAvailable(); + } + } + catch (ws::ParseError& e) + { + qWarning() << e.what(); + emit error( e.enumValue() ); + } +} + + +Track +RadioTuner::takeNextTrack() +{ + //TODO presumably, we should check if fetchMoreTracks is working? + if (m_queue.isEmpty()) + return Track(); + + Track result = m_queue.takeFirst(); + if (m_queue.isEmpty()) + fetchFiveMoreTracks(); + + return result; +} diff --git a/thirdparty/liblastfm2/src/radio/RadioTuner.h b/thirdparty/liblastfm2/src/radio/RadioTuner.h new file mode 100644 index 000000000..7032dfd08 --- /dev/null +++ b/thirdparty/liblastfm2/src/radio/RadioTuner.h @@ -0,0 +1,76 @@ +/* + Copyright 2009 Last.fm Ltd. + - Primarily authored by Max Howell, Jono Cole and Doug Mansell + + This file is part of liblastfm. + + liblastfm 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. + + liblastfm 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 liblastfm. If not, see . +*/ +#ifndef LASTFM_TUNER_H +#define LASTFM_TUNER_H + +#include +#include +#include +#include + +namespace lastfm +{ + /** With regard to error handling. We handle Ws::TryAgain up to 5 times, + * don't try again after that! Just tell the user to try again later. + */ + class LASTFM_DLLEXPORT RadioTuner : public QObject + { + Q_OBJECT + + public: + /** You need to have assigned Ws::* for this to work, creating the tuner + * automatically fetches the first 5 tracks for the station */ + explicit RadioTuner( const RadioStation& ); + + Track takeNextTrack(); + + void retune( const RadioStation& ); + + signals: + void title( const QString& ); + void supportsDisco( bool supportsDisco ); + void trackAvailable(); + void error( lastfm::ws::Error ); + + private slots: + void onTuneReturn(); + void onGetPlaylistReturn(); + + private: + /** Tries again up to 5 times + * @returns true if we tried again, otherwise you should emit error */ + bool tryAgain(); + /** Will emit 5 tracks from tracks(), they have to played within an hour + * or the streamer will refuse to stream them. Also the previous five are + * invalidated apart from the one that is currently playing, so sorry, you + * can't build up big lists of tracks. + * + * I feel I must point out that asking the user which one they want to play + * is also not allowed according to our terms and conditions, which you + * already agreed to in order to get your API key. Sorry about that dude. + */ + bool fetchFiveMoreTracks(); + + QList m_queue; + uint m_retry_counter; + }; +} + +#endif diff --git a/thirdparty/liblastfm2/src/scrobble/Audioscrobbler.cpp b/thirdparty/liblastfm2/src/scrobble/Audioscrobbler.cpp new file mode 100644 index 000000000..0a98b7994 --- /dev/null +++ b/thirdparty/liblastfm2/src/scrobble/Audioscrobbler.cpp @@ -0,0 +1,203 @@ +/* + Copyright 2009 Last.fm Ltd. + - Primarily authored by Max Howell, Jono Cole and Doug Mansell + + This file is part of liblastfm. + + liblastfm 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. + + liblastfm 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 liblastfm. If not, see . +*/ + +#include + +#include "Audioscrobbler.h" +#include "ScrobbleCache.h" + +#include "../types/User.h" +#include "../types/Track.h" +#include "../ws/ws.h" +#include "../core/XmlQuery.h" + + +namespace lastfm +{ + class AudioscrobblerPrivate + { + public: + AudioscrobblerPrivate(const QString& id) + : m_id( id ) + , m_cache( ws::Username ) + {} + + ~AudioscrobblerPrivate() + { + } + + const QString m_id; + ScrobbleCache m_cache; + QList m_batch; + QPointer m_nowPlayingReply; + QPointer m_scrobbleReply; + Track m_nowPlayingTrack; + }; +} + + +lastfm::Audioscrobbler::Audioscrobbler( const QString& id ) + : d( new AudioscrobblerPrivate(id) ) +{ + submit(); +} + + +lastfm::Audioscrobbler::~Audioscrobbler() +{ + delete d; +} + + +void +lastfm::Audioscrobbler::nowPlaying( const Track& track ) +{ + if ( d->m_nowPlayingReply.isNull()) + { + d->m_nowPlayingTrack = track; + d->m_nowPlayingReply = track.updateNowPlaying(); + connect( d->m_nowPlayingReply, SIGNAL(finished()), SLOT(onNowPlayingReturn())); + } +} + + +void +lastfm::Audioscrobbler::cache( const Track& track ) +{ + QList tracks; + tracks.append( track ); + cacheBatch( tracks ); +} + + +void +lastfm::Audioscrobbler::cacheBatch( const QList& tracks ) +{ + d->m_cache.add( tracks ); + + foreach ( const Track& track, d->m_cache.tracks() ) + MutableTrack( track ).setScrobbleStatus( Track::Cached ); + + emit scrobblesCached( tracks ); + submit(); +} + + +void +lastfm::Audioscrobbler::submit() +{ + if (d->m_cache.tracks().isEmpty() // there are no tracks to submit + || !d->m_scrobbleReply.isNull() ) // we are already submitting scrobbles + return; + + // copy tracks to be submitted to a temporary list + d->m_batch = d->m_cache.tracks().mid( 0, 50 ); + + // if there is only one track use track.scrobble, otherwise use track.scrobbleBatch + if (d->m_batch.count() == 1) + d->m_scrobbleReply = d->m_batch[0].scrobble(); + else + d->m_scrobbleReply = lastfm::Track::scrobble( d->m_batch ); + + connect( d->m_scrobbleReply, SIGNAL(finished()), SLOT(onTrackScrobbleReturn())); +} + +void +lastfm::Audioscrobbler::parseTrack( const XmlQuery& trackXml, const Track& track ) +{ + MutableTrack mTrack = MutableTrack( track ); + bool isScrobble = QDomElement(trackXml).tagName() == "scrobble"; + + if ( trackXml["ignoredMessage"].attribute("code") == "0" ) + { + if ( isScrobble ) mTrack.setScrobbleStatus( Track::Submitted ); + + // corrections! + if ( trackXml["track"].attribute("corrected") == "1" + || trackXml["artist"].attribute("corrected") == "1" + || trackXml["album"].attribute("corrected") == "1" + || trackXml["albumArtist"].attribute("corrected") == "1") + { + mTrack.setCorrections(trackXml["track"].text(), + trackXml["album"].text(), + trackXml["artist"].text(), + trackXml["albumArtist"].text()); + } + } + else if ( isScrobble ) + { + mTrack.setScrobbleError( static_cast(trackXml["ignoredMessage"].attribute("code").toInt()) ); + mTrack.setScrobbleStatus( Track::Error ); + } +} + +void +lastfm::Audioscrobbler::onNowPlayingReturn() +{ + lastfm::XmlQuery lfm = static_cast(sender())->readAll(); + qDebug() << lfm; + + if ( lfm.attribute("status") == "ok" ) + parseTrack( lfm["nowplaying"], d->m_nowPlayingTrack ); + else + emit nowPlayingError( lfm["error"].attribute("code").toInt(), lfm["error"].text() ); + + d->m_nowPlayingTrack = Track(); + d->m_nowPlayingReply = 0; +} + + +void +lastfm::Audioscrobbler::onTrackScrobbleReturn() +{ + lastfm::XmlQuery lfm = d->m_scrobbleReply->readAll(); + qDebug() << lfm; + + if (lfm.attribute("status") == "ok") + { + int index = 0; + + foreach ( const XmlQuery& scrobble, lfm["scrobbles"].children("scrobble") ) + parseTrack( scrobble, d->m_batch.at( index++ ) ); + + d->m_cache.remove( d->m_batch ); + d->m_batch.clear(); + } + else + { + // The scrobble submission failed + + if ( !(lfm["error"].attribute("code") == "9" // Bad session + || lfm["error"].attribute("code") == "11" // Service offline + || lfm["error"].attribute("code") == "16") ) // Service temporarily unavailable + { + // clear the cache if it was not one of these error codes + d->m_cache.remove( d->m_batch ); + d->m_batch.clear(); + } + else + { + qWarning() << "Got error in scrobble submission:" << lfm[ "error" ] << "and silently ignoring. Submission is cached."; + //Q_ASSERT(false); + } + } + + d->m_scrobbleReply = 0; +} diff --git a/thirdparty/liblastfm2/src/scrobble/Audioscrobbler.h b/thirdparty/liblastfm2/src/scrobble/Audioscrobbler.h new file mode 100644 index 000000000..8ad93c026 --- /dev/null +++ b/thirdparty/liblastfm2/src/scrobble/Audioscrobbler.h @@ -0,0 +1,75 @@ +/* + Copyright 2009 Last.fm Ltd. + - Primarily authored by Max Howell, Jono Cole and Doug Mansell + + This file is part of liblastfm. + + liblastfm 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. + + liblastfm 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 liblastfm. If not, see . +*/ +#ifndef LASTFM_AUDIOSCROBBLER_H +#define LASTFM_AUDIOSCROBBLER_H + +#include +#include +#include +#include +#include +#include + +namespace lastfm +{ + /** @author Max Howell + * An implementation of the Audioscrobbler Realtime Submissions Protocol + * version 1.2.1 for a single Last.fm user + * http://www.audioscrobbler.net/development/protocol/ + */ + class LASTFM_DLLEXPORT Audioscrobbler : public QObject + { + Q_OBJECT + + public: + /** You will need to do QCoreApplication::setVersion and + * QCoreApplication::setApplicationName for this to work, also you will + * need to have set all the keys in the Ws namespace in WsKeys.h */ + Audioscrobbler( const QString& clientId ); + ~Audioscrobbler(); + + signals: + void scrobblesCached( const QList& tracks ); + void nowPlayingError( int code, QString message ); + + public slots: + /** will ask Last.fm to update the now playing information for the + * authenticated user */ + void nowPlaying( const Track& ); + /** will cache the track and call submit() */ + void cache( const Track& ); + void cacheBatch( const QList& ); + + /** will submit the submission cache for this user */ + void submit(); + + private slots: + void onNowPlayingReturn(); + void onTrackScrobbleReturn(); + + private: + void parseTrack( const XmlQuery& trackXml, const Track& track ); + + private: + class AudioscrobblerPrivate* d; + }; +} + +#endif diff --git a/thirdparty/liblastfm2/src/scrobble/ScrobbleCache.cpp b/thirdparty/liblastfm2/src/scrobble/ScrobbleCache.cpp new file mode 100644 index 000000000..583d4b4d0 --- /dev/null +++ b/thirdparty/liblastfm2/src/scrobble/ScrobbleCache.cpp @@ -0,0 +1,163 @@ +/* + Copyright 2009 Last.fm Ltd. + - Primarily authored by Max Howell, Jono Cole and Doug Mansell + + This file is part of liblastfm. + + liblastfm 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. + + liblastfm 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 liblastfm. If not, see . +*/ +#include "ScrobbleCache.h" +#include "ScrobblePoint.h" +#include +#include +#include +#include +#include + +#if LASTFM_VERSION >= 0x00010000 +using lastfm::ScrobbleCache; +#endif + +ScrobbleCache::ScrobbleCache( const QString& username ) +{ + Q_ASSERT( username.length() ); + + m_path = lastfm::dir::runtimeData().filePath( username + "_subs_cache.xml" ); + m_username = username; + + QDomDocument xml; + read( xml ); +} + + +bool +ScrobbleCache::isValid( const Track& track, Invalidity* v ) +{ + #define TEST( test, x ) \ + if (test) { \ + if (v) *v = x; \ + return false; \ + } + + TEST( track.duration() < ScrobblePoint::kScrobbleMinLength, TooShort ); + + TEST( !track.timestamp().isValid(), NoTimestamp ); + + // actual spam prevention is something like 12 hours, but we are only + // trying to weed out obviously bad data, server side criteria for + // "the future" may change, so we should let the server decide, not us + TEST( track.timestamp() > QDateTime::currentDateTime().addMonths( 1 ), FromTheFuture ); + + TEST( track.timestamp() < QDateTime::fromString( "2003-01-01", Qt::ISODate ), FromTheDistantPast ); + + // Check if any required fields are empty + TEST( track.artist().isNull(), ArtistNameMissing ); + TEST( track.title().isEmpty(), TrackNameMissing ); + + TEST( (QStringList() << "unknown artist" + << "unknown" + << "[unknown]" + << "[unknown artist]").contains( track.artist().name().toLower() ), + ArtistInvalid ); + + return true; +} + + +void +ScrobbleCache::read( QDomDocument& xml ) +{ + m_tracks.clear(); + + QFile file( m_path ); + file.open( QFile::Text | QFile::ReadOnly ); + QTextStream stream( &file ); + stream.setCodec( "UTF-8" ); + + xml.setContent( stream.readAll() ); + + for (QDomNode n = xml.documentElement().firstChild(); !n.isNull(); n = n.nextSibling()) + if (n.nodeName() == "track") + m_tracks += Track( n.toElement() ); +} + + +void +ScrobbleCache::write() +{ + if (m_tracks.isEmpty()) + { + QFile::remove( m_path ); + } + else { + QDomDocument xml; + QDomElement e = xml.createElement( "submissions" ); + e.setAttribute( "product", QCoreApplication::applicationName() ); + e.setAttribute( "version", "2" ); + + foreach (Track i, m_tracks) + e.appendChild( i.toDomElement( xml ) ); + + xml.appendChild( e ); + + QFile file( m_path ); + file.open( QIODevice::WriteOnly | QIODevice::Text ); + + QTextStream stream( &file ); + stream.setCodec( "UTF-8" ); + stream << "\n"; + stream << xml.toString( 2 ); + file.close(); + } +} + + +void +ScrobbleCache::add( const QList& tracks ) +{ + foreach (const Track& track, tracks) + { + ScrobbleCache::Invalidity invalidity; + + if ( !isValid( track, &invalidity ) ) + { + qWarning() << invalidity; + } + else if (track.isNull()) + qDebug() << "Will not cache an empty track"; + else + m_tracks += track; + } + + write(); +} + + +int +ScrobbleCache::remove( const QList& toremove ) +{ + QMutableListIterator i( m_tracks ); + while (i.hasNext()) { + Track t = i.next(); + for (int x = 0; x < toremove.count(); ++x) + if (toremove[x] == t) + i.remove(); + } + + write(); + + // yes we return # remaining, rather # removed, but this is an internal + // function and the behaviour is documented so it's alright imo --mxcl + return m_tracks.count(); +} diff --git a/thirdparty/liblastfm2/src/scrobble/ScrobbleCache.h b/thirdparty/liblastfm2/src/scrobble/ScrobbleCache.h new file mode 100644 index 000000000..31219263b --- /dev/null +++ b/thirdparty/liblastfm2/src/scrobble/ScrobbleCache.h @@ -0,0 +1,84 @@ +/* + Copyright 2009 Last.fm Ltd. + - Primarily authored by Max Howell, Jono Cole and Doug Mansell + + This file is part of liblastfm. + + liblastfm 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. + + liblastfm 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 liblastfm. If not, see . +*/ +#ifndef LASTFM_SCROBBLE_CACHE_H +#define LASTFM_SCROBBLE_CACHE_H + +#include "lastfm/Track" +#include +#include + +#if LASTFM_VERSION >= 0x00010000 +namespace lastfm { +#else +using lastfm::Track; +#endif + +/** absolutely not thread-safe */ +class LASTFM_DLLEXPORT ScrobbleCache +{ + QString m_username; + + void write(); /// writes m_tracks to m_path + +protected: + ScrobbleCache() + {} + + QString m_path; + QList m_tracks; + + void read( QDomDocument& xml ); /// reads from m_path into m_tracks + +public: + explicit ScrobbleCache( const QString& username ); + + /** note this is unique for Track::sameAs() and equal timestamps + * obviously playcounts will not be increased for the same timestamp */ + void add( const QList& ); + + /** returns the number of tracks left in the queue */ + int remove( const QList& ); + + QList tracks() const { return m_tracks; } + QString path() const { return m_path; } + QString username() const { return m_username; } + +private: + bool operator==( const ScrobbleCache& ); //undefined + + enum Invalidity + { + TooShort, + ArtistNameMissing, + TrackNameMissing, + ArtistInvalid, + NoTimestamp, + FromTheFuture, + FromTheDistantPast + }; + + bool isValid( const Track& track, Invalidity* = 0 ); +}; + +#if LASTFM_VERSION >= 0x00010000 +} +#endif + +#endif diff --git a/thirdparty/liblastfm2/src/scrobble/ScrobblePoint.h b/thirdparty/liblastfm2/src/scrobble/ScrobblePoint.h new file mode 100644 index 000000000..fce857d46 --- /dev/null +++ b/thirdparty/liblastfm2/src/scrobble/ScrobblePoint.h @@ -0,0 +1,59 @@ +/* + Copyright 2009 Last.fm Ltd. + - Primarily authored by Max Howell, Jono Cole and Doug Mansell + + This file is part of liblastfm. + + liblastfm 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. + + liblastfm 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 liblastfm. If not, see . +*/ +#ifndef LASTFM_SCROBBLE_POINT_H +#define LASTFM_SCROBBLE_POINT_H + +#include +#include + + +class LASTFM_DLLEXPORT ScrobblePoint +{ + uint i; + +public: + ScrobblePoint() : i( kScrobbleTimeMax ) + {} + + /** j is in seconds, and should be 50% the duration of a track */ + explicit ScrobblePoint( uint j ) + { + // we special case 0, returning kScrobbleTimeMax because we are + // cruel and callous people + if (j == 0) --j; + + i = qBound( uint(kScrobbleMinLength), + j, + uint(kScrobbleTimeMax) ); + } + operator uint() const { return i; } + + // scrobbles can occur between these two percentages of track duration + static const uint kScrobblePointMin = 50; + static const uint kScrobblePointMax = 100; + static const uint kDefaultScrobblePoint = 50; + + // Shortest track length allowed to scrobble in seconds + static const uint kScrobbleMinLength = 31; + // Upper limit for scrobble time in seconds + static const uint kScrobbleTimeMax = 240; +}; + +#endif diff --git a/thirdparty/liblastfm2/src/types/AbstractType.h b/thirdparty/liblastfm2/src/types/AbstractType.h new file mode 100755 index 000000000..873b5dec9 --- /dev/null +++ b/thirdparty/liblastfm2/src/types/AbstractType.h @@ -0,0 +1,43 @@ +/* + Copyright 2010 Last.fm Ltd. + - Primarily authored by Micahel Coffey and Jono Cole + + This file is part of liblastfm. + + liblastfm 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. + + liblastfm 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 liblastfm. If not, see . +*/ + +#ifndef ABSTRACTTYPE_H +#define ABSTRACTTYPE_H + +#include +#include +#include + +#include + +namespace lastfm +{ + class LASTFM_DLLEXPORT AbstractType + { + public: + virtual QString toString() const = 0; + virtual QDomElement toDomElement( QDomDocument& ) const = 0; + virtual QUrl www() const = 0; + virtual QUrl imageUrl( ImageSize size, bool square ) const = 0; + virtual ~AbstractType() {;} + }; +}; + +#endif // ABSTRACTTYPE_H diff --git a/thirdparty/liblastfm2/src/types/Album.cpp b/thirdparty/liblastfm2/src/types/Album.cpp new file mode 100644 index 000000000..fce542911 --- /dev/null +++ b/thirdparty/liblastfm2/src/types/Album.cpp @@ -0,0 +1,87 @@ +/* + Copyright 2009-2010 Last.fm Ltd. + - Primarily authored by Max Howell, Jono Cole and Doug Mansell + + This file is part of liblastfm. + + liblastfm 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. + + liblastfm 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 liblastfm. If not, see . +*/ +#include "Album.h" +#include "Artist.h" +#include "User.h" +#include "../core/UrlBuilder.h" +#include "../core/XmlQuery.h" +#include "../ws/ws.h" +#include +#include +#include + +QNetworkReply* +lastfm::Album::getInfo(const QString& user, const QString& sk) const +{ + QMap map; + map["method"] = "album.getInfo"; + map["artist"] = m_artist; + map["album"] = m_title; + if (!user.isEmpty()) map["username"] = user; + if (!sk.isEmpty()) map["sk"] = sk; + return lastfm::ws::get(map); +} + + +QNetworkReply* +lastfm::Album::getTags() const +{ + QMap map; + map["method"] = "album.getTags"; + map["artist"] = m_artist; + map["album"] = m_title; + return lastfm::ws::get(map); +} + + +QNetworkReply* +lastfm::Album::share( const QStringList& recipients, const QString& message, bool isPublic ) const +{ + QMap map; + map["method"] = "album.share"; + map["artist"] = m_artist; + map["album"] = m_title; + map["recipient"] = recipients.join(","); + map["public"] = isPublic ? "1" : "0"; + if (message.size()) map["message"] = message; + return lastfm::ws::post(map); +} + + +QUrl +lastfm::Album::www() const +{ + return lastfm::UrlBuilder( "music" ).slash( m_artist ).slash( m_title ).url(); +} + + +QNetworkReply* +lastfm::Album::addTags( const QStringList& tags ) const +{ + if (tags.isEmpty()) + return 0; + + QMap map; + map["method"] = "album.addTags"; + map["artist"] = m_artist; + map["album"] = m_title; + map["tags"] = tags.join( QChar(',') ); + return lastfm::ws::post(map); +} diff --git a/thirdparty/liblastfm2/src/types/Album.h b/thirdparty/liblastfm2/src/types/Album.h new file mode 100644 index 000000000..afbd52e05 --- /dev/null +++ b/thirdparty/liblastfm2/src/types/Album.h @@ -0,0 +1,73 @@ +/* + Copyright 2009 Last.fm Ltd. + - Primarily authored by Max Howell, Jono Cole and Doug Mansell + + This file is part of liblastfm. + + liblastfm 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. + + liblastfm 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 liblastfm. If not, see . +*/ +#ifndef LASTFM_ALBUM_H +#define LASTFM_ALBUM_H + +#include +#include +#include +#include + +namespace lastfm +{ + class LASTFM_DLLEXPORT Album + { + Mbid m_mbid; + Artist m_artist; + QString m_title; + + public: + Album() + {} + + explicit Album( Mbid mbid ) : m_mbid( mbid ) + {} + + Album( Artist artist, QString title ) : m_artist( artist ), m_title( title ) + {} + + bool operator==( const Album& that ) const { return m_title == that.m_title && m_artist == that.m_artist; } + bool operator!=( const Album& that ) const { return m_title != that.m_title || m_artist != that.m_artist; } + + operator QString() const { return title(); } + QString title() const { return m_title.isEmpty() ? "[unknown]" : m_title; } + Artist artist() const { return m_artist; } + Mbid mbid() const { return m_mbid; } + + /** artist may have been set, since we allow that in the ctor, but should we handle untitled albums? */ + bool isNull() const { return m_title.isEmpty() && m_mbid.isNull(); } + + /** Album.getInfo WebService */ + QNetworkReply* getInfo(const QString& user = "", const QString& sk = "") const; + QNetworkReply* share( const QStringList& recipients, const QString& message = "", bool isPublic = true ) const; + + /** use Tag::list to get the tag list out of the finished reply */ + QNetworkReply* getTags() const; + QNetworkReply* getTopTags() const; + + /** Last.fm dictates that you may submit at most 10 of these */ + QNetworkReply* addTags( const QStringList& ) const; + + /** the Last.fm website url for this album */ + QUrl www() const; + }; +} + +#endif //LASTFM_ALBUM_H diff --git a/thirdparty/liblastfm2/src/types/Artist.cpp b/thirdparty/liblastfm2/src/types/Artist.cpp new file mode 100644 index 000000000..242834e83 --- /dev/null +++ b/thirdparty/liblastfm2/src/types/Artist.cpp @@ -0,0 +1,202 @@ +/* + Copyright 2009 Last.fm Ltd. + - Primarily authored by Max Howell, Jono Cole and Doug Mansell + + This file is part of liblastfm. + + liblastfm 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. + + liblastfm 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 liblastfm. If not, see . +*/ +#include "Artist.h" +#include "User.h" +#include "../core/UrlBuilder.h" +#include "../core/XmlQuery.h" +#include "../ws/ws.h" +#include +#include + +using lastfm::Artist; +using lastfm::User; +using lastfm::ImageSize; +using lastfm::XmlQuery; + +QUrl +Artist::imageUrl( ImageSize size, bool square ) const +{ + if( !square ) return m_images.value( size ); + + QUrl url = m_images.value( size ); + QRegExp re( "/serve/(\\d*)s?/" ); + return QUrl( url.toString().replace( re, "/serve/\\1s/" )); +} + +static inline QList images( const lastfm::XmlQuery& e ) +{ + QList images; + images += e["image size=small"].text(); + images += e["image size=medium"].text(); + images += e["image size=large"].text(); + return images; +} + + +Artist::Artist( const XmlQuery& xml ) + :AbstractType() +{ + m_name = xml["name"].text(); + m_images = images( xml ); +} + + +QMap //private +Artist::params( const QString& method ) const +{ + QMap map; + map["method"] = "artist."+method; + map["artist"] = m_name; + return map; +} + + +QNetworkReply* +Artist::share( const QStringList& recipients, const QString& message, bool isPublic ) const +{ + QMap map = params("share"); + map["recipient"] = recipients.join(","); + map["public"] = isPublic ? "1" : "0"; + if (message.size()) map["message"] = message; + return lastfm::ws::post(map); +} + + +QUrl +Artist::www() const +{ + return UrlBuilder( "music" ).slash( Artist::name() ).url(); +} + +QNetworkReply* +Artist::getEvents(int limit) const +{ + QMap map = params("getEvents"); + if (limit) map["limit"] = QString::number(limit); + return ws::get( map ); +} + +QNetworkReply* +Artist::getInfo(const QString& user, const QString& sk) const +{ + QMap map = params("getInfo"); + if (!user.isEmpty()) map["username"] = user; + if (!sk.isEmpty()) map["sk"] = sk; + return ws::get( map ); +} + + +QNetworkReply* +Artist::getTags() const +{ + return ws::get( params("getTags") ); +} + +QNetworkReply* +Artist::getTopTags() const +{ + return ws::get( params("getTopTags") ); +} + + +QNetworkReply* +Artist::getSimilar() const +{ + return ws::get( params("getSimilar") ); +} + + +QNetworkReply* +Artist::search( int limit ) const +{ + QMap map = params("search"); + if (limit > 0) map["limit"] = QString::number(limit); + return ws::get(map); +} + + +QMap /* static */ +Artist::getSimilar( QNetworkReply* r ) +{ + QMap artists; + try + { + XmlQuery lfm = ws::parse(r); + foreach (XmlQuery e, lfm.children( "artist" )) + { + // convert floating percentage to int in range 0 to 10,000 + int const match = e["match"].text().toFloat() * 100; + artists.insertMulti( match, e["name"].text() ); + } + } + catch (ws::ParseError& e) + { + qWarning() << e.what(); + } + return artists; +} + + + +QList /* static */ +Artist::list( QNetworkReply* r ) +{ + QList artists; + try { + XmlQuery lfm = ws::parse(r); + foreach (XmlQuery xq, lfm.children( "artist" )) { + Artist artist( xq ); + artists += artist; + } + } + catch (ws::ParseError& e) + { + qWarning() << e.what(); + } + return artists; +} + + +Artist +Artist::getInfo( QNetworkReply* r ) +{ + try { + XmlQuery lfm = ws::parse(r); + Artist artist = lfm["artist"]["name"].text(); + artist.m_images = images( lfm["artist"] ); + return artist; + } + catch (ws::ParseError& e) + { + qWarning() << e.what(); + return Artist(); + } +} + + +QNetworkReply* +Artist::addTags( const QStringList& tags ) const +{ + if (tags.isEmpty()) + return 0; + QMap map = params("addTags"); + map["tags"] = tags.join( QChar(',') ); + return ws::post(map); +} diff --git a/thirdparty/liblastfm2/src/types/Artist.h b/thirdparty/liblastfm2/src/types/Artist.h new file mode 100644 index 000000000..2bfd6d007 --- /dev/null +++ b/thirdparty/liblastfm2/src/types/Artist.h @@ -0,0 +1,100 @@ +/* + Copyright 2009-2010 Last.fm Ltd. + - Primarily authored by Max Howell, Jono Cole, Doug Mansell and Michael Coffey + + This file is part of liblastfm. + + liblastfm 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. + + liblastfm 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 liblastfm. If not, see . +*/ +#ifndef LASTFM_ARTIST_H +#define LASTFM_ARTIST_H + +#include +#include +#include + +#include +#include + + +namespace lastfm +{ + class LASTFM_DLLEXPORT Artist : public AbstractType + { + private: + QString m_name; + QList m_images; + + public: + Artist() : AbstractType() + {} + + Artist( const QString& name ) : AbstractType(), m_name( name ) + {} + + Artist( const class XmlQuery& xml ); + + /** will be QUrl() unless you got this back from a getInfo or something call */ + QUrl imageUrl( ImageSize size = Large, bool square = false ) const; + + bool isNull() const { return m_name.isEmpty(); } + + /** the url for this artist's page at www.last.fm */ + QUrl www() const; + + bool operator==( const Artist& that ) const { return m_name == that.m_name; } + bool operator!=( const Artist& that ) const { return m_name != that.m_name; } + + operator QString() const + { + /** if no artist name is set, return the musicbrainz unknown identifier + * in case some part of the GUI tries to display it anyway. Note isNull + * returns false still. So you should have queried that! */ + return m_name.isEmpty() ? "[unknown]" : m_name; + } + + QString toString() const { return name(); } + QString name() const { return QString(*this); } + + QDomElement toDomElement( QDomDocument& ) const { return QDomElement(); } + + QNetworkReply* share( const QStringList& recipients, const QString& message = "", bool isPublic = true ) const; + + QNetworkReply* getEvents(int limit = 0) const; + QNetworkReply* getInfo(const QString& user = "", const QString& sk = "") const; + static Artist getInfo( QNetworkReply* ); + + QNetworkReply* getSimilar() const; + /** The match percentage is returned from last.fm as a 4 significant + * figure floating point value. So we multply it by 100 to make an + * integer in the range of 0 to 10,000. This is possible confusing + * for you, but I felt it best not to lose any precision, and floats + * aren't much fun. */ + static QMap getSimilar( QNetworkReply* ); + + /** use Tag::list to get the tag list out of the finished reply */ + QNetworkReply* getTags() const; + QNetworkReply* getTopTags() const; + + /** Last.fm dictates that you may submit at most 10 of these */ + QNetworkReply* addTags( const QStringList& ) const; + + QNetworkReply* search( int limit = -1 ) const; + static QList list( QNetworkReply* ); + + QMap params( const QString& method ) const; + }; +} + +#endif diff --git a/thirdparty/liblastfm2/src/types/FingerprintId.cpp b/thirdparty/liblastfm2/src/types/FingerprintId.cpp new file mode 100644 index 000000000..b201534a0 --- /dev/null +++ b/thirdparty/liblastfm2/src/types/FingerprintId.cpp @@ -0,0 +1,55 @@ +/* + Copyright 2009 Last.fm Ltd. + - Primarily authored by Max Howell, Jono Cole and Doug Mansell + + This file is part of liblastfm. + + liblastfm 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. + + liblastfm 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 liblastfm. If not, see . +*/ +#include "FingerprintId.h" +#include "../ws/ws.h" +#include +#include + + +QNetworkReply* +lastfm::FingerprintId::getSuggestions() const +{ + if (isNull()) return 0; + + QUrl const url( "http://ws.audioscrobbler.com/fingerprint/" + QString(*this) + ".xml" ); + QNetworkRequest const request( url ); + return lastfm::nam()->get( request ); +} + + +QMap //static +lastfm::FingerprintId::getSuggestions( QNetworkReply* reply ) +{ + QDomDocument xml; + xml.setContent( reply->readAll() ); + QDomNodeList nodes = xml.documentElement().elementsByTagName( "track" ); + + QMap tracks; + for (int x = 0; x < nodes.count(); ++x) + { + QDomElement const e = nodes.at(x).toElement(); + + MutableTrack t; + t.setTitle( e.firstChildElement( "title" ).text() ); + t.setArtist( e.firstChildElement( "artist" ).text() ); + tracks.insert( e.attribute( "confidence", "0.0" ).toFloat(), t ); + } + return tracks; +} diff --git a/thirdparty/liblastfm2/src/types/FingerprintId.h b/thirdparty/liblastfm2/src/types/FingerprintId.h new file mode 100644 index 000000000..5028c7736 --- /dev/null +++ b/thirdparty/liblastfm2/src/types/FingerprintId.h @@ -0,0 +1,62 @@ +/* + Copyright 2009 Last.fm Ltd. + - Primarily authored by Max Howell, Jono Cole and Doug Mansell + + This file is part of liblastfm. + + liblastfm 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. + + liblastfm 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 liblastfm. If not, see . +*/ +#ifndef LASTFM_FINGERPRINT_ID_H +#define LASTFM_FINGERPRINT_ID_H + +#include +#include + +namespace lastfm +{ + class LASTFM_DLLEXPORT FingerprintId + { + int id; + + public: + FingerprintId() : id( -1 ) + {} + + FingerprintId( uint i ) : id( i ) + {} + + bool isNull() const { return id == -1; } + + /** we query Last.fm for suggested metadata, how awesome is that? + * @returns null if isNull() */ + QNetworkReply* getSuggestions() const; + static QMap getSuggestions( QNetworkReply* ); + + /** -1 if you need to generate it */ + operator int() const { return id; } + /** isEmpty() if you need to generate it */ + operator QString() const { return id == -1 ? "" : QString::number( id ); } + }; +} + + +inline QDebug operator<<( QDebug d, lastfm::FingerprintId id) +{ + if (id.isNull()) + return d << "(null)"; + else + return d << int(id); +} + +#endif diff --git a/thirdparty/liblastfm2/src/types/Mbid.cpp b/thirdparty/liblastfm2/src/types/Mbid.cpp new file mode 100644 index 000000000..1ce1d90c5 --- /dev/null +++ b/thirdparty/liblastfm2/src/types/Mbid.cpp @@ -0,0 +1,36 @@ +/* + Copyright 2009 Last.fm Ltd. + - Primarily authored by Max Howell, Jono Cole and Doug Mansell + + This file is part of liblastfm. + + liblastfm 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. + + liblastfm 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 liblastfm. If not, see . +*/ +#include "Mbid.h" +#include "mbid_mp3.c" +#include + +namespace lastfm +{ + Mbid //static + Mbid::fromLocalFile( const QString& path ) + { + char out[MBID_BUFFER_SIZE]; + QByteArray const bytes = QFile::encodeName( path ); + int const r = getMP3_MBID( bytes.data(), out ); + Mbid mbid; + if (r == 0) mbid.id = QString::fromLatin1( out ); + return mbid; + } +} diff --git a/thirdparty/liblastfm2/src/types/Mbid.h b/thirdparty/liblastfm2/src/types/Mbid.h new file mode 100644 index 000000000..071d4d1fc --- /dev/null +++ b/thirdparty/liblastfm2/src/types/Mbid.h @@ -0,0 +1,45 @@ +/* + Copyright 2009 Last.fm Ltd. + - Primarily authored by Max Howell, Jono Cole and Doug Mansell + + This file is part of liblastfm. + + liblastfm 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. + + liblastfm 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 liblastfm. If not, see . +*/ +#ifndef LASTFM_MBID_H +#define LASTFM_MBID_H + +#include +#include + +namespace lastfm +{ + class LASTFM_DLLEXPORT Mbid + { + QString id; + + public: + explicit Mbid( const QString& p = "" ) : id( p ) + {} + + bool isNull() const { return id.isNull() || id.isEmpty(); } + operator QString() const { return id; } + + /** if this is not an mp3 file you will be wasting time, as it won't work + * but we will do what you say anyway because you are the boss */ + static Mbid fromLocalFile( const QString& path ); + }; +} + +#endif diff --git a/thirdparty/liblastfm2/src/types/Playlist.cpp b/thirdparty/liblastfm2/src/types/Playlist.cpp new file mode 100644 index 000000000..807da1c6a --- /dev/null +++ b/thirdparty/liblastfm2/src/types/Playlist.cpp @@ -0,0 +1,63 @@ +/* + Copyright 2009 Last.fm Ltd. + - Primarily authored by Max Howell, Jono Cole and Doug Mansell + + This file is part of liblastfm. + + liblastfm 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. + + liblastfm 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 liblastfm. If not, see . +*/ +#include "Playlist.h" +#include "Track.h" +#include "../ws/ws.h" + + +QNetworkReply* +lastfm::Playlist::addTrack( const Track& t ) const +{ + QMap map; + map["method"] = "playlist.addTrack"; + map["playlistID"] = m_id; + map["artist"] = t.artist(); + map["track"] = t.title(); + return lastfm::ws::post(map); +} + + +QNetworkReply* +lastfm::Playlist::fetch() const +{ + return fetch( QUrl("lastfm://playlist/" + QString::number( m_id )) ); +} + + +QNetworkReply* //static +lastfm::Playlist::fetch( const QUrl& url ) +{ + QMap map; + map["method"] = "playlist.fetch"; + map["playlistURL"] = url.toString(); + return lastfm::ws::get(map); +} + + +QNetworkReply* //static +lastfm::Playlist::create( const QString& title, const QString& description /*=""*/ ) +{ + QMap map; + map["method"] = "playlist.create"; + map["title"] = title; + if (description.size()) + map["description"] = description; + return lastfm::ws::post(map); +} diff --git a/thirdparty/liblastfm2/src/types/Playlist.h b/thirdparty/liblastfm2/src/types/Playlist.h new file mode 100644 index 000000000..a271e7d5b --- /dev/null +++ b/thirdparty/liblastfm2/src/types/Playlist.h @@ -0,0 +1,53 @@ +/* + Copyright 2009 Last.fm Ltd. + - Primarily authored by Max Howell, Jono Cole and Doug Mansell + + This file is part of liblastfm. + + liblastfm 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. + + liblastfm 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 liblastfm. If not, see . +*/ +#ifndef LASTFM_PLAYLIST_H +#define LASTFM_PLAYLIST_H + +#include +#include +#include +#include + +namespace lastfm +{ + class LASTFM_DLLEXPORT Playlist + { + int m_id; + + Playlist() : m_id( -1 ) + {} + + public: + Playlist( int id ) : m_id( id ) + {} + + int id() const { return m_id; } + + QNetworkReply* addTrack( const Track& ) const; + QNetworkReply* fetch() const; + + static QNetworkReply* create( const QString& title, const QString& description = "" ); + static QNetworkReply* fetch( const QUrl& url ); + + static Xspf fetch( QNetworkReply* ); + }; +} + +#endif diff --git a/thirdparty/liblastfm2/src/types/Tag.cpp b/thirdparty/liblastfm2/src/types/Tag.cpp new file mode 100644 index 000000000..40ddfb43b --- /dev/null +++ b/thirdparty/liblastfm2/src/types/Tag.cpp @@ -0,0 +1,77 @@ +/* + Copyright 2009 Last.fm Ltd. + - Primarily authored by Max Howell, Jono Cole and Doug Mansell + + This file is part of liblastfm. + + liblastfm 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. + + liblastfm 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 liblastfm. If not, see . +*/ +#include "Tag.h" +#include "User.h" +#include "../core/UrlBuilder.h" +#include "../core/XmlQuery.h" +#include "../ws/ws.h" +using lastfm::Tag; +using lastfm::User; + + +QUrl +Tag::www() const +{ + return UrlBuilder( "tag" ).slash( m_name ).url(); +} + + +QUrl +Tag::www( const User& user ) const +{ + return UrlBuilder( "user" ).slash( user.name() ).slash( "tags" ).slash( Tag::name() ).url(); +} + + +QNetworkReply* +Tag::search() const +{ + QMap map; + map["method"] = "tag.search"; + map["tag"] = m_name; + return ws::get(map); +} + +//static +QNetworkReply* +Tag::getTopTags() +{ + QMap map; + map["method"] = "tag.getTopTags"; + return ws::get(map); +} + +QMap //static +Tag::list( QNetworkReply* r ) +{ + QMap tags; + try { + foreach (XmlQuery xq, XmlQuery(ws::parse(r)).children("tag")) + // we toLower always as otherwise it is ugly mixed case, as first + // ever tag decides case, and Last.fm is case insensitive about it + // anyway + tags.insertMulti( xq["count"].text().toInt(), xq["name"].text().toLower() ); + } + catch (ws::ParseError& e) + { + qWarning() << e.what(); + } + return tags; +} diff --git a/thirdparty/liblastfm2/src/types/Tag.h b/thirdparty/liblastfm2/src/types/Tag.h new file mode 100644 index 000000000..bf4d5cfca --- /dev/null +++ b/thirdparty/liblastfm2/src/types/Tag.h @@ -0,0 +1,60 @@ +/* + Copyright 2009 Last.fm Ltd. + - Primarily authored by Max Howell, Jono Cole and Doug Mansell + + This file is part of liblastfm. + + liblastfm 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. + + liblastfm 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 liblastfm. If not, see . +*/ +#ifndef LASTFM_TAG_H +#define LASTFM_TAG_H + +#include +#include +#include +#include + +namespace lastfm +{ + class LASTFM_DLLEXPORT Tag + { + QString m_name; + + public: + Tag( const QString& name ) : m_name( name ) + {} + + operator QString() const { return m_name; } + QString name() const { return m_name; } + + /** the global tag page at www.last.fm */ + QUrl www() const; + /** the tag page for user @p user at www.last.fm */ + QUrl www( const class User& user ) const; + /** pass the finished QNetworkReply to Tag::list() */ + class QNetworkReply* search() const; + + /** the top global tags on Last.fm, sorted by popularity (number of times used) */ + static class QNetworkReply* getTopTags(); + + /** the integer is the weighting, not all list type return requests + * have a weighting, so the int may just be zero, if you don't care + * about the weight just do this: + * QStringList tags = Tag::list( reply ).values(); + */ + static QMap list( QNetworkReply* ); + }; +} + +#endif diff --git a/thirdparty/liblastfm2/src/types/Track.cpp b/thirdparty/liblastfm2/src/types/Track.cpp new file mode 100644 index 000000000..cececee1b --- /dev/null +++ b/thirdparty/liblastfm2/src/types/Track.cpp @@ -0,0 +1,481 @@ +/* + Copyright 2009 Last.fm Ltd. + - Primarily authored by Max Howell, Jono Cole and Doug Mansell + + This file is part of liblastfm. + + liblastfm 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. + + liblastfm 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 liblastfm. If not, see . +*/ +#include "Track.h" +#include "User.h" +#include "../core/UrlBuilder.h" +#include "../core/XmlQuery.h" +#include "../ws/ws.h" +#include +#include + + +lastfm::TrackData::TrackData() + : trackNumber( 0 ), + duration( 0 ), + source( Track::Unknown ), + rating( 0 ), + fpid( -1 ), + loved( false ), + null( false ), + scrobbleStatus( Track::Null ), + scrobbleError( Track::None ) +{} + +lastfm::Track::Track() + :AbstractType() +{ + d = new TrackData; + d->null = true; +} + +lastfm::Track::Track( const QDomElement& e ) + :AbstractType() +{ + d = new TrackData; + + if (e.isNull()) { d->null = true; return; } + + d->artist = e.namedItem( "artist" ).toElement().text(); + d->albumArtist = e.namedItem( "albumArtist" ).toElement().text(); + d->album = e.namedItem( "album" ).toElement().text(); + d->title = e.namedItem( "track" ).toElement().text(); + d->correctedArtist = e.namedItem( "correctedArtist" ).toElement().text(); + d->correctedAlbumArtist = e.namedItem( "correctedAlbumArtist" ).toElement().text(); + d->correctedAlbum = e.namedItem( "correctedAlbum" ).toElement().text(); + d->correctedTitle = e.namedItem( "correctedTrack" ).toElement().text(); + d->trackNumber = 0; + d->duration = e.namedItem( "duration" ).toElement().text().toInt(); + d->url = e.namedItem( "url" ).toElement().text(); + d->rating = e.namedItem( "rating" ).toElement().text().toUInt(); + d->source = e.namedItem( "source" ).toElement().text().toInt(); //defaults to 0, or lastfm::Track::Unknown + d->time = QDateTime::fromTime_t( e.namedItem( "timestamp" ).toElement().text().toUInt() ); + d->loved = e.namedItem( "loved" ).toElement().text().toInt(); + d->scrobbleStatus = e.namedItem( "scrobbleStatus" ).toElement().text().toInt(); + d->scrobbleError = e.namedItem( "scrobbleError" ).toElement().text().toInt(); + + for (QDomElement image(e.firstChildElement("image")) ; !image.isNull() ; image = e.nextSiblingElement("image")) + { + d->m_images[static_cast(image.attribute("size").toInt())] = image.text(); + } + + QDomNodeList nodes = e.namedItem( "extras" ).childNodes(); + for (int i = 0; i < nodes.count(); ++i) + { + QDomNode n = nodes.at(i); + QString key = n.nodeName(); + d->extras[key] = n.toElement().text(); + } +} + +void +lastfm::TrackData::onLoveFinished() +{ + XmlQuery lfm = static_cast(sender())->readAll(); + if ( lfm.attribute( "status" ) == "ok") + loved = true; + emit loveToggled( loved ); +} + + +void +lastfm::TrackData::onUnloveFinished() +{ + XmlQuery lfm = static_cast(sender())->readAll(); + if ( lfm.attribute( "status" ) == "ok") + loved = false; + emit loveToggled( loved ); +} + +void +lastfm::TrackData::onGotInfo() +{ + lastfm::XmlQuery lfm( static_cast(sender())->readAll() ); + + QString imageUrl = lfm["track"]["image size=small"].text(); + if ( !imageUrl.isEmpty() ) m_images[lastfm::Small] = imageUrl; + imageUrl = lfm["track"]["image size=medium"].text(); + if ( !imageUrl.isEmpty() ) m_images[lastfm::Medium] = imageUrl; + imageUrl = lfm["track"]["image size=large"].text(); + if ( !imageUrl.isEmpty() ) m_images[lastfm::Large] = imageUrl; + imageUrl = lfm["track"]["image size=extralarge"].text(); + if ( !imageUrl.isEmpty() ) m_images[lastfm::ExtraLarge] = imageUrl; + imageUrl = lfm["track"]["image size=mega"].text(); + if ( !imageUrl.isEmpty() ) m_images[lastfm::Mega] = imageUrl; + + loved = lfm["track"]["userloved"].text().toInt(); + + emit gotInfo( lfm ); + emit loveToggled( loved ); + + // you should connect everytime you call getInfo + disconnect( this, SIGNAL(gotInfo(const XmlQuery&)), 0, 0); +} + + +QDomElement +lastfm::Track::toDomElement( QDomDocument& xml ) const +{ + QDomElement item = xml.createElement( "track" ); + + #define makeElement( tagname, getter ) { \ + QString v = getter; \ + if (!v.isEmpty()) \ + { \ + QDomElement e = xml.createElement( tagname ); \ + e.appendChild( xml.createTextNode( v ) ); \ + item.appendChild( e ); \ + } \ + } + + makeElement( "artist", d->artist ); + makeElement( "albumArtist", d->albumArtist ); + makeElement( "album", d->album ); + makeElement( "track", d->title ); + makeElement( "correctedArtist", d->correctedArtist ); + makeElement( "correctedAlbumArtist", d->correctedAlbumArtist ); + makeElement( "correctedAlbum", d->correctedAlbum ); + makeElement( "correctedTrack", d->correctedTitle ); + makeElement( "duration", QString::number( d->duration ) ); + makeElement( "timestamp", QString::number( d->time.toTime_t() ) ); + makeElement( "url", d->url.toString() ); + makeElement( "source", QString::number( d->source ) ); + makeElement( "rating", QString::number(d->rating) ); + makeElement( "fpId", QString::number(d->fpid) ); + makeElement( "mbId", mbid() ); + makeElement( "loved", QString::number( isLoved() ) ); + makeElement( "scrobbleStatus", QString::number( scrobbleStatus() ) ); + makeElement( "scrobbleError", QString::number( scrobbleError() ) ); + + // put the images urls in the dom + QMapIterator imageIter( d->m_images ); + while (imageIter.hasNext()) { + QDomElement e = xml.createElement( "image" ); + e.appendChild( xml.createTextNode( imageIter.next().value().toString() ) ); + e.setAttribute( "size", imageIter.key() ); + item.appendChild( e ); + } + + // add the extras to the dom + QDomElement extras = xml.createElement( "extras" ); + QMapIterator extrasIter( d->extras ); + while (extrasIter.hasNext()) { + QDomElement e = xml.createElement( extrasIter.next().key() ); + e.appendChild( xml.createTextNode( extrasIter.value() ) ); + extras.appendChild( e ); + } + item.appendChild( extras ); + + return item; +} + + +bool +lastfm::Track::corrected() const +{ + // If any of the corrected string have been set and they are different + // from the initial strings then this track has been corrected. + return ( (!d->correctedTitle.isEmpty() && (d->correctedTitle != d->title)) + || (!d->correctedAlbum.isEmpty() && (d->correctedAlbum != d->album)) + || (!d->correctedArtist.isEmpty() && (d->correctedArtist != d->artist)) + || (!d->correctedAlbumArtist.isEmpty() && (d->correctedAlbumArtist != d->albumArtist))); +} + +lastfm::Artist +lastfm::Track::artist( Corrections corrected ) const +{ + if ( corrected == Corrected && !d->correctedArtist.isEmpty() ) + return Artist( d->correctedArtist ); + + return Artist( d->artist ); +} + +lastfm::Artist +lastfm::Track::albumArtist( Corrections corrected ) const +{ + if ( corrected == Corrected && !d->correctedAlbumArtist.isEmpty() ) + return Artist( d->correctedAlbumArtist ); + + return Artist( d->albumArtist ); +} + +lastfm::Album +lastfm::Track::album( Corrections corrected ) const +{ + if ( corrected == Corrected && !d->correctedAlbum.isEmpty() ) + return Album( artist( corrected ), d->correctedAlbum ); + + return Album( artist( corrected ), d->album ); +} + +QString +lastfm::Track::title( Corrections corrected ) const +{ + /** if no title is set, return the musicbrainz unknown identifier + * in case some part of the GUI tries to display it anyway. Note isNull + * returns false still. So you should have queried this! */ + + if ( corrected == Corrected && !d->correctedTitle.isEmpty() ) + return d->correctedTitle; + + return d->title.isEmpty() ? "[unknown]" : d->title; +} + + +QUrl +lastfm::Track::imageUrl( lastfm::ImageSize size, bool square ) const +{ + if( !square ) return d->m_images.value( size ); + + QUrl url = d->m_images.value( size ); + QRegExp re( "/serve/(\\d*)s?/" ); + return QUrl( url.toString().replace( re, "/serve/\\1s/" )); +} + + +QString +lastfm::Track::toString( const QChar& separator, Corrections corrections ) const +{ + if ( d->artist.isEmpty() ) + { + if ( d->title.isEmpty() ) + return QFileInfo( d->url.path() ).fileName(); + else + return title( corrections ); + } + + if ( d->title.isEmpty() ) + return artist( corrections ); + + return artist( corrections ) + ' ' + separator + ' ' + title( corrections ); +} + + +QString //static +lastfm::Track::durationString( int const duration ) +{ + QTime t = QTime().addSecs( duration ); + if (duration < 60*60) + return t.toString( "m:ss" ); + else + return t.toString( "hh:mm:ss" ); +} + + +QNetworkReply* +lastfm::Track::share( const QStringList& recipients, const QString& message, bool isPublic ) const +{ + QMap map = params("share"); + map["recipient"] = recipients.join(","); + map["public"] = isPublic ? "1" : "0"; + if (message.size()) map["message"] = message; + return ws::post(map); +} + + +void +lastfm::MutableTrack::setFromLfm( const XmlQuery& lfm ) +{ + QString imageUrl = lfm["track"]["image size=small"].text(); + if ( !imageUrl.isEmpty() ) d->m_images[lastfm::Small] = imageUrl; + imageUrl = lfm["track"]["image size=medium"].text(); + if ( !imageUrl.isEmpty() ) d->m_images[lastfm::Medium] = imageUrl; + imageUrl = lfm["track"]["image size=large"].text(); + if ( !imageUrl.isEmpty() ) d->m_images[lastfm::Large] = imageUrl; + imageUrl = lfm["track"]["image size=extralarge"].text(); + if ( !imageUrl.isEmpty() ) d->m_images[lastfm::ExtraLarge] = imageUrl; + imageUrl = lfm["track"]["image size=mega"].text(); + if ( !imageUrl.isEmpty() ) d->m_images[lastfm::Mega] = imageUrl; + + d->loved = lfm["track"]["userloved"].text().toInt(); + + d->forceLoveToggled( d->loved ); +} + + +void +lastfm::MutableTrack::love() +{ + QNetworkReply* reply = ws::post(params("love")); + QObject::connect( reply, SIGNAL(finished()), signalProxy(), SLOT(onLoveFinished())); +} + + +void +lastfm::MutableTrack::unlove() +{ + QNetworkReply* reply = ws::post(params("unlove")); + QObject::connect( reply, SIGNAL(finished()), signalProxy(), SLOT(onUnloveFinished())); +} + + +QNetworkReply* +lastfm::MutableTrack::ban() +{ + d->extras["rating"] = "B"; + return ws::post(params("ban")); +} + + +QMap +lastfm::Track::params( const QString& method, bool use_mbid ) const +{ + QMap map; + map["method"] = "Track."+method; + if (d->mbid.size() && use_mbid) + map["mbid"] = d->mbid; + else { + map["artist"] = d->artist; + map["track"] = d->title; + } + return map; +} + + +QNetworkReply* +lastfm::Track::getTopTags() const +{ + return ws::get( params("getTopTags", true) ); +} + + +QNetworkReply* +lastfm::Track::getTopFans() const +{ + return ws::get( params("getTopFans", true) ); +} + + +QNetworkReply* +lastfm::Track::getTags() const +{ + return ws::get( params("getTags", true) ); +} + +void +lastfm::Track::getInfo(const QString& user, const QString& sk) const +{ + QMap map = params("getInfo", true); + if (!user.isEmpty()) map["username"] = user; + if (!sk.isEmpty()) map["sk"] = sk; + QObject::connect( ws::get( map ), SIGNAL(finished()), d.data(), SLOT(onGotInfo())); +} + + +QNetworkReply* +lastfm::Track::addTags( const QStringList& tags ) const +{ + if (tags.isEmpty()) + return 0; + QMap map = params("addTags"); + map["tags"] = tags.join( QChar(',') ); + return ws::post(map); +} + + +QNetworkReply* +lastfm::Track::removeTag( const QString& tag ) const +{ + if (tag.isEmpty()) + return 0; + QMap map = params( "removeTag" ); + map["tags"] = tag; + return ws::post(map); +} + + +QNetworkReply* +lastfm::Track::updateNowPlaying() const +{ + QMap map = params("updateNowPlaying"); + map["duration"] = QString::number( duration() ); + if ( !album().isNull() ) map["album"] = album(); + map["context"] = extra("playerId"); + + qDebug() << map; + + return ws::post(map); +} + + +QNetworkReply* +lastfm::Track::scrobble() const +{ + QMap map = params("scrobble"); + map["duration"] = QString::number( d->duration ); + map["timestamp"] = QString::number( d->time.toTime_t() ); + map["context"] = extra("playerId"); + map["albumArtist"] = d->albumArtist; + if ( !d->album.isEmpty() ) map["album"] = d->album; + + qDebug() << map; + + return ws::post(map); +} + +QNetworkReply* +lastfm::Track::scrobble(const QList& tracks) +{ + QMap map; + map["method"] = "track.scrobble"; + + for ( int i(0) ; i < tracks.count() ; ++i ) + { + map["duration[" + QString::number(i) + "]"] = QString::number( tracks[i].duration() ); + map["timestamp[" + QString::number(i) + "]"] = QString::number( tracks[i].timestamp().toTime_t() ); + map["track[" + QString::number(i) + "]"] = tracks[i].title(); + map["context[" + QString::number(i) + "]"] = tracks[i].extra("playerId"); + if ( !tracks[i].album().isNull() ) map["album[" + QString::number(i) + "]"] = tracks[i].album(); + map["artist[" + QString::number(i) + "]"] = tracks[i].artist(); + map["albumArtist[" + QString::number(i) + "]"] = tracks[i].albumArtist(); + if ( !tracks[i].mbid().isNull() ) map["mbid[" + QString::number(i) + "]"] = tracks[i].mbid(); + } + + qDebug() << map; + + return ws::post(map); +} + + +QUrl +lastfm::Track::www() const +{ + return UrlBuilder( "music" ).slash( d->artist ).slash( album().isNull() ? QString("_") : album()).slash( d->title ).url(); +} + + +bool +lastfm::Track::isMp3() const +{ + //FIXME really we should check the file header? + return d->url.scheme() == "file" && + d->url.path().endsWith( ".mp3", Qt::CaseInsensitive ); +} + +void +lastfm::MutableTrack::setCorrections( QString title, QString album, QString artist, QString albumArtist ) +{ + d->correctedTitle = title; + d->correctedAlbum = album; + d->correctedArtist = artist; + d->correctedAlbumArtist = albumArtist; + + d->forceCorrected( toString() ); +} + diff --git a/thirdparty/liblastfm2/src/types/Track.h b/thirdparty/liblastfm2/src/types/Track.h new file mode 100644 index 000000000..eb759d0a2 --- /dev/null +++ b/thirdparty/liblastfm2/src/types/Track.h @@ -0,0 +1,323 @@ +/* + Copyright 2009-2010 Last.fm Ltd. + - Primarily authored by Max Howell, Jono Cole, Doug Mansell and Michael Coffey + + This file is part of liblastfm. + + liblastfm 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. + + liblastfm 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 liblastfm. If not, see . +*/ +#ifndef LASTFM_TRACK_H +#define LASTFM_TRACK_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +namespace lastfm { + +class TrackData : public QObject, public QSharedData +{ + Q_OBJECT + + friend class Track; + friend class MutableTrack; +public: + TrackData(); + +public: + QString artist; + QString albumArtist; + QString album; + QString title; + QString correctedArtist; + QString correctedAlbumArtist; + QString correctedAlbum; + QString correctedTitle; + uint trackNumber; + uint duration; + short source; + short rating; + QString mbid; /// musicbrainz id + uint fpid; + QUrl url; + QDateTime time; /// the time the track was started at + bool loved; + QMap m_images; + short scrobbleStatus; + short scrobbleError; + + //FIXME I hate this, but is used for radio trackauth etc. + QMap extras; + + bool null; + +private: + void forceLoveToggled( bool love ) { emit loveToggled( love );} + void forceScrobbleStatusChanged() { emit scrobbleStatusChanged(); } + void forceCorrected( QString correction ) { emit corrected( correction ); } + +private slots: + void onLoveFinished(); + void onUnloveFinished(); + void onGotInfo(); + +signals: + void loveToggled( bool love ); + void loveFinished(); + void unlovedFinished(); + void gotInfo( const XmlQuery& ); + void scrobbleStatusChanged(); + void corrected( QString correction ); +}; + + + +/** Our track type. It's quite good, you may want to use it as your track type + * in general. It is explicitly shared. Which means when you make a copy, they + * both point to the same data still. This is like Qt's implicitly shared + * classes, eg. QString, however if you mod a copy of a QString, the copy + * detaches first, so then you have two copies. Our Track object doesn't + * detach, which is very handy for our usage in the client, but perhaps not + * what you want. If you need a deep copy for eg. work in a thread, call + * clone(). */ +class LASTFM_DLLEXPORT Track : public AbstractType +{ +public: + friend class TrackSignalProxy; + + enum Source + { + // DO NOT UNDER ANY CIRCUMSTANCES CHANGE THE ORDER OR VALUES OF THIS ENUM! + // you will cause broken settings and b0rked scrobbler cache submissions + + Unknown = 0, + LastFmRadio, + Player, + MediaDevice, + NonPersonalisedBroadcast, // eg Shoutcast, BBC Radio 1, etc. + PersonalisedRecommendation, // eg Pandora, but not Last.fm + }; + + enum ScrobbleStatus + { + Null = 0, + Cached, + Submitted, + Error + }; + + enum Corrections + { + Original = 0, + Corrected + }; + + enum ScrobbleError + { + None = 0, + FilteredArtistName = 113, + FilteredTrackName = 114, + FilteredAlbumName = 115, + FilteredTimestamp = 116, + ExceededMaxDailyScrobbles = 118, + InvalidStreamAuth = 119 + }; + + Track(); + explicit Track( const QDomElement& ); + + /** this track and that track point to the same object, so they are the same + * in fact. This doesn't do a deep data comparison. So even if all the + * fields are the same it will return false if they aren't in fact spawned + * from the same initial Track object */ + bool sameObject( const Track& that ) + { + return (this->d == that.d); + } + + bool operator==( const Track& that ) const + { + return ( this->title() == that.title() && + this->album() == that.album() && + this->artist() == that.artist()); + } + bool operator!=( const Track& that ) const + { + return !operator==( that ); + } + + QObject* signalProxy() const { return d.data(); } + + /** only a Track() is null */ + bool isNull() const { return d->null; } + + bool corrected() const; + + Artist artist( Corrections corrected = Original ) const; + Artist albumArtist( Corrections corrected = Original ) const; + Album album( Corrections corrected = Original ) const; + QString title( Corrections corrected = Original ) const; + + uint trackNumber() const { return d->trackNumber; } + uint duration() const { return d->duration; } /// in seconds + Mbid mbid() const { return Mbid(d->mbid); } + QUrl url() const { return d->url; } + QDateTime timestamp() const { return d->time; } + Source source() const { return static_cast(d->source); } + uint fingerprintId() const { return d->fpid; } + bool isLoved() const { return d->loved; } + QUrl imageUrl( lastfm::ImageSize size, bool square ) const; + + QString durationString() const { return durationString( d->duration ); } + static QString durationString( int seconds ); + + ScrobbleStatus scrobbleStatus() const { return static_cast(d->scrobbleStatus); } + ScrobbleError scrobbleError() const { return static_cast(d->scrobbleError); } + + /** default separator is an en-dash */ + QString toString() const { return toString( Corrected ); } + QString toString( Corrections corrections ) const { return toString( QChar(8211), corrections );} + QString toString( const QChar& separator, Corrections corrections = Original ) const; + /** the standard representation of this object as an XML node */ + QDomElement toDomElement( class QDomDocument& ) const; + + QString extra( const QString& key ) const{ return d->extras[ key ]; } + + bool operator<( const Track &that ) const + { + return this->d->time < that.d->time; + } + + bool isMp3() const; + + operator QVariant() const { return QVariant::fromValue( *this ); } + +//////////// lastfm::Ws + + /** See last.fm/api Track section */ + QNetworkReply* share( const QStringList& recipients, const QString& message = "", bool isPublic = true ) const; + + /** you can get any QNetworkReply TagList using Tag::list( QNetworkReply* ) */ + QNetworkReply* getTags() const; // for the logged in user + QNetworkReply* getTopTags() const; + QNetworkReply* getTopFans() const; + void getInfo(const QString& user = "", const QString& sk = "") const; + + /** you can only add 10 tags, we submit everything you give us, but the + * docs state 10 only. Will return 0 if the list is empty. */ + QNetworkReply* addTags( const QStringList& ) const; + /** will return 0 if the string is "" */ + QNetworkReply* removeTag( const QString& ) const; + + /** scrobble the track */ + QNetworkReply* updateNowPlaying() const; + QNetworkReply* scrobble() const; + static QNetworkReply* scrobble(const QList& tracks); + + /** the url for this track's page at last.fm */ + QUrl www() const; + +protected: + QExplicitlySharedDataPointer d; + QMap params( const QString& method, bool use_mbid = false ) const; + +private: + Track( TrackData* that_d ) : d( that_d ) + {} +}; + + + +/** This class allows you to change Track objects, it is easy to use: + * MutableTrack( some_track_object ).setTitle( "Arse" ); + * + * We have a separate MutableTrack class because in our usage, tracks + * only get mutated once, and then after that, very rarely. This pattern + * encourages such usage, which is generally sensible. You can feel more + * comfortable that the data hasn't accidently changed behind your back. + */ +class LASTFM_DLLEXPORT MutableTrack : public Track +{ +public: + MutableTrack() + { + d->null = false; + } + + /** NOTE that passing a Track() to this ctor will automatically make it non + * null. Which may not be what you want. So be careful + * Rationale: this is the most maintainable way to do it + */ + MutableTrack( const Track& that ) : Track( that ) + { + d->null = false; + } + + void setFromLfm( const XmlQuery& lfm ); + + void setArtist( QString artist ) { d->artist = artist.trimmed(); } + void setAlbumArtist( QString albumArtist ) { d->albumArtist = albumArtist.trimmed(); } + void setAlbum( QString album ) { d->album = album.trimmed(); } + void setTitle( QString title ) { d->title = title.trimmed(); } + void setCorrections( QString title, QString album, QString artist, QString albumArtist ); + void setTrackNumber( uint n ) { d->trackNumber = n; } + void setDuration( uint duration ) { d->duration = duration; } + void setUrl( QUrl url ) { d->url = url; } + void setSource( Source s ) { d->source = s; } + void setLoved( bool loved ) { d->loved = loved; } + + void setMbid( Mbid id ) { d->mbid = id; } + void setFingerprintId( uint id ) { d->fpid = id; } + + void setScrobbleStatus( ScrobbleStatus scrobbleStatus ) + { + d->scrobbleStatus = scrobbleStatus; + d->forceScrobbleStatusChanged(); + } + void setScrobbleError( ScrobbleError scrobbleError ) { d->scrobbleError = scrobbleError; } + + /** you also must scrobble this track for the love to become permenant */ + void love(); + void unlove(); + QNetworkReply* ban(); + + void stamp() { d->time = QDateTime::currentDateTime(); } + + void setExtra( const QString& key, const QString& value ) { d->extras[key] = value; } + void removeExtra( QString key ) { d->extras.remove( key ); } + void setTimeStamp( const QDateTime& dt ) { d->time = dt; } +}; + + +} //namespace lastfm + + +inline QDebug operator<<( QDebug d, const lastfm::Track& t ) +{ + return !t.isNull() + ? d << t.toString( '-' ) << t.url() + : d << "Null Track object"; +} + + +Q_DECLARE_METATYPE( lastfm::Track ); + +#endif //LASTFM_TRACK_H diff --git a/thirdparty/liblastfm2/src/types/User.cpp b/thirdparty/liblastfm2/src/types/User.cpp new file mode 100644 index 000000000..03e1a1eda --- /dev/null +++ b/thirdparty/liblastfm2/src/types/User.cpp @@ -0,0 +1,286 @@ +/* + Copyright 2009 Last.fm Ltd. + - Primarily authored by Max Howell, Jono Cole and Doug Mansell + + This file is part of liblastfm. + + liblastfm 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. + + liblastfm 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 liblastfm. If not, see . +*/ +#include "User.h" +#include "Track.h" +#include "../core/UrlBuilder.h" +#include "../core/XmlQuery.h" +#include +#include + +using lastfm::User; +using lastfm::UserList; +using lastfm::UserDetails; +using lastfm::XmlQuery; +using lastfm::ImageSize; + +User::User( const XmlQuery& xml ) + :AbstractType(), m_match( -1.0f ) +{ + m_name = xml["name"].text(); + m_images << xml["image size=small"].text() + << xml["image size=medium"].text() + << xml["image size=large"].text(); + m_realName = xml["realname"].text(); +} + + +QUrl +User::imageUrl( ImageSize size, bool square ) const +{ + if( !square ) return m_images.value( size ); + + QUrl url = m_images.value( size ); + QRegExp re( "/serve/(\\d*)s?/" ); + return QUrl( url.toString().replace( re, "/serve/\\1s/" )); +} + + +QMap +User::params(const QString& method) const +{ + QMap map; + map["method"] = "user."+method; + map["user"] = m_name; + return map; +} + + +QNetworkReply* +User::getFriends( int perPage, int page ) const +{ + QMap map = params( "getFriends" ); + map["limit"] = QString::number(perPage); + map["page"] = QString::number(page); + return ws::get( map ); +} + + +QNetworkReply* +User::getTopTags() const +{ + return ws::get( params( "getTopTags" ) ); +} + + +QNetworkReply* +User::getTopArtists() const +{ + return ws::get( params( "getTopArtists" ) ); +} + + +QNetworkReply* +User::getRecentArtists() const +{ + return ws::get( params( "getRecentArtists" ) ); +} + + +QNetworkReply* +User::getRecentTracks() const +{ + return ws::get( params( "getRecentTracks" ) ); +} + +QNetworkReply* +User::getRecentStations() const +{ + return ws::post( params( "getRecentStations" ) ); +} + +QNetworkReply* +User::getNeighbours() const +{ + return ws::get( params( "getNeighbours" ) ); +} + + +QNetworkReply* +User::getPlaylists() const +{ + return ws::get( params( "getPlaylists" ) ); +} + + +UserList //static +User::list( QNetworkReply* r ) +{ + UserList users; + try { + XmlQuery lfm = ws::parse(r); + foreach (XmlQuery e, lfm.children( "user" )) + { + User u( e ); + users += u; + } + + users.total = lfm["friends"].attribute("total").toInt(); + users.page = lfm["friends"].attribute("page").toInt(); + users.perPage = lfm["friends"].attribute("perPage").toInt(); + users.totalPages = lfm["friends"].attribute("totalPages").toInt(); + } + catch (ws::ParseError& e) + { + qWarning() << e.what(); + } + return users; +} + + +QNetworkReply* //static +UserDetails::getInfo( const QString& username ) +{ + QMap map; + map["method"] = "user.getInfo"; + map["user"] = username; + return ws::post( map ); +} + + + + +/* +QNetworkReply* //static +UserDetails::getRecommendedArtists() +{ + QMap map; + map["method"] = "user.getRecommendedArtists"; + return ws::post( map ); +} +*/ + +QUrl +User::www() const +{ + return UrlBuilder( "user" ).slash( m_name ).url(); +} + +UserDetails::UserDetails() + : User() + , m_age( 0 ) + , m_scrobbles( 0 ) + , m_registered( QDateTime() ) + , m_isSubscriber( false ) + , m_canBootstrap( false ) +{} + +UserDetails::UserDetails( QNetworkReply* reply ) +{ + try + { + XmlQuery user = XmlQuery(ws::parse(reply))["user"]; + m_age = user["age"].text().toUInt(); + m_scrobbles = user["playcount"].text().toUInt(); + m_registered = QDateTime::fromTime_t(user["registered"].attribute("unixtime").toUInt()); + m_country = user["country"].text(); + m_isSubscriber = ( user["subscriber"].text() == "1" ); + m_canBootstrap = ( user["bootstrap"].text() == "1" ); + m_gender = user["gender"].text(); + m_realName = user["realname"].text(); + m_name = user["name"].text(); + m_images << user["image size=small"].text() + << user["image size=medium"].text() + << user["image size=large"].text(); + } + catch (ws::ParseError& e) + { + qWarning() << e.what(); + } +} + + +QString +UserDetails::getInfoString() const +{ + #define tr QObject::tr +; + + QString text; + if (m_gender.known() && m_age > 0 && m_scrobbles > 0) + { + text = tr("A %1, %2 years of age with %L3 scrobbles") + .arg( m_gender.toString() ) + .arg( m_age ) + .arg( m_scrobbles ); + } + else if (m_scrobbles > 0) + { + text = tr("%L1 scrobbles").arg( m_scrobbles ); + } + + return text; + + #undef tr +} + +void +UserDetails::setScrobbleCount( quint32 scrobbleCount ) +{ + m_scrobbles = scrobbleCount; +} + + +void +UserDetails::setDateRegistered( const QDateTime& date ) +{ + m_registered = date; +} + +void +UserDetails::setImages( const QList& images ) +{ + m_images = images; +} + +void +UserDetails::setRealName( const QString& realName ) +{ + m_realName = realName; +} +void +UserDetails::setAge( unsigned short age ) +{ + m_age = age; +} + +void +UserDetails::setIsSubscriber( bool subscriber ) +{ + m_isSubscriber = subscriber; +} + +void +UserDetails::setCanBootstrap( bool canBootstrap ) +{ + m_canBootstrap = canBootstrap; +} + +void +UserDetails::setGender( const QString& s ) +{ + m_gender = Gender( s ); +} + +void +UserDetails::setCountry( const QString& country ) +{ + m_country = country; +} + diff --git a/thirdparty/liblastfm2/src/types/User.h b/thirdparty/liblastfm2/src/types/User.h new file mode 100644 index 000000000..2d3eb0d84 --- /dev/null +++ b/thirdparty/liblastfm2/src/types/User.h @@ -0,0 +1,181 @@ +/* + Copyright 2009-2010 Last.fm Ltd. + - Primarily authored by Max Howell, Jono Cole, Doug Mansell and Michael Coffey + + This file is part of liblastfm. + + liblastfm 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. + + liblastfm 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 liblastfm. If not, see . +*/ +#ifndef LASTFM_USER_H +#define LASTFM_USER_H + +#include +#include +#include + +#include +#include + +namespace lastfm +{ + class UserList; + + class LASTFM_DLLEXPORT User : public AbstractType + { + public: + User() : AbstractType(), m_name( lastfm::ws::Username ), m_match( -1.0f ) + {} + + User( const QString& name ) : AbstractType(), m_name( name ), m_match( -1.0f ) + {} + + User( const class XmlQuery& xml ); + + bool operator==(const lastfm::User& that) const { return m_name == that.m_name; } + + operator QString() const { return m_name; } + QString name() const { return m_name; } + void setName( const QString& name ){ m_name = name; } + + /** use Tag::list() on the response to get a WeightedStringList */ + QNetworkReply* getTopTags() const; + + /** use User::list() on the response to get a QList */ + QNetworkReply* getFriends(int perPage = 50, int page = 1) const; + QNetworkReply* getNeighbours() const; + + QNetworkReply* getPlaylists() const; + QNetworkReply* getTopArtists() const; + QNetworkReply* getRecentTracks() const; + QNetworkReply* getRecentArtists() const; + QNetworkReply* getRecentStations() const; + + static UserList list( QNetworkReply* ); + + QString toString() const { return name(); } + QDomElement toDomElement( QDomDocument& ) const { return QDomElement(); } + + ////// + QUrl imageUrl( ImageSize size = Large, bool square = false ) const; + + QString realName() const { return m_realName; } + + /** the user's profile page at www.last.fm */ + QUrl www() const; + + /** Returns the match between the logged in user and the user which this + * object represents (if < 0.0f then not set) */ + float match() const { return m_match; } + + protected: + QString m_name; + + QList m_images; + + float m_match; + + QString m_realName; + + QMap params( const QString& method ) const; + }; + + + /** The Extended User contains extra information about a user's account */ + class LASTFM_DLLEXPORT UserDetails : public User + { + public: + UserDetails(); + /** User details */ + UserDetails( QNetworkReply* ); + + /** you can only get information about the any user */ + static QNetworkReply* getInfo( const QString& username = lastfm::ws::Username ); + + /** a verbose string, eg. "A man with 36,153 scrobbles" */ + QString getInfoString() const; + + bool isSubscriber() const{ return m_isSubscriber; } + bool canBootstrap() const{ return m_canBootstrap; } + quint32 scrobbleCount() const{ return m_scrobbles; } + QDateTime dateRegistered() const { return m_registered; } + + void setScrobbleCount( quint32 scrobblesCount ); + void setDateRegistered( const QDateTime& date ); + void setImages( const QList& images ); + void setRealName( const QString& realName ); + void setAge( unsigned short age ); + void setIsSubscriber( bool subscriber ); + void setCanBootstrap( bool canBootstrap ); + void setGender( const QString& s ); + void setCountry( const QString& country ); + + + // pass the result to Artist::list(), if you want the other data + // you have to parse the lfm() yourself members + // http://www.last.fm/api/show?service=388 + // static QNetworkReply* getRecommendedArtists(); + + protected: + + class Gender + { + QString s; + + public: + Gender() :s(/*confused!*/){} + + Gender( const QString& ss ) :s( ss.toLower() ) + {} + + bool known() const { return male() || female(); } + bool male() const { return s == "m"; } + bool female() const { return s == "f"; } + + QString toString() const + { + #define tr QObject::tr + QStringList list; + if (male()) + list << tr("boy") << tr("lad") << tr("chap") << tr("guy"); + else if (female()) + // I'm not sexist, it's just I'm gutless and couldn't think + // of any other non offensive terms for women! + list << tr("girl") << tr("lady") << tr("lass"); + else + return tr("person"); + + return list.value( QDateTime::currentDateTime().toTime_t() % list.count() ); + #undef tr + } + } m_gender; + + unsigned short m_age; + unsigned int m_scrobbles; + QDateTime m_registered; + QString m_country; + bool m_isSubscriber; + bool m_canBootstrap; + }; + + class LASTFM_DLLEXPORT UserList : public QList + { + public: + int total; + int page; + int perPage; + int totalPages; + }; +} + +#endif diff --git a/thirdparty/liblastfm2/src/types/Xspf.cpp b/thirdparty/liblastfm2/src/types/Xspf.cpp new file mode 100644 index 000000000..2100ec265 --- /dev/null +++ b/thirdparty/liblastfm2/src/types/Xspf.cpp @@ -0,0 +1,49 @@ +/* + Copyright 2009 Last.fm Ltd. + - Primarily authored by Max Howell, Jono Cole and Doug Mansell + + This file is part of liblastfm. + + liblastfm 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. + + liblastfm 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 liblastfm. If not, see . +*/ +#include "Xspf.h" +#include "../core/XmlQuery.h" +#include + +lastfm::Xspf::Xspf( const QDomElement& playlist_node ) +{ + XmlQuery e( playlist_node ); + + m_title = e["title"].text(); + + //FIXME should we use UnicornUtils::urlDecode()? + //The title is url encoded, has + instead of space characters + //and has a + at the begining. So it needs cleaning up: + m_title.replace( '+', ' ' ); + m_title = QUrl::fromPercentEncoding( m_title.toAscii()); + m_title = m_title.trimmed(); + + foreach (XmlQuery e, e["trackList"].children( "track" )) + { + MutableTrack t; + t.setUrl( e["location"].text() ); + t.setExtra( "trackauth", e["extension"]["trackauth"].text() ); + t.setTitle( e["title"].text() ); + t.setArtist( e["creator"].text() ); + t.setAlbum( e["album"].text() ); + t.setDuration( e["duration"].text().toInt() / 1000 ); + t.setLoved( e["extension"]["loved"].text() == "1" ); + m_tracks += t; // outside try block since location is enough basically + } +} diff --git a/thirdparty/liblastfm2/src/types/Xspf.h b/thirdparty/liblastfm2/src/types/Xspf.h new file mode 100644 index 000000000..e8bb55a15 --- /dev/null +++ b/thirdparty/liblastfm2/src/types/Xspf.h @@ -0,0 +1,43 @@ +/* + Copyright 2009 Last.fm Ltd. + - Primarily authored by Max Howell, Jono Cole and Doug Mansell + + This file is part of liblastfm. + + liblastfm 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. + + liblastfm 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 liblastfm. If not, see . +*/ +#ifndef LASTFM_XSPF_H +#define LASTFM_XSPF_H + +#include +#include + +namespace lastfm +{ + class LASTFM_DLLEXPORT Xspf + { + public: + /** pass in the playlist node! */ + Xspf( const class QDomElement& playlist_node ); + + QList tracks() const { return m_tracks; } + QString title() const{ return m_title; } + + private: + QList m_tracks; + QString m_title; + }; +} + +#endif diff --git a/thirdparty/liblastfm2/src/types/mbid_mp3.c b/thirdparty/liblastfm2/src/types/mbid_mp3.c new file mode 100644 index 000000000..ab494a32f --- /dev/null +++ b/thirdparty/liblastfm2/src/types/mbid_mp3.c @@ -0,0 +1,181 @@ +/* +* LICENSE +* +* Copyright (c) 2006, David Nicolson +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions +* are met: +* +* 1. Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* 2. Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* 3. Neither the name of the author nor the names of its contributors +* may be used to endorse or promote products derived from this software +* without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT UNLESS REQUIRED BY +* LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER OR CONTRIBUTOR +* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, +* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT +* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, PROFITS; OR +* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef __MBID_MP3_H +#define __MBID_MP3_H + +#define MBID_BUFFER_SIZE 37 + +// ----------------------------------------------------------------------------- + +void mfile(size_t length, char ret[], FILE *fp, int *s) { + size_t bytes = fread(ret,1,length,fp); + + if (bytes != length) { + *s = 0; + } +} + +// ----------------------------------------------------------------------------- + +int to_synch_safe(char bytes[]) { + return ((int)bytes[0] << 21) + ((int)bytes[1] << 14) + ((int)bytes[2] << 7) + (int)bytes[3]; +} + +int to_integer(char bytes[]) { + size_t size = 0; + uint i; + for (i=0; i < sizeof(bytes); i++) { + size = size * 256 + ((int)bytes[i] & 0x000000FF); + } + return static_cast(size); +} + +// ----------------------------------------------------------------------------- + +int getMP3_MBID(const char *path, char mbid[MBID_BUFFER_SIZE]) +{ + FILE *fp; + static int s = 1; + char head[3]; + char version[2]; + char flag[1]; + char size[4]; + char size_extended[4]; + int tag_size = 0; + int extended_size = 0; + char frame[4]; + char frame_header[4]; + int frame_size; + int version_major, version_minor; + + if (path == NULL) { + //debug("Received null path\n"); + return -1; + } + + fp = fopen(path,"rb"); + if (fp == NULL) { + //debug("Failed to open music file: %s\n",path); + return -1; + } + + while (s) { + mfile(3,head,fp,&s); + if (!strncmp(head,"ID3",3) == 0) { + //debug("No ID3v2 tag found: %s\n",path); + break; + } + + mfile(2,version,fp,&s); + version_major = (int)version[0]; + version_minor = (int)version[1]; + if (version_major == 2) { + //debug("ID3v2.2.0 does not support MBIDs: %s\n",path); + break; + } + if (version_major != 3 && version_major != 4) { + //debug("Unsupported ID3 version: v2.%d.%d\n",version_major,version_minor); + break; + } + + mfile(1,flag,fp,&s); + if ((unsigned int)flag[0] & 0x00000040) { + //debug("Extended header found\n"); + if (version[0] == 4) { + mfile(4,size_extended,fp,&s); + extended_size = to_synch_safe(size_extended); + } else { + mfile(4,size_extended,fp,&s); + extended_size = to_integer(size_extended); + } + //debug("Extended header size: %d\n",extended_size); + fseek(fp,extended_size,SEEK_CUR); + } + + mfile(4,size,fp,&s); + tag_size = to_synch_safe(size); + //debug("Tag size: %d\n",tag_size); + + while (s) { + if (ftell(fp) > tag_size || ftell(fp) > 1048576) { + break; + } + + mfile(4,frame,fp,&s); + if (frame[0] == 0x00) { + break; + } + if (version_major == 4) { + mfile(4,frame_header,fp,&s); + frame_size = to_synch_safe(frame_header); + } else { + mfile(4,frame_header,fp,&s); + frame_size = to_integer(frame_header); + } + + fseek(fp,2,SEEK_CUR); + //debug("Reading %d bytes from frame %s\n",frame_size,frame); + + if (strncmp(frame,"UFID",4) == 0) { + //char frame_data[frame_size]; + char frame_data[59]; + mfile(59,frame_data,fp,&s); + if (frame_size >= 59 && strncmp(frame_data,"http://musicbrainz.org",22) == 0) { + char *tmbid = frame_data; + tmbid = frame_data + 23; + strncpy(mbid,tmbid,MBID_BUFFER_SIZE-1); + mbid[MBID_BUFFER_SIZE-1] = 0x00; + fclose(fp); + return 0; + } + } else { + fseek(fp,frame_size,SEEK_CUR); + } + } + break; + } + + if (fp) { + fclose(fp); + } + //if (!s) { + // debug("Failed to read music file: %s\n",path); + //} + return -1; + +} + +#endif + +// ----------------------------------------------------------------------------- diff --git a/thirdparty/liblastfm2/src/ws/InternetConnectionMonitor.cpp b/thirdparty/liblastfm2/src/ws/InternetConnectionMonitor.cpp new file mode 100644 index 000000000..44eb51b4d --- /dev/null +++ b/thirdparty/liblastfm2/src/ws/InternetConnectionMonitor.cpp @@ -0,0 +1,113 @@ +/* + Copyright 2009 Last.fm Ltd. + - Primarily authored by Max Howell, Jono Cole and Doug Mansell + + This file is part of liblastfm. + + liblastfm 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. + + liblastfm 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 liblastfm. If not, see . +*/ + +#include "InternetConnectionMonitor.h" +#include "linux/LNetworkConnectionMonitor.h" +#include "mac/MNetworkConnectionMonitor.h" +#include "win/WNetworkConnectionMonitor.h" +#include "NetworkConnectionMonitor.h" +#include "ws.h" + +lastfm::InternetConnectionMonitor::InternetConnectionMonitor( QObject *parent ) + : QObject( parent ) + , m_up( true ) +{ + m_networkMonitor = createNetworkConnectionMonitor(); + + if ( m_networkMonitor ) + { + connect( m_networkMonitor, SIGNAL( networkUp() ), this, SLOT( onNetworkUp() ) ); + connect( m_networkMonitor, SIGNAL( networkDown() ), this, SLOT( onNetworkDown() ) ); + } + + connect( lastfm::nam(), SIGNAL( finished( QNetworkReply* ) ), this, SLOT( onFinished( QNetworkReply* ) ) ); +} + +void +lastfm::InternetConnectionMonitor::onFinished( QNetworkReply* reply ) +{ + switch( reply->error() ) + { + case QNetworkReply::NoError: + if ( !m_up ) + { + m_up = true; + emit up(); + emit connectivityChanged( m_up ); + } + break; + case QNetworkReply::HostNotFoundError: + case QNetworkReply::TimeoutError: + case QNetworkReply::ProxyConnectionRefusedError: + case QNetworkReply::ProxyConnectionClosedError: + case QNetworkReply::ProxyNotFoundError: + case QNetworkReply::ProxyTimeoutError: + case QNetworkReply::ProxyAuthenticationRequiredError: + if ( m_up ) + { + m_up = false; + emit down(); + emit connectivityChanged( m_up ); + } + break; + default: + break; + } +} + +void +lastfm::InternetConnectionMonitor::onNetworkUp() +{ +#ifdef Q_OS_MAC + // We don't need to check on mac as the + // check is done as part of the reach api + m_up = true; + emit up(); + emit connectivityChanged( m_up ); +#else + qDebug() << "Network seems to be up again. Let's try if there's internet connection!"; + lastfm::nam()->head( QNetworkRequest( QUrl( tr( "http://www.last.fm/" ) ) ) ); +#endif +} + +void +lastfm::InternetConnectionMonitor::onNetworkDown() +{ + qDebug() << "Internet is down :( boo!!"; + m_up = false; + emit down(); + emit connectivityChanged( m_up ); +} + +NetworkConnectionMonitor* +lastfm::InternetConnectionMonitor::createNetworkConnectionMonitor() +{ + NetworkConnectionMonitor* ncm = 0; + +#ifdef Q_WS_X11 + ncm = new LNetworkConnectionMonitor( this ); +#elif defined(Q_WS_WIN) + ncm = new WNetworkConnectionMonitor( this ); +#elif defined(Q_WS_MAC) + ncm = new MNetworkConnectionMonitor( this ); +#endif + + return ncm; +} diff --git a/thirdparty/liblastfm2/src/ws/InternetConnectionMonitor.h b/thirdparty/liblastfm2/src/ws/InternetConnectionMonitor.h new file mode 100644 index 000000000..3f770125c --- /dev/null +++ b/thirdparty/liblastfm2/src/ws/InternetConnectionMonitor.h @@ -0,0 +1,80 @@ +/* + Copyright 2009 Last.fm Ltd. + - Primarily authored by Max Howell, Jono Cole and Doug Mansell + + This file is part of liblastfm. + + liblastfm 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. + + liblastfm 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 liblastfm. If not, see . +*/ + +#ifndef LASTFM_CONNECTION_MONITOR_H +#define LASTFM_CONNECTION_MONITOR_H + +#include +#include + +class NetworkConnectionMonitor; + +#ifdef Q_WS_X11 +class LNetworkConnectionMonitor; +#endif + +namespace lastfm { + +class LASTFM_DLLEXPORT InternetConnectionMonitor : public QObject +{ + Q_OBJECT + enum NMState + { + Unknown, + Asleep, + Connecting, + Connected, + Disconnected + }; + +public: + /** if internet is unavailable you will get a down() signal soon, otherwise + * you won't get a signal until the net goes down */ + InternetConnectionMonitor( QObject *parent = 0 ); + + bool isDown() const { return !m_up; } + bool isUp() const { return m_up; } + + NetworkConnectionMonitor* createNetworkConnectionMonitor(); + +signals: + /** yay! internet has returned */ + void up( const QString& connectionName = "" ); + + /** we think the internet is unavailable, but well, still try, but show + * an unhappy face in the statusbar or something */ + void down( const QString& connectionName = "" ); + + /** emitted after the above */ + void connectivityChanged( bool ); + +private slots: + void onFinished( QNetworkReply* reply ); + void onNetworkUp(); + void onNetworkDown(); + +private: + bool m_up; + NetworkConnectionMonitor* m_networkMonitor; +}; + +} //namespace lastfm + +#endif diff --git a/thirdparty/liblastfm2/src/ws/NetworkAccessManager.cpp b/thirdparty/liblastfm2/src/ws/NetworkAccessManager.cpp new file mode 100644 index 000000000..2d628b1ad --- /dev/null +++ b/thirdparty/liblastfm2/src/ws/NetworkAccessManager.cpp @@ -0,0 +1,159 @@ +/* + Copyright 2009 Last.fm Ltd. + - Primarily authored by Max Howell, Jono Cole and Doug Mansell + + This file is part of liblastfm. + + liblastfm 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. + + liblastfm 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 liblastfm. If not, see . +*/ +#include "NetworkAccessManager.h" +#include "InternetConnectionMonitor.h" +#include +#include +#include +#include +#ifdef WIN32 +#include "win/IeSettings.h" +#include "win/Pac.h" +#endif +#ifdef __APPLE__ +#include "mac/ProxyDict.h" +#endif + + +static struct NetworkAccessManagerInit +{ + // We do this upfront because then our Firehose QTcpSocket will have a proxy + // set by default. As well as any plain QNetworkAcessManager stuff, and the + // scrobbler + // In theory we should do this every request in case the configuration + // changes but that is fairly unlikely use case, init? Maybe we should + // anyway.. + + NetworkAccessManagerInit() + { + #ifdef WIN32 + IeSettings s; + // if it's autodetect, we determine the proxy everytime in proxy() + // we don't really want to do a PAC lookup here, as it times out + // at two seconds, so that hangs startup + if (!s.fAutoDetect && s.lpszProxy) + { + QUrl url( QString::fromUtf16(s.lpszProxy) ); + QNetworkProxy proxy( QNetworkProxy::HttpProxy ); + proxy.setHostName( url.host() ); + proxy.setPort( url.port() ); + QNetworkProxy::setApplicationProxy( proxy ); + } + #endif + #ifdef __APPLE__ + ProxyDict dict; + if (dict.isProxyEnabled()) + { + QNetworkProxy proxy( QNetworkProxy::HttpProxy ); + proxy.setHostName( dict.host ); + proxy.setPort( dict.port ); + + QNetworkProxy::setApplicationProxy( proxy ); + } + #endif + } +} init; + + +namespace lastfm +{ + LASTFM_DLLEXPORT QByteArray UserAgent; +} + + +lastfm::NetworkAccessManager::NetworkAccessManager( QObject* parent ) + : QNetworkAccessManager( parent ) + #ifdef WIN32 + , m_pac( 0 ) + , m_monitor( 0 ) + #endif +{ + // can't be done in above init, as applicationName() won't be set + if (lastfm::UserAgent.isEmpty()) + { + QByteArray name = QCoreApplication::applicationName().toUtf8(); + QByteArray version = QCoreApplication::applicationVersion().toUtf8(); + if (version.size()) version.prepend( ' ' ); + lastfm::UserAgent = name + version + " (" + lastfm::platform() + ")"; + } +} + + +lastfm::NetworkAccessManager::~NetworkAccessManager() +{ +#ifdef WIN32 + delete m_pac; +#endif +} + + +QNetworkProxy +lastfm::NetworkAccessManager::proxy( const QNetworkRequest& request ) +{ + Q_UNUSED( request ); + +#ifdef WIN32 + IeSettings s; + if (s.fAutoDetect) + { + if (!m_pac) { + m_pac = new Pac; + if ( !m_monitor ) + { + m_monitor = new InternetConnectionMonitor( this ); + connect( m_monitor, SIGNAL( connectivityChanged( bool ) ), SLOT( onConnectivityChanged( bool ) ) ); + } + } + return m_pac->resolve( request, s.lpszAutoConfigUrl ); + } +#endif + + return QNetworkProxy::applicationProxy(); +} + + +QNetworkReply* +lastfm::NetworkAccessManager::createRequest( Operation op, const QNetworkRequest& request_, QIODevice* outgoingData ) +{ + QNetworkRequest request = request_; + + request.setAttribute( QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferCache ); + request.setRawHeader( "User-Agent", lastfm::UserAgent ); + +#ifdef WIN32 + // PAC proxies can vary by domain, so we have to check everytime :( + QNetworkProxy proxy = this->proxy( request ); + if (proxy.type() != QNetworkProxy::NoProxy) + QNetworkAccessManager::setProxy( proxy ); +#endif + + return QNetworkAccessManager::createRequest( op, request, outgoingData ); +} + + +void +lastfm::NetworkAccessManager::onConnectivityChanged( bool up ) +{ + Q_UNUSED( up ); + +#ifdef WIN32 + if (up && m_pac) m_pac->resetFailedState(); +#endif +} diff --git a/thirdparty/liblastfm2/src/ws/NetworkAccessManager.h b/thirdparty/liblastfm2/src/ws/NetworkAccessManager.h new file mode 100644 index 000000000..a3022ce25 --- /dev/null +++ b/thirdparty/liblastfm2/src/ws/NetworkAccessManager.h @@ -0,0 +1,66 @@ +/* + Copyright 2009 Last.fm Ltd. + - Primarily authored by Max Howell, Jono Cole and Doug Mansell + + This file is part of liblastfm. + + liblastfm 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. + + liblastfm 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 liblastfm. If not, see . +*/ +#ifndef LASTFM_WS_ACCESS_MANAGER_H +#define LASTFM_WS_ACCESS_MANAGER_H + +#include +#include +#include +#include + + +namespace lastfm { + +/** Sets useragent and proxy. Auto detecting the proxy where possible. */ +class LASTFM_DLLEXPORT NetworkAccessManager : public QNetworkAccessManager +{ + Q_OBJECT + +#ifdef Q_WS_WIN + class Pac *m_pac; + class InternetConnectionMonitor* m_monitor; +#endif + +public: + NetworkAccessManager( QObject *parent = 0 ); + ~NetworkAccessManager(); + + /** PAC allows different proxy configurations depending on the request + * URL and even UserAgent! Thus we allow you to pass that in, we + * automatically configure the proxy for every request through + * WsAccessManager */ + QNetworkProxy proxy( const QNetworkRequest& = QNetworkRequest() ); + +protected: + virtual QNetworkReply* createRequest( Operation, const QNetworkRequest&, QIODevice* outgoingdata = 0 ); + +private slots: + void onConnectivityChanged( bool ); + +private: + /** this function calls QNetworkAccessManager::setProxy, and thus + * configures the proxy correctly for the next request created by + * createRequest. This is necessary due */ + void applyProxy( const QNetworkRequest& ); +}; + +} //namespace lastfm + +#endif diff --git a/thirdparty/liblastfm2/src/ws/NetworkConnectionMonitor.cpp b/thirdparty/liblastfm2/src/ws/NetworkConnectionMonitor.cpp new file mode 100644 index 000000000..b4a62732b --- /dev/null +++ b/thirdparty/liblastfm2/src/ws/NetworkConnectionMonitor.cpp @@ -0,0 +1,51 @@ +/* + Copyright 2010 Last.fm Ltd. + - Primarily authored by Max Howell, Jono Cole and Doug Mansell + + This file is part of liblastfm. + + liblastfm 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. + + liblastfm 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 liblastfm. If not, see . +*/ + +#include "NetworkConnectionMonitor.h" + +NetworkConnectionMonitor::NetworkConnectionMonitor( QObject* /*parent*/ ) + : m_connected( true ) +{ +} + +NetworkConnectionMonitor::~NetworkConnectionMonitor() +{ +} + +bool +NetworkConnectionMonitor::isConnected() const +{ + return m_connected; +} + +void +NetworkConnectionMonitor::setConnected( bool connected ) +{ + if ( m_connected != connected ) + { + m_connected = connected; + + if ( connected ) + emit networkUp(); + else + emit networkDown(); + } +} + diff --git a/thirdparty/liblastfm2/src/ws/NetworkConnectionMonitor.h b/thirdparty/liblastfm2/src/ws/NetworkConnectionMonitor.h new file mode 100644 index 000000000..a21cf73e3 --- /dev/null +++ b/thirdparty/liblastfm2/src/ws/NetworkConnectionMonitor.h @@ -0,0 +1,46 @@ +/* + Copyright 2010 Last.fm Ltd. + - Primarily authored by Jono Cole, Michael Coffey, and William Viana + + This file is part of liblastfm. + + liblastfm 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. + + liblastfm 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 liblastfm. If not, see . +*/ + +#ifndef NETWORK_CONNECTION_MONITOR_H +#define NETWORK_CONNECTION_MONITOR_H + +#include +#include + +class LASTFM_DLLEXPORT NetworkConnectionMonitor : public QObject +{ + Q_OBJECT +public: + NetworkConnectionMonitor( QObject *parent = 0 ); + ~NetworkConnectionMonitor(); + bool isConnected() const; + +signals: + void networkUp(); + void networkDown(); + +protected: + void setConnected( bool connected ); + +private: + bool m_connected; +}; + +#endif // NETWORK_CONNECTION_MONITOR_H diff --git a/thirdparty/liblastfm2/src/ws/linux/LNetworkConnectionMonitor.h b/thirdparty/liblastfm2/src/ws/linux/LNetworkConnectionMonitor.h new file mode 100644 index 000000000..9f7895bed --- /dev/null +++ b/thirdparty/liblastfm2/src/ws/linux/LNetworkConnectionMonitor.h @@ -0,0 +1,54 @@ +/* + Copyright 2010 Last.fm Ltd. + - Primarily authored by Max Howell, Jono Cole and Doug Mansell + + This file is part of liblastfm. + + liblastfm 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. + + liblastfm 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 liblastfm. If not, see . +*/ + + +#ifndef LNETWORK_CONNECTION_MONITOR_H +#define LNETWORK_CONNECTION_MONITOR_H + +#include "../NetworkConnectionMonitor.h" +#include +#include + +class QDBusConnection; +class QDBusInterface; + +class LNetworkConnectionMonitor : public NetworkConnectionMonitor +{ + Q_OBJECT + + enum NMState + { + Unknown=1, + Asleep, + Connected, + Disconnected + }; + +public: + LNetworkConnectionMonitor( QObject* parent = 0 ); + ~LNetworkConnectionMonitor(); +private slots: + void onStateChange( uint newState ); +private: + QDBusInterface* m_nmInterface; +}; + +#endif // LNETWORK_CONNECTION_MONITOR_H + diff --git a/thirdparty/liblastfm2/src/ws/linux/LNetworkConnectionMonitor_linux.cpp b/thirdparty/liblastfm2/src/ws/linux/LNetworkConnectionMonitor_linux.cpp new file mode 100644 index 000000000..d8dea9dd6 --- /dev/null +++ b/thirdparty/liblastfm2/src/ws/linux/LNetworkConnectionMonitor_linux.cpp @@ -0,0 +1,86 @@ +/* + Copyright 2010 Last.fm Ltd. + - Primarily authored by Max Howell, Jono Cole and Doug Mansell + + This file is part of liblastfm. + + liblastfm 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. + + liblastfm 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 liblastfm. If not, see . +*/ + + +#include "LNetworkConnectionMonitor.h" + +#include +#include +#include + +LNetworkConnectionMonitor::LNetworkConnectionMonitor( QObject* parent ) : + NetworkConnectionMonitor( parent ) +{ + m_nmInterface = new QDBusInterface( QString( "org.freedesktop.NetworkManager" ), + QString( "/org/freedesktop/NetworkManager" ), + QString( "org.freedesktop.NetworkManager" ), + QDBusConnection::systemBus(), + this ); + + //get current connection state + QDBusInterface* dbusInterface = new QDBusInterface( QString( "org.freedesktop.NetworkManager" ), + QString( "/org/freedesktop/NetworkManager" ), + QString( "org.freedesktop.DBus.Properties" ), + QDBusConnection::systemBus(), + this ); + + QDBusReply reply = dbusInterface->call( "Get", "org.freedesktop.NetworkManager", "state" ); + if ( reply.isValid() ) + { + if ( reply.value() == Connected ) + { + setConnected( true ); + } + else if ( reply.value() == Disconnected ) + { + setConnected( false ); + } + } + else + { + qDebug() << "Error: " << reply.error(); + } + delete dbusInterface; + + //connect network manager signals + connect( m_nmInterface, SIGNAL( StateChange( uint ) ), this, SLOT( onStateChange( uint ) ) ); + +} + +LNetworkConnectionMonitor::~LNetworkConnectionMonitor() +{ + delete m_nmInterface; +} + + +void +LNetworkConnectionMonitor::onStateChange( uint newState ) +{ + qDebug() << "Networkmanager state change!"; + + if ( newState == Disconnected ) + { + setConnected( false ); + } + else if ( newState == Connected ) + { + setConnected( true ); + } +} diff --git a/thirdparty/liblastfm2/src/ws/mac/MNetworkConnectionMonitor.h b/thirdparty/liblastfm2/src/ws/mac/MNetworkConnectionMonitor.h new file mode 100644 index 000000000..96aca0767 --- /dev/null +++ b/thirdparty/liblastfm2/src/ws/mac/MNetworkConnectionMonitor.h @@ -0,0 +1,52 @@ +/* + Copyright 2009 Last.fm Ltd. + - Primarily authored by Jono Cole and Michael Coffey + + This file is part of liblastfm. + + liblastfm 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. + + liblastfm 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 liblastfm. If not, see . +*/ + +#ifndef MNETWORK_CONNECTION_MONITOR_H +#define MNETWORK_CONNECTION_MONITOR_H + +#include "../NetworkConnectionMonitor.h" +#include +#include + +#ifdef Q_WS_MAC +#include //TODO remove +#include +#endif + +class __SCNetworkReachability; + +class MNetworkConnectionMonitor : public NetworkConnectionMonitor +{ + Q_OBJECT +public: + MNetworkConnectionMonitor( QObject* parent = 0 ); + ~MNetworkConnectionMonitor(); +private slots: + +private: +#ifdef Q_WS_MAC + static void callback( SCNetworkReachabilityRef target, + SCNetworkConnectionFlags flags, + void *info ); +#endif +}; + +#endif // MNETWORK_CONNECTION_MONITOR_H + diff --git a/thirdparty/liblastfm2/src/ws/mac/MNetworkConnectionMonitor_mac.cpp b/thirdparty/liblastfm2/src/ws/mac/MNetworkConnectionMonitor_mac.cpp new file mode 100644 index 000000000..9f99e89fd --- /dev/null +++ b/thirdparty/liblastfm2/src/ws/mac/MNetworkConnectionMonitor_mac.cpp @@ -0,0 +1,71 @@ +/* + Copyright 2009 Last.fm Ltd. + - Primarily authored by Jono Cole and Michael Coffey + + This file is part of liblastfm. + + liblastfm 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. + + liblastfm 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 liblastfm. If not, see . +*/ + +#include "MNetworkConnectionMonitor.h" +#include "../ws.h" + +#include +#include + +MNetworkConnectionMonitor* context = 0; + +MNetworkConnectionMonitor::MNetworkConnectionMonitor( QObject* parent ) : + NetworkConnectionMonitor( parent ) +{ + context = this; + + SCNetworkReachabilityRef ref = SCNetworkReachabilityCreateWithName( NULL, LASTFM_WS_HOSTNAME ); + SCNetworkReachabilityScheduleWithRunLoop( ref, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode ); + SCNetworkReachabilitySetCallback( ref, callback, NULL ); + CFRelease( ref ); +} + +MNetworkConnectionMonitor::~MNetworkConnectionMonitor() +{ +} + + +void +MNetworkConnectionMonitor::callback( SCNetworkReachabilityRef target, + SCNetworkConnectionFlags flags, + void * ) +{ + static bool up = true; + + // I couldn't find any diffinitive usage examples for these flags + // so I had to guess, since I can't test, eg. dial up :( + + bool b; + if (flags & kSCNetworkFlagsConnectionRequired) + b = false; + else + b = flags & (kSCNetworkFlagsReachable | kSCNetworkFlagsTransientConnection | kSCNetworkFlagsConnectionAutomatic); + + qDebug() << "Can reach " LASTFM_WS_HOSTNAME ":" << b << ", flags:" << flags; + + // basically, avoids telling everyone that we're up already on startup + if (up == b) + return; + + up = b; + + context->setConnected(b); +} + diff --git a/thirdparty/liblastfm2/src/ws/mac/ProxyDict.h b/thirdparty/liblastfm2/src/ws/mac/ProxyDict.h new file mode 100644 index 000000000..2d873f88a --- /dev/null +++ b/thirdparty/liblastfm2/src/ws/mac/ProxyDict.h @@ -0,0 +1,75 @@ +/* + Copyright 2009 Last.fm Ltd. + - Primarily authored by Max Howell, Jono Cole and Doug Mansell + + This file is part of liblastfm. + + liblastfm 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. + + liblastfm 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 liblastfm. If not, see . +*/ +#include + + +struct ProxyDict +{ + ProxyDict(); + + int port; + QString host; + + bool isProxyEnabled() const { return port > 0 && host.size(); } +}; + + +inline ProxyDict::ProxyDict() : port( 0 ) +{ + // Get the dictionary. + CFDictionaryRef proxyDict = SCDynamicStoreCopyProxies( NULL ); + bool result = (proxyDict != NULL); + + // Get the enable flag. This isn't a CFBoolean, but a CFNumber. + CFNumberRef enableNum; + int enable; + if (result) { + enableNum = (CFNumberRef) CFDictionaryGetValue( proxyDict, kSCPropNetProxiesHTTPEnable ); + result = (enableNum != NULL) && (CFGetTypeID(enableNum) == CFNumberGetTypeID()); + } + if (result) + result = CFNumberGetValue( enableNum, kCFNumberIntType, &enable ) && (enable != 0); + + // Get the proxy host. DNS names must be in ASCII. If you + // put a non-ASCII character in the "Secure Web Proxy" + // field in the Network preferences panel, the CFStringGetCString + // function will fail and this function will return false. + CFStringRef hostStr; + if (result) { + hostStr = (CFStringRef) CFDictionaryGetValue( proxyDict, kSCPropNetProxiesHTTPProxy ); + result = (hostStr != NULL) && (CFGetTypeID(hostStr) == CFStringGetTypeID()); + } + if (result) + host = lastfm::CFStringToQString( hostStr ); + + // get the proxy port + CFNumberRef portNum; + + if (result) { + portNum = (CFNumberRef) CFDictionaryGetValue( proxyDict, kSCPropNetProxiesHTTPPort ); + result = (portNum != NULL) && (CFGetTypeID(portNum) == CFNumberGetTypeID()); + } + if (result) + result = CFNumberGetValue( portNum, kCFNumberIntType, &port ); + + // clean up. + if (proxyDict != NULL) + CFRelease( proxyDict ); +} diff --git a/thirdparty/liblastfm2/src/ws/win/ComSetup.h b/thirdparty/liblastfm2/src/ws/win/ComSetup.h new file mode 100644 index 000000000..fc3816bd7 --- /dev/null +++ b/thirdparty/liblastfm2/src/ws/win/ComSetup.h @@ -0,0 +1,63 @@ +/* + Copyright 2009 Last.fm Ltd. + - Primarily authored by Max Howell, Jono Cole and Doug Mansell + + This file is part of liblastfm. + + liblastfm 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. + + liblastfm 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 liblastfm. If not, see . +*/ +#ifndef _WIN32_WINNT +// bring in CoInitializeSecurity from objbase.h +#define _WIN32_WINNT 0x0400 +#endif + +#include +#include +#include + + +/** @brief WsConnectionMonitor needs Com to work as early as possible so we do this + * @author + */ +class ComSetup +{ +public: + ComSetup() + { + HRESULT hr = CoInitialize(0); + m_bComInitialised = SUCCEEDED(hr); + _ASSERT(m_bComInitialised); + if (m_bComInitialised) { + setupSecurity(); + } + } + + void setupSecurity() + { + CSecurityDescriptor sd; + sd.InitializeFromThreadToken(); + HRESULT hr = CoInitializeSecurity(sd, -1, NULL, NULL, RPC_C_AUTHN_LEVEL_PKT, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE, NULL); + _ASSERT(SUCCEEDED(hr)); + } + + ~ComSetup() + { + if (m_bComInitialised) { + CoUninitialize(); + } + } + +private: + bool m_bComInitialised; +}; diff --git a/thirdparty/liblastfm2/src/ws/win/IeSettings.h b/thirdparty/liblastfm2/src/ws/win/IeSettings.h new file mode 100644 index 000000000..5d756a5ea --- /dev/null +++ b/thirdparty/liblastfm2/src/ws/win/IeSettings.h @@ -0,0 +1,43 @@ +/* + Copyright 2009 Last.fm Ltd. + - Primarily authored by Max Howell, Jono Cole and Doug Mansell + + This file is part of liblastfm. + + liblastfm 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. + + liblastfm 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 liblastfm. If not, see . +*/ +#include +#include + + +/** @brief memory managing wrapper for WINHTTP_CURRENT_USER_IE_PROXY_CONFIG + * @author + */ +struct IeSettings : WINHTTP_CURRENT_USER_IE_PROXY_CONFIG +{ + IeSettings() + { + if (!WinHttpGetIEProxyConfigForCurrentUser(this)) { + fAutoDetect = FALSE; + lpszAutoConfigUrl = lpszProxy = lpszProxyBypass = 0; + } + } + + ~IeSettings() + { + if (lpszAutoConfigUrl) GlobalFree(lpszAutoConfigUrl); + if (lpszProxy) GlobalFree(lpszProxy); + if (lpszProxyBypass) GlobalFree(lpszProxyBypass); + } +}; diff --git a/thirdparty/liblastfm2/src/ws/win/NdisEvents.cpp b/thirdparty/liblastfm2/src/ws/win/NdisEvents.cpp new file mode 100644 index 000000000..7263af24b --- /dev/null +++ b/thirdparty/liblastfm2/src/ws/win/NdisEvents.cpp @@ -0,0 +1,87 @@ +/* + Copyright 2009 Last.fm Ltd. + - Primarily authored by Max Howell, Jono Cole and Doug Mansell + + This file is part of liblastfm. + + liblastfm 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. + + liblastfm 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 liblastfm. If not, see . +*/ +#include "NdisEvents.h" +#include "WmiSink.h" + +// see http://msdn.microsoft.com/en-us/magazine/cc301850.aspx for +// more about Ndis and wmi and getting these events + +// Link to wbemuuid.lib to resolve IWbemObjectSink and IWbemClassObject +// interface definitions. + +NdisEvents::NdisEvents() + : m_pSink(0) +{} + +NdisEvents::~NdisEvents() +{ + if (m_pSink) + m_pSink->disconnect(); + if (m_pServices && m_pSink) + m_pServices->CancelAsyncCall(m_pSink); + // and reference counting will take care of the WmiSink object +} + +HRESULT +NdisEvents::registerForNdisEvents() +{ + HRESULT hr = m_pLocator.CoCreateInstance(CLSID_WbemLocator); + if (FAILED(hr)) + return hr; + + // Connect to the root\wmi namespace with the current user. + hr = m_pLocator->ConnectServer(CComBSTR("ROOT\\WMI"), // strNetworkResource + NULL, // strUser + NULL, // strPassword + NULL, // strLocale + 0, // lSecurityFlags + CComBSTR(""), // strAuthority + NULL, // pCtx + &m_pServices + ); + if (FAILED(hr)) + return hr; + + m_pSink = new WmiSink(this); + + ////////////////////////// + + // other notifications we're not interested in right now include... + // MSNdis_NotifyAdapterArrival \DEVICE\ + // MSNdis_NotifyAdapterRemoval + // MSNdis_StatusLinkSpeedChange + // MSNdis_NotifyVcArrival + // MSNdis_NotifyVcRemoval + // MSNdis_StatusResetStart + // MSNdis_StatusResetEnd + // MSNdis_StatusProtocolBind + // MSNdis_StatusProtocolUnbind + // MSNdis_StatusMediaSpecificIndication + + CComBSTR wql("WQL"); + CComBSTR query("SELECT * FROM MSNdis_StatusMediaDisconnect"); + hr = m_pServices->ExecNotificationQueryAsync(wql, query, 0, 0, m_pSink); + + query = "SELECT * FROM MSNdis_StatusMediaConnect"; + hr = m_pServices->ExecNotificationQueryAsync(wql, query, 0, 0, m_pSink); + + return S_OK; +} + diff --git a/thirdparty/liblastfm2/src/ws/win/NdisEvents.h b/thirdparty/liblastfm2/src/ws/win/NdisEvents.h new file mode 100644 index 000000000..1e1cfcf9c --- /dev/null +++ b/thirdparty/liblastfm2/src/ws/win/NdisEvents.h @@ -0,0 +1,44 @@ +/* + Copyright 2009 Last.fm Ltd. + - Primarily authored by Max Howell, Jono Cole and Doug Mansell + + This file is part of liblastfm. + + liblastfm 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. + + liblastfm 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 liblastfm. If not, see . +*/ +#ifndef NDIS_EVENTS_H +#define NDIS_EVENTS_H + +#include +#include +#include + +class NdisEvents +{ +public: + NdisEvents(); + ~NdisEvents(); + HRESULT registerForNdisEvents(); + + virtual void onConnectionUp(BSTR name) = 0; + virtual void onConnectionDown(BSTR name) = 0; + +private: + CComPtr m_pLocator; + CComPtr m_pServices; + class WmiSink *m_pSink; +}; + +#endif + diff --git a/thirdparty/liblastfm2/src/ws/win/Pac.cpp b/thirdparty/liblastfm2/src/ws/win/Pac.cpp new file mode 100644 index 000000000..3e3d72b12 --- /dev/null +++ b/thirdparty/liblastfm2/src/ws/win/Pac.cpp @@ -0,0 +1,128 @@ +/* + Copyright 2009 Last.fm Ltd. + - Primarily authored by Max Howell, Jono Cole and Doug Mansell + + This file is part of liblastfm. + + liblastfm 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. + + liblastfm 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 liblastfm. If not, see . +*/ +#include "Pac.h" +#include +#include +#include +#include +#include + + +static bool +parsePacServer(const QString &s, QNetworkProxy &p) +{ + // remove optional leading "scheme=" portion + int start = s.indexOf('='); + QUrl url(s.mid(start+1), QUrl::TolerantMode); + + if (url.isValid()) + { + p.setHostName(url.host()); + p.setPort(url.port()); + return true; + } + return false; +} + + +static QList +parsePacResult(const QString &pacResult) +{ + // msdn says: "The proxy server list contains one or more of the + // following strings separated by semicolons or whitespace." + // ([=]["://"][":"]) + + QList result; + QStringList proxies = pacResult.split(QRegExp("[\\s;]"), QString::SkipEmptyParts); + foreach(const QString &s, proxies) + { + QNetworkProxy proxy( QNetworkProxy::HttpProxy ); + if (parsePacServer(s, proxy)) + { + result << proxy; + } + } + return result; +} + + +//////////////// + + +lastfm::Pac::Pac() + : m_bFailed( false ) + , m_hSession( 0 ) +{} + +lastfm::Pac::~Pac() +{ + if (m_hSession) + WinHttpCloseHandle(m_hSession); +} + +QNetworkProxy +lastfm::Pac::resolve(const QNetworkRequest &request, const wchar_t* pacUrl) +{ + QNetworkProxy out; + if (m_bFailed) return out; + + if (!m_hSession) + { + QByteArray user_agent = request.rawHeader("user-agent"); + m_hSession = WinHttpOpen(CA2W(user_agent), WINHTTP_ACCESS_TYPE_NO_PROXY, 0, 0, WINHTTP_FLAG_ASYNC); + } + if (m_hSession) + { + WINHTTP_PROXY_INFO info; + WINHTTP_AUTOPROXY_OPTIONS opts; + memset(&opts, 0, sizeof(opts)); + if (pacUrl) + { + opts.dwFlags = WINHTTP_AUTOPROXY_CONFIG_URL; + opts.lpszAutoConfigUrl = pacUrl; + } + else + { + opts.dwFlags = WINHTTP_AUTOPROXY_AUTO_DETECT; + opts.dwAutoDetectFlags = WINHTTP_AUTO_DETECT_TYPE_DHCP | WINHTTP_AUTO_DETECT_TYPE_DNS_A; + } + opts.fAutoLogonIfChallenged = TRUE; + + if (WinHttpGetProxyForUrl(m_hSession, request.url().toString().utf16(), &opts, &info)) { + if (info.lpszProxy) + { + QList proxies = parsePacResult(QString::fromUtf16(info.lpszProxy)); + if (!proxies.empty()) + { + out = proxies.at(0); + } + GlobalFree(info.lpszProxy); + } + if (info.lpszProxyBypass) + { + GlobalFree(info.lpszProxyBypass); + } + } else { + m_bFailed = true; + } + } + + return out; +} diff --git a/thirdparty/liblastfm2/src/ws/win/Pac.h b/thirdparty/liblastfm2/src/ws/win/Pac.h new file mode 100644 index 000000000..075d128f6 --- /dev/null +++ b/thirdparty/liblastfm2/src/ws/win/Pac.h @@ -0,0 +1,52 @@ +/* + Copyright 2009 Last.fm Ltd. + - Primarily authored by Max Howell, Jono Cole and Doug Mansell + + This file is part of liblastfm. + + liblastfm 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. + + liblastfm 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 liblastfm. If not, see . +*/ +#ifndef WS_AUTOPROXY_H +#define WS_AUTOPROXY_H + +#include +#include +#include +class QNetworkRequest; + +namespace lastfm +{ + /** @brief simple wrapper to do per url automatic proxy detection + * @author + */ + class Pac + { + HINTERNET m_hSession; + bool m_bFailed; + + public: + Pac(); + ~Pac(); + + QNetworkProxy resolve( const QNetworkRequest& url, const wchar_t* pacUrl ); + + void resetFailedState() { m_bFailed = false; } + + private: + Pac( const Pac& ); //undefined + Pac operator=( const Pac& ); //undefined + }; +} + +#endif \ No newline at end of file diff --git a/thirdparty/liblastfm2/src/ws/win/WNetworkConnectionMonitor.h b/thirdparty/liblastfm2/src/ws/win/WNetworkConnectionMonitor.h new file mode 100755 index 000000000..26fb58cdd --- /dev/null +++ b/thirdparty/liblastfm2/src/ws/win/WNetworkConnectionMonitor.h @@ -0,0 +1,44 @@ +/* + Copyright 2010 Last.fm Ltd. + - Primarily authored by Jono Cole, Michael Coffey, and William Viana + + This file is part of liblastfm. + + liblastfm 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. + + liblastfm 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 liblastfm. If not, see . +*/ + +#ifndef WNETWORK_CONNECTION_MONITOR_H +#define WNETWORK_CONNECTION_MONITOR_H + +#include "../NetworkConnectionMonitor.h" +#include +#include + +namespace lastfm { class NdisEventsProxy; } + +class WNetworkConnectionMonitor : public NetworkConnectionMonitor +{ + Q_OBJECT +public: + friend class lastfm::NdisEventsProxy; + + WNetworkConnectionMonitor( QObject* parent = 0 ); + ~WNetworkConnectionMonitor(); + +private: + lastfm::NdisEventsProxy* m_ndisEventsProxy; +}; + +#endif // WNETWORK_CONNECTION_MONITOR_H + diff --git a/thirdparty/liblastfm2/src/ws/win/WNetworkConnectionMonitor_win.cpp b/thirdparty/liblastfm2/src/ws/win/WNetworkConnectionMonitor_win.cpp new file mode 100755 index 000000000..09fd97cc5 --- /dev/null +++ b/thirdparty/liblastfm2/src/ws/win/WNetworkConnectionMonitor_win.cpp @@ -0,0 +1,67 @@ +/* + Copyright 2010 Last.fm Ltd. + - Primarily authored by Jono Cole, Michael Coffey, and William Viana + + This file is part of liblastfm. + + liblastfm 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. + + liblastfm 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 liblastfm. If not, see . +*/ + +#include "WNetworkConnectionMonitor.h" + +// WsAccessManager needs special init (on Windows), and it needs to be done +// early, so be careful about moving this +#include "../win/ComSetup.h" //must be first header or compile fail results! +#include "../win/NdisEvents.h" +static ComSetup com_setup; + +namespace lastfm { + +// bounce NdisEvents signals through here so we don't have to expose the +// NdisEvents interface in InternetConnectionMonitor :) +class NdisEventsProxy : public NdisEvents +{ +public: + NdisEventsProxy(WNetworkConnectionMonitor* icm) + :m_icm(icm) + { + } + + // WmiSink callbacks: + void onConnectionUp( BSTR /*name*/ ) + { + m_icm->setConnected( true ); + } + + void onConnectionDown( BSTR /*name*/ ) + { + m_icm->setConnected( false ); + } + + WNetworkConnectionMonitor* m_icm; +}; + +} + +WNetworkConnectionMonitor::WNetworkConnectionMonitor( QObject* parent ) : + NetworkConnectionMonitor( parent ) +{ + m_ndisEventsProxy = new lastfm::NdisEventsProxy( this ); + m_ndisEventsProxy->registerForNdisEvents(); +} + +WNetworkConnectionMonitor::~WNetworkConnectionMonitor() +{ + delete m_ndisEventsProxy; +} diff --git a/thirdparty/liblastfm2/src/ws/win/WmiSink.cpp b/thirdparty/liblastfm2/src/ws/win/WmiSink.cpp new file mode 100644 index 000000000..3d564266c --- /dev/null +++ b/thirdparty/liblastfm2/src/ws/win/WmiSink.cpp @@ -0,0 +1,202 @@ +/* + Copyright 2009 Last.fm Ltd. + - Primarily authored by Max Howell, Jono Cole and Doug Mansell + + This file is part of liblastfm. + + liblastfm 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. + + liblastfm 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 liblastfm. If not, see . +*/ +#include "WmiSink.h" +#include "NdisEvents.h" + + +WmiSink::WmiSink(NdisEvents *callback) + : m_cRef(1) + , m_callback(callback) +{} + +WmiSink::~WmiSink() +{} + +void +WmiSink::disconnect() +{ + m_callback = 0; +} + +STDMETHODIMP +WmiSink::QueryInterface(REFIID riid, LPVOID * ppv) +{ + *ppv = 0; + + if (IID_IUnknown==riid || IID_IWbemObjectSink == riid) + { + *ppv = (IWbemObjectSink *) this; + AddRef(); + return NOERROR; + } + + return E_NOINTERFACE; +} + + +ULONG +WmiSink::AddRef() +{ + return ++m_cRef; +} + +ULONG +WmiSink::Release() +{ + if (0 != --m_cRef) + return m_cRef; + + delete this; + return 0; +} + +// This method receives notification objects. +HRESULT +WmiSink::Indicate(long lObjectCount, IWbemClassObject** ppObjArray) +{ + // For each object in the array, extract the object and display the + // information in the object. + for (long i=0; iGet(L"InstanceName", 0, &vt, NULL, NULL); + ppObjArray[i]->Get(L"__Class", 0, &vtClass, NULL, NULL); + + if (!wcscmp (vtClass.bstrVal, L"MSNdis_StatusMediaDisconnect")) + { + if (m_callback) m_callback->onConnectionDown(vt.bstrVal); + } + else if (!wcscmp (vtClass.bstrVal, L"MSNdis_StatusMediaConnect")) + { + if (m_callback) m_callback->onConnectionUp(vt.bstrVal); + } + // notifications we aren't interested in right now: + // + //else if (!wcscmp (vtClass.bstrVal, L"MSNdis_NotifyAdapterRemoval")) + //{ + // bstrLog = (_bstr_t) vt.bstrVal; + // VariantClear (&vt); + // ppObjArray[i]->Get (L"DeviceName", 0, &vt, NULL, NULL); + // bstrLog += (_bstr_t) _T(": ") + (_bstr_t) vt.bstrVal + (_bstr_t) _T(" has been removed"); + // displayDlg.LogEvent (bstrLog); + //} + //else if (!wcscmp (vtClass.bstrVal, L"MSNdis_NotifyAdapterArrival")) + //{ + // bstrLog = (_bstr_t) vt.bstrVal; + // VariantClear (&vt); + // ppObjArray[i]->Get(L"DeviceName", 0, &vt, NULL, NULL); + // bstrLog += (_bstr_t) _T(": ") + (_bstr_t) vt.bstrVal + (_bstr_t) _T(" has been added"); + // displayDlg.LogEvent (bstrLog); + //} + //else if (!wcscmp (vtClass.bstrVal, L"MSNdis_StatusResetStart")) + //{ + // bstrLog = (_bstr_t) vt.bstrVal + (_bstr_t) _T(" has begun a reset"); + // displayDlg.LogEvent (bstrLog); + //} + //else if (!wcscmp (vtClass.bstrVal, L"MSNdis_StatusResetEnd")) + //{ + // bstrLog = (_bstr_t) vt.bstrVal + (_bstr_t) _T(" has finished a reset"); + // displayDlg.LogEvent (bstrLog); + //} + //else if (!wcscmp (vtClass.bstrVal, L"MSNdis_NotifyVcArrival")) + //{ + // bstrLog = (_bstr_t) _T("VC arrival: ") + (_bstr_t) vt.bstrVal; + // displayDlg.LogEvent (bstrLog); + //} + //else if (!wcscmp (vtClass.bstrVal, L"MSNdis_NotifyVcRemoval")) + //{ + // bstrLog = (_bstr_t) _T("VC removal: ") + (_bstr_t) vt.bstrVal; + // displayDlg.LogEvent (bstrLog); + //} + //else if (!wcscmp (vtClass.bstrVal, L"MSNdis_StatusMediaSpecificIndication")) + //{ + // ATLTRACE (_T("Media specific indication: %s\n"), (TCHAR *) (_bstr_t) vt.bstrVal); + // VariantClear (&vt); + // ppObjArray[i]->Get (L"NdisStatusMediaSpecificIndication", 0, &vt, NULL, NULL); + // LONG lLowerBound, lUpperBound, j; + // UCHAR ch; + // SafeArrayGetLBound (V_ARRAY (&vt), 1, &lLowerBound); + // SafeArrayGetUBound (V_ARRAY (&vt), 1, &lUpperBound); + // ATLTRACE (" "); + // for (j = lLowerBound; j<= lUpperBound; j++ ) + // { + // SafeArrayGetElement (V_ARRAY (&vt), &j, &ch); + // ATLTRACE (_T("%4i"), ch); + + // if (((j - lLowerBound) % 8 == 7) && (j <= lUpperBound)) + // ATLTRACE (_T("\n")); + // } + //} + //else if (!wcscmp (vtClass.bstrVal, L"MSNdis_StatusProtocolBind")) + //{ + // bstrLog = (_bstr_t) vt.bstrVal; + // VariantClear (&vt); + // ppObjArray[i]->Get (L"Transport", 0, &vt, NULL, NULL); + // bstrLog += (_bstr_t) _T(" is now bound to ") + (_bstr_t) vt.bstrVal; + // displayDlg.LogEvent (bstrLog); + //} + //else if (!wcscmp (vtClass.bstrVal, L"MSNdis_StatusProtocolBind")) + //{ + // bstrLog = (_bstr_t) vt.bstrVal; + // VariantClear (&vt); + // ppObjArray[i]->Get(L"Transport", 0, &vt, NULL, NULL); + // bstrLog += (_bstr_t) _T(" was unbound from ") + (_bstr_t) vt.bstrVal; + // displayDlg.LogEvent (bstrLog); + //} + //else if (!wcscmp (vtClass.bstrVal, L"MSNdis_StatusLinkSpeedChange")) + //{ + // IWbemClassObject* pWMIObj=NULL; + // bstrLog = (_bstr_t) _T("Link speed change ") + (_bstr_t) vt.bstrVal; + // VariantClear (&vt); + // ppObjArray[i]->Get (L"NdisStatusLinkSpeedChange", 0, &vt, NULL, NULL); + // if SUCCEEDED (vt.punkVal->QueryInterface (IID_IWbemClassObject, (void**)&pWMIObj)) + // { + // TCHAR szNum[50]; + // pWMIObj->Get (L"Inbound", 0, &vt2, NULL, NULL); + // _stprintf (szNum, _T(" Inbound = %u "), vt2.lVal); + // bstrLog += (_bstr_t) szNum; + // VariantClear (&vt2); + // pWMIObj->Get (L"Outbound", 0, &vt2, NULL, NULL); + // _stprintf (szNum, _T(" Outbound = %u "), vt2.lVal); + // bstrLog += (_bstr_t) szNum; + // VariantClear (&vt2); + // pWMIObj->Release (); + // pWMIObj = NULL; + // } + // displayDlg.LogEvent (bstrLog); + //} + + VariantClear (&vtClass); + VariantClear (&vt); + } + return WBEM_NO_ERROR; +} + + +// Misc. status codes sent by sink. +HRESULT +WmiSink::SetStatus(long lFlags, HRESULT hResult, BSTR strParam, IWbemClassObject __RPC_FAR *pObjParam) +{ + lFlags; + hResult; + strParam; + pObjParam; + return WBEM_NO_ERROR; +} diff --git a/thirdparty/liblastfm2/src/ws/win/WmiSink.h b/thirdparty/liblastfm2/src/ws/win/WmiSink.h new file mode 100644 index 000000000..1bce28fdd --- /dev/null +++ b/thirdparty/liblastfm2/src/ws/win/WmiSink.h @@ -0,0 +1,49 @@ +/* + Copyright 2009 Last.fm Ltd. + - Primarily authored by Max Howell, Jono Cole and Doug Mansell + + This file is part of liblastfm. + + liblastfm 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. + + liblastfm 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 liblastfm. If not, see . +*/ +#ifndef WMISINK_WIN_H +#define WMISINK_WIN_H + +#include "WbemCli.h" + +// Sink object for WMI NDIS notifications +class WmiSink : public IWbemObjectSink +{ + UINT m_cRef; + +public: + WmiSink(class NdisEvents *callback); + ~WmiSink(); + + // IUnknown members + STDMETHODIMP QueryInterface(REFIID, LPVOID *); + STDMETHODIMP_(ULONG) AddRef(void); + STDMETHODIMP_(ULONG) Release(void); + + // IWbemObjectSink + STDMETHODIMP Indicate(long, IWbemClassObject**); + STDMETHODIMP SetStatus(long, HRESULT, BSTR, IWbemClassObject *); + + void disconnect(); + +private: + class NdisEvents *m_callback; +}; + +#endif \ No newline at end of file diff --git a/thirdparty/liblastfm2/src/ws/ws.cpp b/thirdparty/liblastfm2/src/ws/ws.cpp new file mode 100644 index 000000000..89c6ece6b --- /dev/null +++ b/thirdparty/liblastfm2/src/ws/ws.cpp @@ -0,0 +1,268 @@ +/* + Copyright 2009 Last.fm Ltd. + - Primarily authored by Max Howell, Jono Cole and Doug Mansell + + This file is part of liblastfm. + + liblastfm 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. + + liblastfm 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 liblastfm. If not, see . +*/ +#include "ws.h" +#include "../core/misc.h" +#include "NetworkAccessManager.h" +#include +#include +#include +#include +#include +#include +static QNetworkAccessManager* nam = 0; + + +QString +lastfm::ws::host() +{ + QStringList const args = QCoreApplication::arguments(); + if (args.contains( "--debug")) + return "ws.staging.audioscrobbler.com"; + + int const n = args.indexOf( "--host" ); + if (n != -1 && args.count() > n+1) + return args[n+1]; + + return LASTFM_WS_HOSTNAME; +} + +static QUrl url() +{ + QUrl url; + url.setScheme( "http" ); + url.setHost( lastfm::ws::host() ); + url.setPath( "/2.0/" ); + return url; +} + +static QString iso639() +{ + return QLocale().name().left( 2 ).toLower(); +} + +void autograph( QMap& params ) +{ + params["api_key"] = lastfm::ws::ApiKey; + params["lang"] = iso639(); +} + +static void sign( QMap& params, bool sk = true ) +{ + autograph( params ); + // it's allowed for sk to be null if we this is an auth call for instance + if (sk && lastfm::ws::SessionKey.size()) + params["sk"] = lastfm::ws::SessionKey; + + QString s; + QMapIterator i( params ); + while (i.hasNext()) { + i.next(); + s += i.key() + i.value(); + } + s += lastfm::ws::SharedSecret; + + params["api_sig"] = lastfm::md5( s.toUtf8() ); +} + + +QNetworkReply* +lastfm::ws::get( QMap params ) +{ + sign( params ); + QUrl url = ::url(); + // Qt setQueryItems doesn't encode a bunch of stuff, so we do it manually + QMapIterator i( params ); + while (i.hasNext()) { + i.next(); + QByteArray const key = QUrl::toPercentEncoding( i.key() ); + QByteArray const value = QUrl::toPercentEncoding( i.value() ); + url.addEncodedQueryItem( key, value ); + } + + qDebug() << url; + + return nam()->get( QNetworkRequest(url) ); +} + + +QNetworkReply* +lastfm::ws::post( QMap params, bool sk ) +{ + sign( params, sk ); + QByteArray query; + QMapIterator i( params ); + while (i.hasNext()) { + i.next(); + query += QUrl::toPercentEncoding( i.key() ) + + '=' + + QUrl::toPercentEncoding( i.value() ) + + '&'; + } + + return nam()->post( QNetworkRequest(url()), query ); +} + + +QByteArray +lastfm::ws::parse( QNetworkReply* reply ) throw( ParseError ) +{ + try + { + QByteArray data = reply->readAll(); + + if (!data.size()) + throw MalformedResponse; + + QDomDocument xml; + xml.setContent( data ); + QDomElement lfm = xml.documentElement(); + + if (lfm.isNull()) + throw MalformedResponse; + + QString const status = lfm.attribute( "status" ); + QDomElement error = lfm.firstChildElement( "error" ); + uint const n = lfm.childNodes().count(); + + // no elements beyond the lfm is perfectably acceptable <-- wtf? + // if (n == 0) // nothing useful in the response + if (status == "failed" || (n == 1 && !error.isNull()) ) + throw error.isNull() + ? MalformedResponse + : Error( error.attribute( "code" ).toUInt() ); + + switch (reply->error()) + { + case QNetworkReply::RemoteHostClosedError: + case QNetworkReply::ConnectionRefusedError: + case QNetworkReply::TimeoutError: + case QNetworkReply::ContentAccessDenied: + case QNetworkReply::ContentOperationNotPermittedError: + case QNetworkReply::UnknownContentError: + case QNetworkReply::ProtocolInvalidOperationError: + case QNetworkReply::ProtocolFailure: + throw TryAgainLater; + + case QNetworkReply::NoError: + default: + break; + } + + //FIXME pretty wasteful to parse XML document twice.. + return data; + } + catch (Error e) + { + switch (e) + { + case OperationFailed: + case InvalidApiKey: + case InvalidSessionKey: + // NOTE will never be received during the LoginDialog stage + // since that happens before this slot is registered with + // QMetaObject in App::App(). Neat :) + QMetaObject::invokeMethod( qApp, "onWsError", Q_ARG( lastfm::ws::Error, e ) ); + default: + throw ParseError(e); + } + } + + // bit dodgy, but prolly for the best + reply->deleteLater(); +} + + +QNetworkAccessManager* +lastfm::nam() +{ + if (!::nam) ::nam = new NetworkAccessManager( qApp ); + return ::nam; +} + + +void +lastfm::setNetworkAccessManager( QNetworkAccessManager* nam ) +{ + delete ::nam; + ::nam = nam; + nam->setParent( qApp ); // ensure it isn't deleted out from under us +} + + +/** This useful function, fromHttpDate, comes from QNetworkHeadersPrivate + * in qnetworkrequest.cpp. Qt copyright and license apply. */ +static QDateTime QByteArrayToHttpDate(const QByteArray &value) +{ + // HTTP dates have three possible formats: + // RFC 1123/822 - ddd, dd MMM yyyy hh:mm:ss "GMT" + // RFC 850 - dddd, dd-MMM-yy hh:mm:ss "GMT" + // ANSI C's asctime - ddd MMM d hh:mm:ss yyyy + // We only handle them exactly. If they deviate, we bail out. + + int pos = value.indexOf(','); + QDateTime dt; + if (pos == -1) { + // no comma -> asctime(3) format + dt = QDateTime::fromString(QString::fromLatin1(value), Qt::TextDate); + } else { + // eat the weekday, the comma and the space following it + QString sansWeekday = QString::fromLatin1(value.constData() + pos + 2); + + QLocale c = QLocale::c(); + if (pos == 3) + // must be RFC 1123 date + dt = c.toDateTime(sansWeekday, QLatin1String("dd MMM yyyy hh:mm:ss 'GMT")); + else + // must be RFC 850 date + dt = c.toDateTime(sansWeekday, QLatin1String("dd-MMM-yy hh:mm:ss 'GMT'")); + } + + if (dt.isValid()) + dt.setTimeSpec(Qt::UTC); + return dt; +} + + +QDateTime +lastfm::ws::expires( QNetworkReply* reply ) +{ + return QByteArrayToHttpDate( reply->rawHeader( "Expires" ) ); +} + + +namespace lastfm +{ + namespace ws + { + QString SessionKey; + QString Username; + + /** we leave these unset as you can't use the webservices without them + * so lets make the programmer aware of it during testing by crashing */ + const char* SharedSecret; + const char* ApiKey; + + /** if this is found set to "" we conjure ourselves a suitable one */ + const char* UserAgent = 0; + } +} + + +QDebug operator<<( QDebug, lastfm::ws::Error ); diff --git a/thirdparty/liblastfm2/src/ws/ws.h b/thirdparty/liblastfm2/src/ws/ws.h new file mode 100644 index 000000000..b6f7cd922 --- /dev/null +++ b/thirdparty/liblastfm2/src/ws/ws.h @@ -0,0 +1,161 @@ +/* + Copyright 2009 Last.fm Ltd. + - Primarily authored by Max Howell, Jono Cole and Doug Mansell + + This file is part of liblastfm. + + liblastfm 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. + + liblastfm 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 liblastfm. If not, see . +*/ +#ifndef LASTFM_WS_H +#define LASTFM_WS_H + +#include +#include +#include +#include +#include + +#ifdef Q_CC_MSVC +// ms admits its lousy compiler doesn't care about throw declarations +#pragma warning( disable : 4290 ) +#endif + + +namespace lastfm +{ + /** if you don't set one, we create our own, our own is pretty good + * for instance, it auto detects proxy settings on windows and mac + * We take ownership of the NAM, do not delete it out from underneath us! + * So don't keep any other pointers to this around in case you accidently + * call delete on them :P */ + LASTFM_DLLEXPORT void setNetworkAccessManager( QNetworkAccessManager* nam ); + LASTFM_DLLEXPORT QNetworkAccessManager* nam(); + + namespace ws + { + /** both of these are provided when you register at http://last.fm/api */ + LASTFM_DLLEXPORT extern const char* SharedSecret; + LASTFM_DLLEXPORT extern const char* ApiKey; + + /** you need to set this for scrobbling to work (for now) + * Also the AuthenticatedUser class uses it */ + LASTFM_DLLEXPORT extern QString Username; + + /** Some webservices require authentication. See the following + * documentation: + * http://www.last.fm/api/authentication + * http://www.last.fm/api/desktopauth + * You have to authenticate and then assign to SessionKey, liblastfm does + * not do that for you. Also we do not store this. You should store this! + * You only need to authenticate once, and that key lasts forever! + */ + LASTFM_DLLEXPORT extern QString SessionKey; + + enum Error + { + NoError = 1, // because last.fm error numbers start at 2 + + /** numbers follow those at http://last.fm/api/ */ + InvalidService = 2, + InvalidMethod, + AuthenticationFailed, + InvalidFormat, + InvalidParameters, + InvalidResourceSpecified, + OperationFailed, + InvalidSessionKey, + InvalidApiKey, + ServiceOffline, + SubscribersOnly, + + Reserved13, + Reserved14, + Reserved15, + + /** Last.fm sucks. + * There may be an error in networkError(), or this may just be some + * internal error completing your request. + * Advise the user to try again in a _few_minutes_. + * For some cases, you may want to try again yourself, at this point + * in the API you will have to. Eventually we will discourage this and + * do it for you, as we don't want to strain Last.fm's servers + */ + TryAgainLater = 16, + + Reserved17, + Reserved18, + Reserved19, + + NotEnoughContent = 20, + NotEnoughMembers, + NotEnoughFans, + NotEnoughNeighbours, + + /** Last.fm fucked up, or something mangled the response on its way */ + MalformedResponse = 100, + + /** call QNetworkReply::error() as it's nothing to do with us */ + UnknownError + }; + + LASTFM_DLLEXPORT QString host(); + + /** the map needs a method entry, as per http://last.fm/api */ + LASTFM_DLLEXPORT QNetworkReply* get( QMap ); + /** generates api sig, includes api key, and posts, don't add the api + * key yourself as well--it'll break */ + LASTFM_DLLEXPORT QNetworkReply* post( QMap, bool sessionKey = true ); + + + class ParseError : public std::runtime_error + { + Error e; + public: + explicit ParseError(Error e) : std::runtime_error("lastfm::ws::Error"), e(e) + {} + Error enumValue() const { return e; } + }; + + /** Generally you don't use this, eg. if you called Artist::getInfo(), + * use the Artist::getInfo( QNetworkReply* ) function to get the + * results, you have to pass a QDomDocument because QDomElements stop + * existing when the parent DomDocument is deleted. + * + * The QByteArray is basically reply->readAll(), so all this function + * does is sanity check the response and throw if it is bad. + * + * Thus if you don't care about errors just do: reply->readAll() + * + * Not caring about errors is often fine with Qt as you just get null + * strings and that instead, and you can handle those as you go. + * + * The QByteArray is an XML document. You can parse it with QDom or + * use our much more convenient lastfm::XmlQuery. + */ + LASTFM_DLLEXPORT QByteArray parse( QNetworkReply* reply ) throw( ParseError ); + + /** returns the expiry date of this HTTP response */ + LASTFM_DLLEXPORT QDateTime expires( QNetworkReply* ); + } +} + + +inline QDebug operator<<( QDebug d, QNetworkReply::NetworkError e ) +{ + return d << lastfm::qMetaEnumString( e, "NetworkError" ); +} + +#define LASTFM_WS_HOSTNAME "ws.audioscrobbler.com" + +#endif diff --git a/thirdparty/liblastfm2/tests/TestTrack.h b/thirdparty/liblastfm2/tests/TestTrack.h new file mode 100644 index 000000000..c771a45e9 --- /dev/null +++ b/thirdparty/liblastfm2/tests/TestTrack.h @@ -0,0 +1,35 @@ +/* + This software is in the public domain, furnished "as is", without technical + support, and with no warranty, express or implied, as to its usefulness for + any purpose. +*/ +#include +#include +using lastfm::Track; + +class TestTrack : public QObject +{ + Q_OBJECT + + Track example() + { + lastfm::MutableTrack t; + t.setTitle( "Test Title" ); + t.setArtist( "Test Artist" ); + t.setAlbum( "Test Album" ); + return t; + } + +private slots: + void testClone() + { + Track original = example(); + Track copy = original; + + #define TEST( x ) QVERIFY( original.x == copy.x ) + TEST( title() ); + TEST( artist() ); + TEST( album() ); + #undef TEST + } +}; diff --git a/thirdparty/liblastfm2/tests/TestUrlBuilder.h b/thirdparty/liblastfm2/tests/TestUrlBuilder.h new file mode 100644 index 000000000..909d10e37 --- /dev/null +++ b/thirdparty/liblastfm2/tests/TestUrlBuilder.h @@ -0,0 +1,80 @@ +/* + This software is in the public domain, furnished "as is", without technical + support, and with no warranty, express or implied, as to its usefulness for + any purpose. +*/ +#include +#include +#include +#include +#include + +static inline int getResponseCode( const QUrl& url ) +{ + QNetworkAccessManager nam; + QNetworkReply* reply = nam.head( QNetworkRequest(url) ); + + QEventLoop loop; + loop.connect( reply, SIGNAL(finished()), SLOT(quit()) ); + loop.exec(); + + int const code = reply->attribute( QNetworkRequest::HttpStatusCodeAttribute ).toInt(); + + if (reply->error() != QNetworkReply::NoError) + qDebug() << url << lastfm::qMetaEnumString( reply->error(), "NetworkError" ) << code; + + return code; +} + + +class TestUrlBuilder : public QObject +{ + Q_OBJECT + +private slots: + void encode() /** @author */ + { + QFETCH( QString, input ); + QFETCH( QString, output ); + QCOMPARE( lastfm::UrlBuilder::encode( input ), output.toAscii() ); + } + + void encode_data() /** @author */ + { + QTest::addColumn("input"); + QTest::addColumn("output"); + + QTest::newRow( "ascii" ) << "Metallica" << "Metallica"; + QTest::newRow( "ascii alphanumeric" ) << "Apollo 440" << "Apollo+440"; + QTest::newRow( "ascii with symbols" ) << "some track [original version]" << "some+track+%5Boriginal+version%5D"; + QTest::newRow( "ascii with last.fm-special symbols" ) << "Survivalism [Revision #1]" << "Survivalism%2B%255BRevision%2B%25231%255D"; + } + + void no404() /** @author */ + { + QFETCH( QString, artist ); + QFETCH( QString, track ); + + QUrl url = lastfm::UrlBuilder( "music" ).slash( artist ).slash( "_" ).slash( track ).url(); + + QCOMPARE( getResponseCode( url ), 200 ); + } + + void no404_data() /** @author */ + { + QTest::addColumn("artist"); + QTest::addColumn("track"); + + #define NEW_ROW( x, y ) QTest::newRow( x " - " y ) << x << y; + NEW_ROW( "Air", "Radio #1" ); + NEW_ROW( "Pink Floyd", "Speak to Me / Breathe" ); + NEW_ROW( "Radiohead", "2 + 2 = 5" ); + NEW_ROW( "Above & Beyond", "World On Fire (Maor Levi Remix)" ); + #undef NEW_ROW + } + + void test404() /** @author */ + { + QCOMPARE( getResponseCode( QUrl("http://www.last.fm/404") ), 404 ); + } +}; diff --git a/thirdparty/liblastfm2/tests/main.cpp b/thirdparty/liblastfm2/tests/main.cpp new file mode 100644 index 000000000..5995bffa2 --- /dev/null +++ b/thirdparty/liblastfm2/tests/main.cpp @@ -0,0 +1,22 @@ +/* + This software is in the public domain, furnished "as is", without technical + support, and with no warranty, express or implied, as to its usefulness for + any purpose. +*/ +#include +#include +#include "TestTrack.h" +#include "TestUrlBuilder.h" + +int main( int argc, char** argv) +{ + QCoreApplication app( argc, argv ); + + #define TEST( Type ) { \ + Type o; \ + if (int r = QTest::qExec( &o, argc, argv ) != 0) return r; } + + TEST( TestTrack ); + TEST( TestUrlBuilder ); + return 0; +} diff --git a/thirdparty/liblastfm2/tests/tests.pro b/thirdparty/liblastfm2/tests/tests.pro new file mode 100644 index 000000000..da65f1cde --- /dev/null +++ b/thirdparty/liblastfm2/tests/tests.pro @@ -0,0 +1,4 @@ +QT = core testlib network xml +LIBS += -llastfm -L$$DESTDIR +SOURCES = main.cpp +HEADERS = TestTrack.h TestUrlBuilder.h \ No newline at end of file diff --git a/thirdparty/libportfwd/CMakeLists.txt b/thirdparty/libportfwd/CMakeLists.txt index ad0799f9f..e19485414 100644 --- a/thirdparty/libportfwd/CMakeLists.txt +++ b/thirdparty/libportfwd/CMakeLists.txt @@ -17,6 +17,8 @@ ELSE() ENDIF() INCLUDE_DIRECTORIES(${MINIUPNP_DIR} include) +SET( CMAKE_C_FLAGS ${CLEAN_C_FLAGS} ) + ADD_LIBRARY(portfwd STATIC # the needed bits of miniupnpc (no python module, no tests, no cli) @@ -46,6 +48,6 @@ ENDIF() # ) #TARGET_LINK_LIBRARIES(portfwd-demo portfwd) -INSTALL(TARGETS portfwd ARCHIVE DESTINATION lib) +# INSTALL(TARGETS portfwd ARCHIVE DESTINATION lib${LIB_SUFFIX}) #INSTALL(TARGETS portfwd-demo RUNTIME DESTINATION bin) #INSTALL(DIRECTORY include/portfwd DESTINATION include PATTERN "*~" EXCLUDE) diff --git a/thirdparty/libportfwd/include/portfwd/portfwd.h b/thirdparty/libportfwd/include/portfwd/portfwd.h index faedd8f03..e54a00f0b 100644 --- a/thirdparty/libportfwd/include/portfwd/portfwd.h +++ b/thirdparty/libportfwd/include/portfwd/portfwd.h @@ -1,3 +1,20 @@ +/* + * 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 LIBPORTFWD_PORTFWD_H #define LIBPORTFWD_PORTFWD_H true #include diff --git a/thirdparty/libportfwd/src/main.cpp b/thirdparty/libportfwd/src/main.cpp index 724a54bf6..5d4b5050c 100644 --- a/thirdparty/libportfwd/src/main.cpp +++ b/thirdparty/libportfwd/src/main.cpp @@ -1,4 +1,22 @@ +/* + * 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 "portfwd/portfwd.h" + int main(int argc, char** argv) { if(argc!=2) diff --git a/thirdparty/libportfwd/src/portfwd.cpp b/thirdparty/libportfwd/src/portfwd.cpp index e64024621..0dd315218 100644 --- a/thirdparty/libportfwd/src/portfwd.cpp +++ b/thirdparty/libportfwd/src/portfwd.cpp @@ -1,3 +1,20 @@ +/* + * 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 "portfwd/portfwd.h" #include "miniwget.h" diff --git a/thirdparty/qtweetlib/CMakeLists.txt b/thirdparty/qtweetlib/CMakeLists.txt index 2f93871af..f3beb6c46 100644 --- a/thirdparty/qtweetlib/CMakeLists.txt +++ b/thirdparty/qtweetlib/CMakeLists.txt @@ -9,10 +9,10 @@ INCLUDE( ${QT_USE_FILE} ) add_definitions( ${QT_DEFINITIONS} ) add_definitions( -DQT_SHARED ) +add_definitions( -DQTWEETLIB_MAKEDLL ) if(WIN32) set(PLATFORM_SPECIFIC_LIBS "ws2_32.dll" "advapi32.dll" ) - add_definitions( -DQTWEETLIB_MAKEDLL ) endif(WIN32) set(TOMAHAWK_QTWEETLIB_SOURCES @@ -164,6 +164,7 @@ include_directories( . ${QT_INCLUDE_DIR} ${QT_INCLUDES} + ${QJSON_INCLUDE_DIR} qtweetlib/src ) @@ -171,15 +172,20 @@ qt4_wrap_cpp( TOMAHAWK_QTWEETLIB_MOC ${TOMAHAWK_QTWEETLIB_HEADERS} ) ADD_LIBRARY(tomahawk_qtweetlib SHARED ${TOMAHAWK_QTWEETLIB_SOURCES} ${TOMAHAWK_QTWEETLIB_MOC}) +if(APPLE) + SET(QJSON_FLAGS ${QJSON_LIBRARIES}) +else(APPLE) + SET(QJSON_FLAGS ${QJSON_LDFLAGS}) +endif(APPLE) + target_link_libraries(tomahawk_qtweetlib ${QT_LIBRARIES} - qjson -) - -#SET_TARGET_PROPERTIES( tomahawk_qtweetlib PROPERTIES DEFINE_SYMBOL MAKE_QTWEETLIB_LIB ) + ${QJSON_FLAGS} + ${QJSON_LIBRARIES} +) INCLUDE( ${CMAKE_CURRENT_SOURCE_DIR}/twitter-api-keys ) -INSTALL(TARGETS tomahawk_qtweetlib DESTINATION lib) +INSTALL(TARGETS tomahawk_qtweetlib DESTINATION lib${LIB_SUFFIX}) diff --git a/thirdparty/qtweetlib/qtweetlib/src/qtweetconvert.cpp b/thirdparty/qtweetlib/qtweetlib/src/qtweetconvert.cpp index 4fce208cc..5e8ecf3f3 100644 --- a/thirdparty/qtweetlib/qtweetlib/src/qtweetconvert.cpp +++ b/thirdparty/qtweetlib/qtweetlib/src/qtweetconvert.cpp @@ -61,6 +61,7 @@ QTweetStatus QTweetConvert::variantMapToStatus(const QVariantMap &var) status.setId(var["id"].toLongLong()); status.setInReplyToUserId(var["in_reply_to_user_id"].toLongLong()); status.setInReplyToScreenName(var["in_reply_to_screen_name"].toString()); + status.setFavorited(var["favorited"].toBool()); QVariantMap userMap = var["user"].toMap(); QTweetUser user = variantMapToUserInfo(userMap); diff --git a/thirdparty/qtweetlib/qtweetlib/src/qtweetfriendshipdestroy.cpp b/thirdparty/qtweetlib/qtweetlib/src/qtweetfriendshipdestroy.cpp index 1871ed0a2..2eab28264 100644 --- a/thirdparty/qtweetlib/qtweetlib/src/qtweetfriendshipdestroy.cpp +++ b/thirdparty/qtweetlib/qtweetlib/src/qtweetfriendshipdestroy.cpp @@ -64,7 +64,7 @@ void QTweetFriendshipDestroy::unfollow(qint64 userid, bool includeEntities) QNetworkRequest req(url); - QByteArray oauthHeader = oauthTwitter()->generateAuthorizationHeader(url, OAuth::GET); + QByteArray oauthHeader = oauthTwitter()->generateAuthorizationHeader(url, OAuth::DELETE); req.setRawHeader(AUTH_HEADER, oauthHeader); QNetworkReply *reply = oauthTwitter()->networkAccessManager()->deleteResource(req); @@ -92,7 +92,7 @@ void QTweetFriendshipDestroy::unfollow(const QString &screenName, bool includeEn QNetworkRequest req(url); - QByteArray oauthHeader = oauthTwitter()->generateAuthorizationHeader(url, OAuth::GET); + QByteArray oauthHeader = oauthTwitter()->generateAuthorizationHeader(url, OAuth::DELETE); req.setRawHeader(AUTH_HEADER, oauthHeader); QNetworkReply *reply = oauthTwitter()->networkAccessManager()->deleteResource(req); diff --git a/thirdparty/qtweetlib/qtweetlib/src/qtweetsearchresult.cpp b/thirdparty/qtweetlib/qtweetlib/src/qtweetsearchresult.cpp index 834af4e60..6e264e588 100644 --- a/thirdparty/qtweetlib/qtweetlib/src/qtweetsearchresult.cpp +++ b/thirdparty/qtweetlib/qtweetlib/src/qtweetsearchresult.cpp @@ -69,7 +69,7 @@ void QTweetSearchResult::setCreatedAt(const QDateTime &dateTime) void QTweetSearchResult::setCreatedAt(const QString &twitterDate) { QString dateString = twitterDate.left(3) + ' ' + twitterDate.mid(8, 3) + ' ' + - twitterDate.mid(5, 2) + ' ' + twitterDate.mid(13, 4); + twitterDate.mid(5, 2) + ' ' + twitterDate.mid(12, 4); QString timeString = twitterDate.mid(17, 8); QDate date = QDate::fromString(dateString); diff --git a/thirdparty/qtweetlib/qtweetlib/src/qtweetuserstream.cpp b/thirdparty/qtweetlib/qtweetlib/src/qtweetuserstream.cpp index 7e2526e46..a3d63b697 100644 --- a/thirdparty/qtweetlib/qtweetlib/src/qtweetuserstream.cpp +++ b/thirdparty/qtweetlib/qtweetlib/src/qtweetuserstream.cpp @@ -33,14 +33,28 @@ #define TWITTER_USERSTREAM_URL "https://userstream.twitter.com/2/user.json" +// ### TODO User Agent or X-User-Agent + /** * Constructor */ QTweetUserStream::QTweetUserStream(QObject *parent) : - QObject(parent), m_oauthTwitter(0), m_reply(0), m_backofftimer(new QTimer(this)) + QObject(parent), m_oauthTwitter(0), m_reply(0), + m_backofftimer(new QTimer(this)), + m_timeoutTimer(new QTimer(this)), + m_streamTryingReconnect(false) { + m_backofftimer->setInterval(20000); m_backofftimer->setSingleShot(true); connect(m_backofftimer, SIGNAL(timeout()), this, SLOT(startFetching())); + + m_timeoutTimer->setInterval(90000); + connect(m_timeoutTimer, SIGNAL(timeout()), this, SLOT(replyTimeout())); + +#ifdef STREAM_LOGGER + m_streamLog.setFileName("streamlog.txt"); + m_streamLog.open(QIODevice::WriteOnly | QIODevice::Text); +#endif } /** @@ -59,49 +73,6 @@ OAuthTwitter* QTweetUserStream::oauthTwitter() const return m_oauthTwitter; } -/** - * Called when there is network error - */ -void QTweetUserStream::replyError(QNetworkReply::NetworkError code) -{ - qDebug() << "Reply error: " << code; - - // ### TODO: determine network error codes, assumptions here - - if (code < 200) { - //linear backoff - if (m_backofftimer->interval() < 250) { - m_backofftimer->setInterval(250); - } else { - int nextLinInterval = m_backofftimer->interval() + 250; - - if (nextLinInterval > 16000) //cap - nextLinInterval = 16000; - - m_backofftimer->setInterval(nextLinInterval); - } - - m_backofftimer->start(); - return; - } - - if (code > 200) { - //exp. backoff - if (m_backofftimer->interval() < 10000) { - m_backofftimer->setInterval(10000); - } else { - int nextExpInterval = 2 * m_backofftimer->interval(); - - if (nextExpInterval > 240000) - nextExpInterval = 240000; - - m_backofftimer->setInterval(240000); - } - - m_backofftimer->start(); - } -} - /** * Starts fetching user stream */ @@ -122,7 +93,9 @@ void QTweetUserStream::startFetching() m_reply = m_oauthTwitter->networkAccessManager()->get(req); connect(m_reply, SIGNAL(finished()), this, SLOT(replyFinished())); connect(m_reply, SIGNAL(readyRead()), this, SLOT(replyReadyRead())); - connect(m_reply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(replyError(QNetworkReply::NetworkError))); + + connect(m_reply, SIGNAL(readyRead()), m_timeoutTimer, SLOT(start())); + connect(m_reply, SIGNAL(finished()), m_timeoutTimer, SLOT(stop())); } /** @@ -130,16 +103,40 @@ void QTweetUserStream::startFetching() */ void QTweetUserStream::replyFinished() { - if (!m_reply->error()) { //no error, reconnect + qDebug() << "User stream closed "; - //sligh delay for reconnect - m_backofftimer->setInterval(250); + m_streamTryingReconnect = true; + + if (!m_reply->error()) { //no error, reconnect + qDebug() << "No error, reconnect"; + + m_reply->deleteLater(); + m_reply = 0; + + startFetching(); + } else { //error + qDebug() << "Error: " << m_reply->error() << ", " << m_reply->errorString(); + + m_reply->deleteLater(); + m_reply = 0; + + //if (m_backofftimer->interval() < 20001) { + // m_backofftimer->start(); + // return; + //} + + //increase back off interval + int nextInterval = 2 * m_backofftimer->interval(); + + if (nextInterval > 300000) { + m_backofftimer->setInterval(300000); + emit failureConnect(); + } + + m_backofftimer->setInterval(nextInterval); m_backofftimer->start(); - m_reply->deleteLater(); - m_reply = 0; - } else { //error, delete QNetworkReply - m_reply->deleteLater(); - m_reply = 0; + + qDebug() << "Exp backoff interval: " << nextInterval; } } @@ -147,30 +144,53 @@ void QTweetUserStream::replyReadyRead() { QByteArray response = m_reply->readAll(); - //reset timer - m_backofftimer->setInterval(0); +#ifdef STREAM_LOGGER + m_streamLog.write(response); + m_streamLog.write("\n"); +#endif - //split to response to delimited and not delimited part - int lastCarrReturn = response.lastIndexOf('\r'); - QByteArray rightNotDelimitedPart = response.mid(lastCarrReturn + 1); - QByteArray leftDelimitedPart = response.left(lastCarrReturn); - - //prepend to left previous not delimited response - leftDelimitedPart = leftDelimitedPart.prepend(m_cashedResponse); - - QList elements = leftDelimitedPart.split('\r'); - - for (int i = 0; i < elements.size(); ++i) { - if (elements.at(i) != QByteArray(1, '\n')) { - emit stream(elements.at(i)); - parseStream(elements.at(i)); - } + if (m_streamTryingReconnect) { + emit reconnected(); + m_streamTryingReconnect = false; } - if (rightNotDelimitedPart != QByteArray(1, '\n')) - m_cashedResponse = rightNotDelimitedPart; - else - m_cashedResponse.clear(); + //set backoff timer to initial interval + m_backofftimer->setInterval(20000); + + QByteArray responseWithPreviousCache = response.prepend(m_cachedResponse); + + int start = 0; + int end; + + while ((end = responseWithPreviousCache.indexOf('\r', start)) != -1) { + if (start != end) { + QByteArray element = responseWithPreviousCache.mid(start, end - start); + + if (!element.isEmpty()) { + emit stream(element); + parseStream(element); + } + } + + int skip = (response.at(end + 1) == QLatin1Char('\n')) ? 2 : 1; + start = end + skip; + } + + //undelimited part just cache it + m_cachedResponse.clear(); + + if (start != responseWithPreviousCache.size()) { + QByteArray element = responseWithPreviousCache.mid(start); + if (!element.isEmpty()) + m_cachedResponse = element; + } +} + +void QTweetUserStream::replyTimeout() +{ + qDebug() << "Timeout connection"; + + m_reply->abort(); } void QTweetUserStream::parseStream(const QByteArray& data) @@ -187,6 +207,9 @@ void QTweetUserStream::parsingFinished(const QVariant &json, bool ok, const QStr { if (!ok) { qDebug() << "JSON parsing error: " << errorMsg; +#ifdef STREAM_LOGGER + m_streamLog.write("***** JSON parsing error *****\n"); +#endif return; } diff --git a/thirdparty/qtweetlib/qtweetlib/src/qtweetuserstream.h b/thirdparty/qtweetlib/qtweetlib/src/qtweetuserstream.h index c7f1e3bc6..114cd758f 100644 --- a/thirdparty/qtweetlib/qtweetlib/src/qtweetuserstream.h +++ b/thirdparty/qtweetlib/qtweetlib/src/qtweetuserstream.h @@ -25,6 +25,10 @@ #include #include "qtweetlib_global.h" +#ifdef STREAM_LOGGER + #include +#endif + class QNetworkAccessManager; class QNetworkReply; class OAuthTwitter; @@ -49,7 +53,6 @@ signals: * Emits stream elements */ void stream(const QByteArray& ); - /** * Emits tweets (parsed) elements from stream */ @@ -68,14 +71,24 @@ signals: * Emits deletion of status in the stream */ void deleteStatusStream(qint64 id, qint64 userid); + /** + * Emited when user stream is reconnected after failure + * Usefull when user stream connection fails to fetch missed tweets with REST API + */ + void reconnected(); + /** + * Emited when user stream doesn't connect and backoff timer reaches maximum value (300 seconds) + * Usefull when users stream fails to revert to REST API + */ + void failureConnect(); public slots: void startFetching(); private slots: - void replyError(QNetworkReply::NetworkError code); void replyFinished(); void replyReadyRead(); + void replyTimeout(); void parsingFinished(const QVariant& json, bool ok, const QString& errorMsg); private: @@ -84,10 +97,16 @@ private: void parseDirectMessage(const QVariantMap& streamObject); void parseDeleteStatus(const QVariantMap& streamObject); + QByteArray m_cachedResponse; OAuthTwitter *m_oauthTwitter; QNetworkReply *m_reply; QTimer *m_backofftimer; - QByteArray m_cashedResponse; + QTimer *m_timeoutTimer; + bool m_streamTryingReconnect; + +#ifdef STREAM_LOGGER + QFile m_streamLog; +#endif }; #endif // QTWEETUSERSTREAM_H diff --git a/thirdparty/qtweetlib/tomahawk-custom/tomahawkoauthtwitter.cpp b/thirdparty/qtweetlib/tomahawk-custom/tomahawkoauthtwitter.cpp index 580ba6daf..8f2549643 100644 --- a/thirdparty/qtweetlib/tomahawk-custom/tomahawkoauthtwitter.cpp +++ b/thirdparty/qtweetlib/tomahawk-custom/tomahawkoauthtwitter.cpp @@ -1,5 +1,6 @@ #include "tomahawkoauthtwitter.h" #include +#include TomahawkOAuthTwitter::TomahawkOAuthTwitter( QObject* parent ) : OAuthTwitter( parent ) @@ -7,12 +8,21 @@ TomahawkOAuthTwitter::TomahawkOAuthTwitter( QObject* parent ) } -int TomahawkOAuthTwitter::authorizationWidget() +int +TomahawkOAuthTwitter::authorizationWidget() { bool ok; - int i = QInputDialog::getInt(0, QString( "Twitter PIN" ), QString( "After authenticating on Twitter's web site,\nenter the displayed PIN number here:" ), 0, 0, 2147483647, 1, &ok); + int i = QInputDialog::getInt(0, tr( "Twitter PIN" ), tr( "After authenticating on Twitter's web site,\nenter the displayed PIN number here:" ), 0, 0, 2147483647, 1, &ok); if (ok) return i; return 0; } + +void +TomahawkOAuthTwitter::error() +{ + qDebug() << Q_FUNC_INFO; + setOAuthToken( QString().toLatin1() ); + setOAuthTokenSecret( QString().toLatin1() ); +} diff --git a/thirdparty/qtweetlib/tomahawk-custom/tomahawkoauthtwitter.h b/thirdparty/qtweetlib/tomahawk-custom/tomahawkoauthtwitter.h index 22690ac3c..8336f4158 100644 --- a/thirdparty/qtweetlib/tomahawk-custom/tomahawkoauthtwitter.h +++ b/thirdparty/qtweetlib/tomahawk-custom/tomahawkoauthtwitter.h @@ -16,6 +16,9 @@ public: protected: virtual int authorizationWidget(); + +private slots: + void error(); }; #endif diff --git a/thirdparty/qxt/qxtweb-standalone/CMakeLists.txt b/thirdparty/qxt/qxtweb-standalone/CMakeLists.txt index ec81d04cb..d29e7b989 100644 --- a/thirdparty/qxt/qxtweb-standalone/CMakeLists.txt +++ b/thirdparty/qxt/qxtweb-standalone/CMakeLists.txt @@ -3,6 +3,10 @@ CMAKE_MINIMUM_REQUIRED(VERSION 2.8 FATAL_ERROR) SET(CMAKE_VERBOSE_MAKEFILE ON) SET(CMAKE_INSTALL_PREFIX ".") +IF( ${CMAKE_VERSION} VERSION_GREATER 2.8.3 ) + CMAKE_POLICY(SET CMP0017 NEW) +ENDIF( ${CMAKE_VERSION} VERSION_GREATER 2.8.3 ) + FIND_PACKAGE( Qt4 4.6.0 COMPONENTS QtCore QtNetwork REQUIRED ) set(QT_USE_QTNETWORK TRUE) include( ${QT_USE_FILE} )