diff --git a/.gitignore b/.gitignore index e3f800ccc..8a9671a2c 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,6 @@ moc_* thirdparty/qtweetlib/WARNING-twitter-api-keys .kdev4 tomahawk.kdev4 +clang/ +win/ +gcc/ diff --git a/CMakeLists.txt b/CMakeLists.txt index 22f979886..83c342cd7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,6 @@ PROJECT( tomahawk ) CMAKE_MINIMUM_REQUIRED( VERSION 2.8 ) +SET( CMAKE_MODULE_PATH "${CMAKE_MODULE_PATH}" "${CMAKE_SOURCE_DIR}/CMakeModules" ) IF( ${CMAKE_VERSION} VERSION_GREATER 2.8.3 ) CMAKE_POLICY(SET CMP0017 NEW) @@ -11,15 +12,43 @@ ENDIF( ${CMAKE_VERSION} VERSION_GREATER 2.8.3 ) SET( TOMAHAWK_ORGANIZATION_NAME "Tomahawk" ) SET( TOMAHAWK_ORGANIZATION_DOMAIN "tomahawk-player.org" ) SET( TOMAHAWK_APPLICATION_NAME "Tomahawk" ) -SET( TOMAHAWK_VERSION "0.1.0" ) +SET( TOMAHAWK_DESCRIPTION_SUMMARY "The social media player" ) + +SET( TOMAHAWK_VERSION_MAJOR 0 ) +SET( TOMAHAWK_VERSION_MINOR 1 ) +SET( TOMAHAWK_VERSION_PATCH 0 ) + +# SET( TOMAHAWK_VERSION_RC 0 ) + +IF( NOT CMAKE_BUILD_TYPE STREQUAL "Release" ) + # Use the date as the tweak level. + INCLUDE( ${CMAKE_MODULE_PATH}/kwsysDateStamp.cmake ) + SET( TOMAHAWK_VERSION_TWEAK "${KWSYS_DATE_STAMP_YEAR}${KWSYS_DATE_STAMP_MONTH}${KWSYS_DATE_STAMP_DAY}" ) + INCLUDE( ${CMAKE_MODULE_PATH}/CMakeVersionSource.cmake ) +ENDIF() + +SET( TOMAHAWK_VERSION ${TOMAHAWK_VERSION_MAJOR}.${TOMAHAWK_VERSION_MINOR}.${TOMAHAWK_VERSION_PATCH} ) + +IF( ${TOMAHAWK_VERSION_TWEAK} GREATER 0) + SET( TOMAHAWK_VERSION ${TOMAHAWK_VERSION}.${TOMAHAWK_VERSION_TWEAK} ) +ENDIF() +IF( TOMAHAWK_VERSION_RC ) + SET( CMake_VERSION ${TOMAHAWK_VERSION}-rc${TOMAHAWK_VERSION_RC} ) +ENDIF() +IF( CMAKE_VERSION_SOURCE ) + SET( TOMAHAWK_VERSION ${TOMAHAWK_VERSION}-${CMAKE_VERSION_SOURCE} ) +ENDIF() + # set paths -SET( CMAKE_MODULE_PATH "${CMAKE_MODULE_PATH}" "${CMAKE_SOURCE_DIR}/CMakeModules" ) SET( THIRDPARTY_DIR ${CMAKE_SOURCE_DIR}/thirdparty ) -SET(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}") -SET(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}") -SET(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}") +SET( CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}" ) +SET( CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}" ) +SET( CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}" ) + +# installer creation +INCLUDE( CPack.cmake ) # Check if we need qtgui: IF( "${gui}" STREQUAL "no" ) diff --git a/CMakeModules/CMakeVersionSource.cmake b/CMakeModules/CMakeVersionSource.cmake new file mode 100644 index 000000000..acc177a75 --- /dev/null +++ b/CMakeModules/CMakeVersionSource.cmake @@ -0,0 +1,37 @@ +# Try to identify the current development source version. +set(CMAKE_VERSION_SOURCE "") +if(EXISTS ${CMAKE_SOURCE_DIR}/.git/HEAD) + find_program(GIT_EXECUTABLE NAMES git git.cmd) + mark_as_advanced(GIT_EXECUTABLE) + if(GIT_EXECUTABLE) + execute_process( + COMMAND ${GIT_EXECUTABLE} rev-parse --verify -q --short=4 HEAD + OUTPUT_VARIABLE head + OUTPUT_STRIP_TRAILING_WHITESPACE + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} + ) + if(head) + set(CMAKE_VERSION_SOURCE "g${head}") + execute_process( + COMMAND ${GIT_EXECUTABLE} update-index -q --refresh + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} + ) + execute_process( + COMMAND ${GIT_EXECUTABLE} diff-index --name-only HEAD -- + OUTPUT_VARIABLE dirty + OUTPUT_STRIP_TRAILING_WHITESPACE + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} + ) + if(dirty) + set(CMAKE_VERSION_SOURCE "${CMAKE_VERSION_SOURCE}-dirty") + endif() + endif() + endif() +elseif(EXISTS ${CMAKE_SOURCE_DIR}/CVS/Repository) + file(READ ${CMAKE_SOURCE_DIR}/CVS/Repository repo) + set(branch "") + if("${repo}" MATCHES "\\.git/") + string(REGEX REPLACE ".*\\.git/([^\r\n]*).*" "-\\1" branch "${repo}") + endif() + set(CMAKE_VERSION_SOURCE "cvs${branch}") +endif() diff --git a/CMakeModules/NSIS.InstallOptions.ini.in b/CMakeModules/NSIS.InstallOptions.ini.in new file mode 100644 index 000000000..566c0b47d --- /dev/null +++ b/CMakeModules/NSIS.InstallOptions.ini.in @@ -0,0 +1,24 @@ +[Settings] +NumFields=3 + +[Field 1] +Type=Label +Left=0 +Right=-1 +Top=0 +Bottom=24 + +[Field 2] +Type=RadioButton +Left=30 +Right=-1 +Top=50 +Bottom=58 +State=1 + +[Field 3] +Type=RadioButton +Left=30 +Right=-1 +Top=70 +Bottom=78 diff --git a/CMakeModules/NSIS.template.in b/CMakeModules/NSIS.template.in new file mode 100644 index 000000000..1a0b0d6ec --- /dev/null +++ b/CMakeModules/NSIS.template.in @@ -0,0 +1,665 @@ +;Tomahawk installer script. + +;----------------------------------------------------------------------------- +; Some installer script options (comment-out options not required) +;----------------------------------------------------------------------------- +;!define OPTION_LICENSE_AGREEMENT +!define OPTION_UAC_PLUGIN_ENHANCED +!define OPTION_SECTION_SC_START_MENU +!define OPTION_SECTION_SC_DESKTOP +!define OPTION_SECTION_SC_QUICK_LAUNCH +!define OPTION_FINISHPAGE +!define OPTION_FINISHPAGE_LAUNCHER +!define OPTION_FINISHPAGE_RELEASE_NOTES + +;----------------------------------------------------------------------------- +; Some paths. +;----------------------------------------------------------------------------- +!ifndef MING_PATH + !define MING_PATH "/usr/i686-w64-mingw32/sys-root/mingw" +!endif +!define MING_BIN "${MING_PATH}/bin" +!define MING_LIB "${MING_PATH}/lib" +!define BUILD_PATH "@CMAKE_BINARY_DIR@" +!define QT_DLL_PATH "${MING_BIN}" +!define SQLITE_DLL_PATH "${MING_LIB}/qt4/plugins/sqldrivers" +!define IMAGEFORMATS_DLL_PATH "${MING_LIB}/qt4/plugins/imageformats" +!define VLC_PLUGIN_PATH "${MING_LIB}\vlc\plugins" +!define NSI_PATH "@CMAKE_SOURCE_DIR@/admin/win/nsi" + +;----------------------------------------------------------------------------- +; Increment installer revision number as part of this script. +;----------------------------------------------------------------------------- +!define /file REVISION_LAST ${NSI_PATH}/revision.txt +!define /math REVISION ${REVISION_LAST} + 1 +!delfile revision.txt +!appendfile revision.txt ${REVISION} + +!define VER_MAJOR "@CPACK_PACKAGE_VERSION_MAJOR@" +!define VER_MINOR "@CPACK_PACKAGE_VERSION_MINOR@" +!define VER_BUILD "@CPACK_PACKAGE_VERSION_PATCH@" +!define VERSION "@CPACK_PACKAGE_VERSION@" + +;----------------------------------------------------------------------------- +; Installer build timestamp. +;----------------------------------------------------------------------------- +!define /date BUILD_TIME "built on %Y/%m/%d at %I:%M %p (rev. ${REVISION})" + +;----------------------------------------------------------------------------- +; Initial installer setup and definitions. +;----------------------------------------------------------------------------- +Name "@CPACK_NSIS_PACKAGE_NAME@" +Caption "Tomahawk Installer" +BrandingText "Tomahawk ${VERSION} -- ${BUILD_TIME}" +OutFile "@CPACK_TOPLEVEL_DIRECTORY@/@CPACK_OUTPUT_FILE_NAME@" +InstallDir "$PROGRAMFILES\@CPACK_PACKAGE_INSTALL_DIRECTORY@" +InstallDirRegKey HKCU "Software\Tomahawk" "" +InstType Standard +InstType Full +InstType Minimal +CRCCheck On +SetCompressor @CPACK_NSIS_COMPRESSOR@ +RequestExecutionLevel user ;Now using the UAC plugin. +ReserveFile NSIS.InstallOptions.ini +ReserveFile "${NSISDIR}\Plugins\InstallOptions.dll" + +@CPACK_NSIS_SECTION_SELECTED_VARS@ + +;----------------------------------------------------------------------------- +; Include some required header files. +;----------------------------------------------------------------------------- +!include LogicLib.nsh ;Used by APPDATA uninstaller. +!include nsDialogs.nsh ;Used by APPDATA uninstaller. +!include MUI2.nsh ;Used by APPDATA uninstaller. +!include InstallOptions.nsh ;Required by MUI2 to support old MUI_INSTALLOPTIONS. +!include Memento.nsh ;Remember user selections. +!include WinVer.nsh ;Windows version detection. +!include WordFunc.nsh ;Used by VersionCompare macro function. +!include UAC.nsh ;Used by the UAC elevation to install as user or admin. + +;----------------------------------------------------------------------------- +; Memento selections stored in registry. +;----------------------------------------------------------------------------- +!define MEMENTO_REGISTRY_ROOT HKLM +!define MEMENTO_REGISTRY_KEY Software\Microsoft\Windows\CurrentVersion\Uninstall\Tomahawk + +;----------------------------------------------------------------------------- +; Modern User Interface (MUI) defintions and setup. +;----------------------------------------------------------------------------- +!define MUI_ABORTWARNING +!define MUI_ICON ${NSI_PATH}\installer.ico +!define MUI_UNICON ${NSI_PATH}\installer.ico +!define MUI_WELCOMEFINISHPAGE_BITMAP ${NSI_PATH}\welcome.bmp +!define MUI_WELCOMEPAGE_TITLE "@CPACK_PACKAGE_NAME@ ${VERSION} Setup$\r$\nInstaller Build Revision ${REVISION}" +!define MUI_WELCOMEPAGE_TEXT "This wizard will guide you through the installation.$\r$\n$\r$\n$_CLICK" +!define MUI_HEADERIMAGE +!define MUI_HEADERIMAGE_BITMAP ${NSI_PATH}\page_header.bmp +!define MUI_COMPONENTSPAGE_SMALLDESC +!define MUI_FINISHPAGE_TITLE "@CPACK_PACKAGE_NAME@ Install Completed" +!define MUI_FINISHPAGE_LINK "Click here to visit the @CPACK_PACKAGE_NAME@ website." +!define MUI_FINISHPAGE_LINK_LOCATION "http://@TOMAHAWK_ORGANIZATION_DOMAIN@" +!define MUI_FINISHPAGE_NOREBOOTSUPPORT +!ifdef OPTION_FINISHPAGE_RELEASE_NOTES + !define MUI_FINISHPAGE_SHOWREADME_NOTCHECKED + !define MUI_FINISHPAGE_SHOWREADME "$INSTDIR\NOTES.txt" + !define MUI_FINISHPAGE_SHOWREADME_TEXT "Show release notes" +!endif +!ifdef OPTION_FINISHPAGE_LAUNCHER + !define MUI_FINISHPAGE_NOAUTOCLOSE + !define MUI_FINISHPAGE_RUN + !define MUI_FINISHPAGE_RUN_FUNCTION "LaunchTomahawk" +!endif + +;----------------------------------------------------------------------------- +; Page macros. +;----------------------------------------------------------------------------- +!insertmacro MUI_PAGE_WELCOME +!ifdef OPTION_LICENSE_AGREEMENT + !insertmacro MUI_PAGE_LICENSE "LICENSE.txt" +!endif +Page custom PageReinstall PageLeaveReinstall +!insertmacro MUI_PAGE_COMPONENTS +!insertmacro MUI_PAGE_DIRECTORY +!insertmacro MUI_PAGE_INSTFILES +!ifdef OPTION_FINISHPAGE + !insertmacro MUI_PAGE_FINISH +!endif +!insertmacro MUI_UNPAGE_CONFIRM +UninstPage custom un.UnPageUserAppData un.UnPageUserAppDataLeave +!insertmacro MUI_UNPAGE_INSTFILES + +;----------------------------------------------------------------------------- +; Other MUI macros. +;----------------------------------------------------------------------------- +!insertmacro MUI_LANGUAGE "English" + +############################################################################## +# # +# FINISH PAGE LAUNCHER FUNCTIONS # +# # +############################################################################## + +Function LaunchTomahawk + ${UAC.CallFunctionAsUser} LaunchTomahawkAsUser +FunctionEnd + +Function LaunchTomahawkAsUser + Exec "$INSTDIR\tomahawk.exe" +FunctionEnd + +############################################################################## +# # +# 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 + +############################################################################## +# # +# RE-INSTALLER FUNCTIONS # +# # +############################################################################## + +Function PageReinstall + ReadRegStr $R0 HKLM "Software\Tomahawk" "" + StrCmp $R0 "" 0 +2 + Abort + + ;Detect version + ReadRegDWORD $R0 HKLM "Software\Tomahawk" "VersionMajor" + IntCmp $R0 ${VER_MAJOR} minor_check new_version older_version + minor_check: + ReadRegDWORD $R0 HKLM "Software\Tomahawk" "VersionMinor" + IntCmp $R0 ${VER_MINOR} build_check new_version older_version + build_check: + ReadRegDWORD $R0 HKLM "Software\Tomahawk" "VersionBuild" + IntCmp $R0 ${VER_BUILD} revision_check new_version older_version + revision_check: + ReadRegDWORD $R0 HKLM "Software\Tomahawk" "VersionRevision" + IntCmp $R0 ${REVISION} same_version new_version older_version + + new_version: + !insertmacro INSTALLOPTIONS_WRITE "NSIS.InstallOptions.ini" "Field 1" "Text" "An older version of Tomahawk is installed on your system. It is recommended that you uninstall the current version before installing. Select the operation you want to perform and click Next to continue." + !insertmacro INSTALLOPTIONS_WRITE "NSIS.InstallOptions.ini" "Field 2" "Text" "Uninstall before installing" + !insertmacro INSTALLOPTIONS_WRITE "NSIS.InstallOptions.ini" "Field 3" "Text" "Do not uninstall" + !insertmacro MUI_HEADER_TEXT "Already Installed" "Choose how you want to install Tomahawk." + StrCpy $R0 "1" + Goto reinst_start + + older_version: + !insertmacro INSTALLOPTIONS_WRITE "NSIS.InstallOptions.ini" "Field 1" "Text" "A newer version of Tomahawk is already installed! It is not recommended that you install an older version. If you really want to install this older version, it is better to uninstall the current version first. Select the operation you want to perform and click Next to continue." + !insertmacro INSTALLOPTIONS_WRITE "NSIS.InstallOptions.ini" "Field 2" "Text" "Uninstall before installing" + !insertmacro INSTALLOPTIONS_WRITE "NSIS.InstallOptions.ini" "Field 3" "Text" "Do not uninstall" + !insertmacro MUI_HEADER_TEXT "Already Installed" "Choose how you want to install Tomahawk." + StrCpy $R0 "1" + Goto reinst_start + + same_version: + !insertmacro INSTALLOPTIONS_WRITE "NSIS.InstallOptions.ini" "Field 1" "Text" "Tomahawk ${VERSION} is already installed.\r\nSelect the operation you want to perform and click Next to continue." + !insertmacro INSTALLOPTIONS_WRITE "NSIS.InstallOptions.ini" "Field 2" "Text" "Add/Reinstall components" + !insertmacro INSTALLOPTIONS_WRITE "NSIS.InstallOptions.ini" "Field 3" "Text" "Uninstall Tomahawk" + !insertmacro MUI_HEADER_TEXT "Already Installed" "Choose the maintenance option to perform." + StrCpy $R0 "2" + + reinst_start: + !insertmacro INSTALLOPTIONS_DISPLAY "NSIS.InstallOptions.ini" +FunctionEnd + +Function PageLeaveReinstall + !insertmacro INSTALLOPTIONS_READ $R1 "NSIS.InstallOptions.ini" "Field 2" "State" + StrCmp $R0 "1" 0 +2 + StrCmp $R1 "1" reinst_uninstall reinst_done + StrCmp $R0 "2" 0 +3 + StrCmp $R1 "1" reinst_done reinst_uninstall + reinst_uninstall: + ReadRegStr $R1 HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Tomahawk" "UninstallString" + HideWindow + ClearErrors + ExecWait '$R1 _?=$INSTDIR' + IfErrors no_remove_uninstaller + IfFileExists "$INSTDIR\tomahawk.exe" no_remove_uninstaller + Delete $R1 + RMDir $INSTDIR + no_remove_uninstaller: + StrCmp $R0 "2" 0 +3 + UAC::Unload + Quit + BringToFront + reinst_done: +FunctionEnd + +############################################################################## +# # +# INSTALLER SECTIONS # +# # +############################################################################## +Section "Tomahawk Player" SEC_TOMAHAWK_PLAYER + SectionIn 1 2 3 RO + SetDetailsPrint listonly + + SetDetailsPrint textonly + DetailPrint "Installing Tomahawk Player essentials." + SetDetailsPrint listonly + SetOutPath "$INSTDIR" + + !ifdef INSTALL_PATH + ;Main executable. + File "${INSTALL_PATH}\bin\tomahawk.exe" + + File "${INSTALL_PATH}\bin\libqxtweb-standalone.dll" + File "${INSTALL_PATH}\bin\libtomahawk_portfwd.dll" + File "${INSTALL_PATH}\bin\libtomahawk_lastfm2.dll" + File "${INSTALL_PATH}\bin\libtomahawklib.dll" + File "${INSTALL_PATH}\lib\libtomahawk_sip*.dll" + !endif + !ifndef INSTALL_PATH + ;Main executable. + File "${BUILD_PATH}\tomahawk.exe" + + File "${BUILD_PATH}\libtomahawklib.dll" + File "${BUILD_PATH}\libqxtweb-standalone.dll" + File "${BUILD_PATH}\libtomahawk_portfwd.dll" + File "${BUILD_PATH}\libtomahawk_lastfm2.dll" + File "${BUILD_PATH}\libtomahawk_sip*.dll" + !endif + + ;License & release notes. + File "@CPACK_RESOURCE_FILE_LICENSE@" + File /oname=NOTES.txt ${NSI_PATH}\RELEASE_NOTES.txt + + ;QT stuff: + File "${QT_DLL_PATH}\QtCore4.dll" + File "${QT_DLL_PATH}\QtGui4.dll" + File "${QT_DLL_PATH}\QtNetwork4.dll" + File "${QT_DLL_PATH}\QtSql4.dll" + File "${QT_DLL_PATH}\QtXml4.dll" + File "${QT_DLL_PATH}\QtWebKit4.dll" + + ;SQLite driver + 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_BIN}\cygmad-0.dll" + ;File "${MING_BIN}\libgcc_s_dw2-1.dll" + ;File "${MING_BIN}\mingwm10.dll" + File "${MING_BIN}\libgcc_s_sjlj-1.dll" + File "${MING_BIN}\libstdc++-6.dll" + + ;Phonon stuff + + ;Fix the phonon build to not use Dbus + File "${QT_DLL_PATH}\QtDbus4.dll" + File "${MING_BIN}\libdbus-1-3.dll" + File "${MING_BIN}\dbus-daemon.exe" + + File "${MING_BIN}\libphonon.dll" + SetOutPath "$INSTDIR\phonon_backend" + File "${MING_BIN}\phonon_backend\phonon_vlc.dll" + SetOutPath "$INSTDIR" + + ;VLC + ;SetOutPath "$INSTDIR\phonon_backend" + File "${MING_BIN}\libvlc.dll" + File "${MING_BIN}\libvlccore.dll" + SetOutPath "$INSTDIR\plugins" + File /r "${VLC_PLUGIN_PATH}\*.dll" + SetOutPath "$INSTDIR" + File "${MING_BIN}\libmad-0.dll" ; MP3 + File "${MING_BIN}\libFLAC-8.dll" ; FLAC + File "${MING_BIN}\libogg-0.dll" ; OGG, FLAC + File "${MING_BIN}\libvorbis-0.dll" ; OGG + File "${MING_BIN}\libvorbisenc-2.dll" ; OGG + + + + ; Other + File "${MING_BIN}\libqjson.dll" + File "${MING_BIN}\libtag.dll" + File "${MING_BIN}\libpng15-15.dll" + File "${MING_BIN}\libjpeg-8.dll" + File "${MING_BIN}\zlib1.dll" + + File "${MING_BIN}\libechonest.dll" + File "${MING_BIN}\libQTweetLib.dll" + + ; Jabber + File "${MING_BIN}\libjreen.dll" + File "${MING_BIN}\libqca.dll" + SetOutPath "$INSTDIR\crypto" + File "${MING_LIB}\qt4\plugins\crypto\libqca-ossl.dll" + SetOutPath "$INSTDIR" + File "${MING_BIN}\libssl-8.dll" + File "${MING_BIN}\libcrypto-8.dll" + + File "${MING_LIB}\libclucene-core.dll" + File "${MING_LIB}\libclucene-shared.dll" + + File "${MING_BIN}\libqtsparkle.dll" +SectionEnd + +SectionGroup "Shortcuts" + +!ifdef OPTION_SECTION_SC_START_MENU + ${MementoSection} "Start Menu Program Group" SEC_START_MENU + SectionIn 1 2 + SetDetailsPrint textonly + DetailPrint "Adding shortcuts for the Tomahawk program group to the Start Menu." + SetDetailsPrint listonly + SetShellVarContext all + RMDir /r "$SMPROGRAMS\Tomahawk" + CreateDirectory "$SMPROGRAMS\Tomahawk" + 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" + SetShellVarContext current + ${MementoSectionEnd} +!endif + +!ifdef OPTION_SECTION_SC_DESKTOP + ${MementoSection} "Desktop Shortcut" SEC_DESKTOP + SectionIn 1 2 + SetDetailsPrint textonly + DetailPrint "Creating Desktop Shortcuts" + SetDetailsPrint listonly + CreateShortCut "$DESKTOP\Tomahawk.lnk" "$INSTDIR\tomahawk.exe" + ${MementoSectionEnd} +!endif + +!ifdef OPTION_SECTION_SC_QUICK_LAUNCH + ${MementoSection} "Quick Launch Shortcut" SEC_QUICK_LAUNCH + SectionIn 1 2 + SetDetailsPrint textonly + DetailPrint "Creating Quick Launch Shortcut" + SetDetailsPrint listonly + CreateShortCut "$QUICKLAUNCH\Tomahawk.lnk" "$INSTDIR\tomahawk.exe" + ${MementoSectionEnd} +!endif + +SectionGroupEnd + +${MementoSectionDone} + +; Installer section descriptions +;-------------------------------- +!insertmacro MUI_FUNCTION_DESCRIPTION_BEGIN +!insertmacro MUI_DESCRIPTION_TEXT ${SEC_TOMAHAWK_PLAYER} "Tomahawk player essentials." +!insertmacro MUI_DESCRIPTION_TEXT ${SEC_START_MENU} "Tomahawk program group." +!insertmacro MUI_DESCRIPTION_TEXT ${SEC_DESKTOP} "Desktop shortcut for Tomahawk." +!insertmacro MUI_DESCRIPTION_TEXT ${SEC_QUICK_LAUNCH} "Quick Launch shortcut for Tomahawk." +!insertmacro MUI_FUNCTION_DESCRIPTION_END + +Section -post + + ;Uninstaller file. + SetDetailsPrint textonly + DetailPrint "Writing Uninstaller" + SetDetailsPrint listonly + WriteUninstaller $INSTDIR\uninstall.exe + + ;Registry keys required for installer version handling and uninstaller. + SetDetailsPrint textonly + DetailPrint "Writing Installer Registry Keys" + SetDetailsPrint listonly + + ;Version numbers used to detect existing installation version for comparisson. + WriteRegStr HKLM "Software\Tomahawk" "" $INSTDIR + WriteRegDWORD HKLM "Software\Tomahawk" "VersionMajor" "${VER_MAJOR}" + WriteRegDWORD HKLM "Software\Tomahawk" "VersionMinor" "${VER_MINOR}" + WriteRegDWORD HKLM "Software\Tomahawk" "VersionRevision" "${REVISION}" + WriteRegDWORD HKLM "Software\Tomahawk" "VersionBuild" "${VER_BUILD}" + + ;Add or Remove Programs entry. + WriteRegExpandStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Tomahawk" "UninstallString" '"$INSTDIR\Uninstall.exe"' + WriteRegExpandStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Tomahawk" "InstallLocation" "$INSTDIR" + WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Tomahawk" "DisplayName" "Tomahawk" + WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Tomahawk" "Publisher" "Tomahawk-player.org" + WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Tomahawk" "DisplayIcon" "$INSTDIR\Uninstall.exe,0" + WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Tomahawk" "DisplayVersion" "${VERSION}" + WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Tomahawk" "VersionMajor" "${VER_MAJOR}" + WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Tomahawk" "VersionMinor" "${VER_MINOR}.${REVISION}" + WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Tomahawk" "URLInfoAbout" "http://tomahawk-player.org/" + WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Tomahawk" "HelpLink" "http://tomahawk-player.org/" + 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 + +############################################################################## +# # +# UNINSTALLER SECTION # +# # +############################################################################## + +Var UnPageUserAppDataDialog +Var UnPageUserAppDataCheckbox +Var UnPageUserAppDataCheckbox_State +Var UnPageUserAppDataEditBox + +Function un.UnPageUserAppData + !insertmacro MUI_HEADER_TEXT "Uninstall Tomahawk" "Remove Tomahawk's data folder from your computer." + nsDialogs::Create /NOUNLOAD 1018 + Pop $UnPageUserAppDataDialog + + ${If} $UnPageUserAppDataDialog == error + Abort + ${EndIf} + + ${NSD_CreateLabel} 0 0 100% 12u "Do you want to delete Tomahawk's data folder?" + Pop $0 + + ${NSD_CreateText} 0 13u 100% 12u "$LOCALAPPDATA\Tomahawk" + Pop $UnPageUserAppDataEditBox + SendMessage $UnPageUserAppDataEditBox ${EM_SETREADONLY} 1 0 + + ${NSD_CreateLabel} 0 46u 100% 24u "Leave unchecked to keep the data folder for later use or check to delete the data folder." + Pop $0 + + ${NSD_CreateCheckbox} 0 71u 100% 8u "Yes, delete this data folder." + Pop $UnPageUserAppDataCheckbox + + nsDialogs::Show +FunctionEnd + +Function un.UnPageUserAppDataLeave + ${NSD_GetState} $UnPageUserAppDataCheckbox $UnPageUserAppDataCheckbox_State +FunctionEnd + +Section Uninstall + IfFileExists "$INSTDIR\tomahawk.exe" tomahawk_installed + MessageBox MB_YESNO "It does not appear that Tomahawk is installed in the directory '$INSTDIR'.$\r$\nContinue anyway (not recommended)?" IDYES tomahawk_installed + Abort "Uninstall aborted by user" + tomahawk_installed: + + ;Delete registry keys. + DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Tomahawk" + DeleteRegValue HKLM "Software\Tomahawk" "VersionBuild" + DeleteRegValue HKLM "Software\Tomahawk" "VersionMajor" + DeleteRegValue HKLM "Software\Tomahawk" "VersionMinor" + DeleteRegValue HKLM "Software\Tomahawk" "VersionRevision" + DeleteRegValue HKLM "Software\Tomahawk" "" + DeleteRegKey HKLM "Software\Tomahawk" + + DeleteRegKey HKCR "tomahawk" + + ;Start menu shortcuts. + !ifdef OPTION_SECTION_SC_START_MENU + SetShellVarContext all + RMDir /r "$SMPROGRAMS\Tomahawk" + SetShellVarContext current + !endif + + ;Desktop shortcut. + !ifdef OPTION_SECTION_SC_DESKTOP + IfFileExists "$DESKTOP\Tomahawk.lnk" 0 +2 + Delete "$DESKTOP\Tomahawk.lnk" + !endif + + ;Quick Launch shortcut. + !ifdef OPTION_SECTION_SC_QUICK_LAUNCH + IfFileExists "$QUICKLAUNCH\Tomahawk.lnk" 0 +2 + Delete "$QUICKLAUNCH\Tomahawk.lnk" + !endif + + ;Remove all the Program Files. + RMDir /r $INSTDIR + + ;Uninstall User Data if option is checked, otherwise skip. + ${If} $UnPageUserAppDataCheckbox_State == ${BST_CHECKED} + RMDir /r "$LOCALAPPDATA\Tomahawk" + ${EndIf} + + SetDetailsPrint textonly + DetailPrint "Finsihed." +SectionEnd + +############################################################################## +# # +# NSIS Installer Event Handler Functions # +# # +############################################################################## + +Function .onInit + !insertmacro INSTALLOPTIONS_EXTRACT "NSIS.InstallOptions.ini" + + ;Remove Quick Launch option from Windows 7, as no longer applicable - usually. + ${IfNot} ${AtMostWinVista} + SectionSetText ${SEC_QUICK_LAUNCH} "Quick Launch Shortcut (N/A)" + SectionSetFlags ${SEC_QUICK_LAUNCH} ${SF_RO} + SectionSetInstTypes ${SEC_QUICK_LAUNCH} 0 + ${EndIf} + + ${MementoSectionRestore} + + 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_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 + + ;Prevent multiple instances. + System::Call 'kernel32::CreateMutexA(i 0, i 0, t "tomahawkInstaller") i .r1 ?e' + Pop $R0 + 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} + UAC::Unload ;Must call unload! +FunctionEnd + +Function .onInstFailed + UAC::Unload ;Must call unload! +FunctionEnd + +############################################################################## +# # +# NSIS Uninstaller Event Handler Functions # +# # +############################################################################## + +Function un.onInit + 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_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 + + ;Prevent multiple instances. + System::Call 'kernel32::CreateMutexA(i 0, i 0, t "tomahawkUninstaller") i .r1 ?e' + Pop $R0 + StrCmp $R0 0 +3 + MessageBox MB_OK|MB_ICONEXCLAMATION "This uninstaller is already running." + Abort +FunctionEnd + +Function un.onUnInstSuccess + UAC::Unload ;Must call unload! +FunctionEnd + +Function un.onUnInstFailed + UAC::Unload ;Must call unload! +FunctionEnd diff --git a/CMakeModules/kwsysDateStamp.cmake b/CMakeModules/kwsysDateStamp.cmake new file mode 100644 index 000000000..f17c56cec --- /dev/null +++ b/CMakeModules/kwsysDateStamp.cmake @@ -0,0 +1,21 @@ +# Do not edit! Generated by kwsysDateStamp.py +#============================================================================= +# KWSys - Kitware System Library +# Copyright 2000-2009 Kitware, Inc., Insight Software Consortium +# +# Distributed under the OSI-approved BSD License (the "License"); +# see accompanying file Copyright.txt for details. +# +# This software is distributed WITHOUT ANY WARRANTY; without even the +# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the License for more information. +#============================================================================= + +# KWSys version date year component. Format is CCYY. +SET(KWSYS_DATE_STAMP_YEAR 2011) + +# KWSys version date month component. Format is MM. +SET(KWSYS_DATE_STAMP_MONTH 07) + +# KWSys version date day component. Format is DD. +SET(KWSYS_DATE_STAMP_DAY 06) diff --git a/CPack.cmake b/CPack.cmake new file mode 100644 index 000000000..1573c1b2c --- /dev/null +++ b/CPack.cmake @@ -0,0 +1,53 @@ +INCLUDE( InstallRequiredSystemLibraries ) + +SET( CPACK_PACKAGE_CONTACT "Dominik Schmidt " ) + +SET( CPACK_PACKAGE_FILE_NAME tomahawk-${TOMAHAWK_VERSION} ) # Package file name without extension. Also a directory of installer cmake-2.5.0-Linux-i686 + +# CPACK_GENERATOR CPack generator to be used STGZ;TGZ;TZ +# CPACK_INCLUDE_TOPLEVEL_DIRECTORY Controls whether CPack adds a top-level directory, usually of the form ProjectName-Version-OS, to the top of package tree. 0 to disable, 1 to enable +# CPACK_INSTALL_CMAKE_PROJECTS List of four values: Build directory, Project Name, Project Component, Directory in the package /home/andy/vtk/CMake-bin;CMake;ALL;/ +SET( CPACK_PACKAGE_DESCRIPTION_FILE "${CMAKE_SOURCE_DIR}/README" ) # File used as a description of a project /path/to/project/ReadMe.txt +SET( CPACK_PACKAGE_DESCRIPTION_SUMMARY ${TOMAHAWK_DESCRIPTION_SUMMARY} ) # Description summary of a project +# CPACK_PACKAGE_EXECUTABLES List of pairs of executables and labels. Used by the NSIS generator to create Start Menu shortcuts. ccmake;CMake +SET( CPACK_PACKAGE_INSTALL_DIRECTORY ${TOMAHAWK_APPLICATION_NAME} ) # Installation directory on the target system -> C:\Program Files\fellody +SET( CPACK_PACKAGE_INSTALL_REGISTRY_KEY ${TOMAHAWK_APPLICATION_NAME} ) # Registry key used when installing this project CMake 2.5.0 +SET( CPACK_PACKAGE_NAME ${TOMAHAWK_APPLICATION_NAME} ) # Package name, defaults to the project name +SET( CPACK_PACKAGE_VENDOR ${TOMAHAWK_ORGANIZATION_NAME} ) # Package vendor name +SET( CPACK_PACKAGE_VERSION_MAJOR ${TOMAHAWK_VERSION_MAJOR} ) +SET( CPACK_PACKAGE_VERSION_MINOR ${TOMAHAWK_VERSION_MINOR} ) +SET( CPACK_PACKAGE_VERSION_PATCH ${TOMAHAWK_VERSION_PATCH} ) + +# CPACK_SOURCE_GENERATOR List of generators used for the source package TGZ;TZ + +SET( CPACK_SOURCE_GENERATOR TGZ ) +SET( CPACK_SOURCE_IGNORE_FILES "/\\\\.git/" ".*~$" ".kate-swp$" "/build_dir/" "/clang/" "/gcc/" "/build/" "/win/" ) # Pattern of files in the source tree that won't be packaged +SET( CPACK_SOURCE_PACKAGE_FILE_NAME tomahawk-${TOMAHAWK_VERSION} ) # Name of the source package +# CPACK_SOURCE_STRIP_FILES List of files in the source tree that will be stripped. Starting with CMake 2.6.0 CPACK_SOURCE_STRIP_FILES will be a boolean variable which enables stripping of all files (a list of files evaluates to TRUE in CMake, so this change is compatible). +# CPACK_STRIP_FILES List of files to be stripped. Starting with CMake 2.6.0 CPACK_STRIP_FILES will be a boolean variable which enables stripping of all files (a list of files evaluates to TRUE in CMake, so this change is compatible). bin/ccmake;bin/cmake;bin/cpack;bin/ctest +# CPACK_SYSTEM_NAME System name, defaults to the value of ${CMAKE_SYSTEM_NAME}. Linux-i686 + +# Advanced settings +# CPACK_CMAKE_GENERATOR What CMake generator should be used if the project is CMake project. Defaults to the value of CMAKE_GENERATOR. Unix Makefiles +SET( CPACK_RESOURCE_FILE_LICENSE "${CMAKE_SOURCE_DIR}/LICENSE.txt" ) # License file for the project, used by the STGZ, NSIS, and PackageMaker generators. /home/andy/vtk/CMake/Copyright.txt +# CPACK_RESOURCE_FILE_README ReadMe file for the project, used by PackageMaker generator. /home/andy/vtk/CMake/Templates/CPack.GenericDescription.txt +# CPACK_RESOURCE_FILE_WELCOME Welcome file for the project, used by PackageMaker generator. /home/andy/vtk/CMake/Templates/CPack.GenericWelcome.txt +SET( CPACK_PACKAGE_VERSION ${TOMAHAWK_VERSION} ) + +SET( CPACK_TOPLEVEL_TAG "narf" ) # Directory for the installed files. - needed to provide anything to avoid an error# CPACK_INSTALL_COMMANDS Extra commands to install components. +# CPACK_INSTALL_DIRECTORIES Extra directories to install. +# CPACK_MONOLITHIC_INSTALL When set disables the component-based installer. +# CPACK_PACKAGING_INSTALL_PREFIX Sets the default root that the generated package installs into, '/usr' is the default for the debian and redhat generators /usr/local + +## +# INSTALL DEPS +## + + + +# Set the options file that needs to be included inside CMakeCPackOptions.cmake +#SET(QT_DIALOG_CPACK_OPTIONS_FILE ${CMake_BINARY_DIR}/Source/QtDialog/QtDialogCPack.cmake) +configure_file("${CMAKE_SOURCE_DIR}/CPackOptions.cmake.in" + "${CMAKE_BINARY_DIR}/CPackOptions.cmake" @ONLY) +set(CPACK_PROJECT_CONFIG_FILE "${CMAKE_BINARY_DIR}/CPackOptions.cmake") # File included at cpack time, once per generator after setting CPACK_GENERATOR to the actual generator being used; allows per-generator setting of CPACK_* variables at cpack time. ${PROJECT_BINARY_DIR}/CPackOptions.cmake +include(CPack) diff --git a/CPackOptions.cmake.in b/CPackOptions.cmake.in new file mode 100644 index 000000000..8084cf9aa --- /dev/null +++ b/CPackOptions.cmake.in @@ -0,0 +1,73 @@ +# This file is configured at cmake time, and loaded at cpack time. +# To pass variables to cpack from cmake, they must be configured +# in this file. + +if(CPACK_GENERATOR MATCHES "NSIS") + #SET(CPACK_NSIS_INSTALL_ROOT "@CPACK_NSIS_INSTALL_ROOT@") + + # set the install/unistall icon used for the installer itself + # There is a bug in NSI that does not handle full unix paths properly. + #SET(CPACK_NSIS_MUI_ICON "@CMake_SOURCE_DIR@/Utilities/Release\\CMakeLogo.ico") + #SET(CPACK_NSIS_MUI_UNIICON "@CMake_SOURCE_DIR@/Utilities/Release\\CMakeLogo.ico") + # set the package header icon for MUI + #SET(CPACK_PACKAGE_ICON "@CMake_SOURCE_DIR@/Utilities/Release\\CMakeInstall.bmp") + # tell cpack to create links to the doc files + #SET(CPACK_NSIS_MENU_LINKS + # "doc/cmake-@CMake_VERSION_MAJOR@.@CMake_VERSION_MINOR@/cmake-gui.html" "cmake-gui Help" + # "doc/cmake-@CMake_VERSION_MAJOR@.@CMake_VERSION_MINOR@/cmake.html" "CMake Help" + # "doc/cmake-@CMake_VERSION_MAJOR@.@CMake_VERSION_MINOR@/cmake-properties.html" + # "CMake Properties and Variables Help" + # "doc/cmake-@CMake_VERSION_MAJOR@.@CMake_VERSION_MINOR@/ctest.html" "CTest Help" + # "doc/cmake-@CMake_VERSION_MAJOR@.@CMake_VERSION_MINOR@/cmake-modules.html" "CMake Modules Help" + # "doc/cmake-@CMake_VERSION_MAJOR@.@CMake_VERSION_MINOR@/cmake-commands.html" "CMake Commands Help" + # "doc/cmake-@CMake_VERSION_MAJOR@.@CMake_VERSION_MINOR@/cpack.html" "CPack Help" + # "http://www.cmake.org" "CMake Web Site" + # ) + # Use the icon from cmake-gui for add-remove programs + #SET(CPACK_NSIS_INSTALLED_ICON_NAME "bin\\cmake-gui.exe") + # + #SET(CPACK_NSIS_PACKAGE_NAME "@CPACK_NSIS_PACKAGE_NAME@") + #SET(CPACK_NSIS_DISPLAY_NAME "@CPACK_NSIS_PACKAGE_NAME@, a cross-platform, open-source build system") + #SET(CPACK_NSIS_HELP_LINK "http://www.cmake.org") + #SET(CPACK_NSIS_URL_INFO_ABOUT "http://www.kitware.com") + #SET(CPACK_NSIS_CONTACT @CPACK_PACKAGE_CONTACT@) + #SET(CPACK_NSIS_MODIFY_PATH ON) + + + +##### all options +#CPACK_NSIS_MUI_ICON The icon file (.ico) for the generated install program. Both this and CPACK_NSIS_MUI_UNIICON need to set for this to have any effect. installer.ico +#CPACK_NSIS_MUI_UNIICON The icon file (.ico) for the generated uninstall program. Both this and CPACK_NSIS_MUI_ICON need to set for this to have any effect. uninstaller.ico +SET( CPACK_PACKAGE_ICON @CMAKE_SOURCE_DIR@/admin/win/nsi/installer.ico ) # A branding image that will be displayed on the top bar inside the installer. installer.bmp +#CPACK_NSIS_EXTRA_INSTALL_COMMANDS Extra NSIS commands that will be added to the install Section. ExecWait '\\\"$INSTDIR\\\\vcredist_x86.exe\\\" /q:a' +#CPACK_NSIS_EXTRA_UNINSTALL_COMMANDS Extra NSIS commands that will be added to the uninstall Section. +SET( CPACK_NSIS_COMPRESSOR "/SOLID lzma" ) # The arguments that will be passed to the NSIS SetCompressor command. /SOLID lzma +#CPACK_NSIS_MODIFY_PATH If this is set to "ON", then an extra page will appear in the installer that will allow the user to choose whether the program directory should be added to the system PATH variable. ON +#CPACK_NSIS_DISPLAY_NAME Undocumented. "${CPACK_PACKAGE_INSTALL_DIRECTORY} My Famous Project" +#CPACK_NSIS_INSTALLED_ICON_NAME Set the icon used for the Windows "Add or Remove Programs" tool. "bin\\\\MyExecutable.exe" +#CPACK_NSIS_HELP_LINK Adds link to registry. URI. "http:\\\\\\\\www.my-project-home-page.org" +#CPACK_NSIS_URL_INFO_ABOUT Adds link to registry and the vendor in add/remove programs' "Click here for support information" in program entry links here. "http:\\\\\\\\www.my-personal-home-page.com" +#CPACK_NSIS_CONTACT Adds link to add/remove programs' "Click here for support information" in program entry. "me@my-personal-home-page.com" +#CPACK_NSIS_CREATE_ICONS_EXTRA Additional NSIS commands for creating start menu shortcuts. set(CPACK_NSIS_CREATE_ICONS "CreateShortCut '\$SMPROGRAMS\\\\$STARTMENU_FOLDER\\\\${PROJECT_NAME}.lnk' '\$INSTDIR\\\\${PROJECT_NAME}.exe'") +#CPACK_NSIS_DELETE_ICONS_EXTRA Undocumented. Possibly: Additional NSIS commands to uninstall start menu shortcuts. +#CPACK_NSIS_MENU_LINKS Used to override the Start Menu links. "doc/cmake-@CMake_VERSION_MAJOR@.@CMake_VERSION_MINOR@/CMakeSetup.html" "CMakeSetup Help" +#CPACK_NSIS_MUI_FINISHPAGE_RUN If used, will make it possible for user to choose (on an additional page, displayed at the end of the installation) to run intalled program. Should point to program name to run, seemingly without any sub-directories of the installation directory in case program installed in such sub-directories (but please check generated NSIS script if you can't make it work). "MyExecutable.exe" + +endif(CPACK_GENERATOR MATCHES "NSIS") + +## include the cpack options for qt dialog if they exisit +## they might not if qt was not enabled for the build +#INCLUDE("@QT_DIALOG_CPACK_OPTIONS_FILE@" OPTIONAL) + + +#if("${CPACK_GENERATOR}" STREQUAL "PackageMaker") +# if(CMAKE_PACKAGE_QTGUI) +# set(CPACK_PACKAGE_DEFAULT_LOCATION "/Applications") +# else(CMAKE_PACKAGE_QTGUI) +# set(CPACK_PACKAGE_DEFAULT_LOCATION "/usr") +# endif(CMAKE_PACKAGE_QTGUI) +#endif("${CPACK_GENERATOR}" STREQUAL "PackageMaker") + + +SET( CMAKE_SOURCE_DIR @CMAKE_SOURCE_DIR@ ) +SET( CMAKE_BINARY_DIR @CMAKE_BINARY_DIR@ ) \ No newline at end of file diff --git a/README b/README index 3f9e69324..4357bb7b9 100644 --- a/README +++ b/README @@ -19,6 +19,10 @@ Detailed building instructions for OS X --------------------------------------- See: http://wiki.tomahawk-player.org/mediawiki/index.php/Building_OS_X_Application_Bundle_on_Snow_Leopard_(10.6) +Doxygen Documentation +--------------------- + See: http://dev.tomahawk-player.org/api/classes.html + Dependencies ------------ diff --git a/data/images/account-settings.png b/data/images/account-settings.png index 4bef71bb0..45b9e932a 100644 Binary files a/data/images/account-settings.png and b/data/images/account-settings.png differ diff --git a/data/images/advanced-settings.png b/data/images/advanced-settings.png index 85caceeae..68238ba31 100644 Binary files a/data/images/advanced-settings.png and b/data/images/advanced-settings.png differ diff --git a/data/images/dashboard.png b/data/images/dashboard.png new file mode 100644 index 000000000..643cab327 Binary files /dev/null and b/data/images/dashboard.png differ diff --git a/data/images/lastfm-settings.png b/data/images/lastfm-settings.png index 7f4a4575c..d6e37184b 100644 Binary files a/data/images/lastfm-settings.png and b/data/images/lastfm-settings.png differ diff --git a/data/images/list-add.png b/data/images/list-add.png index 874ca23c6..5fa31752b 100644 Binary files a/data/images/list-add.png and b/data/images/list-add.png differ diff --git a/data/images/list-remove.png b/data/images/list-remove.png index 6ec777b0d..bd54ca495 100644 Binary files a/data/images/list-remove.png and b/data/images/list-remove.png differ diff --git a/data/images/loved.png b/data/images/loved.png index 233b076ff..41c6abce2 100644 Binary files a/data/images/loved.png and b/data/images/loved.png differ diff --git a/data/images/music-settings.png b/data/images/music-settings.png index ff4a5dab2..aa4b8b6b3 100644 Binary files a/data/images/music-settings.png and b/data/images/music-settings.png differ diff --git a/data/images/not-loved.png b/data/images/not-loved.png index be57bb537..9345fb39c 100644 Binary files a/data/images/not-loved.png and b/data/images/not-loved.png differ diff --git a/data/images/post.png b/data/images/post.png new file mode 100644 index 000000000..808a0737d Binary files /dev/null and b/data/images/post.png differ diff --git a/data/images/resolvers-settings.png b/data/images/resolvers-settings.png index ed67d5e38..80b207828 100644 Binary files a/data/images/resolvers-settings.png and b/data/images/resolvers-settings.png differ diff --git a/data/images/sipplugin-add.png b/data/images/sipplugin-add.png index daf308c10..5fa31752b 100644 Binary files a/data/images/sipplugin-add.png and b/data/images/sipplugin-add.png differ diff --git a/data/images/sipplugin-remove.png b/data/images/sipplugin-remove.png index d1528dc6c..bd54ca495 100644 Binary files a/data/images/sipplugin-remove.png and b/data/images/sipplugin-remove.png differ diff --git a/data/js/tomahawk.js b/data/js/tomahawk.js new file mode 100644 index 000000000..278ced0c6 --- /dev/null +++ b/data/js/tomahawk.js @@ -0,0 +1,300 @@ + +// if run in phantomjs add fake Tomahawk environment +if(window.Tomahawk === undefined) +{ + alert("PHANTOMJS ENVIRONMENT"); + var Tomahawk = { + fakeEnv: function() + { + return true; + }, + resolverData: function() + { + return { + scriptPath: function() + { + return "/home/tomahawk/resolver.js"; + } + }; + }, + log: function( message ) + { + console.log( message ); + } + }; +} + + +Tomahawk.resolver = { + scriptPath: Tomahawk.resolverData().scriptPath +}; + +Tomahawk.timestamp = function() { + return Math.round( new Date()/1000 ); +} + +Tomahawk.dumpResult = function( result ) { + var results = result.results; + Tomahawk.log("Dumping " + results.length + " results for query " + result.qid + "..."); + for(var i=0; i> 16) + (y >> 16) + (lsw >> 16); + return (msw << 16) | (lsw & 0xFFFF); + } + + function S (X, n) { return ( X >>> n ) | (X << (32 - n)); } + function R (X, n) { return ( X >>> n ); } + function Ch(x, y, z) { return ((x & y) ^ ((~x) & z)); } + function Maj(x, y, z) { return ((x & y) ^ (x & z) ^ (y & z)); } + function Sigma0256(x) { return (S(x, 2) ^ S(x, 13) ^ S(x, 22)); } + function Sigma1256(x) { return (S(x, 6) ^ S(x, 11) ^ S(x, 25)); } + function Gamma0256(x) { return (S(x, 7) ^ S(x, 18) ^ R(x, 3)); } + function Gamma1256(x) { return (S(x, 17) ^ S(x, 19) ^ R(x, 10)); } + + function core_sha256 (m, l) { + var K = new Array(0x428A2F98, 0x71374491, 0xB5C0FBCF, 0xE9B5DBA5, 0x3956C25B, 0x59F111F1, 0x923F82A4, 0xAB1C5ED5, 0xD807AA98, 0x12835B01, 0x243185BE, 0x550C7DC3, 0x72BE5D74, 0x80DEB1FE, 0x9BDC06A7, 0xC19BF174, 0xE49B69C1, 0xEFBE4786, 0xFC19DC6, 0x240CA1CC, 0x2DE92C6F, 0x4A7484AA, 0x5CB0A9DC, 0x76F988DA, 0x983E5152, 0xA831C66D, 0xB00327C8, 0xBF597FC7, 0xC6E00BF3, 0xD5A79147, 0x6CA6351, 0x14292967, 0x27B70A85, 0x2E1B2138, 0x4D2C6DFC, 0x53380D13, 0x650A7354, 0x766A0ABB, 0x81C2C92E, 0x92722C85, 0xA2BFE8A1, 0xA81A664B, 0xC24B8B70, 0xC76C51A3, 0xD192E819, 0xD6990624, 0xF40E3585, 0x106AA070, 0x19A4C116, 0x1E376C08, 0x2748774C, 0x34B0BCB5, 0x391C0CB3, 0x4ED8AA4A, 0x5B9CCA4F, 0x682E6FF3, 0x748F82EE, 0x78A5636F, 0x84C87814, 0x8CC70208, 0x90BEFFFA, 0xA4506CEB, 0xBEF9A3F7, 0xC67178F2); + var HASH = new Array(0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A, 0x510E527F, 0x9B05688C, 0x1F83D9AB, 0x5BE0CD19); + var W = new Array(64); + var a, b, c, d, e, f, g, h, i, j; + var T1, T2; + + m[l >> 5] |= 0x80 << (24 - l % 32); + m[((l + 64 >> 9) << 4) + 15] = l; + + for ( var i = 0; i>5] |= (str.charCodeAt(i / chrsz) & mask) << (24 - i%32); + } + return bin; + } + + function Utf8Encode(string) { + string = string.replace(/\r\n/g,"\n"); + var utftext = ""; + + for (var n = 0; n < string.length; n++) { + + var c = string.charCodeAt(n); + + if (c < 128) { + utftext += String.fromCharCode(c); + } + else if((c > 127) && (c < 2048)) { + utftext += String.fromCharCode((c >> 6) | 192); + utftext += String.fromCharCode((c & 63) | 128); + } + else { + utftext += String.fromCharCode((c >> 12) | 224); + utftext += String.fromCharCode(((c >> 6) & 63) | 128); + utftext += String.fromCharCode((c & 63) | 128); + } + + } + + return utftext; + } + + function binb2hex (binarray) { + var hex_tab = hexcase ? "0123456789ABCDEF" : "0123456789abcdef"; + var str = ""; + for(var i = 0; i < binarray.length * 4; i++) { + str += hex_tab.charAt((binarray[i>>2] >> ((3 - i%4)*8+4)) & 0xF) + + hex_tab.charAt((binarray[i>>2] >> ((3 - i%4)*8 )) & 0xF); + } + return str; + } + + s = Utf8Encode(s); + return binb2hex(core_sha256(str2binb(s), s.length * chrsz)); + +} \ No newline at end of file diff --git a/data/sql/dbmigrate-24_to_25.sql b/data/sql/dbmigrate-24_to_25.sql new file mode 100644 index 000000000..181df2705 --- /dev/null +++ b/data/sql/dbmigrate-24_to_25.sql @@ -0,0 +1,18 @@ +-- Script to migate from db version 24 to 25. +-- Added the social_attributes table. +-- +-- Separate each command with %% + +ALTER TABLE dynamic_playlist RENAME TO tmp_dynamic_playlist; + +CREATE TABLE IF NOT EXISTS dynamic_playlist ( + guid TEXT NOT NULL REFERENCES playlist(guid) ON DELETE CASCADE ON UPDATE CASCADE DEFERRABLE INITIALLY DEFERRED, + pltype TEXT, -- the generator type + plmode INTEGER -- the mode of this playlist +); + +INSERT INTO dynamic_playlist( guid, pltype, plmode ) SELECT guid, pltype, plmode FROM tmp_dynamic_playlist; + +DROP TABLE tmp_dynamic_playlist; + +UPDATE settings SET v = '25' WHERE k == 'schema_version'; diff --git a/resource.qrc b/resource.qrc new file mode 100644 index 000000000..e69de29bb diff --git a/resources.prc b/resources.prc new file mode 100644 index 000000000..e69de29bb diff --git a/resources.qrc b/resources.qrc index 38dbcb60a..763a4d08e 100644 --- a/resources.qrc +++ b/resources.qrc @@ -96,5 +96,7 @@ ./data/www/tomahawk_banner_small.png ./data/sql/dbmigrate-22_to_23.sql ./data/sql/dbmigrate-23_to_24.sql + ./data/sql/dbmigrate-24_to_25.sql + ./data/js/tomahawk.js diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 2a6c9b127..e9940b50a 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -264,4 +264,5 @@ IF( UNIX AND NOT APPLE AND KDE4_INSTALLED ) #install protocol file ENDIF() INSTALL( FILES ${CMAKE_BINARY_DIR}/tomahawk.protocol DESTINATION ${PROTOCOL_INSTALL_DIR} ) ENDIF() + #INCLUDE( "CPack.txt" ) diff --git a/src/audiocontrols.cpp b/src/audiocontrols.cpp index b6c77d682..f2ed4c808 100644 --- a/src/audiocontrols.cpp +++ b/src/audiocontrols.cpp @@ -80,6 +80,7 @@ AudioControls::AudioControls( QWidget* parent ) ui->volumeLowButton->setPixmap( RESPATH "images/volume-icon-muted.png" ); ui->volumeHighButton->setPixmap( RESPATH "images/volume-icon-full.png" ); ui->loveButton->setPixmap( RESPATH "images/not-loved.png" ); + ui->loveButton->setCheckable( true ); ui->ownerLabel->setForegroundRole( QPalette::Dark ); ui->metaDataArea->setStyleSheet( "QWidget#metaDataArea {\nborder-width: 4px;\nborder-image: url(" RESPATH "images/now-playing-panel.png) 4 4 4 4 stretch stretch; }" ); @@ -151,7 +152,7 @@ AudioControls::AudioControls( QWidget* parent ) connect( ui->artistTrackLabel, SIGNAL( clickedArtist() ), SLOT( onArtistClicked() ) ); connect( ui->artistTrackLabel, SIGNAL( clickedTrack() ), SLOT( onTrackClicked() ) ); connect( ui->albumLabel, SIGNAL( clickedAlbum() ), SLOT( onAlbumClicked() ) ); - connect( ui->loveButton, SIGNAL( clicked() ), SLOT( onLoveButtonClicked() ) ); + connect( ui->loveButton, SIGNAL( clicked( bool ) ), SLOT( onLoveButtonClicked( bool ) ) ); // connect( AudioEngine::instance(), SIGNAL( loading( Tomahawk::result_ptr ) ), SLOT( onPlaybackLoading( Tomahawk::result_ptr ) ) ); @@ -166,8 +167,8 @@ AudioControls::AudioControls( QWidget* parent ) .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 ) ) ); + SIGNAL( info( Tomahawk::InfoSystem::InfoRequestData, QVariant ) ), + SLOT( infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData, QVariant ) ) ); connect( Tomahawk::InfoSystem::InfoSystem::instance(), SIGNAL( finished( QString ) ), SLOT( infoSystemFinished( QString ) ) ); @@ -217,20 +218,21 @@ AudioControls::onPlaybackStarted( const Tomahawk::result_ptr& result ) trackInfo["artist"] = result->artist()->name(); trackInfo["album"] = result->album()->name(); - Tomahawk::InfoSystem::InfoSystem::instance()->getInfo( - s_acInfoIdentifier, Tomahawk::InfoSystem::InfoAlbumCoverArt, - QVariant::fromValue< Tomahawk::InfoSystem::InfoCriteriaHash >( trackInfo ), Tomahawk::InfoSystem::InfoCustomData() ); + Tomahawk::InfoSystem::InfoRequestData requestData; + requestData.caller = s_acInfoIdentifier; + requestData.type = Tomahawk::InfoSystem::InfoAlbumCoverArt; + requestData.input = QVariant::fromValue< Tomahawk::InfoSystem::InfoCriteriaHash >( trackInfo ); + requestData.customData = QVariantMap(); + + Tomahawk::InfoSystem::InfoSystem::instance()->getInfo( requestData ); } void -AudioControls::infoSystemInfo( QString caller, Tomahawk::InfoSystem::InfoType type, QVariant input, QVariant output, Tomahawk::InfoSystem::InfoCustomData customData ) +AudioControls::infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData requestData, QVariant output ) { - 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() << Q_FUNC_INFO << requestData.caller << requestData.type << s_acInfoIdentifier << Tomahawk::InfoSystem::InfoAlbumCoverArt; + if ( requestData.caller != s_acInfoIdentifier || requestData.type != Tomahawk::InfoSystem::InfoAlbumCoverArt ) { qDebug() << "Info of wrong type or not with our identifier"; return; @@ -242,13 +244,13 @@ AudioControls::infoSystemInfo( QString caller, Tomahawk::InfoSystem::InfoType ty return; } - if ( !output.canConvert< Tomahawk::InfoSystem::InfoCustomData >() ) + if ( !output.canConvert< QVariantMap >() ) { qDebug() << "Cannot convert fetched art from a QByteArray"; return; } - Tomahawk::InfoSystem::InfoCustomData returnedData = output.value< Tomahawk::InfoSystem::InfoCustomData >(); + QVariantMap returnedData = output.value< QVariantMap >(); const QByteArray ba = returnedData["imgbytes"].toByteArray(); if ( ba.length() ) { @@ -282,7 +284,6 @@ AudioControls::onPlaybackLoading( const Tomahawk::result_ptr& result ) ui->albumLabel->setResult( result ); ui->ownerLabel->setText( result->friendlySource() ); ui->coverImage->setPixmap( m_defaultCover ); - ui->loveButton->setVisible( true ); ui->timeLabel->setText( TomahawkUtils::timeToString( 0 ) ); ui->timeLeftLabel->setText( "-" + TomahawkUtils::timeToString( result->duration() ) ); @@ -298,6 +299,21 @@ AudioControls::onPlaybackLoading( const Tomahawk::result_ptr& result ) ui->pauseButton->setVisible( true ); ui->playPauseButton->setVisible( false ); ui->playPauseButton->setEnabled( false ); + ui->loveButton->setEnabled( true ); + ui->loveButton->setVisible( true ); + + result->loadSocialActions(); + + if ( result->loved() ) + { + ui->loveButton->setPixmap( RESPATH "images/loved.png" ); + ui->loveButton->setChecked( true ); + } + else + { + ui->loveButton->setPixmap( RESPATH "images/not-loved.png" ); + ui->loveButton->setChecked( false ); + } } @@ -340,12 +356,13 @@ AudioControls::onPlaybackStopped() ui->timeLeftLabel->setText( "" ); ui->coverImage->setPixmap( QPixmap() ); ui->seekSlider->setVisible( false ); - ui->loveButton->setVisible( false ); ui->pauseButton->setVisible( false ); ui->pauseButton->setEnabled( false ); ui->playPauseButton->setEnabled( true ); ui->playPauseButton->setVisible( true ); + ui->loveButton->setEnabled( false ); + ui->loveButton->setVisible( false ); /* m_pauseAction->setEnabled( false ); m_playAction->setEnabled( true ); */ @@ -489,18 +506,32 @@ AudioControls::onTrackClicked() void -AudioControls::onLoveButtonClicked() +AudioControls::onLoveButtonClicked( bool checked ) { Tomahawk::InfoSystem::InfoCriteriaHash trackInfo; trackInfo["title"] = m_currentTrack->track(); trackInfo["artist"] = m_currentTrack->artist()->name(); trackInfo["album"] = m_currentTrack->album()->name(); - Tomahawk::InfoSystem::InfoSystem::instance()->pushInfo( - s_acInfoIdentifier, Tomahawk::InfoSystem::InfoLove, - QVariant::fromValue< Tomahawk::InfoSystem::InfoCriteriaHash >( trackInfo ) ); + if ( checked ) + { + Tomahawk::InfoSystem::InfoSystem::instance()->pushInfo( + s_acInfoIdentifier, Tomahawk::InfoSystem::InfoLove, + QVariant::fromValue< Tomahawk::InfoSystem::InfoCriteriaHash >( trackInfo ) ); - DatabaseCommand_SocialAction* cmd = new DatabaseCommand_SocialAction( m_currentTrack, QString( "Love" ) ); - Database::instance()->enqueue( QSharedPointer(cmd) ); + DatabaseCommand_SocialAction* cmd = new DatabaseCommand_SocialAction( m_currentTrack, QString( "Love" ), QString( "true") ); + Database::instance()->enqueue( QSharedPointer(cmd) ); + ui->loveButton->setPixmap( RESPATH "images/loved.png" ); + } + else + { + Tomahawk::InfoSystem::InfoSystem::instance()->pushInfo( + s_acInfoIdentifier, Tomahawk::InfoSystem::InfoUnLove, + QVariant::fromValue< Tomahawk::InfoSystem::InfoCriteriaHash >( trackInfo ) ); + + DatabaseCommand_SocialAction* cmd = new DatabaseCommand_SocialAction( m_currentTrack, QString( "Love" ), QString( "false" ) ); + Database::instance()->enqueue( QSharedPointer(cmd) ); + ui->loveButton->setPixmap( RESPATH "images/not-loved.png" ); + } } diff --git a/src/audiocontrols.h b/src/audiocontrols.h index a74bd4925..b887d51f1 100644 --- a/src/audiocontrols.h +++ b/src/audiocontrols.h @@ -65,9 +65,9 @@ private slots: void onArtistClicked(); void onAlbumClicked(); void onTrackClicked(); - void onLoveButtonClicked(); + void onLoveButtonClicked( bool ); - void infoSystemInfo( QString caller, Tomahawk::InfoSystem::InfoType type, QVariant input, QVariant output, Tomahawk::InfoSystem::InfoCustomData customData ); + void infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData requestData, QVariant output ); void infoSystemFinished( QString target ); private: diff --git a/src/audiocontrols.ui b/src/audiocontrols.ui index 2a72910b5..872d5089f 100644 --- a/src/audiocontrols.ui +++ b/src/audiocontrols.ui @@ -250,20 +250,40 @@ - - - - - - 0 - 0 - - - - love - - - + + + + + + + + 0 + 0 + + + + PointingHandCursor + + + love + + + + + + + Qt::Vertical + + + + 20 + 13 + + + + + + diff --git a/src/delegateconfigwrapper.h b/src/delegateconfigwrapper.h index 9155c2cd4..e0a354e2d 100644 --- a/src/delegateconfigwrapper.h +++ b/src/delegateconfigwrapper.h @@ -21,6 +21,7 @@ #include #include #include +#include class DelegateConfigWrapper : public QDialog { @@ -39,6 +40,7 @@ public: v->addWidget( m_widget ); QDialogButtonBox* buttons = new QDialogButtonBox( QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal, this ); + m_okButton = buttons->button( QDialogButtonBox::Ok ); connect( buttons, SIGNAL( clicked( QAbstractButton*) ), this, SLOT( closed( QAbstractButton* ) ) ); connect( this, SIGNAL( rejected() ), this, SLOT( rejected() ) ); v->addWidget( buttons ); @@ -57,6 +59,11 @@ public: } public slots: + void toggleOkButton( bool dataError ) + { + // if dataError is True we want to set the button enabled to false + m_okButton->setEnabled( !dataError ); + } void closed( QAbstractButton* b ) { // let the config widget live to see another day @@ -90,6 +97,7 @@ public slots: private: QWidget* m_widget; + QPushButton* m_okButton; }; #endif diff --git a/src/headlesscheck.h b/src/headlesscheck.h index f82fcea33..05f55f4ce 100644 --- a/src/headlesscheck.h +++ b/src/headlesscheck.h @@ -22,8 +22,7 @@ #ifdef ENABLE_HEADLESS #define TOMAHAWK_APPLICATION QCoreApplication -#define TOMAHAWK_HEADLESS -#include > +#include #else diff --git a/src/libtomahawk/CMakeLists.txt b/src/libtomahawk/CMakeLists.txt index 7ba067273..9755b0b07 100644 --- a/src/libtomahawk/CMakeLists.txt +++ b/src/libtomahawk/CMakeLists.txt @@ -31,6 +31,7 @@ set( libSources viewpage.cpp viewmanager.cpp globalactionmanager.cpp + contextMenu.cpp sip/SipPlugin.cpp sip/SipHandler.cpp @@ -81,6 +82,7 @@ set( libSources database/databasecommand_addclientauth.cpp database/databasecommand_clientauthvalid.cpp database/databasecommand_socialaction.cpp + database/databasecommand_loadsocialactions.cpp database/database.cpp infosystem/infosystemcache.cpp @@ -169,7 +171,9 @@ set( libSources widgets/welcomewidget.cpp widgets/welcomeplaylistmodel.cpp widgets/overlaywidget.cpp + widgets/HeaderLabel.cpp widgets/infowidgets/sourceinfowidget.cpp + widgets/infowidgets/ArtistInfoWidget.cpp kdsingleapplicationguard/kdsingleapplicationguard.cpp kdsingleapplicationguard/kdsharedmemorylocker.cpp @@ -191,9 +195,9 @@ set( libHeaders result.h source.h sourceplaylistinterface.h - viewpage.h viewmanager.h globalactionmanager.h + contextMenu.h artist.h album.h @@ -249,6 +253,7 @@ set( libHeaders database/databasecommand_addclientauth.h database/databasecommand_clientauthvalid.h database/databasecommand_socialaction.h + database/databasecommand_loadsocialactions.h infosystem/infosystem.h infosystem/infosystemworker.h @@ -304,7 +309,6 @@ set( libHeaders playlist/dynamic/DynamicPlaylist.h playlist/dynamic/DynamicControl.h - playlist/dynamic/GeneratorFactory.h playlist/dynamic/GeneratorInterface.h playlist/dynamic/DynamicView.h playlist/dynamic/DynamicModel.h @@ -320,7 +324,6 @@ set( libHeaders playlist/dynamic/widgets/DynamicSetupWidget.h playlist/dynamic/widgets/LoadingSpinner.h - utils/tomahawkutils.h utils/querylabel.h utils/elidedlabel.h utils/animatedcounterlabel.h @@ -337,23 +340,31 @@ set( libHeaders widgets/welcomewidget.h widgets/welcomeplaylistmodel.h widgets/overlaywidget.h + widgets/HeaderLabel.h widgets/infowidgets/sourceinfowidget.h + widgets/infowidgets/ArtistInfoWidget.h kdsingleapplicationguard/kdsingleapplicationguard.h - kdsingleapplicationguard/kdsharedmemorylocker.h - kdsingleapplicationguard/kdtoolsglobal.h - kdsingleapplicationguard/kdlockedsharedmemorypointer.h ) set( libHeaders_NoMOC - playlist/dynamic/GeneratorInterface.h + viewpage.h + + infosystem/infoplugins/unix/imageconverter.h + + playlist/dynamic/GeneratorInterface.h + playlist/dynamic/GeneratorFactory.h + + utils/tomahawkutils.h ) + set( libUI ${libUI} widgets/playlisttypeselectordlg.ui widgets/newplaylistwidget.ui widgets/searchwidget.ui widgets/welcomewidget.ui widgets/infowidgets/sourceinfowidget.ui + widgets/infowidgets/ArtistInfoWidget.ui playlist/topbar/topbar.ui playlist/infobar/infobar.ui ) @@ -379,17 +390,21 @@ include_directories( . ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_BINARY_DIR}/. IF( UNIX AND NOT APPLE ) SET( libSources ${libSources} - infosystem/infoplugins/win/gfwnotifyplugin.cpp infosystem/infoplugins/unix/fdonotifyplugin.cpp infosystem/infoplugins/unix/imageconverter.cpp ) SET( libHeaders ${libHeaders} - infosystem/infoplugins/win/gfwnotifyplugin.h infosystem/infoplugins/unix/fdonotifyplugin.h infosystem/infoplugins/unix/imageconverter.h ) ENDIF( UNIX AND NOT APPLE ) IF( WIN32 ) + SET( libSources ${libSources} + infosystem/infoplugins/win/gfwnotifyplugin.cpp ) + + SET( libHeaders ${libHeaders} + infosystem/infoplugins/win/gfwnotifyplugin.h ) + SET( OS_SPECIFIC_LINK_LIBRARIES ${OS_SPECIFIC_LINK_LIBRARIES} # System diff --git a/src/libtomahawk/album.h b/src/libtomahawk/album.h index 96b901b0e..12b7a41e3 100644 --- a/src/libtomahawk/album.h +++ b/src/libtomahawk/album.h @@ -70,6 +70,8 @@ signals: void tracksAdded( const QList& tracks ); void trackCountChanged( unsigned int tracks ); void sourceTrackCountChanged( unsigned int tracks ); + + void nextTrackReady(); private slots: void onTracksAdded( const QList& tracks ); diff --git a/src/libtomahawk/artist.cpp b/src/libtomahawk/artist.cpp index b801c38a6..ec96f206b 100644 --- a/src/libtomahawk/artist.cpp +++ b/src/libtomahawk/artist.cpp @@ -26,9 +26,27 @@ using namespace Tomahawk; -Artist::Artist() {} -Artist::~Artist() {} +Artist::Artist() +{ +} + + +Artist::~Artist() +{ +} + + +artist_ptr +Artist::get( const QString& name, bool autoCreate ) +{ + int artid = Database::instance()->impl()->artistId( name, autoCreate ); + if ( artid < 1 ) + return artist_ptr(); + + return Artist::get( artid, name ); +} + artist_ptr Artist::get( unsigned int id, const QString& name ) diff --git a/src/libtomahawk/artist.h b/src/libtomahawk/artist.h index f1b631d81..26d655b2a 100644 --- a/src/libtomahawk/artist.h +++ b/src/libtomahawk/artist.h @@ -1,5 +1,5 @@ /* === This file is part of Tomahawk Player - === - * + * * Copyright 2010-2011, Christian Muehlhaeuser * * Tomahawk is free software: you can redistribute it and/or modify @@ -36,6 +36,7 @@ class DLLEXPORT Artist : public QObject, public PlaylistInterface Q_OBJECT public: + static artist_ptr get( const QString& name, bool autoCreate = false ); static artist_ptr get( unsigned int id, const QString& name ); Artist( unsigned int id, const QString& name ); @@ -54,7 +55,7 @@ public: virtual bool hasNextItem(); virtual Tomahawk::result_ptr currentItem() const { return m_currentItem; } - + virtual PlaylistInterface::RepeatMode repeatMode() const { return PlaylistInterface::NoRepeat; } virtual bool shuffled() const { return false; } @@ -71,6 +72,8 @@ signals: void trackCountChanged( unsigned int tracks ); void sourceTrackCountChanged( unsigned int tracks ); + void nextTrackReady(); + private slots: void onTracksAdded( const QList& tracks ); diff --git a/src/libtomahawk/audio/audioengine.cpp b/src/libtomahawk/audio/audioengine.cpp index e86a9b6d1..464bcd748 100644 --- a/src/libtomahawk/audio/audioengine.cpp +++ b/src/libtomahawk/audio/audioengine.cpp @@ -22,10 +22,10 @@ #include "playlistinterface.h" #include "sourceplaylistinterface.h" +#include "tomahawksettings.h" #include "database/database.h" #include "database/databasecommand_logplayback.h" -#include "infosystem/infosystem.h" #include "network/servent.h" #include "album.h" @@ -51,6 +51,8 @@ AudioEngine::AudioEngine() , m_queue( 0 ) , m_timeElapsed( 0 ) , m_expectStop( false ) + , m_waitingOnNewTrack( false ) + , m_infoSystemConnected( false ) { s_instance = this; qDebug() << "Init AudioEngine"; @@ -74,17 +76,12 @@ AudioEngine::AudioEngine() // Since it's indendent, we'll set it to 75% since that's nicer setVolume( 75 ); #endif - - m_retryTimer.setInterval( 10000 ); - m_retryTimer.setSingleShot( false ); - connect( &m_retryTimer, SIGNAL( timeout() ), SLOT( loadNextTrack() ) ); } AudioEngine::~AudioEngine() { qDebug() << Q_FUNC_INFO; - m_retryTimer.stop(); m_mediaObject->stop(); // stop(); @@ -133,28 +130,36 @@ AudioEngine::pause() m_mediaObject->pause(); emit paused(); - Tomahawk::InfoSystem::InfoSystem::instance()->pushInfo( - s_aeInfoIdentifier, Tomahawk::InfoSystem::InfoNowPaused, QVariant() ); + Tomahawk::InfoSystem::InfoSystem::instance()->pushInfo( s_aeInfoIdentifier, Tomahawk::InfoSystem::InfoNowPaused, QVariant() ); } void -AudioEngine::stop( bool sendNotification ) +AudioEngine::stop() { qDebug() << Q_FUNC_INFO; m_mediaObject->stop(); - m_retryTimer.stop(); if ( m_playlist ) m_playlist->reset(); - + setCurrentTrack( Tomahawk::result_ptr() ); emit stopped(); - if ( sendNotification ) - Tomahawk::InfoSystem::InfoSystem::instance()->pushInfo( - s_aeInfoIdentifier, Tomahawk::InfoSystem::InfoNowStopped, QVariant() ); + Tomahawk::InfoSystem::InfoTypeMap map; + map[ Tomahawk::InfoSystem::InfoNowStopped ] = QVariant(); + + if ( m_waitingOnNewTrack ) + sendWaitingNotification(); + else if ( TomahawkSettings::instance()->verboseNotifications() ) + { + QVariantMap stopInfo; + stopInfo["message"] = QString( "Tomahawk is stopped." ); + map[ Tomahawk::InfoSystem::InfoNotifyUser ] = QVariant::fromValue< QVariantMap >( stopInfo ); + } + + Tomahawk::InfoSystem::InfoSystem::instance()->pushInfo( s_aeInfoIdentifier, map ); } @@ -165,7 +170,7 @@ AudioEngine::previous() if ( !m_playlist ) return; - + if ( m_playlist->skipRestrictions() == PlaylistInterface::NoSkip || m_playlist->skipRestrictions() == PlaylistInterface::NoSkipBackwards ) return; @@ -181,7 +186,7 @@ AudioEngine::next() if ( !m_playlist ) return; - + if ( m_playlist->skipRestrictions() == PlaylistInterface::NoSkip || m_playlist->skipRestrictions() == PlaylistInterface::NoSkipForwards ) return; @@ -203,10 +208,10 @@ AudioEngine::seek( int ms ) { if ( !m_playlist ) return; - + if ( m_playlist->seekRestrictions() == PlaylistInterface::NoSeek ) return; - + if ( isPlaying() || isPaused() ) { qDebug() << Q_FUNC_INFO << ms; @@ -232,6 +237,92 @@ AudioEngine::mute() } +void +AudioEngine::sendWaitingNotification() const +{ + QVariantMap retryInfo; + retryInfo["message"] = QString( "The current track could not be resolved. Tomahawk will pick back up with the next resolvable track from this source." ); + Tomahawk::InfoSystem::InfoSystem::instance()->pushInfo( + s_aeInfoIdentifier, Tomahawk::InfoSystem::InfoNotifyUser, + QVariant::fromValue< QVariantMap >( retryInfo ) ); +} + + +void +AudioEngine::sendNowPlayingNotification() +{ + qDebug() << Q_FUNC_INFO; + + if ( ! m_infoSystemConnected ) + { + connect( Tomahawk::InfoSystem::InfoSystem::instance(), + SIGNAL( info( Tomahawk::InfoSystem::InfoRequestData, QVariant ) ), + SLOT( infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData, QVariant ) ) ); + + connect( Tomahawk::InfoSystem::InfoSystem::instance(), SIGNAL( finished( QString ) ), SLOT( infoSystemFinished( QString ) ) ); + + m_infoSystemConnected = true; + } + + Tomahawk::InfoSystem::InfoCriteriaHash trackInfo; + trackInfo["artist"] = m_currentTrack->album()->artist()->name(); + trackInfo["album"] = m_currentTrack->album()->name(); + + Tomahawk::InfoSystem::InfoRequestData requestData; + requestData.caller = s_aeInfoIdentifier; + requestData.type = Tomahawk::InfoSystem::InfoAlbumCoverArt; + requestData.input = QVariant::fromValue< Tomahawk::InfoSystem::InfoCriteriaHash >( trackInfo ); + requestData.customData = QVariantMap(); + + Tomahawk::InfoSystem::InfoSystem::instance()->getInfo( requestData ); +} + + +void +AudioEngine::infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData requestData, QVariant output ) +{ + qDebug() << Q_FUNC_INFO; + + if ( requestData.caller != s_aeInfoIdentifier || + requestData.type != Tomahawk::InfoSystem::InfoAlbumCoverArt ) + { + qDebug() << Q_FUNC_INFO << " not destined for us or wrong type, caller is " << requestData.caller << " and type is " << requestData.type; + return; + } + + QVariantMap playInfo; + playInfo["message"] = QString( "Tomahawk is playing \"%1\" by %2 on album %3." ) + .arg( m_currentTrack->track() ) + .arg( m_currentTrack->artist()->name() ) + .arg( m_currentTrack->album()->name() ); + if ( !output.isNull() && output.isValid() ) + { + qDebug() << Q_FUNC_INFO << " output is valid"; + QVariantMap returnedData = output.value< QVariantMap >(); + const QByteArray ba = returnedData["imgbytes"].toByteArray(); + qDebug() << "ba.length = " << ba.length(); + if ( ba.length() ) + { + QPixmap pm; + pm.loadFromData( ba ); + playInfo["image"] = QVariant( pm.toImage() ); + } + } + + Tomahawk::InfoSystem::InfoSystem::instance()->pushInfo( + s_aeInfoIdentifier, Tomahawk::InfoSystem::InfoNotifyUser, + QVariant::fromValue< QVariantMap >( playInfo ) ); +} + + +void +AudioEngine::infoSystemFinished( QString caller ) +{ + Q_UNUSED( caller ); + qDebug() << Q_FUNC_INFO; +} + + void AudioEngine::onTrackAboutToFinish() { @@ -244,8 +335,6 @@ AudioEngine::loadTrack( const Tomahawk::result_ptr& result ) { qDebug() << Q_FUNC_INFO << thread() << result; - m_retryTimer.stop(); - bool err = false; { @@ -305,15 +394,18 @@ AudioEngine::loadTrack( const Tomahawk::result_ptr& result ) DatabaseCommand_LogPlayback* cmd = new DatabaseCommand_LogPlayback( m_currentTrack, DatabaseCommand_LogPlayback::Started ); Database::instance()->enqueue( QSharedPointer(cmd) ); - Tomahawk::InfoSystem::InfoCriteriaHash trackInfo; - - trackInfo["title"] = m_currentTrack->track(); - trackInfo["artist"] = m_currentTrack->artist()->name(); - trackInfo["album"] = m_currentTrack->album()->name(); - Tomahawk::InfoSystem::InfoSystem::instance()->pushInfo( - s_aeInfoIdentifier, Tomahawk::InfoSystem::InfoNowPlaying, - QVariant::fromValue< Tomahawk::InfoSystem::InfoCriteriaHash >( trackInfo ) ); + Tomahawk::InfoSystem::InfoCriteriaHash trackInfo; + trackInfo["title"] = m_currentTrack->track(); + trackInfo["artist"] = m_currentTrack->artist()->name(); + trackInfo["album"] = m_currentTrack->album()->name(); + + if ( TomahawkSettings::instance()->verboseNotifications() ) + sendNowPlayingNotification(); + Tomahawk::InfoSystem::InfoSystem::instance()->pushInfo( + s_aeInfoIdentifier, + Tomahawk::InfoSystem::InfoNowPlaying, + QVariant::fromValue< Tomahawk::InfoSystem::InfoCriteriaHash >( trackInfo ) ); } } @@ -323,6 +415,7 @@ AudioEngine::loadTrack( const Tomahawk::result_ptr& result ) return false; } + m_waitingOnNewTrack = false; return true; } @@ -330,9 +423,7 @@ void AudioEngine::loadPreviousTrack() { qDebug() << Q_FUNC_INFO; - - m_retryTimer.stop(); - + if ( !m_playlist ) { stop(); @@ -352,9 +443,6 @@ AudioEngine::loadNextTrack() { qDebug() << Q_FUNC_INFO; - bool wasRetrying = m_retryTimer.isActive(); - m_retryTimer.stop(); - Tomahawk::result_ptr result; if ( m_queue && m_queue->trackCount() ) @@ -371,20 +459,9 @@ AudioEngine::loadNextTrack() loadTrack( result ); else { - stop( false ); if ( m_playlist && m_playlist->retryMode() == Tomahawk::PlaylistInterface::Retry ) - { - if ( !wasRetrying ) - { - Tomahawk::InfoSystem::InfoCriteriaHash retryInfo; - retryInfo["message"] = QString( "The current track could not be resolved. Tomahawk will keep trying..." ); - Tomahawk::InfoSystem::InfoSystem::instance()->pushInfo( - s_aeInfoIdentifier, Tomahawk::InfoSystem::InfoNotifyUser, - QVariant::fromValue< Tomahawk::InfoSystem::InfoCriteriaHash >( retryInfo ) ); - } - m_retryTimer.setInterval( m_playlist->retryInterval() ); - m_retryTimer.start(); - } + m_waitingOnNewTrack = true; + stop(); } } @@ -396,25 +473,31 @@ AudioEngine::playItem( Tomahawk::PlaylistInterface* playlist, const Tomahawk::re if ( m_playlist ) m_playlist->reset(); - + setPlaylist( playlist ); m_currentTrackPlaylist = playlist; if ( !result.isNull() ) loadTrack( result ); - else if ( m_playlist->retryMode() == PlaylistInterface::Retry ) + else if ( m_playlist && m_playlist->retryMode() == PlaylistInterface::Retry ) { - Tomahawk::InfoSystem::InfoCriteriaHash retryInfo; - retryInfo["message"] = QString( "The current track could not be resolved. Tomahawk will keep trying..." ); - Tomahawk::InfoSystem::InfoSystem::instance()->pushInfo( - s_aeInfoIdentifier, Tomahawk::InfoSystem::InfoNotifyUser, - QVariant::fromValue< Tomahawk::InfoSystem::InfoCriteriaHash >( retryInfo ) ); - m_retryTimer.setInterval( playlist->retryInterval() ); - m_retryTimer.start(); + m_waitingOnNewTrack = true; + stop(); } } +void +AudioEngine::playlistNextTrackReady() +{ + if ( !m_waitingOnNewTrack ) + return; + + m_waitingOnNewTrack = false; + next(); +} + + void AudioEngine::onAboutToFinish() { @@ -476,7 +559,12 @@ AudioEngine::setPlaylist( PlaylistInterface* playlist ) { if ( m_playlist ) m_playlist->reset(); + m_playlist = playlist; + + if ( m_playlist && m_playlist->object() && m_playlist->retryMode() == PlaylistInterface::Retry ) + connect( m_playlist->object(), SIGNAL( nextTrackReady() ), SLOT( playlistNextTrackReady() ) ); + emit playlistChanged( playlist ); } @@ -500,7 +588,7 @@ AudioEngine::setCurrentTrack( const Tomahawk::result_ptr& result ) bool AudioEngine::isHttpResult( const QString& url ) const { - return url.startsWith( "http://" ); + return url.startsWith( "http://" ) || url.startsWith( "https://" ); } diff --git a/src/libtomahawk/audio/audioengine.h b/src/libtomahawk/audio/audioengine.h index 92f687518..571215edb 100644 --- a/src/libtomahawk/audio/audioengine.h +++ b/src/libtomahawk/audio/audioengine.h @@ -25,6 +25,8 @@ #include #include +#include "infosystem/infosystem.h" + #include "result.h" #include "typedefs.h" @@ -60,12 +62,12 @@ public: Tomahawk::PlaylistInterface* playlist() const { return m_playlist; } Tomahawk::result_ptr currentTrack() const { return m_currentTrack; } - + public slots: void playPause(); void play(); void pause(); - void stop( bool sendNotification = true ); + void stop(); void previous(); void next(); @@ -83,6 +85,11 @@ public slots: void onTrackAboutToFinish(); + void playlistNextTrackReady(); + + void infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData requestData, QVariant output ); + void infoSystemFinished( QString caller ); + signals: void loading( const Tomahawk::result_ptr& track ); void started( const Tomahawk::result_ptr& track ); @@ -115,6 +122,8 @@ private slots: private: bool isHttpResult( const QString& ) const; bool isLocalResult( const QString& ) const; + void sendWaitingNotification() const; + void sendNowPlayingNotification(); bool m_isPlayingHttp; QSharedPointer m_input; @@ -130,8 +139,8 @@ private: unsigned int m_timeElapsed; bool m_expectStop; - - QTimer m_retryTimer; + bool m_waitingOnNewTrack; + bool m_infoSystemConnected; static AudioEngine* s_instance; }; diff --git a/src/libtomahawk/contextMenu.cpp b/src/libtomahawk/contextMenu.cpp new file mode 100644 index 000000000..82053fba2 --- /dev/null +++ b/src/libtomahawk/contextMenu.cpp @@ -0,0 +1,218 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2010-2011, Christian Muehlhaeuser + * + * Tomahawk is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Tomahawk is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU 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 "contextMenu.h" + +#include + +#include "globalactionmanager.h" +#include "playlistview.h" +#include "viewmanager.h" + +using namespace Tomahawk; + + +ContextMenu::ContextMenu( QWidget* parent ) + : QMenu( parent ) +{ + m_sigmap = new QSignalMapper( this ); + connect( m_sigmap, SIGNAL( mapped( int ) ), SLOT( onTriggered( int ) ) ); + + m_supportedActions = ActionPlay | ActionQueue | ActionCopyLink; +} + + +void +ContextMenu::clear() +{ + QMenu::clear(); + + m_queries.clear(); + m_albums.clear(); + m_artists.clear(); +} + + +void +ContextMenu::setQueries( const QList& queries ) +{ + if ( queries.isEmpty() ) + return; + + QMenu::clear(); + m_queries.clear(); + m_queries << queries; + + if ( m_supportedActions & ActionPlay && itemCount() == 1 ) + m_sigmap->setMapping( addAction( tr( "&Play" ) ), ActionPlay ); + + if ( m_supportedActions & ActionQueue ) + m_sigmap->setMapping( addAction( tr( "Add to &Queue" ) ), ActionQueue ); + + //m_sigmap->setMapping( addAction( tr( "&Add to Playlist" ) ), ActionAddToPlaylist ); + + addSeparator(); + + if ( m_supportedActions & ActionCopyLink && itemCount() == 1 ) + m_sigmap->setMapping( addAction( tr( "Copy Track &Link" ) ), ActionCopyLink ); + + addSeparator(); + + if ( m_supportedActions & ActionDelete ) + m_sigmap->setMapping( addAction( queries.count() > 1 ? tr( "&Delete Items" ) : tr( "&Delete Item" ) ), ActionDelete ); + + foreach ( QAction* action, actions() ) + { + connect( action, SIGNAL( triggered() ), m_sigmap, SLOT( map() ) ); + } +} + + +void +ContextMenu::setQuery( const Tomahawk::query_ptr& query ) +{ + QList queries; + queries << query; + setQueries( queries ); +} + + +void +ContextMenu::setAlbums( const QList& albums ) +{ + if ( albums.isEmpty() ) + return; + + QMenu::clear(); + m_albums.clear(); + m_albums << albums; + + if ( m_supportedActions & ActionPlay && itemCount() == 1 ) + m_sigmap->setMapping( addAction( tr( "&Play" ) ), ActionPlay ); + + if ( m_supportedActions & ActionQueue ) + m_sigmap->setMapping( addAction( tr( "Add to &Queue" ) ), ActionQueue ); + + //m_sigmap->setMapping( addAction( tr( "&Add to Playlist" ) ), ActionAddToPlaylist ); + + addSeparator(); + +/* if ( m_supportedActions & ActionCopyLink && itemCount() == 1 ) + m_sigmap->setMapping( addAction( tr( "Copy Album &Link" ) ), ActionCopyLink ); */ + + foreach ( QAction* action, actions() ) + { + connect( action, SIGNAL( triggered() ), m_sigmap, SLOT( map() ) ); + } +} + + +void +ContextMenu::setAlbum( const Tomahawk::album_ptr& album ) +{ + QList albums; + albums << album; + setAlbums( albums ); +} + + +void +ContextMenu::setArtists( const QList& artists ) +{ + if ( artists.isEmpty() ) + return; + + QMenu::clear(); + m_artists.clear(); + m_artists << artists; + + if ( m_supportedActions & ActionPlay && itemCount() == 1 ) + m_sigmap->setMapping( addAction( tr( "&Play" ) ), ActionPlay ); + + if ( m_supportedActions & ActionQueue ) + m_sigmap->setMapping( addAction( tr( "Add to &Queue" ) ), ActionQueue ); + + //m_sigmap->setMapping( addAction( tr( "&Add to Playlist" ) ), ActionAddToPlaylist ); + + addSeparator(); + +/* if ( m_supportedActions & ActionCopyLink && itemCount() == 1 ) + m_sigmap->setMapping( addAction( tr( "Copy Artist &Link" ) ), ActionCopyLink ); */ + + foreach ( QAction* action, actions() ) + { + connect( action, SIGNAL( triggered() ), m_sigmap, SLOT( map() ) ); + } +} + + +void +ContextMenu::setArtist( const Tomahawk::artist_ptr& artist ) +{ + QList artists; + artists << artist; + setArtists( artists ); +} + + +void +ContextMenu::onTriggered( int action ) +{ + switch ( action ) + { + case ActionQueue: + addToQueue(); + break; + + case ActionCopyLink: + copyLink(); + break; + + default: + emit triggered( action ); + } +} + + +void ContextMenu::addToQueue() +{ + foreach ( const query_ptr& query, m_queries ) + { + ViewManager::instance()->queue()->model()->append( query ); + } + foreach ( const artist_ptr& artist, m_artists ) + { + ViewManager::instance()->queue()->model()->append( artist ); + } + foreach ( const album_ptr& album, m_albums ) + { + ViewManager::instance()->queue()->model()->append( album ); + } + + ViewManager::instance()->showQueue(); +} + + +void +ContextMenu::copyLink() +{ + if ( m_queries.count() ) + { + GlobalActionManager::instance()->copyToClipboard( m_queries.first() ); + } +} diff --git a/src/libtomahawk/contextMenu.h b/src/libtomahawk/contextMenu.h new file mode 100644 index 000000000..f217d3581 --- /dev/null +++ b/src/libtomahawk/contextMenu.h @@ -0,0 +1,78 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2010-2011, Christian Muehlhaeuser + * + * Tomahawk is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Tomahawk is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU 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 CONTEXTMENU_H +#define CONTEXTMENU_H + +#include +#include + +#include "typedefs.h" + +#include "dllmacro.h" + +namespace Tomahawk +{ + +class DLLEXPORT ContextMenu : public QMenu +{ +Q_OBJECT + +public: + enum MenuActions + { ActionPlay = 1, ActionQueue = 2, ActionDelete = 4, ActionCopyLink = 8 }; + + explicit ContextMenu( QWidget* parent = 0 ); + + int supportedActions() const { return m_supportedActions; } + void setSupportedActions( int actions ) { m_supportedActions = actions; } + + void setQuery( const Tomahawk::query_ptr& query ); + void setQueries( const QList& queries ); + + void setArtist( const Tomahawk::artist_ptr& artist ); + void setArtists( const QList& artists ); + + void setAlbum( const Tomahawk::album_ptr& album ); + void setAlbums( const QList& albums ); + + void clear(); + + unsigned int itemCount() const { return m_queries.count() + m_artists.count() + m_albums.count(); } + +signals: + void triggered( int action ); + +private slots: + void onTriggered( int action ); + + void copyLink(); + void addToQueue(); + +private: + QSignalMapper* m_sigmap; + int m_supportedActions; + + QList m_queries; + QList m_artists; + QList m_albums; +}; + +}; // ns + +#endif diff --git a/src/libtomahawk/database/database.h b/src/libtomahawk/database/database.h index 239e59def..d04cb74d9 100644 --- a/src/libtomahawk/database/database.h +++ b/src/libtomahawk/database/database.h @@ -69,6 +69,8 @@ private slots: void setIsReadyTrue() { m_ready = true; } private: + DatabaseImpl* impl() const { return m_impl; } + bool m_ready; DatabaseImpl* m_impl; DatabaseWorker* m_workerRW; @@ -77,6 +79,8 @@ private: int m_maxConcurrentThreads; static Database* s_instance; + + friend class Tomahawk::Artist; }; #endif // DATABASE_H diff --git a/src/libtomahawk/database/databasecommand.cpp b/src/libtomahawk/database/databasecommand.cpp index 92843b4f8..7537bd121 100644 --- a/src/libtomahawk/database/databasecommand.cpp +++ b/src/libtomahawk/database/databasecommand.cpp @@ -31,6 +31,7 @@ #include "databasecommand_deletedynamicplaylist.h" #include "databasecommand_setdynamicplaylistrevision.h" #include "databasecommand_socialaction.h" +#include "databasecommand_loadsocialactions.h" DatabaseCommand::DatabaseCommand( QObject* parent ) diff --git a/src/libtomahawk/database/databasecommand_addfiles.cpp b/src/libtomahawk/database/databasecommand_addfiles.cpp index b8a09c90a..b02e23378 100644 --- a/src/libtomahawk/database/databasecommand_addfiles.cpp +++ b/src/libtomahawk/database/databasecommand_addfiles.cpp @@ -148,14 +148,16 @@ DatabaseCommand_AddFiles::exec( DatabaseImpl* dbi ) if( !source()->isLocal() ) url = QString( "servent://%1\t%2" ).arg( source()->userName() ).arg( url ); - bool isnew; - artistid = dbi->artistId( artist, isnew ); + bool autoCreate = true; + artistid = dbi->artistId( artist, autoCreate ); if ( artistid < 1 ) continue; - trackid = dbi->trackId( artistid, track, isnew ); + autoCreate = true; // artistId overwrites autoCreate (reference) + trackid = dbi->trackId( artistid, track, autoCreate ); if ( trackid < 1 ) continue; - albumid = dbi->albumId( artistid, album, isnew ); + autoCreate = true; // trackId overwrites autoCreate (reference) + albumid = dbi->albumId( artistid, album, autoCreate ); // Now add the association query_filejoin.bindValue( 0, fileid ); diff --git a/src/libtomahawk/database/databasecommand_loadsocialactions.cpp b/src/libtomahawk/database/databasecommand_loadsocialactions.cpp new file mode 100644 index 000000000..4a29e9359 --- /dev/null +++ b/src/libtomahawk/database/databasecommand_loadsocialactions.cpp @@ -0,0 +1,75 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2010-2011, Christopher Reichert + * + * Tomahawk is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Tomahawk is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU 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_loadsocialactions.h" + +#include + +#include "database/database.h" +#include "databaseimpl.h" +#include "network/servent.h" +#include "result.h" + +using namespace Tomahawk; + + +void +DatabaseCommand_LoadSocialActions::exec( DatabaseImpl* dbi ) +{ + qDebug() << Q_FUNC_INFO; + Q_ASSERT( !source().isNull() ); + + TomahawkSqlQuery query = dbi->newquery(); + + QVariant srcid = source()->isLocal() ? QVariant( QVariant::Int ) : source()->id(); + + bool autoCreate = true; + int artid = dbi->artistId( m_artist, autoCreate ); + if( artid < 1 ) + return; + + autoCreate = true; // artistId overwrites autoCreate (reference) + int trkid = dbi->trackId( artid, m_track, autoCreate ); + if( trkid < 1 ) + return; + + QString whereToken; + whereToken = QString( "WHERE id IS %1" ).arg( trkid ); + + QString sql = QString( + "SELECT k, v, timestamp, source " + "FROM social_attributes %1 " + "ORDER BY timestamp ASC" ).arg( whereToken ); + + query.prepare( sql ); + query.exec(); + + QList< Tomahawk::SocialAction > allSocialActions; + while ( query.next() ) { + Tomahawk::SocialAction action; + action.action = query.value( 0 ); // action + action.value = query.value( 1 ); // comment + action.timestamp = query.value( 2 ); // timestamp + action.source = query.value( 3 ); // source + + allSocialActions.append( action ); + } + + m_result->setAllSocialActions( allSocialActions ); +} + diff --git a/src/libtomahawk/database/databasecommand_loadsocialactions.h b/src/libtomahawk/database/databasecommand_loadsocialactions.h new file mode 100644 index 000000000..7d80bcd89 --- /dev/null +++ b/src/libtomahawk/database/databasecommand_loadsocialactions.h @@ -0,0 +1,73 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2011, Christopher Reichert + * + * Tomahawk is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Tomahawk is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU 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_LOADSOCIALACTIONS_H +#define DATABASECOMMAND_LOADSOCIALACTIONS_H + +#include +#include +#include "database/databasecommand.h" + +#include "sourcelist.h" +#include "typedefs.h" +#include "artist.h" +#include "result.h" + +#include "dllmacro.h" + + +class DLLEXPORT DatabaseCommand_LoadSocialActions : public DatabaseCommand +{ +Q_OBJECT + +public: + + explicit DatabaseCommand_LoadSocialActions( QObject* parent = 0 ) + : DatabaseCommand( parent ) + {} + + + explicit DatabaseCommand_LoadSocialActions( Tomahawk::Result* result, QObject* parent = 0 ) + : DatabaseCommand( parent ), m_result( result ) + { + setSource( SourceList::instance()->getLocal() ); + setArtist( result->artist()->name() ); + setTrack( result->track() ); + } + + virtual QString commandname() const { return "loadsocialactions"; } + + virtual void exec( DatabaseImpl* ); + + QString artist() const { return m_artist; } + void setArtist( const QString& s ) { m_artist = s; } + + QString track() const { return m_track; } + void setTrack( const QString& s ) { m_track = s; } + +signals: + void done( QList< Tomahawk::SocialAction >& allSocialActions ); + +private: + Tomahawk::Result* m_result; + QString m_artist; + QString m_track; + +}; + +#endif // DATABASECOMMAND_LOADSOCIALACTIONS_H diff --git a/src/libtomahawk/database/databasecommand_logplayback.cpp b/src/libtomahawk/database/databasecommand_logplayback.cpp index b14ab6e3c..b6aeff307 100644 --- a/src/libtomahawk/database/databasecommand_logplayback.cpp +++ b/src/libtomahawk/database/databasecommand_logplayback.cpp @@ -40,7 +40,7 @@ DatabaseCommand_LogPlayback::postCommitHook() // do not auto resolve this track Tomahawk::query_ptr q = Tomahawk::Query::get( m_artist, m_track, QString() ); - q->setPlayedBy( source() ); + q->setPlayedBy( source(), m_playtime ); if ( m_action == Finished ) { @@ -80,12 +80,13 @@ DatabaseCommand_LogPlayback::exec( DatabaseImpl* dbi ) query.bindValue( 0, srcid ); - bool isnew; - int artid = dbi->artistId( m_artist, isnew ); + bool autoCreate = true; + int artid = dbi->artistId( m_artist, autoCreate ); if( artid < 1 ) return; - int trkid = dbi->trackId( artid, m_track, isnew ); + autoCreate = true; // artistId overwrites autoCreate (reference) + int trkid = dbi->trackId( artid, m_track, autoCreate ); if( trkid < 1 ) return; diff --git a/src/libtomahawk/database/databasecommand_playbackhistory.cpp b/src/libtomahawk/database/databasecommand_playbackhistory.cpp index a6c77e534..6e5c3af8d 100644 --- a/src/libtomahawk/database/databasecommand_playbackhistory.cpp +++ b/src/libtomahawk/database/databasecommand_playbackhistory.cpp @@ -67,11 +67,11 @@ DatabaseCommand_PlaybackHistory::exec( DatabaseImpl* dbi ) if ( query.value( 3 ).toUInt() == 0 ) { - q->setPlayedBy( SourceList::instance()->getLocal() ); + q->setPlayedBy( SourceList::instance()->getLocal(), query.value( 1 ).toUInt() ); } else { - q->setPlayedBy( SourceList::instance()->get( query.value( 3 ).toUInt() ) ); + q->setPlayedBy( SourceList::instance()->get( query.value( 3 ).toUInt() ), query.value( 1 ).toUInt() ); } ql << q; diff --git a/src/libtomahawk/database/databasecommand_socialaction.cpp b/src/libtomahawk/database/databasecommand_socialaction.cpp index 850cfbc5f..d284d432c 100644 --- a/src/libtomahawk/database/databasecommand_socialaction.cpp +++ b/src/libtomahawk/database/databasecommand_socialaction.cpp @@ -45,21 +45,22 @@ DatabaseCommand_SocialAction::exec( DatabaseImpl* dbi ) Q_ASSERT( !source().isNull() ); TomahawkSqlQuery query = dbi->newquery(); - + query.prepare( "INSERT INTO social_attributes(id, source, k, v, timestamp) " "VALUES (?, ?, ?, ?, ?)" ); - + QVariant srcid = source()->isLocal() ? QVariant( QVariant::Int ) : source()->id(); - bool isnew; - int artid = dbi->artistId( m_artist, isnew ); + bool autoCreate = true; + int artid = dbi->artistId( m_artist, autoCreate ); if( artid < 1 ) return; - int trkid = dbi->trackId( artid, m_track, isnew ); + autoCreate = true; // artistId overwrites autoCreate (reference) + int trkid = dbi->trackId( artid, m_track, autoCreate ); if( trkid < 1 ) return; - + query.bindValue( 0, trkid ); query.bindValue( 1, srcid ); query.bindValue( 2, m_action ); diff --git a/src/libtomahawk/database/databasecommand_socialaction.h b/src/libtomahawk/database/databasecommand_socialaction.h index 070cb38bb..20188a95c 100644 --- a/src/libtomahawk/database/databasecommand_socialaction.h +++ b/src/libtomahawk/database/databasecommand_socialaction.h @@ -6,7 +6,7 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. - * + * * Tomahawk is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -28,7 +28,17 @@ #include "dllmacro.h" - +/** + * \class DatabaseCommand_SocialAction + * \brief Database command used to write social actions to database. + * + * This Database command allows Tomahawk to write social actions to + * the local database. These social actions can be interfaced with social + * networking API's such as LastFm, Facebook, or Twitter to allow the user + * to sync these actions with their accounts on these sites. + * + * \see DatabaseCommand_LoadSocialAction + */ class DLLEXPORT DatabaseCommand_SocialAction : public DatabaseCommand { Q_OBJECT @@ -38,10 +48,24 @@ Q_PROPERTY( int timestamp READ timestamp WRITE setTimestamp ) public: + /** + * \brief Default constructor for DatabaseCommand_SocialAction. + * + * Constructs an empty database command for a social action. + */ explicit DatabaseCommand_SocialAction( QObject* parent = 0 ) : DatabaseCommand( parent ) {} + /** + * \brief Overloaded constructor for DatabaseCommand_SocialAction. + * \param result Pointer to a Tomahawk::Result. + * \param action Name of the social action to be written to the database. + * \param comment Comment associated with this social action. + * \param parent Parent class. + * + * Constructor which creates a new database command for the specified social action. + */ explicit DatabaseCommand_SocialAction( const Tomahawk::result_ptr& result, QString action, QString comment="", QObject* parent = 0 ) : DatabaseCommand( parent ), m_result( result ), m_action( action ) { @@ -53,26 +77,94 @@ public: setTimestamp( QDateTime::currentDateTime().toTime_t() ); } + /** + * \brief Returns the name of this database command. + * \return QString containing the database command name 'socialaction'. + */ virtual QString commandname() const { return "socialaction"; } - virtual void exec( DatabaseImpl* ); + /** + * \brief Executes the database command. + * \param dbi Database instance. + * + * This method prepares an sql query to write this social action + * into the local database. + */ + virtual void exec( DatabaseImpl* dbi ); + + /** + * \brief Triggers a Database Sync. + */ virtual void postCommitHook(); + /** + * \brief Returns the artist associated with this database command. + * \return Name of the artist. + * \see setArtist() + */ QString artist() const { return m_artist; } + + /** + * \brief Sets the artist name for this database command. + * \param s QString containing the artist name. + * \see artist() + */ void setArtist( const QString& s ) { m_artist = s; } + /** + * \brief Returns the track name associated with this social action. + * \return QString containing the track name. + * \see setTrack() + */ QString track() const { return m_track; } - void setTrack( const QString& s ) { m_track = s; } - // key + /** + * \brief Sets the track name associated with this database command. + * \param track QString containing the track name. + * \see track() + */ + void setTrack( const QString& track ) { m_track = track; } + + /** + * \brief Returns the social action for this database command instance. + * \return QString containing the action name. + * \see setAction() + */ QString action() const { return m_action; } + + /** + * \brief Sets the social actions + * \param a QString containing action to be set in this class. + * \see action() + */ void setAction( QString a ) { m_action = a; } - // value + /** + * \brief Returns comment associated with this social action. + * \return QString containing comment associated with this social action. + * \see setComment() + */ QString comment() const { return m_comment; } + + /** + * \brief Sets the comment associated with this social action. + * \param com Comment associated with this social action. + * \see comment() + */ void setComment( const QString& com ) { m_comment = com; } + /** + * \brief Returns the timestamp associated with this social action. + * \return unsigned integer containing timestamp + * \see setTimesetamp() + */ int timestamp() const { return m_timestamp; } + + /** + * \brief Sets the timestamp associated with this social action. + * \param ts unsigned integer associated with this social action. + * \see timestamp() + */ void setTimestamp( const int ts ) { m_timestamp = ts; } private: diff --git a/src/libtomahawk/database/databaseimpl.cpp b/src/libtomahawk/database/databaseimpl.cpp index d8c21b024..b7bb8d159 100644 --- a/src/libtomahawk/database/databaseimpl.cpp +++ b/src/libtomahawk/database/databaseimpl.cpp @@ -38,7 +38,7 @@ */ #include "schema.sql.h" -#define CURRENT_SCHEMA_VERSION 24 +#define CURRENT_SCHEMA_VERSION 25 DatabaseImpl::DatabaseImpl( const QString& dbname, Database* parent ) @@ -149,10 +149,10 @@ DatabaseImpl::updateSchema( int oldVersion ) QStringList statements = sql.split( ";", QString::SkipEmptyParts ); db.transaction(); - foreach( const QString& sl, statements ) + foreach ( const QString& sl, statements ) { QString s( sl.trimmed() ); - if( s.length() == 0 ) + if ( s.length() == 0 ) continue; qDebug() << "Executing:" << s; @@ -173,18 +173,18 @@ DatabaseImpl::updateSchema( int oldVersion ) QString path = QString( RESPATH "sql/dbmigrate-%1_to_%2.sql" ).arg( cur - 1 ).arg( cur ); QFile script( path ); - if( !script.exists() || !script.open( QIODevice::ReadOnly ) ) + if ( !script.exists() || !script.open( QIODevice::ReadOnly ) ) { - qWarning() << "Failed to find or open upgrade script from" << (cur-1) << "to" << cur << " (" << path << ")! Aborting upgrade.."; + qWarning() << "Failed to find or open upgrade script from" << (cur-1) << "to" << cur << " (" << path << ")! Aborting upgrade..."; return false; } QString sql = QString::fromUtf8( script.readAll() ).trimmed(); QStringList statements = sql.split( ";", QString::SkipEmptyParts ); - foreach( const QString& sql, statements ) + foreach ( const QString& sql, statements ) { QString clean = cleanSql( sql ).trimmed(); - if( clean.isEmpty() ) + if ( clean.isEmpty() ) continue; qDebug() << "Executing upgrade statement:" << clean; @@ -224,7 +224,7 @@ DatabaseImpl::file( int fid ) "WHERE file.id = file_join.file AND file.id = %1" ) .arg( fid ) ); - if( query.next() ) + if ( query.next() ) { Tomahawk::source_ptr s; @@ -266,10 +266,10 @@ DatabaseImpl::file( int fid ) int -DatabaseImpl::artistId( const QString& name_orig, bool& isnew ) +DatabaseImpl::artistId( const QString& name_orig, bool& autoCreate ) { - isnew = false; - if( m_lastart == name_orig ) + bool isnew = false; + if ( m_lastart == name_orig ) return m_lastartid; int id = 0; @@ -279,31 +279,36 @@ DatabaseImpl::artistId( const QString& name_orig, bool& isnew ) query.prepare( "SELECT id FROM artist WHERE sortname = ?" ); query.addBindValue( sortname ); query.exec(); - if( query.next() ) + if ( query.next() ) { id = query.value( 0 ).toInt(); } - if( id ) + if ( id ) { m_lastart = name_orig; m_lastartid = id; return id; } - // not found, insert it. - query.prepare( "INSERT INTO artist(id,name,sortname) VALUES(NULL,?,?)" ); - query.addBindValue( name_orig ); - query.addBindValue( sortname ); - if( !query.exec() ) + if ( autoCreate ) { - qDebug() << "Failed to insert artist:" << name_orig; - return 0; + // not found, insert it. + query.prepare( "INSERT INTO artist(id,name,sortname) VALUES(NULL,?,?)" ); + query.addBindValue( name_orig ); + query.addBindValue( sortname ); + if ( !query.exec() ) + { + qDebug() << "Failed to insert artist:" << name_orig; + return 0; + } + + id = query.lastInsertId().toInt(); + isnew = true; + m_lastart = name_orig; + m_lastartid = id; } - id = query.lastInsertId().toInt(); - isnew = true; - m_lastart = name_orig; - m_lastartid = id; + autoCreate = isnew; return id; } diff --git a/src/libtomahawk/database/databaseimpl.h b/src/libtomahawk/database/databaseimpl.h index 151c5ee27..cb0eda4c9 100644 --- a/src/libtomahawk/database/databaseimpl.h +++ b/src/libtomahawk/database/databaseimpl.h @@ -53,7 +53,7 @@ public: TomahawkSqlQuery newquery() { return TomahawkSqlQuery( db ); } QSqlDatabase& database() { return db; } - int artistId( const QString& name_orig, bool& isnew ); + int artistId( const QString& name_orig, bool& autoCreate ); int trackId( int artistid, const QString& name_orig, bool& isnew ); int albumId( int artistid, const QString& name_orig, bool& isnew ); diff --git a/src/libtomahawk/database/schema.sql b/src/libtomahawk/database/schema.sql index 1f5e5593b..723943106 100644 --- a/src/libtomahawk/database/schema.sql +++ b/src/libtomahawk/database/schema.sql @@ -106,7 +106,7 @@ CREATE TABLE IF NOT EXISTS playlist_revision ( -- VALUES('revisionguid-11', 'dynamic_playlist-guid-2', '[]'); CREATE TABLE IF NOT EXISTS dynamic_playlist ( - guid TEXT PRIMARY KEY, + guid TEXT NOT NULL REFERENCES playlist(guid) ON DELETE CASCADE ON UPDATE CASCADE DEFERRABLE INITIALLY DEFERRED, pltype TEXT, -- the generator type plmode INTEGER -- the mode of this playlist ); @@ -282,4 +282,4 @@ CREATE TABLE IF NOT EXISTS settings ( v TEXT NOT NULL DEFAULT '' ); -INSERT INTO settings(k,v) VALUES('schema_version', '24'); +INSERT INTO settings(k,v) VALUES('schema_version', '25'); diff --git a/src/libtomahawk/database/schema.sql.h b/src/libtomahawk/database/schema.sql.h index bd085067a..b9ff40493 100644 --- a/src/libtomahawk/database/schema.sql.h +++ b/src/libtomahawk/database/schema.sql.h @@ -1,5 +1,5 @@ /* - This file was automatically generated from ./schema.sql on Sun Jun 12 05:17:25 CEST 2011. + This file was automatically generated from ./schema.sql on Fri Jun 24 09:10:23 CEST 2011. */ static const char * tomahawk_schema_sql = @@ -76,7 +76,7 @@ static const char * tomahawk_schema_sql = " previous_revision TEXT REFERENCES playlist_revision(guid) DEFERRABLE INITIALLY DEFERRED" ");" "CREATE TABLE IF NOT EXISTS dynamic_playlist (" -" guid TEXT PRIMARY KEY," +" guid TEXT NOT NULL REFERENCES playlist(guid) ON DELETE CASCADE ON UPDATE CASCADE DEFERRABLE INITIALLY DEFERRED," " pltype TEXT, " " plmode INTEGER " ");" @@ -183,7 +183,7 @@ static const char * tomahawk_schema_sql = " k TEXT NOT NULL PRIMARY KEY," " v TEXT NOT NULL DEFAULT ''" ");" -"INSERT INTO settings(k,v) VALUES('schema_version', '24');" +"INSERT INTO settings(k,v) VALUES('schema_version', '25');" ; const char * get_tomahawk_sql() diff --git a/src/libtomahawk/infosystem/infoplugins/generic/echonestplugin.cpp b/src/libtomahawk/infosystem/infoplugins/generic/echonestplugin.cpp index 979b84a9c..6e0f4d879 100644 --- a/src/libtomahawk/infosystem/infoplugins/generic/echonestplugin.cpp +++ b/src/libtomahawk/infosystem/infoplugins/generic/echonestplugin.cpp @@ -20,6 +20,10 @@ #include #include +#include "utils/tomahawkutils.h" + +#include + using namespace Tomahawk::InfoSystem; using namespace Echonest; @@ -43,43 +47,56 @@ EchoNestPlugin::namChangedSlot( QNetworkAccessManager *nam ) qDebug() << Q_FUNC_INFO; if( !nam ) return; + + QNetworkAccessManager* currNam = Echonest::Config::instance()->nam(); + TomahawkUtils::NetworkProxyFactory* oldProxyFactory = dynamic_cast< TomahawkUtils::NetworkProxyFactory* >( nam->proxyFactory() ); + + if ( !oldProxyFactory ) + { + qDebug() << "Could not get old proxyFactory!"; + return; + } - m_nam = QWeakPointer< QNetworkAccessManager >( nam ); - Echonest::Config::instance()->setNetworkAccessManager( nam ); + currNam->setConfiguration( nam->configuration() ); + currNam->setNetworkAccessible( nam->networkAccessible() ); + TomahawkUtils::NetworkProxyFactory* newProxyFactory = new TomahawkUtils::NetworkProxyFactory(); + newProxyFactory->setNoProxyHosts( oldProxyFactory->noProxyHosts() ); + newProxyFactory->setProxy( oldProxyFactory->proxy() ); + currNam->setProxyFactory( newProxyFactory ); } void -EchoNestPlugin::getInfo(const QString caller, const Tomahawk::InfoSystem::InfoType type, const QVariant input, const Tomahawk::InfoSystem::InfoCustomData customData) +EchoNestPlugin::getInfo( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData ) { - switch (type) + switch ( requestData.type ) { case Tomahawk::InfoSystem::InfoArtistBiography: - return getArtistBiography(caller, input, customData); + return getArtistBiography( requestId, requestData ); case Tomahawk::InfoSystem::InfoArtistFamiliarity: - return getArtistFamiliarity(caller, input, customData); + return getArtistFamiliarity( requestId, requestData ); case Tomahawk::InfoSystem::InfoArtistHotttness: - return getArtistHotttnesss(caller, input, customData); + return getArtistHotttnesss( requestId, requestData ); case Tomahawk::InfoSystem::InfoArtistTerms: - return getArtistTerms(caller, input, customData); + return getArtistTerms( requestId, requestData ); case Tomahawk::InfoSystem::InfoTrackEnergy: - return getSongProfile(caller, input, customData, "energy"); + return getSongProfile( requestId, requestData, "energy" ); case Tomahawk::InfoSystem::InfoMiscTopTerms: - return getMiscTopTerms(caller, input, customData); + return getMiscTopTerms( requestId, requestData ); default: { - emit info(caller, Tomahawk::InfoSystem::InfoNoInfo, QVariant(), QVariant(), customData); + emit info( requestId, requestData, QVariant() ); return; } } } void -EchoNestPlugin::getSongProfile(const QString &caller, const QVariant &input, const InfoCustomData &customData, const QString &item) +EchoNestPlugin::getSongProfile( uint requestId, const Tomahawk::InfoSystem::InfoRequestData &requestData, const QString &item ) { //WARNING: Totally not implemented yet Q_UNUSED( item ); - if( !isValidTrackData( caller, input, customData ) ) + if( !isValidTrackData( requestId, requestData ) ) return; // Track track( input.toString() ); @@ -91,74 +108,69 @@ EchoNestPlugin::getSongProfile(const QString &caller, const QVariant &input, con } void -EchoNestPlugin::getArtistBiography(const QString &caller, const QVariant &input, const InfoCustomData &customData) +EchoNestPlugin::getArtistBiography( uint requestId, const Tomahawk::InfoSystem::InfoRequestData &requestData ) { - if( !isValidArtistData( caller, input, customData ) ) + if( !isValidArtistData( requestId, requestData ) ) return; - Echonest::Artist artist( input.toString() ); + Echonest::Artist artist( requestData.input.toString() ); QNetworkReply *reply = artist.fetchBiographies(); - reply->setProperty("artist", QVariant::fromValue(artist)); - reply->setProperty( "input", input ); - m_replyMap[reply] = customData; - m_callerMap[reply] = caller; - connect(reply, SIGNAL(finished()), SLOT(getArtistBiographySlot())); + reply->setProperty( "artist", QVariant::fromValue< Echonest::Artist >( artist ) ); + reply->setProperty( "requestId", requestId ); + reply->setProperty( "requestData", QVariant::fromValue< Tomahawk::InfoSystem::InfoRequestData >( requestData ) ); + connect( reply, SIGNAL( finished() ), SLOT( getArtistBiographySlot() ) ); } void -EchoNestPlugin::getArtistFamiliarity(const QString &caller, const QVariant &input, const InfoCustomData &customData) +EchoNestPlugin::getArtistFamiliarity( uint requestId, const Tomahawk::InfoSystem::InfoRequestData &requestData ) { - if( !isValidArtistData( caller, input, customData ) ) + if( !isValidArtistData( requestId, requestData ) ) return; - qDebug() << "Fetching artist familiarity!" << input; - Echonest::Artist artist( input.toString() ); + qDebug() << "Fetching artist familiarity!" << requestData.input; + Echonest::Artist artist( requestData.input.toString() ); QNetworkReply* reply = artist.fetchFamiliarity(); - reply->setProperty( "artist", QVariant::fromValue(artist)); - reply->setProperty( "input", input ); - m_replyMap[reply] = customData; - m_callerMap[reply] = caller; - connect(reply, SIGNAL(finished()), SLOT(getArtistFamiliaritySlot())); + reply->setProperty( "artist", QVariant::fromValue< Echonest::Artist >( artist ) ); + reply->setProperty( "requestId", requestId ); + reply->setProperty( "requestData", QVariant::fromValue< Tomahawk::InfoSystem::InfoRequestData >( requestData ) ); + connect( reply, SIGNAL( finished() ), SLOT( getArtistFamiliaritySlot() ) ); } void -EchoNestPlugin::getArtistHotttnesss(const QString &caller, const QVariant &input, const InfoCustomData &customData) +EchoNestPlugin::getArtistHotttnesss( uint requestId, const Tomahawk::InfoSystem::InfoRequestData &requestData ) { - if( !isValidArtistData( caller, input, customData ) ) + if( !isValidArtistData( requestId, requestData ) ) return; - Echonest::Artist artist( input.toString() ); + Echonest::Artist artist( requestData.input.toString() ); QNetworkReply* reply = artist.fetchHotttnesss(); - reply->setProperty( "artist", QVariant::fromValue(artist)); - reply->setProperty( "input", input ); - m_replyMap[reply] = customData; - m_callerMap[reply] = caller; - connect(reply, SIGNAL(finished()), SLOT(getArtistHotttnesssSlot())); + reply->setProperty( "artist", QVariant::fromValue< Echonest::Artist >( artist ) ); + reply->setProperty( "requestId", requestId ); + reply->setProperty( "requestData", QVariant::fromValue< Tomahawk::InfoSystem::InfoRequestData >( requestData ) ); + connect( reply, SIGNAL( finished() ), SLOT( getArtistHotttnesssSlot() ) ); } void -EchoNestPlugin::getArtistTerms(const QString &caller, const QVariant &input, const InfoCustomData &customData) +EchoNestPlugin::getArtistTerms( uint requestId, const Tomahawk::InfoSystem::InfoRequestData &requestData ) { - if( !isValidArtistData( caller, input, customData ) ) + if( !isValidArtistData( requestId, requestData ) ) return; - Echonest::Artist artist( input.toString() ); + Echonest::Artist artist( requestData.input.toString() ); QNetworkReply* reply = artist.fetchTerms( Echonest::Artist::Weight ); - reply->setProperty( "artist", QVariant::fromValue(artist)); - reply->setProperty( "input", input ); - m_replyMap[reply] = customData; - m_callerMap[reply] = caller; - connect(reply, SIGNAL(finished()), SLOT(getArtistTermsSlot())); + reply->setProperty( "artist", QVariant::fromValue< Echonest::Artist >( artist ) ); + reply->setProperty( "requestId", requestId ); + reply->setProperty( "requestData", QVariant::fromValue< Tomahawk::InfoSystem::InfoRequestData >( requestData ) ); + connect( reply, SIGNAL( finished() ), SLOT( getArtistTermsSlot() ) ); } void -EchoNestPlugin::getMiscTopTerms(const QString &caller, const QVariant &input, const InfoCustomData& customData) +EchoNestPlugin::getMiscTopTerms( uint requestId, const Tomahawk::InfoSystem::InfoRequestData &requestData ) { - Q_UNUSED( input ); QNetworkReply* reply = Echonest::Artist::topTerms( 20 ); - m_replyMap[reply] = customData; - m_callerMap[reply] = caller; - connect( reply,SIGNAL(finished()), SLOT( getMiscTopSlot())); + reply->setProperty( "requestId", requestId ); + reply->setProperty( "requestData", QVariant::fromValue< Tomahawk::InfoSystem::InfoRequestData >( requestData ) ); + connect( reply, SIGNAL( finished() ), SLOT( getMiscTopSlot() ) ); } @@ -179,9 +191,10 @@ EchoNestPlugin::getArtistBiographySlot() biographyMap[biography.site()]["attribution"] = biography.license().url.toString(); } - emit info( m_callerMap[reply], Tomahawk::InfoSystem::InfoArtistBiography, reply->property( "input" ), QVariant::fromValue(biographyMap), m_replyMap[reply] ); - m_replyMap.remove(reply); - m_callerMap.remove(reply); + Tomahawk::InfoSystem::InfoRequestData requestData = reply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >(); + emit info( reply->property( "requestId" ).toUInt(), + requestData, + QVariant::fromValue< Tomahawk::InfoSystem::InfoGenericMap >( biographyMap ) ); reply->deleteLater(); } @@ -191,9 +204,10 @@ EchoNestPlugin::getArtistFamiliaritySlot() QNetworkReply* reply = qobject_cast( sender() ); Echonest::Artist artist = artistFromReply( reply ); qreal familiarity = artist.familiarity(); - emit info( m_callerMap[reply], Tomahawk::InfoSystem::InfoArtistFamiliarity, reply->property( "input" ), familiarity, m_replyMap[reply] ); - m_replyMap.remove(reply); - m_callerMap.remove(reply); + Tomahawk::InfoSystem::InfoRequestData requestData = reply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >(); + emit info( reply->property( "requestId" ).toUInt(), + requestData, + familiarity ); reply->deleteLater(); } @@ -203,9 +217,10 @@ EchoNestPlugin::getArtistHotttnesssSlot() QNetworkReply* reply = qobject_cast( sender() ); Echonest::Artist artist = artistFromReply( reply ); qreal hotttnesss = artist.hotttnesss(); - emit info( m_callerMap[reply], Tomahawk::InfoSystem::InfoArtistHotttness, reply->property( "input" ), hotttnesss, m_replyMap[reply] ); - m_replyMap.remove(reply); - m_callerMap.remove(reply); + Tomahawk::InfoSystem::InfoRequestData requestData = reply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >(); + emit info( reply->property( "requestId" ).toUInt(), + requestData, + hotttnesss ); reply->deleteLater(); } @@ -222,9 +237,10 @@ EchoNestPlugin::getArtistTermsSlot() termMap[ "frequency" ] = QString::number(term.frequency()); termsMap[ term.name() ] = termMap; } - emit info( m_callerMap[reply], Tomahawk::InfoSystem::InfoArtistTerms, reply->property( "input" ), QVariant::fromValue(termsMap), m_replyMap[reply] ); - m_replyMap.remove(reply); - m_callerMap.remove(reply); + Tomahawk::InfoSystem::InfoRequestData requestData = reply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >(); + emit info( reply->property( "requestId" ).toUInt(), + requestData, + QVariant::fromValue< Tomahawk::InfoSystem::InfoGenericMap >( termsMap ) ); reply->deleteLater(); } @@ -240,57 +256,61 @@ EchoNestPlugin::getMiscTopSlot() termMap[ "frequency" ] = QString::number( term.frequency() ); termsMap[ term.name().toLower() ] = termMap; } - emit info( m_callerMap[reply], Tomahawk::InfoSystem::InfoMiscTopTerms, QVariant(), QVariant::fromValue(termsMap), m_replyMap[reply] ); - m_replyMap.remove(reply); - m_callerMap.remove(reply); + Tomahawk::InfoSystem::InfoRequestData requestData = reply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >(); + emit info( reply->property( "requestId" ).toUInt(), + requestData, + QVariant::fromValue< Tomahawk::InfoSystem::InfoGenericMap >( termsMap ) ); reply->deleteLater(); } bool -EchoNestPlugin::isValidArtistData(const QString &caller, const QVariant &input, const InfoCustomData &customData) +EchoNestPlugin::isValidArtistData( uint requestId, const Tomahawk::InfoSystem::InfoRequestData &requestData ) { - if (input.isNull() || !input.isValid() || !input.canConvert()) + if ( requestData.input.isNull() || !requestData.input.isValid() || !requestData.input.canConvert< QString >() ) { - emit info(caller, Tomahawk::InfoSystem::InfoNoInfo, QVariant(), QVariant(), customData); + emit info( requestId, requestData, QVariant() ); return false; } - QString artistName = input.toString(); - if (artistName.isEmpty() ) + QString artistName = requestData.input.toString(); + if ( artistName.isEmpty() ) { - emit info(caller, Tomahawk::InfoSystem::InfoNoInfo, QVariant(), QVariant(), customData); + emit info( requestId, requestData, QVariant() ); return false; } return true; } bool -EchoNestPlugin::isValidTrackData(const QString &caller, const QVariant &input, const InfoCustomData &customData) +EchoNestPlugin::isValidTrackData( uint requestId, const Tomahawk::InfoSystem::InfoRequestData &requestData ) { - if (input.isNull() || !input.isValid() || !input.canConvert()) + if ( requestData.input.isNull() || !requestData.input.isValid() || !requestData.input.canConvert< QString >() ) { - emit info(caller, Tomahawk::InfoSystem::InfoNoInfo, QVariant(), QVariant(), customData); + emit info( requestId, requestData, QVariant() ); return false; } - QString trackName = input.toString(); - if (trackName.isEmpty() ) + QString trackName = requestData.input.toString(); + if ( trackName.isEmpty() ) { - emit info(caller, Tomahawk::InfoSystem::InfoNoInfo, QVariant(), QVariant(), customData); + emit info( requestId, requestData, QVariant() ); return false; } - if (!customData.contains("artistName") || - customData["artistName"].toString().isEmpty()) + if ( !requestData.customData.contains( "artistName" ) || requestData.customData[ "artistName" ].toString().isEmpty() ) + { + emit info( requestId, requestData, QVariant() ); return false; + } return true; } Artist -EchoNestPlugin::artistFromReply(QNetworkReply* reply) +EchoNestPlugin::artistFromReply( QNetworkReply* reply ) { Echonest::Artist artist = reply->property("artist").value(); try { - artist.parseProfile(reply); + artist.parseProfile( reply ); } catch( const Echonest::ParseError& e ) { qWarning() << "Caught parser error from echonest!" << e.what(); } return artist; } +// \ No newline at end of file diff --git a/src/libtomahawk/infosystem/infoplugins/generic/echonestplugin.h b/src/libtomahawk/infosystem/infoplugins/generic/echonestplugin.h index bd21cbca2..e1461daec 100644 --- a/src/libtomahawk/infosystem/infoplugins/generic/echonestplugin.h +++ b/src/libtomahawk/infosystem/infoplugins/generic/echonestplugin.h @@ -44,7 +44,7 @@ public: virtual ~EchoNestPlugin(); protected slots: - virtual void getInfo( const QString caller, const Tomahawk::InfoSystem::InfoType type, const QVariant input, const Tomahawk::InfoSystem::InfoCustomData customData ); + virtual void getInfo( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData ); virtual void pushInfo( const QString caller, const Tomahawk::InfoSystem::InfoType type, const QVariant data ) { @@ -53,28 +53,26 @@ protected slots: Q_UNUSED( data ); } - virtual void notInCacheSlot( const Tomahawk::InfoSystem::InfoCriteriaHash criteria, const QString caller, const Tomahawk::InfoSystem::InfoType type, const QVariant input, const Tomahawk::InfoSystem::InfoCustomData customData ) + virtual void notInCacheSlot( uint requestId, Tomahawk::InfoSystem::InfoCriteriaHash criteria, Tomahawk::InfoSystem::InfoRequestData requestData ) { + Q_UNUSED( requestId ); Q_UNUSED( criteria ); - Q_UNUSED( caller ); - Q_UNUSED( type ); - Q_UNUSED( input ); - Q_UNUSED( customData ); + Q_UNUSED( requestData ); } public slots: void namChangedSlot( QNetworkAccessManager *nam ); private: - void getSongProfile( const QString &caller, const QVariant &input, const InfoCustomData &customData, const QString &item = QString() ); - void getArtistBiography ( const QString &caller, const QVariant &input, const InfoCustomData &customData ); - void getArtistFamiliarity( const QString &caller, const QVariant &input, const InfoCustomData &customData ); - void getArtistHotttnesss( const QString &caller, const QVariant &input, const InfoCustomData &customData ); - void getArtistTerms( const QString &caller, const QVariant &input, const InfoCustomData &customData ); - void getMiscTopTerms( const QString &caller, const QVariant &input, const InfoCustomData &customData ); + void getSongProfile( uint requestId, const Tomahawk::InfoSystem::InfoRequestData &requestData, const QString &item = QString() ); + void getArtistBiography( uint requestId, const Tomahawk::InfoSystem::InfoRequestData &requestData ); + void getArtistFamiliarity( uint requestId, const Tomahawk::InfoSystem::InfoRequestData &requestData ); + void getArtistHotttnesss( uint requestId, const Tomahawk::InfoSystem::InfoRequestData &requestData ); + void getArtistTerms( uint requestId, const Tomahawk::InfoSystem::InfoRequestData &requestData ); + void getMiscTopTerms( uint requestId, const Tomahawk::InfoSystem::InfoRequestData &requestData ); - bool isValidArtistData( const QString &caller, const QVariant &input, const InfoCustomData& customData ); - bool isValidTrackData( const QString &caller, const QVariant &input, const InfoCustomData& customData ); + bool isValidArtistData( uint requestId, const Tomahawk::InfoSystem::InfoRequestData &requestData ); + bool isValidTrackData( uint requestId, const Tomahawk::InfoSystem::InfoRequestData &requestData ); Echonest::Artist artistFromReply( QNetworkReply* ); private slots: @@ -83,11 +81,6 @@ private slots: void getArtistHotttnesssSlot(); void getArtistTermsSlot(); void getMiscTopSlot(); - -private: - QHash< QNetworkReply*, InfoCustomData > m_replyMap; - QHash< QNetworkReply*, QString > m_callerMap; - QWeakPointer< QNetworkAccessManager > m_nam; }; } diff --git a/src/libtomahawk/infosystem/infoplugins/generic/lastfmplugin.cpp b/src/libtomahawk/infosystem/infoplugins/generic/lastfmplugin.cpp index f01850b38..30f0732d5 100644 --- a/src/libtomahawk/infosystem/infoplugins/generic/lastfmplugin.cpp +++ b/src/libtomahawk/infosystem/infoplugins/generic/lastfmplugin.cpp @@ -21,6 +21,7 @@ #include #include #include +#include #include "album.h" #include "typedefs.h" @@ -45,8 +46,8 @@ LastFmPlugin::LastFmPlugin() : InfoPlugin() , m_scrobbler( 0 ) { - m_supportedGetTypes << InfoAlbumCoverArt << InfoArtistImages << InfoLove; - m_supportedPushTypes << InfoSubmitScrobble << InfoSubmitNowPlaying << InfoLove; + m_supportedGetTypes << InfoAlbumCoverArt << InfoArtistImages << InfoArtistSimilars << InfoArtistSongs; + m_supportedPushTypes << InfoSubmitScrobble << InfoSubmitNowPlaying << InfoLove << InfoUnLove; /* Your API Key is 7194b85b6d1f424fe1668173a78c0c4a @@ -93,39 +94,64 @@ void LastFmPlugin::namChangedSlot( QNetworkAccessManager *nam ) { qDebug() << Q_FUNC_INFO; - if( !nam ) + + if ( !nam ) return; - m_nam = QWeakPointer< QNetworkAccessManager >( nam ); + QNetworkAccessManager* currNam = lastfm::nam(); + TomahawkUtils::NetworkProxyFactory* oldProxyFactory = dynamic_cast< TomahawkUtils::NetworkProxyFactory* >( nam->proxyFactory() ); + + if ( !oldProxyFactory ) + { + qDebug() << "Could not get old proxyFactory!"; + return; + } + + currNam->setConfiguration( nam->configuration() ); + currNam->setNetworkAccessible( nam->networkAccessible() ); + TomahawkUtils::NetworkProxyFactory* newProxyFactory = new TomahawkUtils::NetworkProxyFactory(); + newProxyFactory->setNoProxyHosts( oldProxyFactory->noProxyHosts() ); + newProxyFactory->setProxy( oldProxyFactory->proxy() ); + currNam->setProxyFactory( newProxyFactory ); + //FIXME: on Mac/Win as liblastfm's network access manager also sets its overriding application proxy + //may have to do a QNetworkProxy::setApplicationProxy and clobber our own factory to override it settingsChanged(); // to get the scrobbler set up } void -LastFmPlugin::dataError( const QString &caller, const Tomahawk::InfoSystem::InfoType type, const QVariant &input, const Tomahawk::InfoSystem::InfoCustomData &customData ) +LastFmPlugin::dataError( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData ) { - emit info( caller, type, input, QVariant(), customData ); + emit info( requestId, requestData, QVariant() ); return; } void -LastFmPlugin::getInfo( const QString caller, const Tomahawk::InfoSystem::InfoType type, const QVariant input, const Tomahawk::InfoSystem::InfoCustomData customData ) +LastFmPlugin::getInfo( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData ) { qDebug() << Q_FUNC_INFO; - switch ( type ) + switch ( requestData.type ) { case InfoArtistImages: - fetchArtistImages( caller, type, input, customData ); + fetchArtistImages( requestId, requestData ); break; case InfoAlbumCoverArt: - fetchCoverArt( caller, type, input, customData ); + fetchCoverArt( requestId, requestData ); + break; + + case InfoArtistSimilars: + fetchSimilarArtists( requestId, requestData ); + break; + + case InfoArtistSongs: + fetchTopTracks( requestId, requestData ); break; default: - dataError( caller, type, input, customData ); + dataError( requestId, requestData ); } } @@ -146,7 +172,8 @@ LastFmPlugin::pushInfo( const QString caller, const Tomahawk::InfoSystem::InfoTy break; case InfoLove: - sendLoveSong( input ); + case InfoUnLove: + sendLoveSong( type, input ); break; default: @@ -171,7 +198,6 @@ LastFmPlugin::nowPlaying( const QVariant &input ) if ( !hash.contains( "title" ) || !hash.contains( "artist" ) || !hash.contains( "album" ) || !hash.contains( "duration" ) ) return; - qDebug() << "LastFmPlugin::nowPlaying valid criteria hash"; m_track = lastfm::MutableTrack(); m_track.stamp(); @@ -201,7 +227,7 @@ LastFmPlugin::scrobble() void -LastFmPlugin::sendLoveSong( QVariant input ) +LastFmPlugin::sendLoveSong( const InfoType type, QVariant input ) { qDebug() << Q_FUNC_INFO; @@ -224,23 +250,77 @@ LastFmPlugin::sendLoveSong( QVariant input ) bool ok; track.setDuration( hash["duration"].toUInt( &ok ) ); track.setSource( lastfm::Track::Player ); - track.love(); + + if ( type == Tomahawk::InfoSystem::InfoLove ) + { + track.love(); + } + else if ( type == Tomahawk::InfoSystem::InfoUnLove ) + { + track.unlove(); + } } void -LastFmPlugin::fetchCoverArt( const QString &caller, const InfoType type, const QVariant &input, const Tomahawk::InfoSystem::InfoCustomData &customData ) +LastFmPlugin::fetchSimilarArtists( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData ) { qDebug() << Q_FUNC_INFO; - if ( !input.canConvert< Tomahawk::InfoSystem::InfoCriteriaHash >() ) + if ( !requestData.input.canConvert< Tomahawk::InfoSystem::InfoCriteriaHash >() ) { - dataError( caller, type, input, customData ); + dataError( requestId, requestData ); return; } - InfoCriteriaHash hash = input.value< Tomahawk::InfoSystem::InfoCriteriaHash >(); + InfoCriteriaHash hash = requestData.input.value< Tomahawk::InfoSystem::InfoCriteriaHash >(); + if ( !hash.contains( "artist" ) ) + { + dataError( requestId, requestData ); + return; + } + + Tomahawk::InfoSystem::InfoCriteriaHash criteria; + criteria["artist"] = hash["artist"]; + + emit getCachedInfo( requestId, criteria, 2419200000, requestData ); +} + + +void +LastFmPlugin::fetchTopTracks( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData ) +{ + qDebug() << Q_FUNC_INFO; + if ( !requestData.input.canConvert< Tomahawk::InfoSystem::InfoCriteriaHash >() ) + { + dataError( requestId, requestData ); + return; + } + InfoCriteriaHash hash = requestData.input.value< Tomahawk::InfoSystem::InfoCriteriaHash >(); + if ( !hash.contains( "artist" ) ) + { + dataError( requestId, requestData ); + return; + } + + Tomahawk::InfoSystem::InfoCriteriaHash criteria; + criteria["artist"] = hash["artist"]; + + emit getCachedInfo( requestId, criteria, 2419200000, requestData ); +} + + +void +LastFmPlugin::fetchCoverArt( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData ) +{ + qDebug() << Q_FUNC_INFO; + if ( !requestData.input.canConvert< Tomahawk::InfoSystem::InfoCriteriaHash >() ) + { + dataError( requestId, requestData ); + return; + } + InfoCriteriaHash hash = requestData.input.value< Tomahawk::InfoSystem::InfoCriteriaHash >(); if ( !hash.contains( "artist" ) || !hash.contains( "album" ) ) { - dataError( caller, type, input, customData ); + dataError( requestId, requestData ); return; } @@ -248,47 +328,69 @@ LastFmPlugin::fetchCoverArt( const QString &caller, const InfoType type, const Q criteria["artist"] = hash["artist"]; criteria["album"] = hash["album"]; - emit getCachedInfo( criteria, 2419200000, caller, type, input, customData ); + emit getCachedInfo( requestId, criteria, 2419200000, requestData ); } void -LastFmPlugin::fetchArtistImages( const QString &caller, const InfoType type, const QVariant &input, const Tomahawk::InfoSystem::InfoCustomData &customData ) +LastFmPlugin::fetchArtistImages( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData ) { qDebug() << Q_FUNC_INFO; - if ( !input.canConvert< Tomahawk::InfoSystem::InfoCriteriaHash >() ) + if ( !requestData.input.canConvert< Tomahawk::InfoSystem::InfoCriteriaHash >() ) { - dataError( caller, type, input, customData ); + dataError( requestId, requestData ); return; } - InfoCriteriaHash hash = input.value< Tomahawk::InfoSystem::InfoCriteriaHash >(); + InfoCriteriaHash hash = requestData.input.value< Tomahawk::InfoSystem::InfoCriteriaHash >(); if ( !hash.contains( "artist" ) ) { - dataError( caller, type, input, customData ); + dataError( requestId, requestData ); return; } Tomahawk::InfoSystem::InfoCriteriaHash criteria; criteria["artist"] = hash["artist"]; - emit getCachedInfo( criteria, 2419200000, caller, type, input, customData ); + emit getCachedInfo( requestId, criteria, 2419200000, requestData ); } void -LastFmPlugin::notInCacheSlot( const QHash criteria, const QString caller, const Tomahawk::InfoSystem::InfoType type, const QVariant input, const Tomahawk::InfoSystem::InfoCustomData customData ) +LastFmPlugin::notInCacheSlot( uint requestId, QHash criteria, Tomahawk::InfoSystem::InfoRequestData requestData ) { - qDebug() << Q_FUNC_INFO; + qDebug() << Q_FUNC_INFO << " for requestId " << requestId; - if ( m_nam.isNull() ) + if ( !lastfm::nam() ) { qDebug() << "Have a null QNAM, uh oh"; - emit info( caller, type, input, QVariant(), customData ); + emit info( requestId, requestData, QVariant() ); return; } - switch ( type ) + switch ( requestData.type ) { + case InfoArtistSimilars: + { + lastfm::Artist a( criteria["artist"] ); + QNetworkReply* reply = a.getSimilar(); + reply->setProperty( "requestId", requestId ); + reply->setProperty( "requestData", QVariant::fromValue< Tomahawk::InfoSystem::InfoRequestData >( requestData ) ); + + connect( reply, SIGNAL( finished() ), SLOT( similarArtistsReturned() ) ); + return; + } + + case InfoArtistSongs: + { + lastfm::Artist a( criteria["artist"] ); + QNetworkReply* reply = a.getTopTracks(); + reply->setProperty( "requestId", requestId ); + reply->setProperty( "requestData", QVariant::fromValue< Tomahawk::InfoSystem::InfoRequestData >( requestData ) ); + + connect( reply, SIGNAL( finished() ), SLOT( topTracksReturned() ) ); + return; + } + case InfoAlbumCoverArt: { QString artistName = criteria["artist"]; @@ -296,11 +398,9 @@ LastFmPlugin::notInCacheSlot( const QHash criteria, const QStr QString imgurl = "http://ws.audioscrobbler.com/2.0/?method=album.imageredirect&artist=%1&album=%2&autocorrect=1&size=large&api_key=7a90f6672a04b809ee309af169f34b8b"; QNetworkRequest req( imgurl.arg( artistName ).arg( albumName ) ); - QNetworkReply* reply = m_nam.data()->get( req ); - reply->setProperty( "customData", QVariant::fromValue( customData ) ); - reply->setProperty( "origData", input ); - reply->setProperty( "caller", caller ); - reply->setProperty( "type", (uint)(type) ); + QNetworkReply* reply = lastfm::nam()->get( req ); + reply->setProperty( "requestId", requestId ); + reply->setProperty( "requestData", QVariant::fromValue< Tomahawk::InfoSystem::InfoRequestData >( requestData ) ); connect( reply, SIGNAL( finished() ), SLOT( coverArtReturned() ) ); return; @@ -310,13 +410,11 @@ LastFmPlugin::notInCacheSlot( const QHash criteria, const QStr { QString artistName = criteria["artist"]; - QString imgurl = "http://ws.audioscrobbler.com/2.0/?method=artist.imageredirect&artist=%1&autocorrect=1&size=medium&api_key=7a90f6672a04b809ee309af169f34b8b"; + QString imgurl = "http://ws.audioscrobbler.com/2.0/?method=artist.imageredirect&artist=%1&autocorrect=1&size=large&api_key=7a90f6672a04b809ee309af169f34b8b"; QNetworkRequest req( imgurl.arg( artistName ) ); - QNetworkReply* reply = m_nam.data()->get( req ); - reply->setProperty( "customData", QVariant::fromValue( customData ) ); - reply->setProperty( "origData", input ); - reply->setProperty( "caller", caller ); - reply->setProperty( "type", (uint)(type) ); + QNetworkReply* reply = lastfm::nam()->get( req ); + reply->setProperty( "requestId", requestId ); + reply->setProperty( "requestData", QVariant::fromValue< Tomahawk::InfoSystem::InfoRequestData >( requestData ) ); connect( reply, SIGNAL( finished() ), SLOT( artistImagesReturned() ) ); return; @@ -325,13 +423,78 @@ LastFmPlugin::notInCacheSlot( const QHash criteria, const QStr default: { qDebug() << "Couldn't figure out what to do with this type of request after cache miss"; - emit info( caller, type, input, QVariant(), customData ); + emit info( requestId, requestData, QVariant() ); return; } } } +void +LastFmPlugin::similarArtistsReturned() +{ + qDebug() << Q_FUNC_INFO; + QNetworkReply* reply = qobject_cast( sender() ); + + QMap< int, QString > similarArtists = lastfm::Artist::getSimilar( reply ); + QStringList al; + QStringList sl; + + foreach ( const QString& a, similarArtists.values() ) + { + qDebug() << "Got sim-artist:" << a; + al << a; + } + + QVariantMap returnedData; + returnedData["artists"] = al; + returnedData["score"] = sl; + + Tomahawk::InfoSystem::InfoRequestData requestData = reply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >(); + + emit info( + reply->property( "requestId" ).toUInt(), + requestData, + returnedData + ); + + Tomahawk::InfoSystem::InfoCriteriaHash origData = requestData.input.value< Tomahawk::InfoSystem::InfoCriteriaHash>(); + Tomahawk::InfoSystem::InfoCriteriaHash criteria; + criteria["artist"] = origData["artist"]; + emit updateCache( criteria, 2419200000, requestData.type, returnedData ); +} + + +void +LastFmPlugin::topTracksReturned() +{ + qDebug() << Q_FUNC_INFO; + QNetworkReply* reply = qobject_cast( sender() ); + + QStringList topTracks = lastfm::Artist::getTopTracks( reply ); + foreach ( const QString& t, topTracks ) + { + qDebug() << "Got top-track:" << t; + } + + QVariantMap returnedData; + returnedData["tracks"] = topTracks; + + Tomahawk::InfoSystem::InfoRequestData requestData = reply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >(); + + emit info( + reply->property( "requestId" ).toUInt(), + requestData, + returnedData + ); + + Tomahawk::InfoSystem::InfoCriteriaHash origData = requestData.input.value< Tomahawk::InfoSystem::InfoCriteriaHash>(); + Tomahawk::InfoSystem::InfoCriteriaHash criteria; + criteria["artist"] = origData["artist"]; + emit updateCache( criteria, 2419200000, requestData.type, returnedData ); +} + + void LastFmPlugin::coverArtReturned() { @@ -341,49 +504,49 @@ LastFmPlugin::coverArtReturned() if ( redir.isEmpty() ) { QByteArray ba = reply->readAll(); + if ( ba.isNull() || !ba.length() ) + { + qDebug() << "Uh oh, null byte array"; + emit info( reply->property( "requestId" ).toUInt(), reply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >(), QVariant() ); + return; + } foreach ( const QUrl& url, m_badUrls ) { if ( reply->url().toString().startsWith( url.toString() ) ) ba = QByteArray(); } - InfoCustomData returnedData; + QVariantMap 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()); + Tomahawk::InfoSystem::InfoRequestData requestData = reply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >(); + emit info( - reply->property( "caller" ).toString(), - type, - reply->property( "origData" ), - returnedData, - customData + reply->property( "requestId" ).toUInt(), + requestData, + returnedData ); - InfoCriteriaHash origData = reply->property( "origData" ).value< Tomahawk::InfoSystem::InfoCriteriaHash >(); + Tomahawk::InfoSystem::InfoCriteriaHash origData = requestData.input.value< Tomahawk::InfoSystem::InfoCriteriaHash>(); Tomahawk::InfoSystem::InfoCriteriaHash criteria; criteria["artist"] = origData["artist"]; criteria["album"] = origData["album"]; - emit updateCache( criteria, 2419200000, type, returnedData ); + emit updateCache( criteria, 2419200000, requestData.type, returnedData ); } else { - if ( m_nam.isNull() ) + if ( !lastfm::nam() ) { qDebug() << "Uh oh, nam is null"; - InfoType type = (Tomahawk::InfoSystem::InfoType)(reply->property( "type" ).toUInt()); - InfoCustomData customData = reply->property( "customData" ).value< Tomahawk::InfoSystem::InfoCustomData >(); - emit info( reply->property( "caller" ).toString(), type, reply->property( "origData" ), QVariant(), customData ); + emit info( reply->property( "requestId" ).toUInt(), reply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >(), QVariant() ); return; } // Follow HTTP redirect QNetworkRequest req( redir ); - QNetworkReply* newReply = m_nam.data()->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" ) ); + QNetworkReply* newReply = lastfm::nam()->get( req ); + newReply->setProperty( "requestId", reply->property( "requestId" ) ); + newReply->setProperty( "requestData", reply->property( "requestData" ) ); connect( newReply, SIGNAL( finished() ), SLOT( coverArtReturned() ) ); } @@ -400,42 +563,43 @@ LastFmPlugin::artistImagesReturned() if ( redir.isEmpty() ) { QByteArray ba = reply->readAll(); + if ( ba.isNull() || !ba.length() ) + { + qDebug() << "Uh oh, null byte array"; + emit info( reply->property( "requestId" ).toUInt(), reply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >(), QVariant() ); + return; + } foreach ( const QUrl& url, m_badUrls ) { if ( reply->url().toString().startsWith( url.toString() ) ) ba = QByteArray(); } - - InfoCustomData returnedData; + QVariantMap returnedData; returnedData["imgbytes"] = ba; returnedData["url"] = reply->url().toString(); - InfoType type = (Tomahawk::InfoSystem::InfoType)(reply->property( "type" ).toUInt()); - InfoCustomData customData = reply->property( "customData" ).value< Tomahawk::InfoSystem::InfoCustomData >(); - emit info( reply->property( "caller" ).toString(), type, reply->property( "origData" ), returnedData, customData ); + Tomahawk::InfoSystem::InfoRequestData requestData = reply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >(); + + emit info( reply->property( "requestId" ).toUInt(), requestData, returnedData ); - InfoCriteriaHash origData = reply->property( "origData" ).value< Tomahawk::InfoSystem::InfoCriteriaHash >(); + Tomahawk::InfoSystem::InfoCriteriaHash origData = requestData.input.value< Tomahawk::InfoSystem::InfoCriteriaHash>(); Tomahawk::InfoSystem::InfoCriteriaHash criteria; criteria["artist"] = origData["artist"]; - emit updateCache( criteria, 2419200000, type, returnedData ); + emit updateCache( criteria, 2419200000, requestData.type, returnedData ); } else { - if ( m_nam.isNull() ) + if ( !lastfm::nam() ) { qDebug() << "Uh oh, nam is null"; - InfoType type = (Tomahawk::InfoSystem::InfoType)(reply->property( "type" ).toUInt()); - InfoCustomData customData = reply->property( "customData" ).value< Tomahawk::InfoSystem::InfoCustomData >(); - emit info( reply->property( "caller" ).toString(), type, reply->property( "origData" ), QVariant(), customData ); + emit info( reply->property( "requestId" ).toUInt(), reply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >(), QVariant() ); return; } // Follow HTTP redirect QNetworkRequest req( redir ); - QNetworkReply* newReply = m_nam.data()->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" ) ); + QNetworkReply* newReply = lastfm::nam()->get( req ); + newReply->setProperty( "requestId", reply->property( "requestId" ) ); + newReply->setProperty( "requestData", reply->property( "requestData" ) ); connect( newReply, SIGNAL( finished() ), SLOT( artistImagesReturned() ) ); } diff --git a/src/libtomahawk/infosystem/infoplugins/generic/lastfmplugin.h b/src/libtomahawk/infosystem/infoplugins/generic/lastfmplugin.h index ab194a5b1..dc1c2add6 100644 --- a/src/libtomahawk/infosystem/infoplugins/generic/lastfmplugin.h +++ b/src/libtomahawk/infosystem/infoplugins/generic/lastfmplugin.h @@ -49,33 +49,35 @@ public slots: void onAuthenticated(); void coverArtReturned(); void artistImagesReturned(); + void similarArtistsReturned(); + void topTracksReturned(); void namChangedSlot( QNetworkAccessManager *nam ); protected slots: - virtual void getInfo( const QString caller, const Tomahawk::InfoSystem::InfoType type, const QVariant input, const Tomahawk::InfoSystem::InfoCustomData customData ); - virtual void notInCacheSlot( const Tomahawk::InfoSystem::InfoCriteriaHash criteria, const QString caller, const Tomahawk::InfoSystem::InfoType type, const QVariant input, const Tomahawk::InfoSystem::InfoCustomData customData ); + virtual void getInfo( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData ); + virtual void notInCacheSlot( uint requestId, Tomahawk::InfoSystem::InfoCriteriaHash criteria, Tomahawk::InfoSystem::InfoRequestData requestData ); + + virtual void pushInfo( QString caller, Tomahawk::InfoSystem::InfoType type, QVariant data ); - virtual void pushInfo( const QString caller, const Tomahawk::InfoSystem::InfoType type, const QVariant data ); - private: - void fetchCoverArt( const QString &caller, const Tomahawk::InfoSystem::InfoType type, const QVariant &input, const Tomahawk::InfoSystem::InfoCustomData &customData ); - void fetchArtistImages( const QString &caller, const Tomahawk::InfoSystem::InfoType type, const QVariant &input, const Tomahawk::InfoSystem::InfoCustomData &customData ); + void fetchCoverArt( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData ); + void fetchArtistImages( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData ); + void fetchSimilarArtists( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData ); + void fetchTopTracks( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData ); void createScrobbler(); void nowPlaying( const QVariant &input ); void scrobble(); + void sendLoveSong( const InfoType type, QVariant input ); - void dataError( const QString &caller, const Tomahawk::InfoSystem::InfoType type, const QVariant &input, const Tomahawk::InfoSystem::InfoCustomData &customData ); - void sendLoveSong( QVariant input ); + void dataError( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData ); lastfm::MutableTrack m_track; lastfm::Audioscrobbler* m_scrobbler; QString m_pw; QList< QUrl > m_badUrls; - - QWeakPointer< QNetworkAccessManager > m_nam; }; } diff --git a/src/libtomahawk/infosystem/infoplugins/generic/musixmatchplugin.cpp b/src/libtomahawk/infosystem/infoplugins/generic/musixmatchplugin.cpp index eb60882a4..70cef35d9 100644 --- a/src/libtomahawk/infosystem/infoplugins/generic/musixmatchplugin.cpp +++ b/src/libtomahawk/infosystem/infoplugins/generic/musixmatchplugin.cpp @@ -51,53 +51,52 @@ MusixMatchPlugin::namChangedSlot( QNetworkAccessManager *nam ) } void -MusixMatchPlugin::getInfo( const QString caller, const Tomahawk::InfoSystem::InfoType type, const QVariant input, const Tomahawk::InfoSystem::InfoCustomData customData ) +MusixMatchPlugin::getInfo( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData ) { qDebug() << Q_FUNC_INFO; - if( !isValidTrackData(caller, input, customData) || !input.canConvert() || m_nam.isNull() || type != Tomahawk::InfoSystem::InfoTrackLyrics ) + if( !isValidTrackData( requestId, requestData ) || !requestData.input.canConvert< QVariantMap >() || m_nam.isNull() || requestData.type != Tomahawk::InfoSystem::InfoTrackLyrics ) return; - Tomahawk::InfoSystem::InfoCustomData hash = input.value(); + QVariantMap hash = requestData.input.value< QVariantMap >(); QString artist = hash["artistName"].toString(); QString track = hash["trackName"].toString(); if( artist.isEmpty() || track.isEmpty() ) { - emit info(caller, Tomahawk::InfoSystem::InfoTrackLyrics, input, QVariant(), customData); + emit info( requestId, requestData, QVariant() ); return; } qDebug() << "artist is " << artist << ", track is " << track; - QString requestString("http://api.musixmatch.com/ws/1.1/track.search?format=xml&page_size=1&f_has_lyrics=1"); - QUrl url(requestString); - url.addQueryItem("apikey", m_apiKey); - url.addQueryItem("q_artist", artist); - url.addQueryItem("q_track", track); - QNetworkReply* reply = m_nam.data()->get(QNetworkRequest(url)); - reply->setProperty("customData", QVariant::fromValue(customData)); - reply->setProperty("origData", input); - reply->setProperty("caller", caller); + QString requestString( "http://api.musixmatch.com/ws/1.1/track.search?format=xml&page_size=1&f_has_lyrics=1" ); + QUrl url( requestString ); + url.addQueryItem( "apikey", m_apiKey ); + url.addQueryItem( "q_artist", artist ); + url.addQueryItem( "q_track", track ); + QNetworkReply* reply = m_nam.data()->get( QNetworkRequest( url ) ); + reply->setProperty( "requestId", requestId ); + reply->setProperty( "requestData", QVariant::fromValue< Tomahawk::InfoSystem::InfoRequestData >( requestData ) ); - connect(reply, SIGNAL(finished()), SLOT(trackSearchSlot())); + connect( reply, SIGNAL( finished() ), SLOT( trackSearchSlot() ) ); } bool -MusixMatchPlugin::isValidTrackData( const QString &caller, const QVariant &input, const Tomahawk::InfoSystem::InfoCustomData &customData ) +MusixMatchPlugin::isValidTrackData( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData ) { qDebug() << Q_FUNC_INFO; - if (input.isNull() || !input.isValid() || !input.canConvert()) + if ( requestData.input.isNull() || !requestData.input.isValid() || !requestData.input.canConvert< QVariantMap >() ) { - emit info(caller, Tomahawk::InfoSystem::InfoTrackLyrics, input, QVariant(), customData); + emit info( requestId, requestData, QVariant() ); qDebug() << "MusixMatchPlugin::isValidTrackData: Data null, invalid, or can't convert"; return false; } - InfoCustomData hash = input.value(); - if (hash["trackName"].toString().isEmpty() ) + QVariantMap hash = requestData.input.value< QVariantMap >(); + if ( hash[ "trackName" ].toString().isEmpty() ) { - emit info(caller, Tomahawk::InfoSystem::InfoTrackLyrics, input, QVariant(), customData); + emit info( requestId, requestData, QVariant() ); qDebug() << "MusixMatchPlugin::isValidTrackData: Track name is empty"; return false; } - if (hash["artistName"].toString().isEmpty() ) + if ( hash[ "artistName" ].toString().isEmpty() ) { - emit info(caller, Tomahawk::InfoSystem::InfoTrackLyrics, input, QVariant(), customData); + emit info( requestId, requestData, QVariant() ); qDebug() << "MusixMatchPlugin::isValidTrackData: No artist name found"; return false; } @@ -109,51 +108,46 @@ MusixMatchPlugin::trackSearchSlot() { qDebug() << Q_FUNC_INFO; QNetworkReply* oldReply = qobject_cast( sender() ); - if ( !oldReply || m_nam.isNull() ) - { - emit info(QString(), Tomahawk::InfoSystem::InfoTrackLyrics, QVariant(), QVariant(), Tomahawk::InfoSystem::InfoCustomData()); - return; - } + if ( !oldReply ) + return; //timeout will handle it + QDomDocument doc; doc.setContent(oldReply->readAll()); qDebug() << doc.toString(); QDomNodeList domNodeList = doc.elementsByTagName("track_id"); - if (domNodeList.isEmpty()) + if ( domNodeList.isEmpty() ) { - emit info(oldReply->property("caller").toString(), Tomahawk::InfoSystem::InfoTrackLyrics, oldReply->property("origData"), QVariant(), oldReply->property("customData").value()); + emit info( oldReply->property( "requestId" ).toUInt(), oldReply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >(), QVariant() ); return; } QString track_id = domNodeList.at(0).toElement().text(); - QString requestString("http://api.musixmatch.com/ws/1.1/track.lyrics.get?track_id=%1&format=xml&apikey=%2"); - QUrl url(requestString); - url.addQueryItem("apikey", m_apiKey); - url.addQueryItem("track_id", track_id); - QNetworkReply* newReply = m_nam.data()->get(QNetworkRequest(url)); - newReply->setProperty("origData", oldReply->property("origData")); - newReply->setProperty("customData", oldReply->property("customData")); - newReply->setProperty("caller", oldReply->property("caller")); - connect(newReply, SIGNAL(finished()), SLOT(trackLyricsSlot())); + QString requestString( "http://api.musixmatch.com/ws/1.1/track.lyrics.get?track_id=%1&format=xml&apikey=%2" ); + QUrl url( requestString ); + url.addQueryItem( "apikey", m_apiKey ); + url.addQueryItem( "track_id", track_id ); + QNetworkReply* newReply = m_nam.data()->get( QNetworkRequest( url ) ); + newReply->setProperty( "requestId", oldReply->property( "requestId" ) ); + newReply->setProperty( "requestData", oldReply->property( "requestData" ) ); + connect( newReply, SIGNAL( finished() ), SLOT( trackLyricsSlot() ) ); } void MusixMatchPlugin::trackLyricsSlot() { qDebug() << Q_FUNC_INFO; - QNetworkReply* reply = qobject_cast( sender() ); - if (!reply) - { - emit info(QString(), Tomahawk::InfoSystem::InfoTrackLyrics, QVariant(), QVariant(), Tomahawk::InfoSystem::InfoCustomData()); - return; - } + QNetworkReply* reply = qobject_cast< QNetworkReply* >( sender() ); + if ( !reply ) + return; //timeout will handle it + QDomDocument doc; - doc.setContent(reply->readAll()); - QDomNodeList domNodeList = doc.elementsByTagName("lyrics_body"); - if (domNodeList.isEmpty()) + doc.setContent( reply->readAll() ); + QDomNodeList domNodeList = doc.elementsByTagName( "lyrics_body" ); + if ( domNodeList.isEmpty() ) { - emit info(reply->property("caller").toString(), Tomahawk::InfoSystem::InfoTrackLyrics, reply->property("origData"), QVariant(), reply->property("customData").value()); + emit info( reply->property( "requestId" ).toUInt(), reply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >(), QVariant() ); 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 info( reply->property( "requestId" ).toUInt(), reply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >(), QVariant( lyrics ) ); } diff --git a/src/libtomahawk/infosystem/infoplugins/generic/musixmatchplugin.h b/src/libtomahawk/infosystem/infoplugins/generic/musixmatchplugin.h index 8174e2824..a93e25c01 100644 --- a/src/libtomahawk/infosystem/infoplugins/generic/musixmatchplugin.h +++ b/src/libtomahawk/infosystem/infoplugins/generic/musixmatchplugin.h @@ -45,26 +45,24 @@ public slots: void namChangedSlot( QNetworkAccessManager *nam ); protected slots: - virtual void getInfo( const QString caller, const Tomahawk::InfoSystem::InfoType type, const QVariant input, const Tomahawk::InfoSystem::InfoCustomData customData ); + virtual void getInfo( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData ); - virtual void pushInfo( const QString caller, const Tomahawk::InfoSystem::InfoType type, const QVariant data ) + virtual void pushInfo( QString caller, Tomahawk::InfoSystem::InfoType type, QVariant data ) { Q_UNUSED( caller ); Q_UNUSED( type ); Q_UNUSED( data ); } - virtual void notInCacheSlot( const Tomahawk::InfoSystem::InfoCriteriaHash criteria, const QString caller, const Tomahawk::InfoSystem::InfoType type, const QVariant input, const Tomahawk::InfoSystem::InfoCustomData customData ) +virtual void notInCacheSlot( uint requestId, Tomahawk::InfoSystem::InfoCriteriaHash criteria, Tomahawk::InfoSystem::InfoRequestData requestData ) { + Q_UNUSED( requestId ); Q_UNUSED( criteria ); - Q_UNUSED( caller ); - Q_UNUSED( type ); - Q_UNUSED( input ); - Q_UNUSED( customData ); + Q_UNUSED( requestData ); } private: - bool isValidTrackData( const QString &caller, const QVariant &input, const InfoCustomData &customData ); + bool isValidTrackData( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData ); QString m_apiKey; diff --git a/src/libtomahawk/infosystem/infoplugins/mac/adiumplugin.cpp b/src/libtomahawk/infosystem/infoplugins/mac/adiumplugin.cpp index 7a76a632e..823968b03 100644 --- a/src/libtomahawk/infosystem/infoplugins/mac/adiumplugin.cpp +++ b/src/libtomahawk/infosystem/infoplugins/mac/adiumplugin.cpp @@ -74,6 +74,7 @@ AdiumPlugin::AdiumPlugin() this, SLOT( clearStatus() ) ); } + AdiumPlugin::~AdiumPlugin() { qDebug() << Q_FUNC_INFO; @@ -81,6 +82,7 @@ AdiumPlugin::~AdiumPlugin() setStatus( "" ); } + void AdiumPlugin::clearStatus() { @@ -88,6 +90,7 @@ AdiumPlugin::clearStatus() setStatus( "" ); } + void AdiumPlugin::settingsChanged() { @@ -96,21 +99,9 @@ AdiumPlugin::settingsChanged() setStatus( "" ); } -void -AdiumPlugin::getInfo( const QString caller, const InfoType type, const QVariant data, InfoCustomData customData ) -{ - switch (type) - { - default: - { - emit info(caller, Tomahawk::InfoSystem::InfoNoInfo, QVariant(), QVariant(), customData); - return; - } - } -} void -AdiumPlugin::pushInfo( const QString caller, const Tomahawk::InfoSystem::InfoType type, const QVariant input ) +AdiumPlugin::pushInfo( QString caller, Tomahawk::InfoSystem::InfoType type, QVariant input ) { qDebug() << Q_FUNC_INFO; diff --git a/src/libtomahawk/infosystem/infoplugins/mac/adiumplugin.h b/src/libtomahawk/infosystem/infoplugins/mac/adiumplugin.h index 76417fb74..208891164 100644 --- a/src/libtomahawk/infosystem/infoplugins/mac/adiumplugin.h +++ b/src/libtomahawk/infosystem/infoplugins/mac/adiumplugin.h @@ -39,12 +39,23 @@ public: virtual ~AdiumPlugin(); protected slots: - void getInfo( const QString caller, const InfoType type, const QVariant data, InfoCustomData customData ); - void pushInfo( const QString caller, const Tomahawk::InfoSystem::InfoType type, const QVariant input ); + virtual void getInfo( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData ) + { + Q_UNUSED( requestId ); + Q_UNUSED( requestData ); + } + + void pushInfo( QString caller, Tomahawk::InfoSystem::InfoType type, QVariant input ); public slots: void namChangedSlot( QNetworkAccessManager* /*nam*/ ) {} // unused - void notInCacheSlot( const Tomahawk::InfoSystem::InfoCriteriaHash /*criteria*/, const QString /*caller*/, const Tomahawk::InfoSystem::InfoType /*type*/, const QVariant /*input*/, const Tomahawk::InfoSystem::InfoCustomData /*customData*/ ) {} // unused + + virtual void notInCacheSlot( uint requestId, const Tomahawk::InfoSystem::InfoCriteriaHash criteria, Tomahawk::InfoSystem::InfoRequestData requestData ) + { + Q_UNUSED( requestId ); + Q_UNUSED( criteria ); + Q_UNUSED( requestData ); + } private slots: void clearStatus(); diff --git a/src/libtomahawk/infosystem/infoplugins/unix/fdonotifyplugin.cpp b/src/libtomahawk/infosystem/infoplugins/unix/fdonotifyplugin.cpp index c0b7f9d0d..82ce1bf3c 100644 --- a/src/libtomahawk/infosystem/infoplugins/unix/fdonotifyplugin.cpp +++ b/src/libtomahawk/infosystem/infoplugins/unix/fdonotifyplugin.cpp @@ -59,16 +59,16 @@ FdoNotifyPlugin::~FdoNotifyPlugin() } void -FdoNotifyPlugin::pushInfo( const QString caller, const Tomahawk::InfoSystem::InfoType type, const QVariant pushData ) +FdoNotifyPlugin::pushInfo( QString caller, Tomahawk::InfoSystem::InfoType type, QVariant pushData ) { Q_UNUSED( caller ); qDebug() << Q_FUNC_INFO; - if ( type != Tomahawk::InfoSystem::InfoNotifyUser || !pushData.canConvert< Tomahawk::InfoSystem::InfoCriteriaHash >() ) + if ( type != Tomahawk::InfoSystem::InfoNotifyUser || !pushData.canConvert< QVariantMap >() ) { qDebug() << Q_FUNC_INFO << " not the right type or could not convert the hash"; return; } - Tomahawk::InfoSystem::InfoCriteriaHash hash = pushData.value< Tomahawk::InfoSystem::InfoCriteriaHash >(); + QVariantMap hash = pushData.value< QVariantMap >(); if ( !hash.contains( "message" ) ) { qDebug() << Q_FUNC_INFO << " hash did not contain a message"; @@ -81,11 +81,18 @@ FdoNotifyPlugin::pushInfo( const QString caller, const Tomahawk::InfoSystem::Inf arguments << quint32( 0 ); //notification_id arguments << QString(); //app_icon arguments << QString( "Tomahawk" ); //summary - arguments << hash["message"]; //body + arguments << hash["message"].toString(); //body arguments << QStringList(); //actions QVariantMap dict; dict["desktop-entry"] = QString( "tomahawk" ); - dict["image_data"] = ImageConverter::variantForImage( QImage( RESPATH "icons/tomahawk-icon-128x128.png" ) ); + if ( hash.contains( "image" ) ) + { + QVariant tempVariant = hash["image"]; + QImage tempImage = tempVariant.value< QImage >(); + dict["image_data"] = ImageConverter::variantForImage( tempImage ); + } + else + dict["image_data"] = ImageConverter::variantForImage( QImage( RESPATH "icons/tomahawk-icon-128x128.png" ) ); arguments << dict; //hints arguments << qint32( -1 ); //expire_timeout message.setArguments( arguments ); diff --git a/src/libtomahawk/infosystem/infoplugins/unix/fdonotifyplugin.h b/src/libtomahawk/infosystem/infoplugins/unix/fdonotifyplugin.h index 87cc2ba61..89f0667a1 100644 --- a/src/libtomahawk/infosystem/infoplugins/unix/fdonotifyplugin.h +++ b/src/libtomahawk/infosystem/infoplugins/unix/fdonotifyplugin.h @@ -38,23 +38,19 @@ public: virtual void namChangedSlot( QNetworkAccessManager* ) {} protected slots: - virtual void getInfo( const QString caller, const Tomahawk::InfoSystem::InfoType type, const QVariant input, const Tomahawk::InfoSystem::InfoCustomData customData ) + virtual void getInfo( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData ) { - Q_UNUSED( caller ); - Q_UNUSED( type ); - Q_UNUSED( input ); - Q_UNUSED( customData ); + Q_UNUSED( requestId ); + Q_UNUSED( requestData ); } - virtual void pushInfo( const QString caller, const Tomahawk::InfoSystem::InfoType type, const QVariant pushData ); + virtual void pushInfo( QString caller, Tomahawk::InfoSystem::InfoType type, QVariant pushData ); - virtual void notInCacheSlot( const Tomahawk::InfoSystem::InfoCriteriaHash criteria, const QString caller, const Tomahawk::InfoSystem::InfoType type, const QVariant input, const Tomahawk::InfoSystem::InfoCustomData customData ) + virtual void notInCacheSlot( uint requestId, Tomahawk::InfoSystem::InfoCriteriaHash criteria, Tomahawk::InfoSystem::InfoRequestData requestData ) { + Q_UNUSED( requestId ); Q_UNUSED( criteria ); - Q_UNUSED( caller ); - Q_UNUSED( type ); - Q_UNUSED( input ); - Q_UNUSED( customData ); + Q_UNUSED( requestData ); } }; diff --git a/src/libtomahawk/infosystem/infosystem.cpp b/src/libtomahawk/infosystem/infosystem.cpp index 611533ade..bd8379405 100644 --- a/src/libtomahawk/infosystem/infosystem.cpp +++ b/src/libtomahawk/infosystem/infosystem.cpp @@ -50,8 +50,10 @@ InfoSystem::instance() } -InfoSystem::InfoSystem(QObject *parent) - : QObject(parent) +InfoSystem::InfoSystem( QObject *parent ) + : QObject( parent ) + , m_infoSystemCacheThreadController( 0 ) + , m_infoSystemWorkerThreadController( 0 ) { s_instance = this; @@ -71,11 +73,12 @@ InfoSystem::InfoSystem(QObject *parent) connect( TomahawkSettings::instance(), SIGNAL( changed() ), SLOT( newNam() ) ); - connect( m_cache.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.data(), SIGNAL( info( uint, Tomahawk::InfoSystem::InfoRequestData, QVariant ) ), + m_worker.data(), SLOT( infoSlot( uint, Tomahawk::InfoSystem::InfoRequestData, QVariant ) ), Qt::UniqueConnection ); - connect( m_worker.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_worker.data(), SIGNAL( info( Tomahawk::InfoSystem::InfoRequestData, QVariant ) ), + this, SIGNAL( info( Tomahawk::InfoSystem::InfoRequestData, QVariant ) ), Qt::UniqueConnection ); + connect( m_worker.data(), SIGNAL( finished( QString ) ), this, SIGNAL( finished( QString ) ), Qt::UniqueConnection ); } InfoSystem::~InfoSystem() @@ -116,21 +119,25 @@ InfoSystem::newNam() const void -InfoSystem::getInfo( const QString &caller, const InfoType type, const QVariant& input, InfoCustomData customData ) +InfoSystem::getInfo( const InfoRequestData &requestData, uint timeoutMillis ) { qDebug() << Q_FUNC_INFO; - - m_dataTracker[caller][type] = m_dataTracker[caller][type] + 1; - qDebug() << "current count in dataTracker for type" << type << "is" << m_dataTracker[caller][type]; - QMetaObject::invokeMethod( m_worker.data(), "getInfo", Qt::QueuedConnection, Q_ARG( QString, caller ), Q_ARG( Tomahawk::InfoSystem::InfoType, type ), Q_ARG( QVariant, input ), Q_ARG( Tomahawk::InfoSystem::InfoCustomData, customData ) ); + QMetaObject::invokeMethod( m_worker.data(), "getInfo", Qt::QueuedConnection, Q_ARG( Tomahawk::InfoSystem::InfoRequestData, requestData ), Q_ARG( uint, timeoutMillis ) ); } void -InfoSystem::getInfo( const QString &caller, const InfoMap &input, InfoCustomData customData ) +InfoSystem::getInfo( const QString &caller, const InfoTypeMap &inputMap, const QVariantMap &customData, const InfoTimeoutMap &timeoutMap ) { - Q_FOREACH( InfoType type, input.keys() ) - getInfo( caller, type, input[type], customData ); + InfoRequestData requestData; + requestData.caller = caller; + requestData.customData = customData; + Q_FOREACH( InfoType type, inputMap.keys() ) + { + requestData.type = type; + requestData.input = inputMap[ type ]; + QMetaObject::invokeMethod( m_worker.data(), "getInfo", Qt::QueuedConnection, Q_ARG( Tomahawk::InfoSystem::InfoRequestData, requestData ), Q_ARG( uint, ( timeoutMap.contains( type ) ? timeoutMap[ type ] : 3000 ) ) ); + } } @@ -138,43 +145,15 @@ void InfoSystem::pushInfo( const QString &caller, const InfoType type, const QVariant& input ) { qDebug() << Q_FUNC_INFO; - QMetaObject::invokeMethod( m_worker.data(), "pushInfo", Qt::QueuedConnection, Q_ARG( QString, caller ), Q_ARG( Tomahawk::InfoSystem::InfoType, type ), Q_ARG( QVariant, input ) ); } void -InfoSystem::pushInfo( const QString &caller, const InfoMap &input ) +InfoSystem::pushInfo( const QString &caller, const InfoTypeMap &input ) { Q_FOREACH( InfoType type, input.keys() ) - pushInfo( caller, type, input[type] ); -} - - -void -InfoSystem::infoSlot( QString target, InfoType type, QVariant input, QVariant output, InfoCustomData customData ) -{ - qDebug() << Q_FUNC_INFO; - qDebug() << "current count in dataTracker is " << m_dataTracker[target][type]; - 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); + QMetaObject::invokeMethod( m_worker.data(), "pushInfo", Qt::QueuedConnection, Q_ARG( QString, caller ), Q_ARG( Tomahawk::InfoSystem::InfoType, type ), Q_ARG( QVariant, input[ type ] ) ); } } //namespace InfoSystem diff --git a/src/libtomahawk/infosystem/infosystem.h b/src/libtomahawk/infosystem/infosystem.h index 0dea90be1..9ca664f8c 100644 --- a/src/libtomahawk/infosystem/infosystem.h +++ b/src/libtomahawk/infosystem/infosystem.h @@ -75,8 +75,8 @@ enum InfoType { // as items are saved in cache, mark them here to not change the InfoArtistNews = 29, InfoArtistProfile = 30, InfoArtistReviews = 31, - InfoArtistSongs = 32, - InfoArtistSimilars = 33, + InfoArtistSongs = 32, //cached -- do not change + InfoArtistSimilars = 33, //cached -- do not change InfoArtistTerms = 34, InfoArtistLinks = 35, InfoArtistVideos = 36, @@ -107,9 +107,16 @@ enum InfoType { // as items are saved in cache, mark them here to not change the InfoNotifyUser = 55 }; -typedef QMap< InfoType, QVariant > InfoMap; +struct InfoRequestData { + QString caller; + Tomahawk::InfoSystem::InfoType type; + QVariant input; + QVariantMap customData; +}; + +typedef QMap< InfoType, QVariant > InfoTypeMap; +typedef QMap< InfoType, uint > InfoTimeoutMap; typedef QMap< QString, QMap< QString, QString > > InfoGenericMap; -typedef QHash< QString, QVariant > InfoCustomData; typedef QHash< QString, QString > InfoCriteriaHash; class DLLEXPORT InfoPlugin : public QObject @@ -125,15 +132,15 @@ public: QSet< InfoType > supportedPushTypes() const { return m_supportedPushTypes; } signals: - void getCachedInfo( Tomahawk::InfoSystem::InfoCriteriaHash criteria, qint64 newMaxAge, QString caller, Tomahawk::InfoSystem::InfoType type, QVariant input, Tomahawk::InfoSystem::InfoCustomData customData ); - void updateCache( Tomahawk::InfoSystem::InfoCriteriaHash 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 ); + void getCachedInfo( uint requestId, Tomahawk::InfoSystem::InfoCriteriaHash criteria, qint64 newMaxAge, Tomahawk::InfoSystem::InfoRequestData requestData ); + void info( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData, QVariant output ); + + void updateCache( Tomahawk::InfoSystem::InfoCriteriaHash criteria, qint64 maxAge, Tomahawk::InfoSystem::InfoType type, QVariant output ); protected slots: - virtual void getInfo( const QString caller, const Tomahawk::InfoSystem::InfoType type, const QVariant data, const Tomahawk::InfoSystem::InfoCustomData customData ) = 0; - virtual void pushInfo( const QString caller, const Tomahawk::InfoSystem::InfoType type, const QVariant data ) = 0; - virtual void notInCacheSlot( const Tomahawk::InfoSystem::InfoCriteriaHash criteria, const QString caller, const Tomahawk::InfoSystem::InfoType type, const QVariant input, const Tomahawk::InfoSystem::InfoCustomData customData ) = 0; + virtual void getInfo( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData ) = 0; + virtual void pushInfo( QString caller, Tomahawk::InfoSystem::InfoType type, QVariant data ) = 0; + virtual void notInCacheSlot( uint requestId, Tomahawk::InfoSystem::InfoCriteriaHash criteria, Tomahawk::InfoSystem::InfoRequestData requestData ) = 0; virtual void namChangedSlot( QNetworkAccessManager *nam ) = 0; @@ -158,23 +165,19 @@ public: InfoSystem( QObject *parent ); ~InfoSystem(); - void getInfo( const QString &caller, const InfoType type, const QVariant &input, InfoCustomData customData ); - void getInfo( const QString &caller, const InfoMap &input, InfoCustomData customData ); + void getInfo( const InfoRequestData &requestData, uint timeoutMillis = 3000 ); + void getInfo( const QString &caller, const InfoTypeMap &inputMap, const QVariantMap &customData, const InfoTimeoutMap &timeoutMap = InfoTimeoutMap() ); void pushInfo( const QString &caller, const InfoType type, const QVariant &input ); - void pushInfo( const QString &caller, const InfoMap &input ); + void pushInfo( const QString &caller, const InfoTypeMap &input ); signals: - void info( QString caller, Tomahawk::InfoSystem::InfoType, QVariant input, QVariant output, Tomahawk::InfoSystem::InfoCustomData customData ); + void info( Tomahawk::InfoSystem::InfoRequestData requestData, QVariant output ); void finished( QString target ); public slots: - void infoSlot( const QString target, const Tomahawk::InfoSystem::InfoType type, const QVariant input, const QVariant output, const Tomahawk::InfoSystem::InfoCustomData customData ); - void newNam() const; private: - QHash< QString, QHash< InfoType, int > > m_dataTracker; - QWeakPointer< InfoSystemCache > m_cache; QWeakPointer< InfoSystemWorker > m_worker; QThread* m_infoSystemCacheThreadController; @@ -205,8 +208,8 @@ inline uint qHash( Tomahawk::InfoSystem::InfoCriteriaHash hash ) return returnval; } +Q_DECLARE_METATYPE( Tomahawk::InfoSystem::InfoRequestData ); Q_DECLARE_METATYPE( Tomahawk::InfoSystem::InfoGenericMap ); -Q_DECLARE_METATYPE( Tomahawk::InfoSystem::InfoCustomData ); Q_DECLARE_METATYPE( Tomahawk::InfoSystem::InfoCriteriaHash ); Q_DECLARE_METATYPE( QWeakPointer< Tomahawk::InfoSystem::InfoSystemCache > ); diff --git a/src/libtomahawk/infosystem/infosystemcache.cpp b/src/libtomahawk/infosystem/infosystemcache.cpp index 64ef7bd77..aaca83d71 100644 --- a/src/libtomahawk/infosystem/infosystemcache.cpp +++ b/src/libtomahawk/infosystem/infosystemcache.cpp @@ -84,26 +84,26 @@ InfoSystemCache::pruneTimerFired() void -InfoSystemCache::getCachedInfoSlot( const Tomahawk::InfoSystem::InfoCriteriaHash criteria, const qint64 newMaxAge, const QString caller, const Tomahawk::InfoSystem::InfoType type, const QVariant input, const Tomahawk::InfoSystem::InfoCustomData customData ) +InfoSystemCache::getCachedInfoSlot( uint requestId, Tomahawk::InfoSystem::InfoCriteriaHash criteria, qint64 newMaxAge, Tomahawk::InfoSystem::InfoRequestData requestData ) { qDebug() << Q_FUNC_INFO; const QString criteriaHashVal = criteriaMd5( criteria ); - QHash< QString, QString > fileLocationHash = m_fileLocationCache[type]; + QHash< QString, QString > fileLocationHash = m_fileLocationCache[ requestData.type ]; if ( !fileLocationHash.contains( criteriaHashVal ) ) { if ( !fileLocationHash.isEmpty() ) { //We already know of some values, so no need to re-read the directory again as it's already happened - emit notInCache( criteria, caller, type, input, customData ); + emit notInCache( requestId, criteria, requestData ); return; } - const QString cacheDir = m_cacheBaseDir + QString::number( (int)type ); + const QString cacheDir = m_cacheBaseDir + QString::number( (int)requestData.type ); QDir dir( cacheDir ); if ( !dir.exists() ) { //Dir doesn't exist so clearly not in cache - emit notInCache( criteria, caller, type, input, customData ); + emit notInCache( requestId, criteria, requestData ); return; } @@ -111,20 +111,20 @@ InfoSystemCache::getCachedInfoSlot( const Tomahawk::InfoSystem::InfoCriteriaHash foreach ( QFileInfo file, fileList ) { QString baseName = file.baseName(); - fileLocationHash[baseName] = file.canonicalFilePath(); + fileLocationHash[ baseName ] = file.canonicalFilePath(); } //Store what we've loaded up - m_fileLocationCache[type] = fileLocationHash; + m_fileLocationCache[ requestData.type ] = fileLocationHash; if ( !fileLocationHash.contains( criteriaHashVal ) ) { //Still didn't fine it? It's really not in the cache then - emit notInCache( criteria, caller, type, input, customData ); + emit notInCache( requestId, criteria, requestData ); return; } } - QFileInfo file( fileLocationHash[criteriaHashVal] ); + QFileInfo file( fileLocationHash[ criteriaHashVal ] ); qlonglong currMaxAge = file.suffix().toLongLong(); if ( currMaxAge < QDateTime::currentMSecsSinceEpoch() ) @@ -135,10 +135,10 @@ InfoSystemCache::getCachedInfoSlot( const Tomahawk::InfoSystem::InfoCriteriaHash qDebug() << "Removed stale cache file " << file.canonicalFilePath(); fileLocationHash.remove( criteriaHashVal ); - m_fileLocationCache[type] = fileLocationHash; + m_fileLocationCache[ requestData.type ] = fileLocationHash; m_dataCache.remove( criteriaHashVal ); - emit notInCache( criteria, caller, type, input, customData ); + emit notInCache( requestId, criteria, requestData ); return; } else if ( newMaxAge > 0 ) @@ -148,29 +148,31 @@ InfoSystemCache::getCachedInfoSlot( const Tomahawk::InfoSystem::InfoCriteriaHash if ( !QFile::rename( file.canonicalFilePath(), newFilePath ) ) { qDebug() << "Failed to move old cache file to new location!"; - emit notInCache( criteria, caller, type, input, customData ); + emit notInCache( requestId, criteria, requestData ); return; } - fileLocationHash[criteriaHashVal] = newFilePath; - m_fileLocationCache[type] = fileLocationHash; + fileLocationHash[ criteriaHashVal ] = newFilePath; + m_fileLocationCache[ requestData.type ] = fileLocationHash; } if ( !m_dataCache.contains( criteriaHashVal ) ) { - QSettings cachedSettings( fileLocationHash[criteriaHashVal], QSettings::IniFormat ); - QVariant output = cachedSettings.value( "data" ); + QSettings cachedSettings( fileLocationHash[ criteriaHashVal ], QSettings::IniFormat ); + QVariant output = cachedSettings.value( "data" ); m_dataCache.insert( criteriaHashVal, new QVariant( output ) ); - emit info( caller, type, input, output, customData ); + emit info( requestId, requestData, output ); } else - emit info( caller, type, input, QVariant( *(m_dataCache[criteriaHashVal]) ), customData ); + { + emit info( requestId, requestData, QVariant( *(m_dataCache[criteriaHashVal]) ) ); + } } void -InfoSystemCache::updateCacheSlot( const Tomahawk::InfoSystem::InfoCriteriaHash criteria, const qint64 maxAge, const Tomahawk::InfoSystem::InfoType type, const QVariant output ) +InfoSystemCache::updateCacheSlot( Tomahawk::InfoSystem::InfoCriteriaHash criteria, qint64 maxAge, Tomahawk::InfoSystem::InfoType type, QVariant output ) { qDebug() << Q_FUNC_INFO; diff --git a/src/libtomahawk/infosystem/infosystemcache.h b/src/libtomahawk/infosystem/infosystemcache.h index 90fa31818..d3b822a61 100644 --- a/src/libtomahawk/infosystem/infosystemcache.h +++ b/src/libtomahawk/infosystem/infosystemcache.h @@ -43,12 +43,12 @@ public: virtual ~InfoSystemCache(); signals: - void notInCache( Tomahawk::InfoSystem::InfoCriteriaHash 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 ); + void notInCache( uint requestId, Tomahawk::InfoSystem::InfoCriteriaHash criteria, Tomahawk::InfoSystem::InfoRequestData requestData ); + void info( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData, QVariant output ); public slots: - void getCachedInfoSlot( const Tomahawk::InfoSystem::InfoCriteriaHash criteria, const qint64 newMaxAge, const QString caller, const Tomahawk::InfoSystem::InfoType type, const QVariant input, const Tomahawk::InfoSystem::InfoCustomData customData ); - void updateCacheSlot( const Tomahawk::InfoSystem::InfoCriteriaHash criteria, const qint64 maxAge, const Tomahawk::InfoSystem::InfoType type, const QVariant output ); + void getCachedInfoSlot( uint requestId, Tomahawk::InfoSystem::InfoCriteriaHash criteria, qint64 newMaxAge, Tomahawk::InfoSystem::InfoRequestData requestData ); + void updateCacheSlot( Tomahawk::InfoSystem::InfoCriteriaHash criteria, qint64 maxAge, Tomahawk::InfoSystem::InfoType type, QVariant output ); private slots: void pruneTimerFired(); diff --git a/src/libtomahawk/infosystem/infosystemworker.cpp b/src/libtomahawk/infosystem/infosystemworker.cpp index d85369dca..abbb50a97 100644 --- a/src/libtomahawk/infosystem/infosystemworker.cpp +++ b/src/libtomahawk/infosystem/infosystemworker.cpp @@ -43,8 +43,15 @@ namespace InfoSystem { InfoSystemWorker::InfoSystemWorker() + : QObject() + , m_nextRequest( 0 ) { qDebug() << Q_FUNC_INFO; + + m_checkTimeoutsTimer.setInterval( 1000 ); + m_checkTimeoutsTimer.setSingleShot( false ); + connect( &m_checkTimeoutsTimer, SIGNAL( timeout() ), SLOT( checkTimeoutsTimerFired() ) ); + m_checkTimeoutsTimer.start(); } @@ -91,23 +98,23 @@ InfoSystemWorker::init( QWeakPointer< Tomahawk::InfoSystem::InfoSystemCache> cac { connect( plugin.data(), - SIGNAL( info( QString, Tomahawk::InfoSystem::InfoType, QVariant, QVariant, Tomahawk::InfoSystem::InfoCustomData ) ), - InfoSystem::instance(), - SLOT( infoSlot( QString, Tomahawk::InfoSystem::InfoType, QVariant, QVariant, Tomahawk::InfoSystem::InfoCustomData ) ), + SIGNAL( info( uint, Tomahawk::InfoSystem::InfoRequestData, QVariant ) ), + this, + SLOT( infoSlot( uint, Tomahawk::InfoSystem::InfoRequestData, QVariant ) ), Qt::UniqueConnection ); connect( plugin.data(), - SIGNAL( getCachedInfo( Tomahawk::InfoSystem::InfoCriteriaHash, qint64, QString, Tomahawk::InfoSystem::InfoType, QVariant, Tomahawk::InfoSystem::InfoCustomData ) ), + SIGNAL( getCachedInfo( uint, Tomahawk::InfoSystem::InfoCriteriaHash, qint64, Tomahawk::InfoSystem::InfoRequestData ) ), cache.data(), - SLOT( getCachedInfoSlot( Tomahawk::InfoSystem::InfoCriteriaHash, qint64, QString, Tomahawk::InfoSystem::InfoType, QVariant, Tomahawk::InfoSystem::InfoCustomData ) ) + SLOT( getCachedInfoSlot( uint, Tomahawk::InfoSystem::InfoCriteriaHash, qint64, Tomahawk::InfoSystem::InfoRequestData ) ) ); connect( cache.data(), - SIGNAL( notInCache( Tomahawk::InfoSystem::InfoCriteriaHash, QString, Tomahawk::InfoSystem::InfoType, QVariant, Tomahawk::InfoSystem::InfoCustomData ) ), + SIGNAL( notInCache( uint, Tomahawk::InfoSystem::InfoCriteriaHash, Tomahawk::InfoSystem::InfoRequestData ) ), plugin.data(), - SLOT( notInCacheSlot( Tomahawk::InfoSystem::InfoCriteriaHash, QString, Tomahawk::InfoSystem::InfoType, QVariant, Tomahawk::InfoSystem::InfoCustomData ) ) + SLOT( notInCacheSlot( uint, Tomahawk::InfoSystem::InfoCriteriaHash, Tomahawk::InfoSystem::InfoRequestData ) ) ); connect( plugin.data(), @@ -151,33 +158,53 @@ InfoSystemWorker::determineOrderedMatches( const InfoType type ) const void -InfoSystemWorker::getInfo( QString caller, InfoType type, QVariant input, InfoCustomData customData ) +InfoSystemWorker::getInfo( Tomahawk::InfoSystem::InfoRequestData requestData, uint timeoutMillis ) { qDebug() << Q_FUNC_INFO; - QLinkedList< InfoPluginPtr > providers = determineOrderedMatches(type); + QLinkedList< InfoPluginPtr > providers = determineOrderedMatches( requestData.type ); if ( providers.isEmpty() ) { - emit info( caller, type, QVariant(), QVariant(), customData ); + emit info( requestData, QVariant() ); + checkFinished( requestData.caller ); return; } InfoPluginPtr ptr = providers.first(); if ( !ptr ) { - emit info( caller, type, QVariant(), QVariant(), customData ); + emit info( requestData, QVariant() ); + checkFinished( requestData.caller ); return; } - QMetaObject::invokeMethod( ptr.data(), "getInfo", Qt::QueuedConnection, Q_ARG( QString, caller ), Q_ARG( Tomahawk::InfoSystem::InfoType, type ), Q_ARG( QVariant, input ), Q_ARG( Tomahawk::InfoSystem::InfoCustomData, customData ) ); + uint requestId = ++m_nextRequest; + m_requestSatisfiedMap[ requestId ] = false; + if ( timeoutMillis != 0 ) + { + qint64 currMs = QDateTime::currentMSecsSinceEpoch(); + m_timeRequestMapper.insert( currMs + timeoutMillis, requestId ); + } + qDebug() << "assigning request with requestId " << requestId << " and type " << requestData.type; + m_dataTracker[ requestData.caller ][ requestData.type ] = m_dataTracker[ requestData.caller ][ requestData.type ] + 1; + qDebug() << "current count in dataTracker for type" << requestData.type << "is" << m_dataTracker[ requestData.caller ][ requestData.type ]; + + InfoRequestData* data = new InfoRequestData; + data->caller = requestData.caller; + data->type = requestData.type; + data->input = requestData.input; + data->customData = requestData.customData; + m_savedRequestMap[ requestId ] = data; + + QMetaObject::invokeMethod( ptr.data(), "getInfo", Qt::QueuedConnection, Q_ARG( uint, requestId ), Q_ARG( Tomahawk::InfoSystem::InfoRequestData, requestData ) ); } void -InfoSystemWorker::pushInfo( const QString caller, const InfoType type, const QVariant input ) +InfoSystemWorker::pushInfo( QString caller, InfoType type, QVariant input ) { qDebug() << Q_FUNC_INFO; - Q_FOREACH( InfoPluginPtr ptr, m_infoPushMap[type] ) + Q_FOREACH( InfoPluginPtr ptr, m_infoPushMap[ type ] ) { if( ptr ) QMetaObject::invokeMethod( ptr.data(), "pushInfo", Qt::QueuedConnection, Q_ARG( QString, caller ), Q_ARG( Tomahawk::InfoSystem::InfoType, type ), Q_ARG( QVariant, input ) ); @@ -185,6 +212,101 @@ InfoSystemWorker::pushInfo( const QString caller, const InfoType type, const QVa } +void +InfoSystemWorker::infoSlot( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData, QVariant output ) +{ + qDebug() << Q_FUNC_INFO << " with requestId " << requestId; + if ( m_dataTracker[ requestData.caller ][ requestData.type ] == 0 ) + { + qDebug() << Q_FUNC_INFO << " caller was not waiting for that type of data!"; + return; + } + if ( !m_requestSatisfiedMap.contains( requestId ) || m_requestSatisfiedMap[ requestId ] ) + { + qDebug() << Q_FUNC_INFO << " request was already taken care of!"; + return; + } + + m_requestSatisfiedMap[ requestId ] = true; + emit info( requestData, output ); + + m_dataTracker[ requestData.caller ][ requestData.type ] = m_dataTracker[ requestData.caller ][ requestData.type ] - 1; + qDebug() << "current count in dataTracker for target " << requestData.caller << " is " << m_dataTracker[ requestData.caller ][ requestData.type ]; + delete m_savedRequestMap[ requestId ]; + m_savedRequestMap.remove( requestId ); + checkFinished( requestData.caller ); +} + + +void +InfoSystemWorker::checkFinished( const QString &target ) +{ + 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 ); +} + + +void +InfoSystemWorker::checkTimeoutsTimerFired() +{ + qint64 currTime = QDateTime::currentMSecsSinceEpoch(); + Q_FOREACH( qint64 time, m_timeRequestMapper.keys() ) + { + Q_FOREACH( uint requestId, m_timeRequestMapper.values( time ) ) + { + if ( time < currTime ) + { + if ( m_requestSatisfiedMap[ requestId ] ) + { + qDebug() << Q_FUNC_INFO << " removing mapping of " << requestId << " which expired at time " << time << " and was already satisfied"; + m_timeRequestMapper.remove( time, requestId ); + if ( !m_timeRequestMapper.count( time ) ) + m_timeRequestMapper.remove( time ); + continue; + } + + //doh, timed out + qDebug() << Q_FUNC_INFO << " doh, timed out for requestId " << requestId; + InfoRequestData *savedData = m_savedRequestMap[ requestId ]; + + InfoRequestData returnData; + returnData.caller = savedData->caller; + returnData.type = savedData->type; + returnData.input = savedData->input; + returnData.customData = savedData->customData; + emit info( returnData, QVariant() ); + + delete savedData; + m_savedRequestMap.remove( requestId ); + + m_dataTracker[ returnData.caller ][ returnData.type ] = m_dataTracker[ returnData.caller ][ returnData.type ] - 1; + qDebug() << "current count in dataTracker for target " << returnData.caller << " is " << m_dataTracker[ returnData.caller ][ returnData.type ]; + + m_requestSatisfiedMap[ requestId ] = true; + m_timeRequestMapper.remove( time, requestId ); + if ( !m_timeRequestMapper.count( time ) ) + m_timeRequestMapper.remove( time ); + + checkFinished( returnData.caller ); + } + else + { + //we've caught up, the remaining requets still have time to work + return; + } + } + } +} + + QNetworkAccessManager* InfoSystemWorker::nam() const { @@ -239,7 +361,6 @@ InfoSystemWorker::newNam() //FIXME: Currently leaking nam/proxyfactory above -- how to change in a thread-safe way? } - } //namespace InfoSystem } //namespace Tomahawk diff --git a/src/libtomahawk/infosystem/infosystemworker.h b/src/libtomahawk/infosystem/infosystemworker.h index c7f4ada6d..dd0de2b54 100644 --- a/src/libtomahawk/infosystem/infosystemworker.h +++ b/src/libtomahawk/infosystem/infosystemworker.h @@ -29,6 +29,7 @@ #include #include #include +#include #include "dllmacro.h" @@ -36,6 +37,7 @@ namespace Tomahawk { namespace InfoSystem { + class DLLEXPORT InfoSystemWorker : public QObject { Q_OBJECT @@ -48,16 +50,32 @@ public: QNetworkAccessManager* nam() const; signals: - void info( QString target, Tomahawk::InfoSystem::InfoType, QVariant input, QVariant output, Tomahawk::InfoSystem::InfoCustomData customData ); + void info( Tomahawk::InfoSystem::InfoRequestData requestData, QVariant output ); + void finished( QString target ); + void namChanged( QNetworkAccessManager* ); public slots: void init( QWeakPointer< Tomahawk::InfoSystem::InfoSystemCache > cache ); - void getInfo( const QString caller, const Tomahawk::InfoSystem::InfoType type, const QVariant input, const Tomahawk::InfoSystem::InfoCustomData customData ); - void pushInfo( const QString caller, const Tomahawk::InfoSystem::InfoType type, const QVariant input ); + void getInfo( Tomahawk::InfoSystem::InfoRequestData requestData, uint timeoutMillis ); + void pushInfo( QString caller, Tomahawk::InfoSystem::InfoType type, QVariant input ); + + void infoSlot( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData, QVariant output ); + void newNam(); + +private slots: + void checkTimeoutsTimerFired(); private: + + void checkFinished( const QString &target ); + + QHash< QString, QHash< InfoType, int > > m_dataTracker; + QMultiMap< qint64, uint > m_timeRequestMapper; + QHash< uint, bool > m_requestSatisfiedMap; + QHash< uint, InfoRequestData* > m_savedRequestMap; + QLinkedList< InfoPluginPtr > determineOrderedMatches( const InfoType type ) const; // For now, statically instantiate plugins; this is just somewhere to keep them @@ -67,6 +85,10 @@ private: QMap< InfoType, QLinkedList< InfoPluginPtr > > m_infoPushMap; QWeakPointer< QNetworkAccessManager> m_nam; + + uint m_nextRequest; + + QTimer m_checkTimeoutsTimer; }; } diff --git a/src/libtomahawk/kdsingleapplicationguard/kdtoolsglobal.cpp b/src/libtomahawk/kdsingleapplicationguard/kdtoolsglobal.cpp index 5997fe64c..b057614d7 100644 --- a/src/libtomahawk/kdsingleapplicationguard/kdtoolsglobal.cpp +++ b/src/libtomahawk/kdsingleapplicationguard/kdtoolsglobal.cpp @@ -19,7 +19,7 @@ namespace { } static Version kdParseQtVersion( const char * const version ) { - if ( !version || qstrlen( version ) < 5 || version[1] != '.' || version[3] != '.' || version[5] != 0 && version[5] != '.' && version[5] != '-' ) + 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; diff --git a/src/libtomahawk/network/servent.cpp b/src/libtomahawk/network/servent.cpp index 0ed05161d..da0a8550b 100644 --- a/src/libtomahawk/network/servent.cpp +++ b/src/libtomahawk/network/servent.cpp @@ -195,13 +195,28 @@ Servent::createConnectionKey( const QString& name, const QString &nodeid, const void Servent::setExternalAddress( QHostAddress ha, unsigned int port ) { - m_externalAddress = ha; - m_externalPort = port; - - if( m_externalPort == 0 || m_externalAddress.toString().isEmpty() ) + QString ip = ha.toString(); + if ( !qApp->arguments().contains( "--lanhack" ) ) { - if( !TomahawkSettings::instance()->externalHostname().isEmpty() && - !TomahawkSettings::instance()->externalPort() == 0 ) + if ( ip.startsWith( "10." ) || ip.startsWith( "172.16." ) || ip.startsWith( "192.168." ) ) + { + qDebug() << Q_FUNC_INFO << "Tried to set an invalid ip as external address!"; + return; + } + + m_externalAddress = ha; + m_externalPort = port; + } + else + { + m_externalAddress = ha; + m_externalPort = port; + } + + if ( m_externalPort == 0 || m_externalAddress.toString().isEmpty() ) + { + if ( !TomahawkSettings::instance()->externalHostname().isEmpty() && + !TomahawkSettings::instance()->externalPort() == 0 ) { qDebug() << "UPnP failed, have external address/port -- falling back"; m_externalHostname = TomahawkSettings::instance()->externalHostname(); diff --git a/src/libtomahawk/playlist.h b/src/libtomahawk/playlist.h index 76bab9b74..a74729f7d 100644 --- a/src/libtomahawk/playlist.h +++ b/src/libtomahawk/playlist.h @@ -208,6 +208,8 @@ signals: void trackCountChanged( unsigned int tracks ); void sourceTrackCountChanged( unsigned int tracks ); + void nextTrackReady(); + public slots: // want to update the playlist from the model? // generate a newrev using uuid() and call this: diff --git a/src/libtomahawk/playlist/albummodel.cpp b/src/libtomahawk/playlist/albummodel.cpp index 3db313e8d..81f559765 100644 --- a/src/libtomahawk/playlist/albummodel.cpp +++ b/src/libtomahawk/playlist/albummodel.cpp @@ -44,8 +44,8 @@ AlbumModel::AlbumModel( QObject* parent ) .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 ) ) ); + SIGNAL( info( Tomahawk::InfoSystem::InfoRequestData, QVariant ) ), + SLOT( infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData, QVariant ) ) ); connect( Tomahawk::InfoSystem::InfoSystem::instance(), SIGNAL( finished( QString ) ), SLOT( infoSystemFinished( QString ) ) ); } @@ -311,26 +311,25 @@ AlbumModel::onAlbumsAdded( const QList& albums ) void -AlbumModel::infoSystemInfo( QString caller, Tomahawk::InfoSystem::InfoType type, QVariant input, QVariant output, Tomahawk::InfoSystem::InfoCustomData customData ) +AlbumModel::infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData requestData, QVariant output ) { - Q_UNUSED( customData ); - qDebug() << Q_FUNC_INFO; + qDebug() << Q_FUNC_INFO << " with caller " << requestData.caller; - if ( caller != s_tmInfoIdentifier || - ( type != Tomahawk::InfoSystem::InfoAlbumCoverArt && type != Tomahawk::InfoSystem::InfoArtistImages ) ) + if ( requestData.caller != s_tmInfoIdentifier || + ( requestData.type != Tomahawk::InfoSystem::InfoAlbumCoverArt && requestData.type != Tomahawk::InfoSystem::InfoArtistImages ) ) { qDebug() << "Info of wrong type or not with our identifier"; return; } - if ( !output.canConvert< Tomahawk::InfoSystem::InfoCustomData >() ) + if ( !output.canConvert< QVariantMap >() ) { qDebug() << "Cannot convert fetched art from a QByteArray"; return; } - Tomahawk::InfoSystem::InfoCriteriaHash pptr = input.value< Tomahawk::InfoSystem::InfoCriteriaHash >(); - Tomahawk::InfoSystem::InfoCustomData returnedData = output.value< Tomahawk::InfoSystem::InfoCustomData >(); + Tomahawk::InfoSystem::InfoCriteriaHash pptr = requestData.input.value< Tomahawk::InfoSystem::InfoCriteriaHash >(); + QVariantMap returnedData = output.value< QVariantMap >(); const QByteArray ba = returnedData["imgbytes"].toByteArray(); if ( ba.length() ) { diff --git a/src/libtomahawk/playlist/albummodel.h b/src/libtomahawk/playlist/albummodel.h index ff58fee0e..87e1d88f9 100644 --- a/src/libtomahawk/playlist/albummodel.h +++ b/src/libtomahawk/playlist/albummodel.h @@ -97,7 +97,7 @@ private slots: void onAlbumsAdded( const QList& albums ); void onDataChanged(); - void infoSystemInfo( QString caller, Tomahawk::InfoSystem::InfoType type, QVariant input, QVariant output, Tomahawk::InfoSystem::InfoCustomData customData ); + void infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData requestData, QVariant output ); void infoSystemFinished( QString target ); private: diff --git a/src/libtomahawk/playlist/albumproxymodel.cpp b/src/libtomahawk/playlist/albumproxymodel.cpp index 90541d5df..6bafcdce5 100644 --- a/src/libtomahawk/playlist/albumproxymodel.cpp +++ b/src/libtomahawk/playlist/albumproxymodel.cpp @@ -40,6 +40,7 @@ AlbumProxyModel::AlbumProxyModel( QObject* parent ) setSourceAlbumModel( 0 ); } + void AlbumProxyModel::setSourceModel( QAbstractItemModel* sourceModel ) { @@ -48,6 +49,7 @@ AlbumProxyModel::setSourceModel( QAbstractItemModel* sourceModel ) Q_ASSERT( false ); } + void AlbumProxyModel::setSourceAlbumModel( AlbumModel* sourceModel ) { diff --git a/src/libtomahawk/playlist/albumproxymodel.h b/src/libtomahawk/playlist/albumproxymodel.h index 8b9d5e1f6..991ec6b57 100644 --- a/src/libtomahawk/playlist/albumproxymodel.h +++ b/src/libtomahawk/playlist/albumproxymodel.h @@ -65,6 +65,8 @@ signals: void filterChanged( const QString& filter ); + void nextTrackReady(); + public slots: virtual void setRepeatMode( RepeatMode mode ) { m_repeatMode = mode; emit repeatModeChanged( mode ); } virtual void setShuffled( bool enabled ) { m_shuffled = enabled; emit shuffleModeChanged( enabled ); } diff --git a/src/libtomahawk/playlist/albumview.cpp b/src/libtomahawk/playlist/albumview.cpp index 41b2abe0e..31d686985 100644 --- a/src/libtomahawk/playlist/albumview.cpp +++ b/src/libtomahawk/playlist/albumview.cpp @@ -162,9 +162,13 @@ AlbumView::onScrollTimeout() trackInfo["album"] = item->album()->name(); trackInfo["pptr"] = QString::number( (qlonglong)item ); - Tomahawk::InfoSystem::InfoSystem::instance()->getInfo( - s_tmInfoIdentifier, Tomahawk::InfoSystem::InfoAlbumCoverArt, - QVariant::fromValue< Tomahawk::InfoSystem::InfoCriteriaHash >( trackInfo ), Tomahawk::InfoSystem::InfoCustomData() ); + Tomahawk::InfoSystem::InfoRequestData requestData; + requestData.caller = s_tmInfoIdentifier; + requestData.type = Tomahawk::InfoSystem::InfoAlbumCoverArt; + requestData.input = QVariant::fromValue< Tomahawk::InfoSystem::InfoCriteriaHash >( trackInfo ); + requestData.customData = QVariantMap(); + + Tomahawk::InfoSystem::InfoSystem::instance()->getInfo( requestData ); } } } diff --git a/src/libtomahawk/playlist/artistview.cpp b/src/libtomahawk/playlist/artistview.cpp index a584d9a91..806756dc0 100644 --- a/src/libtomahawk/playlist/artistview.cpp +++ b/src/libtomahawk/playlist/artistview.cpp @@ -46,6 +46,7 @@ ArtistView::ArtistView( QWidget* parent ) , m_proxyModel( 0 ) // , m_delegate( 0 ) , m_loadingSpinner( new LoadingSpinner( this ) ) + , m_contextMenu( new ContextMenu( this ) ) , m_showModes( true ) { setAlternatingRowColors( true ); @@ -59,6 +60,7 @@ ArtistView::ArtistView( QWidget* parent ) setAllColumnsShowFocus( true ); setSelectionMode( QAbstractItemView::ExtendedSelection ); setSelectionBehavior( QAbstractItemView::SelectRows ); + setContextMenuPolicy( Qt::CustomContextMenu ); setHeader( m_header ); setProxyModel( new TreeProxyModel( this ) ); @@ -81,6 +83,8 @@ ArtistView::ArtistView( QWidget* parent ) connect( &m_timer, SIGNAL( timeout() ), SLOT( onScrollTimeout() ) ); connect( this, SIGNAL( doubleClicked( QModelIndex ) ), SLOT( onItemActivated( QModelIndex ) ) ); + connect( this, SIGNAL( customContextMenuRequested( const QPoint& ) ), SLOT( onCustomContextMenu( const QPoint& ) ) ); + connect( m_contextMenu, SIGNAL( triggered( int ) ), SLOT( onMenuTriggered( int ) ) ); } @@ -116,7 +120,7 @@ ArtistView::setTreeModel( TreeModel* model ) if ( m_proxyModel ) { - m_proxyModel->setSourceModel( model ); + m_proxyModel->setSourceTreeModel( model ); m_proxyModel->sort( 0 ); } @@ -238,14 +242,74 @@ ArtistView::onScrollTimeout() for ( int i = left.row(); i < max; i++ ) { TreeModelItem* item = m_model->itemFromIndex( m_proxyModel->mapToSource( m_proxyModel->index( i, 0 ) ) ); + if ( item->artist().isNull() ) + continue; Tomahawk::InfoSystem::InfoCriteriaHash trackInfo; trackInfo["artist"] = item->artist()->name(); trackInfo["pptr"] = QString::number( (qlonglong)item ); - Tomahawk::InfoSystem::InfoSystem::instance()->getInfo( - s_tmInfoIdentifier, Tomahawk::InfoSystem::InfoArtistImages, - QVariant::fromValue< Tomahawk::InfoSystem::InfoCriteriaHash >( trackInfo ), Tomahawk::InfoSystem::InfoCustomData() ); + Tomahawk::InfoSystem::InfoRequestData requestData; + requestData.caller = s_tmInfoIdentifier; + requestData.type = Tomahawk::InfoSystem::InfoArtistImages; + requestData.input = QVariant::fromValue< Tomahawk::InfoSystem::InfoCriteriaHash >( trackInfo ); + requestData.customData = QVariantMap(); + + Tomahawk::InfoSystem::InfoSystem::instance()->getInfo( requestData ); + } +} + + +void +ArtistView::onCustomContextMenu( const QPoint& pos ) +{ + m_contextMenu->clear(); + + QModelIndex idx = indexAt( pos ); + idx = idx.sibling( idx.row(), 0 ); + m_contextMenuIndex = idx; + + if ( !idx.isValid() ) + return; + + QList queries; + QList artists; + QList albums; + + foreach ( const QModelIndex& index, selectedIndexes() ) + { + if ( index.column() || selectedIndexes().contains( index.parent() ) ) + continue; + + TreeModelItem* item = m_proxyModel->itemFromIndex( m_proxyModel->mapToSource( index ) ); + + if ( item && !item->result().isNull() ) + queries << item->result()->toQuery(); + if ( item && !item->artist().isNull() ) + artists << item->artist(); + if ( item && !item->album().isNull() ) + albums << item->album(); + } + + m_contextMenu->setQueries( queries ); + m_contextMenu->setArtists( artists ); + m_contextMenu->setAlbums( albums ); + + m_contextMenu->exec( mapToGlobal( pos ) ); +} + + +void +ArtistView::onMenuTriggered( int action ) +{ + switch ( action ) + { + case ContextMenu::ActionPlay: + onItemActivated( m_contextMenuIndex ); + break; + + default: + break; } } diff --git a/src/libtomahawk/playlist/artistview.h b/src/libtomahawk/playlist/artistview.h index a32b430a6..c60b863e5 100644 --- a/src/libtomahawk/playlist/artistview.h +++ b/src/libtomahawk/playlist/artistview.h @@ -22,6 +22,7 @@ #include #include +#include "contextMenu.h" #include "treemodel.h" #include "treeproxymodel.h" #include "viewpage.h" @@ -79,6 +80,9 @@ private slots: void onViewChanged(); void onScrollTimeout(); + void onCustomContextMenu( const QPoint& pos ); + void onMenuTriggered( int action ); + private: TreeHeader* m_header; TreeModel* m_model; @@ -86,6 +90,8 @@ private: // PlaylistItemDelegate* m_delegate; LoadingSpinner* m_loadingSpinner; + QModelIndex m_contextMenuIndex; + Tomahawk::ContextMenu* m_contextMenu; bool m_showModes; QTimer m_timer; diff --git a/src/libtomahawk/playlist/collectionflatmodel.h b/src/libtomahawk/playlist/collectionflatmodel.h index fbf489975..cc1d5c79e 100644 --- a/src/libtomahawk/playlist/collectionflatmodel.h +++ b/src/libtomahawk/playlist/collectionflatmodel.h @@ -55,6 +55,8 @@ public: 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::artist_ptr& /*artist*/ ) {} + virtual void append( const Tomahawk::album_ptr& /*album*/ ) {} signals: void repeatModeChanged( Tomahawk::PlaylistInterface::RepeatMode mode ); diff --git a/src/libtomahawk/playlist/collectionview.cpp b/src/libtomahawk/playlist/collectionview.cpp index 55ff115b3..96e8cda41 100644 --- a/src/libtomahawk/playlist/collectionview.cpp +++ b/src/libtomahawk/playlist/collectionview.cpp @@ -38,9 +38,6 @@ CollectionView::CollectionView( QWidget* parent ) setDragDropMode( QAbstractItemView::DragOnly ); setAcceptDrops( false ); - - setContextMenuPolicy( Qt::CustomContextMenu ); - connect( this, SIGNAL( customContextMenuRequested( const QPoint& ) ), SLOT( onCustomContextMenu( const QPoint& ) ) ); } @@ -80,42 +77,6 @@ CollectionView::dragEnterEvent( QDragEnterEvent* event ) } -void -CollectionView::setupMenus() -{ - m_itemMenu.clear(); - - m_playItemAction = m_itemMenu.addAction( tr( "&Play" ) ); - m_addItemsToQueueAction = m_itemMenu.addAction( tr( "Add to &Queue" ) ); - m_itemMenu.addSeparator(); - - foreach( QAction* a, actions() ) - m_itemMenu.addAction( a ); -// m_addItemsToPlaylistAction = m_itemMenu.addAction( tr( "&Add to Playlist" ) ); - - connect( m_playItemAction, SIGNAL( triggered() ), SLOT( playItem() ) ); - connect( m_addItemsToQueueAction, SIGNAL( triggered() ), SLOT( addItemsToQueue() ) ); -// connect( m_addItemsToPlaylistAction, SIGNAL( triggered() ), SLOT( addItemsToPlaylist() ) ); -} - - -void -CollectionView::onCustomContextMenu( const QPoint& pos ) -{ - qDebug() << Q_FUNC_INFO; - setupMenus(); - - QModelIndex idx = indexAt( pos ); - idx = idx.sibling( idx.row(), 0 ); - setContextMenuIndex( idx ); - - if ( !idx.isValid() ) - return; - - m_itemMenu.exec( mapToGlobal( pos ) ); -} - - void CollectionView::onTrackCountChanged( unsigned int tracks ) { diff --git a/src/libtomahawk/playlist/collectionview.h b/src/libtomahawk/playlist/collectionview.h index e19c80d28..a3893b270 100644 --- a/src/libtomahawk/playlist/collectionview.h +++ b/src/libtomahawk/playlist/collectionview.h @@ -19,8 +19,6 @@ #ifndef COLLECTIONVIEW_H #define COLLECTIONVIEW_H -#include - #include "trackproxymodel.h" #include "trackmodel.h" #include "trackview.h" @@ -52,20 +50,10 @@ public: virtual bool jumpToCurrentTrack(); private slots: - void onCustomContextMenu( const QPoint& pos ); void onTrackCountChanged( unsigned int tracks ); protected: virtual void dragEnterEvent( QDragEnterEvent* event ); - -private: - void setupMenus(); - - QMenu m_itemMenu; - - QAction* m_playItemAction; - QAction* m_addItemsToQueueAction; - QAction* m_addItemsToPlaylistAction; }; #endif // COLLECTIONVIEW_H diff --git a/src/libtomahawk/playlist/dynamic/DynamicModel.h b/src/libtomahawk/playlist/dynamic/DynamicModel.h index ed8f6feeb..5c56d613f 100644 --- a/src/libtomahawk/playlist/dynamic/DynamicModel.h +++ b/src/libtomahawk/playlist/dynamic/DynamicModel.h @@ -27,7 +27,6 @@ namespace Tomahawk class StationModelItem; - /** * Extends PlaylistModel with support for handling stations */ @@ -55,6 +54,9 @@ public: // a batchof static tracks wre generated void tracksGenerated( const QList< query_ptr > entries, int limitResolvedTo = -1 ); + + using PlaylistModel::loadPlaylist; + signals: void collapseFromTo( int startRow, int num ); void checkForOverflow(); @@ -62,6 +64,7 @@ signals: void trackGenerationFailure( const QString& msg ); void tracksAdded(); + private slots: void newTrackGenerated( const Tomahawk::query_ptr& query ); @@ -69,6 +72,7 @@ private slots: void newTrackLoading(); void filteringTrackResolved( bool successful ); + private: void filterUnresolved( const QList< query_ptr >& entries ); void addToPlaylist( const QList< query_ptr >& entries, bool clearFirst ); diff --git a/src/libtomahawk/playlist/infobar/infobar.cpp b/src/libtomahawk/playlist/infobar/infobar.cpp index 569c8a247..8edc51b20 100644 --- a/src/libtomahawk/playlist/infobar/infobar.cpp +++ b/src/libtomahawk/playlist/infobar/infobar.cpp @@ -1,5 +1,5 @@ /* === This file is part of Tomahawk Player - === - * + * * Copyright 2010-2011, Christian Muehlhaeuser * * Tomahawk is free software: you can redistribute it and/or modify @@ -43,18 +43,25 @@ InfoBar::InfoBar( QWidget* parent ) boldFont.setPixelSize( 12 ); ui->descriptionLabel->setFont( boldFont ); - ui->descriptionLabel->setMargin( 10 ); + + QFont regFont = ui->longDescriptionLabel->font(); + regFont.setPixelSize( 11 ); + ui->longDescriptionLabel->setFont( regFont ); QPalette whitePal = ui->captionLabel->palette(); whitePal.setColor( QPalette::Foreground, Qt::white ); ui->captionLabel->setPalette( whitePal ); ui->descriptionLabel->setPalette( whitePal ); + ui->longDescriptionLabel->setPalette( whitePal ); + + ui->captionLabel->setMargin( 6 ); + ui->descriptionLabel->setMargin( 6 ); + ui->longDescriptionLabel->setMargin( 4 ); ui->captionLabel->setText( QString() ); - ui->captionLabel->setMargin( 6 ); - ui->descriptionLabel->setText( QString() ); + ui->longDescriptionLabel->setText( QString() ); ui->imageLabel->setText( QString() ); setAutoFillBackground( true ); @@ -81,6 +88,13 @@ InfoBar::setDescription( const QString& s ) } +void +InfoBar::setLongDescription( const QString& s ) +{ + ui->longDescriptionLabel->setText( s ); +} + + void InfoBar::setPixmap( const QPixmap& p ) { diff --git a/src/libtomahawk/playlist/infobar/infobar.h b/src/libtomahawk/playlist/infobar/infobar.h index 4bd677c78..5f8939a34 100644 --- a/src/libtomahawk/playlist/infobar/infobar.h +++ b/src/libtomahawk/playlist/infobar/infobar.h @@ -1,5 +1,5 @@ /* === This file is part of Tomahawk Player - === - * + * * Copyright 2010-2011, Christian Muehlhaeuser * * Tomahawk is free software: you can redistribute it and/or modify @@ -39,8 +39,9 @@ public: public slots: void setCaption( const QString& s ); void setDescription( const QString& s ); + void setLongDescription( const QString& s ); void setPixmap( const QPixmap& p ); - + protected: void changeEvent( QEvent* e ); void resizeEvent( QResizeEvent* e ); diff --git a/src/libtomahawk/playlist/infobar/infobar.ui b/src/libtomahawk/playlist/infobar/infobar.ui index 764fd040a..379101249 100644 --- a/src/libtomahawk/playlist/infobar/infobar.ui +++ b/src/libtomahawk/playlist/infobar/infobar.ui @@ -36,7 +36,7 @@ - 32 + 16 20 @@ -65,7 +65,7 @@ - 32 + 16 20 @@ -73,10 +73,13 @@ + + QLayout::SetMinimumSize + - + 0 0 @@ -89,7 +92,7 @@ - + 0 0 @@ -101,6 +104,73 @@ + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 16 + 20 + + + + + + + + QLayout::SetMaximumSize + + + + + + 0 + 0 + + + + + 0 + 62 + + + + + 16777215 + 62 + + + + TextLabel + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + + + true + + + + + + + Qt::Horizontal + + + + 40 + 1 + + + + + + @@ -111,7 +181,7 @@ - 32 + 16 20 diff --git a/src/libtomahawk/playlist/playlistitemdelegate.cpp b/src/libtomahawk/playlist/playlistitemdelegate.cpp index c91a3ac72..68fe42143 100644 --- a/src/libtomahawk/playlist/playlistitemdelegate.cpp +++ b/src/libtomahawk/playlist/playlistitemdelegate.cpp @@ -37,6 +37,8 @@ #define PLAYING_ICON QString( RESPATH "images/now-playing-speaker.png" ) +using namespace Tomahawk; + PlaylistItemDelegate::PlaylistItemDelegate( TrackView* parent, TrackProxyModel* proxy ) : QStyledItemDelegate( (QObject*)parent ) @@ -147,6 +149,8 @@ PlaylistItemDelegate::paintShort( QPainter* painter, const QStyleOptionViewItem& QPixmap pixmap; QString artist, track, upperText, lowerText; + source_ptr source = item->query()->playedBy().first; + if ( item->query()->results().count() ) { artist = item->query()->results().first()->artist()->name(); @@ -158,7 +162,7 @@ PlaylistItemDelegate::paintShort( QPainter* painter, const QStyleOptionViewItem& track = item->query()->track(); } - if ( item->query()->playedBy().isNull() ) + if ( source.isNull() ) { upperText = artist; lowerText = track; @@ -166,13 +170,14 @@ PlaylistItemDelegate::paintShort( QPainter* painter, const QStyleOptionViewItem& else { upperText = QString( "%1 - %2" ).arg( artist ).arg( track ); + QString playtime = TomahawkUtils::ageToString( QDateTime::fromTime_t( item->query()->playedBy().second ) ); - if ( item->query()->playedBy() == SourceList::instance()->getLocal() ) - lowerText = QString( "played by you" ); + if ( source == SourceList::instance()->getLocal() ) + lowerText = QString( "played %1 ago by you" ).arg( playtime ); else - lowerText = QString( "played by %1" ).arg( item->query()->playedBy()->friendlyName() ); + lowerText = QString( "played %1 ago by %2" ).arg( playtime ).arg( source->friendlyName() ); - pixmap = item->query()->playedBy()->avatar(); + pixmap = source->avatar(); } if ( pixmap.isNull() ) @@ -200,15 +205,16 @@ PlaylistItemDelegate::paintShort( QPainter* painter, const QStyleOptionViewItem& QFont boldFont = opt.font; boldFont.setBold( true ); - r.adjust( ir.width() + 12, 0, 0, 0 ); + r.adjust( ir.width() + 12, 0, -12, 0 ); QTextOption to( Qt::AlignTop ); - QString text = painter->fontMetrics().elidedText( upperText, Qt::ElideRight, r.width() - 3 ); + to.setWrapMode( QTextOption::NoWrap ); painter->setFont( boldFont ); + QString text = painter->fontMetrics().elidedText( upperText, Qt::ElideRight, r.width() ); painter->drawText( r.adjusted( 0, 1, 0, 0 ), text, to ); to.setAlignment( Qt::AlignBottom ); - text = painter->fontMetrics().elidedText( lowerText, Qt::ElideRight, r.width() - 3 ); painter->setFont( opt.font ); + text = painter->fontMetrics().elidedText( lowerText, Qt::ElideRight, r.width() ); painter->drawText( r.adjusted( 0, 1, 0, 0 ), text, to ); } painter->restore(); @@ -257,8 +263,8 @@ PlaylistItemDelegate::paintDetailed( QPainter* painter, const QStyleOptionViewIt if ( m_view->header()->visualIndex( index.column() ) == 0 ) { r.adjust( 0, 0, 0, -3 ); - painter->drawPixmap( r.adjusted( 3, 3, 18 - r.width(), 0 ), m_nowPlayingIcon ); - r.adjust( 22, 0, 0, 3 ); + painter->drawPixmap( r.adjusted( 3, 1, 18 - r.width(), 1 ), m_nowPlayingIcon ); + r.adjust( 25, 0, 0, 3 ); } painter->setPen( opt.palette.text().color() ); diff --git a/src/libtomahawk/playlist/playlistmodel.cpp b/src/libtomahawk/playlist/playlistmodel.cpp index 3d9942c86..e7c712591 100644 --- a/src/libtomahawk/playlist/playlistmodel.cpp +++ b/src/libtomahawk/playlist/playlistmodel.cpp @@ -23,7 +23,7 @@ #include #include "album.h" - +#include "pipeline.h" #include "database/database.h" #include "database/databasecommand_playbackhistory.h" #include "dynamic/GeneratorInterface.h" @@ -188,6 +188,9 @@ PlaylistModel::append( const Tomahawk::query_ptr& query ) QList< Tomahawk::query_ptr > ql; ql << query; + if ( !query->resolvingFinished() ) + Pipeline::instance()->resolve( query ); + onTracksAdded( ql ); } @@ -201,7 +204,8 @@ PlaylistModel::append( const Tomahawk::album_ptr& album ) connect( album.data(), SIGNAL( tracksAdded( QList ) ), SLOT( onTracksAdded( QList ) ) ); - if( rowCount( QModelIndex() ) == 0 ) { + if ( rowCount( QModelIndex() ) == 0 ) + { setTitle( album->name() ); setDescription( tr( "All tracks by %1 on album %2" ).arg( album->artist()->name() ).arg( album->name() ) ); m_isTemporary = true; @@ -220,7 +224,8 @@ PlaylistModel::append( const Tomahawk::artist_ptr& artist ) connect( artist.data(), SIGNAL( tracksAdded( QList ) ), SLOT( onTracksAdded( QList ) ) ); - if( rowCount( QModelIndex() ) == 0 ) { + if ( rowCount( QModelIndex() ) == 0 ) + { setTitle( artist->name() ); setDescription( tr( "All tracks by %1" ).arg( artist->name() ) ); m_isTemporary = true; diff --git a/src/libtomahawk/playlist/playlistview.cpp b/src/libtomahawk/playlist/playlistview.cpp index 210558a4b..5a8ce4a9f 100644 --- a/src/libtomahawk/playlist/playlistview.cpp +++ b/src/libtomahawk/playlist/playlistview.cpp @@ -32,16 +32,10 @@ using namespace Tomahawk; PlaylistView::PlaylistView( QWidget* parent ) : TrackView( parent ) , m_model( 0 ) - , m_itemMenu( 0 ) - , m_playItemAction( 0 ) - , m_addItemsToQueueAction( 0 ) - , m_addItemsToPlaylistAction( 0 ) - , m_deleteItemsAction( 0 ) { setProxyModel( new PlaylistProxyModel( this ) ); - setContextMenuPolicy( Qt::CustomContextMenu ); - connect( this, SIGNAL( customContextMenuRequested( const QPoint& ) ), SLOT( onCustomContextMenu( const QPoint& ) ) ); + connect( contextMenu(), SIGNAL( triggered( int ) ), SLOT( onMenuTriggered( int ) ) ); } @@ -81,54 +75,6 @@ PlaylistView::setPlaylistModel( PlaylistModel* model ) } -void -PlaylistView::setupMenus() -{ - m_itemMenu.clear(); - - unsigned int i = 0; - foreach( const QModelIndex& idx, selectedIndexes() ) - if ( idx.column() == 0 ) - i++; - - m_playItemAction = m_itemMenu.addAction( tr( "&Play" ) ); - m_addItemsToQueueAction = m_itemMenu.addAction( tr( "Add to &Queue" ) ); - m_itemMenu.addSeparator(); - - foreach( QAction* a, actions() ) - m_itemMenu.addAction( a ); - -// m_addItemsToPlaylistAction = m_itemMenu.addAction( tr( "&Add to Playlist" ) ); -// m_itemMenu.addSeparator(); - m_deleteItemsAction = m_itemMenu.addAction( i > 1 ? tr( "&Delete Items" ) : tr( "&Delete Item" ) ); - - if ( model() ) - m_deleteItemsAction->setEnabled( !model()->isReadOnly() ); - - connect( m_playItemAction, SIGNAL( triggered() ), SLOT( playItem() ) ); - connect( m_addItemsToQueueAction, SIGNAL( triggered() ), SLOT( addItemsToQueue() ) ); -// connect( m_addItemsToPlaylistAction, SIGNAL( triggered() ), SLOT( addItemsToPlaylist() ) ); - connect( m_deleteItemsAction, SIGNAL( triggered() ), SLOT( deleteItems() ) ); -} - - -void -PlaylistView::onCustomContextMenu( const QPoint& pos ) -{ - qDebug() << Q_FUNC_INFO; - setupMenus(); - - QModelIndex idx = indexAt( pos ); - idx = idx.sibling( idx.row(), 0 ); - setContextMenuIndex( idx ); - - if ( !idx.isValid() ) - return; - - m_itemMenu.exec( mapToGlobal( pos ) ); -} - - void PlaylistView::keyPressEvent( QKeyEvent* event ) { @@ -152,6 +98,7 @@ PlaylistView::deleteItems() proxyModel()->removeIndexes( selectedIndexes() ); } + void PlaylistView::onTrackCountChanged( unsigned int tracks ) { @@ -196,3 +143,19 @@ PlaylistView::isTemporaryPage() const { return ( m_model && m_model->isTemporary() ); } + + +void +PlaylistView::onMenuTriggered( int action ) +{ + switch ( action ) + { + case ContextMenu::ActionDelete: + deleteItems(); + break; + + default: + TrackView::onMenuTriggered( action ); + break; + } +} diff --git a/src/libtomahawk/playlist/playlistview.h b/src/libtomahawk/playlist/playlistview.h index 4bc19650a..945a0d613 100644 --- a/src/libtomahawk/playlist/playlistview.h +++ b/src/libtomahawk/playlist/playlistview.h @@ -19,8 +19,6 @@ #ifndef PLAYLISTVIEW_H #define PLAYLISTVIEW_H -#include - #include "playlist/trackproxymodel.h" #include "playlist/playlistmodel.h" #include "trackview.h" @@ -62,26 +60,19 @@ protected: void keyPressEvent( QKeyEvent* event ); private slots: - void onCustomContextMenu( const QPoint& pos ); void onTrackCountChanged( unsigned int tracks ); + void onMenuTriggered( int action ); void deleteItems(); void onDeleted(); void onChanged(); -private: - void setupMenus(); +private: PlaylistModel* m_model; - QMenu m_itemMenu; QString m_customTitle; QString m_customDescripton; - - QAction* m_playItemAction; - QAction* m_addItemsToQueueAction; - QAction* m_addItemsToPlaylistAction; - QAction* m_deleteItemsAction; }; #endif // PLAYLISTVIEW_H diff --git a/src/libtomahawk/playlist/queueproxymodel.cpp b/src/libtomahawk/playlist/queueproxymodel.cpp index c52fb2c88..8fbff81b9 100644 --- a/src/libtomahawk/playlist/queueproxymodel.cpp +++ b/src/libtomahawk/playlist/queueproxymodel.cpp @@ -1,5 +1,5 @@ /* === This file is part of Tomahawk Player - === - * + * * Copyright 2010-2011, Christian Muehlhaeuser * * Tomahawk is free software: you can redistribute it and/or modify @@ -47,13 +47,12 @@ QueueProxyModel::siblingItem( int itemsAway ) setCurrentIndex( QModelIndex() ); Tomahawk::result_ptr res = PlaylistProxyModel::siblingItem( itemsAway ); - qDebug() << "new rowcount:" << rowCount( QModelIndex() ); - removeIndex( currentIndex() ); return res; } + void QueueProxyModel::onTrackCountChanged( unsigned int count ) { diff --git a/src/libtomahawk/playlist/queueproxymodel.h b/src/libtomahawk/playlist/queueproxymodel.h index e7fc1826d..16dd5273e 100644 --- a/src/libtomahawk/playlist/queueproxymodel.h +++ b/src/libtomahawk/playlist/queueproxymodel.h @@ -1,5 +1,5 @@ /* === This file is part of Tomahawk Player - === - * + * * Copyright 2010-2011, Christian Muehlhaeuser * * Tomahawk is free software: you can redistribute it and/or modify @@ -35,6 +35,8 @@ public: virtual Tomahawk::result_ptr siblingItem( int itemsAway ); + using PlaylistProxyModel::siblingItem; + private slots: void onTrackCountChanged( unsigned int count ); }; diff --git a/src/libtomahawk/playlist/trackmodel.h b/src/libtomahawk/playlist/trackmodel.h index 6f3e4cca1..ce67e0375 100644 --- a/src/libtomahawk/playlist/trackmodel.h +++ b/src/libtomahawk/playlist/trackmodel.h @@ -89,6 +89,8 @@ public: virtual void ensureResolved(); virtual void append( const Tomahawk::query_ptr& query ) = 0; + virtual void append( const Tomahawk::artist_ptr& artist ) = 0; + virtual void append( const Tomahawk::album_ptr& album ) = 0; TrackModelItem* itemFromIndex( const QModelIndex& index ) const; diff --git a/src/libtomahawk/playlist/trackproxymodel.cpp b/src/libtomahawk/playlist/trackproxymodel.cpp index c1e1c34a2..abb54a01f 100644 --- a/src/libtomahawk/playlist/trackproxymodel.cpp +++ b/src/libtomahawk/playlist/trackproxymodel.cpp @@ -300,8 +300,6 @@ TrackProxyModel::removeIndexes( const QList& indexes ) bool TrackProxyModel::lessThan( const QModelIndex& left, const QModelIndex& right ) const { - qDebug() << Q_FUNC_INFO; - TrackModelItem* p1 = itemFromIndex( left ); TrackModelItem* p2 = itemFromIndex( right ); diff --git a/src/libtomahawk/playlist/trackproxymodel.h b/src/libtomahawk/playlist/trackproxymodel.h index 3ddb35779..76381b117 100644 --- a/src/libtomahawk/playlist/trackproxymodel.h +++ b/src/libtomahawk/playlist/trackproxymodel.h @@ -74,6 +74,8 @@ signals: void filterChanged( const QString& filter ); + void nextTrackReady(); + public slots: virtual void setRepeatMode( RepeatMode mode ) { m_repeatMode = mode; emit repeatModeChanged( mode ); } virtual void setShuffled( bool enabled ) { m_shuffled = enabled; emit shuffleModeChanged( enabled ); } diff --git a/src/libtomahawk/playlist/trackview.cpp b/src/libtomahawk/playlist/trackview.cpp index 3b1f4b5ed..d8bf6c0a1 100644 --- a/src/libtomahawk/playlist/trackview.cpp +++ b/src/libtomahawk/playlist/trackview.cpp @@ -34,7 +34,6 @@ #include "trackmodel.h" #include "trackproxymodel.h" #include "track.h" -#include "globalactionmanager.h" using namespace Tomahawk; @@ -49,6 +48,7 @@ TrackView::TrackView( QWidget* parent ) , m_loadingSpinner( new LoadingSpinner( this ) ) , m_resizing( false ) , m_dragging( false ) + , m_contextMenu( new ContextMenu( this ) ) { setAlternatingRowColors( true ); setSelectionMode( QAbstractItemView::ExtendedSelection ); @@ -67,6 +67,7 @@ TrackView::TrackView( QWidget* parent ) setHeader( m_header ); setSortingEnabled( true ); sortByColumn( -1 ); + setContextMenuPolicy( Qt::CustomContextMenu ); #ifndef Q_WS_WIN QFont f = font(); @@ -79,11 +80,9 @@ TrackView::TrackView( QWidget* parent ) setFont( f ); #endif - QAction* createLinkAction = new QAction( tr( "Copy Track Link" ), this ); - connect( createLinkAction, SIGNAL( triggered( bool ) ), this, SLOT( copyLink() ) ); - addAction( createLinkAction ); - connect( this, SIGNAL( doubleClicked( QModelIndex ) ), SLOT( onItemActivated( QModelIndex ) ) ); + connect( this, SIGNAL( customContextMenuRequested( const QPoint& ) ), SLOT( onCustomContextMenu( const QPoint& ) ) ); + connect( m_contextMenu, SIGNAL( triggered( int ) ), SLOT( onMenuTriggered( int ) ) ); } @@ -200,24 +199,6 @@ TrackView::playItem() } -void -TrackView::addItemsToQueue() -{ - foreach( const QModelIndex& idx, selectedIndexes() ) - { - if ( idx.column() ) - continue; - - TrackModelItem* item = model()->itemFromIndex( proxyModel()->mapToSource( idx ) ); - if ( item && item->query()->numResults() ) - { - ViewManager::instance()->queue()->model()->append( item->query() ); - ViewManager::instance()->showQueue(); - } - } -} - - void TrackView::resizeEvent( QResizeEvent* event ) { @@ -374,17 +355,6 @@ TrackView::onFilterChanged( const QString& ) } -void -TrackView::copyLink() -{ - TrackModelItem* item = model()->itemFromIndex( proxyModel()->mapToSource( contextMenuIndex() ) ); - if ( item && !item->query().isNull() ) - { - GlobalActionManager::instance()->copyToClipboard( item->query() ); - } -} - - void TrackView::startDrag( Qt::DropActions supportedActions ) { @@ -419,3 +389,49 @@ TrackView::startDrag( Qt::DropActions supportedActions ) m_proxyModel->removeIndexes( pindexes ); } } + + +void +TrackView::onCustomContextMenu( const QPoint& pos ) +{ + m_contextMenu->clear(); + + QModelIndex idx = indexAt( pos ); + idx = idx.sibling( idx.row(), 0 ); + setContextMenuIndex( idx ); + + if ( !idx.isValid() ) + return; + + if ( model() && !model()->isReadOnly() ) + m_contextMenu->setSupportedActions( m_contextMenu->supportedActions() | ContextMenu::ActionDelete ); + + QList queries; + foreach ( const QModelIndex& index, selectedIndexes() ) + { + if ( index.column() ) + continue; + + TrackModelItem* item = proxyModel()->itemFromIndex( proxyModel()->mapToSource( index ) ); + if ( item && !item->query().isNull() ) + queries << item->query(); + } + + m_contextMenu->setQueries( queries ); + m_contextMenu->exec( mapToGlobal( pos ) ); +} + + +void +TrackView::onMenuTriggered( int action ) +{ + switch ( action ) + { + case ContextMenu::ActionPlay: + onItemActivated( m_contextMenuIndex ); + break; + + default: + break; + } +} diff --git a/src/libtomahawk/playlist/trackview.h b/src/libtomahawk/playlist/trackview.h index 05546442e..d2ad0de82 100644 --- a/src/libtomahawk/playlist/trackview.h +++ b/src/libtomahawk/playlist/trackview.h @@ -22,6 +22,7 @@ #include #include +#include "contextMenu.h" #include "playlistitemdelegate.h" #include "dllmacro.h" @@ -53,6 +54,7 @@ explicit TrackView( QWidget* parent = 0 ); PlaylistItemDelegate* delegate() const { return m_delegate; } TrackHeader* header() const { return m_header; } OverlayWidget* overlay() const { return m_overlay; } + Tomahawk::ContextMenu* contextMenu() const { return m_contextMenu; } QModelIndex contextMenuIndex() const { return m_contextMenuIndex; } void setContextMenuIndex( const QModelIndex& idx ) { m_contextMenuIndex = idx; } @@ -61,7 +63,7 @@ public slots: void onItemActivated( const QModelIndex& index ); void playItem(); - void addItemsToQueue(); + void onMenuTriggered( int action ); protected: virtual void resizeEvent( QResizeEvent* event ); @@ -77,10 +79,9 @@ protected: private slots: void onItemResized( const QModelIndex& index ); - void onFilterChanged( const QString& filter ); - void copyLink(); + void onCustomContextMenu( const QPoint& pos ); private: QString m_guid; @@ -96,6 +97,7 @@ private: QRect m_dropRect; QModelIndex m_contextMenuIndex; + Tomahawk::ContextMenu* m_contextMenu; }; #endif // TRACKVIEW_H diff --git a/src/libtomahawk/playlist/treeitemdelegate.cpp b/src/libtomahawk/playlist/treeitemdelegate.cpp index 3d499649d..c42b51ee6 100644 --- a/src/libtomahawk/playlist/treeitemdelegate.cpp +++ b/src/libtomahawk/playlist/treeitemdelegate.cpp @@ -22,6 +22,7 @@ #include #include #include +#include #include "query.h" #include "result.h" @@ -30,13 +31,15 @@ #include "treemodelitem.h" #include "treeproxymodel.h" +#include "artistview.h" -TreeItemDelegate::TreeItemDelegate( QAbstractItemView* parent, TreeProxyModel* proxy ) +TreeItemDelegate::TreeItemDelegate( ArtistView* parent, TreeProxyModel* proxy ) : QStyledItemDelegate( (QObject*)parent ) , m_view( parent ) , m_model( proxy ) { + m_nowPlayingIcon = QPixmap( RESPATH "images/now-playing-speaker.png" ); } @@ -74,8 +77,35 @@ TreeItemDelegate::paint( QPainter* painter, const QStyleOptionViewItem& option, { QStyleOptionViewItemV4 o( *vioption ); o.palette.setColor( QPalette::Text, textColor ); - return QStyledItemDelegate::paint( painter, o, index ); + + if ( item->isPlaying() ) + { + o.palette.setColor( QPalette::Highlight, o.palette.color( QPalette::Mid ) ); + o.palette.setColor( QPalette::Text, o.palette.color( QPalette::HighlightedText ) ); + o.state |= QStyle::State_Selected; + } + + qApp->style()->drawControl( QStyle::CE_ItemViewItem, &o, painter ); + + { + QRect r = o.rect.adjusted( 3, 0, 0, 0 ); + + // Paint Now Playing Speaker Icon + if ( item->isPlaying() && m_view->header()->visualIndex( index.column() ) == 0 ) + { + r.adjust( 0, 0, 0, -3 ); + painter->drawPixmap( r.adjusted( 3, 1, 18 - r.width(), 1 ), m_nowPlayingIcon ); + r.adjust( 25, 0, 0, 3 ); + } + + painter->setPen( o.palette.text().color() ); + + QTextOption to( Qt::AlignVCenter ); + QString text = painter->fontMetrics().elidedText( index.data().toString(), Qt::ElideRight, r.width() - 3 ); + painter->drawText( r.adjusted( 0, 1, 0, 0 ), text, to ); + } } + return; } else return; diff --git a/src/libtomahawk/playlist/treeitemdelegate.h b/src/libtomahawk/playlist/treeitemdelegate.h index 234b0d326..5074305e0 100644 --- a/src/libtomahawk/playlist/treeitemdelegate.h +++ b/src/libtomahawk/playlist/treeitemdelegate.h @@ -23,6 +23,7 @@ #include "dllmacro.h" +class ArtistView; class TreeProxyModel; class DLLEXPORT TreeItemDelegate : public QStyledItemDelegate @@ -30,7 +31,7 @@ class DLLEXPORT TreeItemDelegate : public QStyledItemDelegate Q_OBJECT public: - TreeItemDelegate( QAbstractItemView* parent = 0, TreeProxyModel* proxy = 0 ); + TreeItemDelegate( ArtistView* parent = 0, TreeProxyModel* proxy = 0 ); protected: void paint( QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index ) const; @@ -39,8 +40,10 @@ protected: // QWidget* createEditor( QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index ) const; private: - QAbstractItemView* m_view; + ArtistView* m_view; TreeProxyModel* m_model; + + QPixmap m_nowPlayingIcon; }; #endif // TREEITEMDELEGATE_H diff --git a/src/libtomahawk/playlist/treemodel.cpp b/src/libtomahawk/playlist/treemodel.cpp index eaa83f07a..a0418be5f 100644 --- a/src/libtomahawk/playlist/treemodel.cpp +++ b/src/libtomahawk/playlist/treemodel.cpp @@ -23,6 +23,7 @@ #include #include +#include "audio/audioengine.h" #include "database/databasecommand_allalbums.h" #include "database/databasecommand_alltracks.h" #include "database/database.h" @@ -42,9 +43,12 @@ TreeModel::TreeModel( QObject* parent ) m_defaultCover = QPixmap( RESPATH "images/no-album-art-placeholder.png" ) .scaled( QSize( 120, 120 ), Qt::IgnoreAspectRatio, Qt::SmoothTransformation ); + connect( AudioEngine::instance(), SIGNAL( finished( Tomahawk::result_ptr ) ), SLOT( onPlaybackFinished( Tomahawk::result_ptr ) ), Qt::DirectConnection ); + connect( AudioEngine::instance(), SIGNAL( stopped() ), SLOT( onPlaybackStopped() ), Qt::DirectConnection ); + 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 ) ) ); + SIGNAL( info( Tomahawk::InfoSystem::InfoRequestData, QVariant ) ), + SLOT( infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData, QVariant ) ) ); connect( Tomahawk::InfoSystem::InfoSystem::instance(), SIGNAL( finished( QString ) ), SLOT( infoSystemFinished( QString ) ) ); } @@ -60,10 +64,17 @@ TreeModel::setCurrentItem( const QModelIndex& index ) { qDebug() << Q_FUNC_INFO; + TreeModelItem* oldEntry = itemFromIndex( m_currentIndex ); + if ( oldEntry ) + { + oldEntry->setIsPlaying( false ); + } + TreeModelItem* entry = itemFromIndex( index ); if ( entry ) { m_currentIndex = index; + entry->setIsPlaying( true ); } else { @@ -367,6 +378,22 @@ TreeModel::addAllCollections() } +void +TreeModel::addArtists( const artist_ptr& artist ) +{ + qDebug() << Q_FUNC_INFO; + + if ( artist.isNull() ) + return; + + emit loadingStarted(); + + QList artists; + artists << artist; + onArtistsAdded( artists ); +} + + void TreeModel::addAlbums( const artist_ptr& artist, const QModelIndex& parent ) { @@ -483,21 +510,24 @@ TreeModel::onArtistsAdded( const QList& artists ) void TreeModel::onAlbumsAdded( const QList& albums, const QVariant& data ) { - qDebug() << Q_FUNC_INFO << albums.count(); + qDebug() << Q_FUNC_INFO << albums.count() << data.toInt(); if ( !albums.count() ) return; - QModelIndex parent = index( data.toUInt(), 0, QModelIndex() ); + QModelIndex parent = index( data.toInt(), 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; + int c = rowCount( parent ); crows.first = c; crows.second = c + albums.count() - 1; - if ( crows.second > 0 ) - emit beginInsertRows( parent, crows.first + 1, crows.second ); + if ( parent.isValid() ) + crows.second -= 1; + + qDebug() << crows.first << crows.second; + if ( !parent.isValid() || crows.second > 0 ) + emit beginInsertRows( parent, crows.first, crows.second ); TreeModelItem* albumitem = 0; foreach( const album_ptr& album, albums ) @@ -512,12 +542,16 @@ TreeModel::onAlbumsAdded( const QList& albums, const QVaria trackInfo["album"] = album->name(); trackInfo["pptr"] = QString::number( (qlonglong)albumitem ); - Tomahawk::InfoSystem::InfoSystem::instance()->getInfo( - s_tmInfoIdentifier, Tomahawk::InfoSystem::InfoAlbumCoverArt, - QVariant::fromValue< Tomahawk::InfoSystem::InfoCriteriaHash >( trackInfo ), Tomahawk::InfoSystem::InfoCustomData() ); + Tomahawk::InfoSystem::InfoRequestData requestData; + requestData.caller = s_tmInfoIdentifier; + requestData.type = Tomahawk::InfoSystem::InfoAlbumCoverArt; + requestData.input = QVariant::fromValue< Tomahawk::InfoSystem::InfoCriteriaHash >( trackInfo ); + requestData.customData = QVariantMap(); + + Tomahawk::InfoSystem::InfoSystem::instance()->getInfo( requestData ); } - if ( crows.second > 0 ) + if ( !parent.isValid() || crows.second > 0 ) emit endInsertRows(); else emit dataChanged( albumitem->index, albumitem->index.sibling( albumitem->index.row(), columnCount( QModelIndex() ) - 1 ) ); @@ -570,32 +604,31 @@ TreeModel::onTracksAdded( const QList& tracks, const QVaria void -TreeModel::infoSystemInfo( QString caller, Tomahawk::InfoSystem::InfoType type, QVariant input, QVariant output, Tomahawk::InfoSystem::InfoCustomData customData ) +TreeModel::infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData requestData, QVariant output ) { - Q_UNUSED( customData ); qDebug() << Q_FUNC_INFO; - if ( caller != s_tmInfoIdentifier || - ( type != Tomahawk::InfoSystem::InfoAlbumCoverArt && type != Tomahawk::InfoSystem::InfoArtistImages ) ) + if ( requestData.caller != s_tmInfoIdentifier || + ( requestData.type != Tomahawk::InfoSystem::InfoAlbumCoverArt && requestData.type != Tomahawk::InfoSystem::InfoArtistImages ) ) { qDebug() << "Info of wrong type or not with our identifier"; return; } - if ( !output.canConvert< Tomahawk::InfoSystem::InfoCustomData >() ) + if ( !output.canConvert< QVariantMap >() ) { qDebug() << "Cannot convert fetched art from a QByteArray"; return; } - Tomahawk::InfoSystem::InfoCriteriaHash pptr = input.value< Tomahawk::InfoSystem::InfoCriteriaHash >(); - Tomahawk::InfoSystem::InfoCustomData returnedData = output.value< Tomahawk::InfoSystem::InfoCustomData >(); + Tomahawk::InfoSystem::InfoCriteriaHash pptr = requestData.input.value< Tomahawk::InfoSystem::InfoCriteriaHash >(); + QVariantMap returnedData = output.value< QVariantMap >(); const QByteArray ba = returnedData["imgbytes"].toByteArray(); + qDebug() << "ba.length = " << ba.length(); if ( ba.length() ) { QPixmap pm; pm.loadFromData( ba ); - bool ok; qlonglong p = pptr["pptr"].toLongLong( &ok ); TreeModelItem* ai = reinterpret_cast(p); @@ -618,6 +651,29 @@ TreeModel::infoSystemFinished( QString target ) } +void +TreeModel::onPlaybackFinished( const Tomahawk::result_ptr& result ) +{ + TreeModelItem* oldEntry = itemFromIndex( m_currentIndex ); + qDebug() << oldEntry->result().data() << result.data(); + if ( oldEntry && !oldEntry->result().isNull() && oldEntry->result().data() == result.data() ) + { + oldEntry->setIsPlaying( false ); + } +} + + +void +TreeModel::onPlaybackStopped() +{ + TreeModelItem* oldEntry = itemFromIndex( m_currentIndex ); + if ( oldEntry ) + { + oldEntry->setIsPlaying( false ); + } +} + + void TreeModel::onDataChanged() { diff --git a/src/libtomahawk/playlist/treemodel.h b/src/libtomahawk/playlist/treemodel.h index 5e9379406..8ade79568 100644 --- a/src/libtomahawk/playlist/treemodel.h +++ b/src/libtomahawk/playlist/treemodel.h @@ -80,6 +80,7 @@ public: void addCollection( const Tomahawk::collection_ptr& collection ); void addFilteredCollection( const Tomahawk::collection_ptr& collection, unsigned int amount, DatabaseCommand_AllArtists::SortOrder order ); + void addArtists( const Tomahawk::artist_ptr& artist ); void addAlbums( const Tomahawk::artist_ptr& artist, const QModelIndex& parent ); void addTracks( const Tomahawk::album_ptr& album, const QModelIndex& parent ); @@ -91,7 +92,9 @@ public: TreeModelItem* itemFromIndex( const QModelIndex& index ) const { if ( index.isValid() ) + { return static_cast( index.internalPointer() ); + } else { return m_rootItem; @@ -122,9 +125,12 @@ private slots: 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 infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData requestData, QVariant output ); void infoSystemFinished( QString target ); + void onPlaybackFinished( const Tomahawk::result_ptr& result ); + void onPlaybackStopped(); + void onDataChanged(); private: diff --git a/src/libtomahawk/playlist/treemodelitem.cpp b/src/libtomahawk/playlist/treemodelitem.cpp index 09688a56d..5406be52f 100644 --- a/src/libtomahawk/playlist/treemodelitem.cpp +++ b/src/libtomahawk/playlist/treemodelitem.cpp @@ -48,6 +48,7 @@ TreeModelItem::TreeModelItem( TreeModelItem* parent, QAbstractItemModel* model ) childCount = 0; toberemoved = false; fetchingMore = false; + m_isPlaying = false; if ( parent ) { @@ -62,6 +63,7 @@ TreeModelItem::TreeModelItem( const Tomahawk::album_ptr& album, TreeModelItem* p { this->parent = parent; fetchingMore = false; + m_isPlaying = false; if ( parent ) { @@ -88,6 +90,7 @@ TreeModelItem::TreeModelItem( const Tomahawk::artist_ptr& artist, TreeModelItem* { this->parent = parent; fetchingMore = false; + m_isPlaying = false; if ( parent ) { @@ -114,6 +117,7 @@ TreeModelItem::TreeModelItem( const Tomahawk::result_ptr& result, TreeModelItem* { this->parent = parent; fetchingMore = false; + m_isPlaying = false; if ( parent ) { diff --git a/src/libtomahawk/playlist/treemodelitem.h b/src/libtomahawk/playlist/treemodelitem.h index 16538727b..01f3b226e 100644 --- a/src/libtomahawk/playlist/treemodelitem.h +++ b/src/libtomahawk/playlist/treemodelitem.h @@ -44,6 +44,9 @@ public: const Tomahawk::album_ptr& album() const { return m_album; }; const Tomahawk::result_ptr& result() const { return m_result; }; + bool isPlaying() { return m_isPlaying; } + void setIsPlaying( bool b ) { m_isPlaying = b; emit dataChanged(); } + void setCover( const QPixmap& cover ) { this->cover = cover; emit dataChanged(); } TreeModelItem* parent; @@ -64,6 +67,8 @@ private: Tomahawk::artist_ptr m_artist; Tomahawk::album_ptr m_album; Tomahawk::result_ptr m_result; + + bool m_isPlaying; }; #endif // TREEMODELITEM_H diff --git a/src/libtomahawk/playlist/treeproxymodel.cpp b/src/libtomahawk/playlist/treeproxymodel.cpp index 2f04dcb84..b1e3450b5 100644 --- a/src/libtomahawk/playlist/treeproxymodel.cpp +++ b/src/libtomahawk/playlist/treeproxymodel.cpp @@ -37,19 +37,27 @@ TreeProxyModel::TreeProxyModel( QObject* parent ) setSortCaseSensitivity( Qt::CaseInsensitive ); setDynamicSortFilter( true ); - setSourceModel( 0 ); + setSourceTreeModel( 0 ); } void -TreeProxyModel::setSourceModel( TreeModel* sourceModel ) +TreeProxyModel::setSourceModel( QAbstractItemModel* sourceModel ) +{ + Q_UNUSED( sourceModel ); + qDebug() << "Explicitly use setSourceTreeModel instead"; + Q_ASSERT( false ); +} + + +void +TreeProxyModel::setSourceTreeModel( TreeModel* sourceModel ) { m_model = sourceModel; if ( m_model && m_model->metaObject()->indexOfSignal( "trackCountChanged(uint)" ) > -1 ) connect( m_model, SIGNAL( trackCountChanged( unsigned int ) ), SIGNAL( sourceTrackCountChanged( unsigned int ) ) ); - QSortFilterProxyModel::setSourceModel( sourceModel ); } diff --git a/src/libtomahawk/playlist/treeproxymodel.h b/src/libtomahawk/playlist/treeproxymodel.h index 66efc7668..68deab1be 100644 --- a/src/libtomahawk/playlist/treeproxymodel.h +++ b/src/libtomahawk/playlist/treeproxymodel.h @@ -34,7 +34,8 @@ public: explicit TreeProxyModel( QObject* parent = 0 ); virtual TreeModel* sourceModel() const { return m_model; } - virtual void setSourceModel( TreeModel* sourceModel ); + virtual void setSourceTreeModel( TreeModel* sourceModel ); + virtual void setSourceModel( QAbstractItemModel* sourceModel ); virtual QPersistentModelIndex currentIndex() const { return mapFromSource( m_model->currentItem() ); } virtual void setCurrentIndex( const QModelIndex& index ) { m_model->setCurrentItem( mapToSource( index ) ); } @@ -70,6 +71,8 @@ signals: void filterChanged( const QString& filter ); + void nextTrackReady(); + public slots: virtual void setRepeatMode( RepeatMode mode ) { m_repeatMode = mode; emit repeatModeChanged( mode ); } virtual void setShuffled( bool enabled ) { m_shuffled = enabled; emit shuffleModeChanged( enabled ); } diff --git a/src/libtomahawk/playlistinterface.h b/src/libtomahawk/playlistinterface.h index ad93a8a03..01f9f3126 100644 --- a/src/libtomahawk/playlistinterface.h +++ b/src/libtomahawk/playlistinterface.h @@ -32,6 +32,7 @@ namespace Tomahawk class DLLEXPORT PlaylistInterface { + public: enum RepeatMode { NoRepeat, RepeatOne, RepeatAll }; enum ViewMode { Unknown, Tree, Flat, Album }; @@ -78,6 +79,7 @@ signals: virtual void shuffleModeChanged( bool enabled ) = 0; virtual void trackCountChanged( unsigned int tracks ) = 0; virtual void sourceTrackCountChanged( unsigned int tracks ) = 0; + virtual void nextTrackReady() = 0; private: QObject* m_object; diff --git a/src/libtomahawk/query.cpp b/src/libtomahawk/query.cpp index 7357bf42a..9281d86e0 100644 --- a/src/libtomahawk/query.cpp +++ b/src/libtomahawk/query.cpp @@ -202,6 +202,14 @@ Query::id() const } +void +Query::setPlayedBy( const Tomahawk::source_ptr& source, unsigned int playtime ) +{ + m_playedBy.first = source; + m_playedBy.second = playtime; +} + + bool Query::resultSorter( const result_ptr& left, const result_ptr& right ) { diff --git a/src/libtomahawk/query.h b/src/libtomahawk/query.h index c28d5bcfb..b0c1b7422 100644 --- a/src/libtomahawk/query.h +++ b/src/libtomahawk/query.h @@ -74,7 +74,7 @@ public: bool isFullTextQuery() const { return !m_fullTextQuery.isEmpty(); } bool resolvingFinished() const { return m_resolveFinished; } - Tomahawk::source_ptr playedBy() const { return m_playedBy; } + QPair< Tomahawk::source_ptr, unsigned int > playedBy() const { return m_playedBy; } Tomahawk::Resolver* currentResolver() const; QList< QWeakPointer< Tomahawk::Resolver > > resolvedBy() const { return m_resolvers; } @@ -94,7 +94,7 @@ public: int duration() const { return m_duration; } void setResolveFinished( bool resolved ) { m_resolveFinished = resolved; } - void setPlayedBy( const Tomahawk::source_ptr& source ) { m_playedBy = source; } + void setPlayedBy( const Tomahawk::source_ptr& source, unsigned int playtime ); signals: void resultsAdded( const QList& ); @@ -139,7 +139,7 @@ private: int m_duration; QString m_resultHint; - Tomahawk::source_ptr m_playedBy; + QPair< Tomahawk::source_ptr, unsigned int > m_playedBy; QList< QWeakPointer< Tomahawk::Resolver > > m_resolvers; mutable QMutex m_mutex; diff --git a/src/libtomahawk/result.cpp b/src/libtomahawk/result.cpp index 9bfae38f7..c9ceffeb1 100644 --- a/src/libtomahawk/result.cpp +++ b/src/libtomahawk/result.cpp @@ -20,10 +20,12 @@ #include "album.h" #include "collection.h" +#include "database/database.h" #include "database/databasecommand_resolve.h" #include "database/databasecommand_alltracks.h" #include "database/databasecommand_addfiles.h" #include "database/databasecommand_loadfile.h" +#include "database/databasecommand_loadsocialactions.h" using namespace Tomahawk; @@ -89,7 +91,9 @@ Result::score() const RID Result::id() const { - Q_ASSERT( !m_rid.isEmpty() ); + if ( m_rid.isEmpty() ) + m_rid = uuid(); + return m_rid; } @@ -166,6 +170,53 @@ Result::onOffline() } +void +Result::loadSocialActions() +{ + DatabaseCommand_LoadSocialActions* cmd = new DatabaseCommand_LoadSocialActions( this ); + connect( cmd, SIGNAL( finished() ), SLOT( onSocialActionsLoaded() )); + Database::instance()->enqueue( QSharedPointer(cmd) ); +} + + +void Result::onSocialActionsLoaded() +{ + parseSocialActions(); +} + + +void +Result::setAllSocialActions(QList< SocialAction > socialActions) +{ + m_allSocialActions = socialActions; +} + + +QList< SocialAction > +Result::allSocialActions() +{ + return m_allSocialActions; +} + + +void +Result::parseSocialActions() +{ + QListIterator< Tomahawk::SocialAction > it( m_allSocialActions ); + unsigned int highestTimestamp = 0; + + while ( it.hasNext() ) + { + Tomahawk::SocialAction socialAction; + socialAction = it.next(); + if ( socialAction.timestamp.toUInt() > highestTimestamp ) + { + m_currentSocialActions[ socialAction.action.toString() ] = socialAction.value.toBool(); + } + } +} + + void Result::setArtist( const Tomahawk::artist_ptr& artist ) { diff --git a/src/libtomahawk/result.h b/src/libtomahawk/result.h index ed4e5cbfd..928319720 100644 --- a/src/libtomahawk/result.h +++ b/src/libtomahawk/result.h @@ -36,6 +36,16 @@ class DatabaseCommand_LoadFile; namespace Tomahawk { + +struct SocialAction +{ + QVariant action; + QVariant value; + QVariant timestamp; + QVariant source; +}; + + class DLLEXPORT Result : public QObject { Q_OBJECT @@ -71,6 +81,8 @@ public: unsigned int albumpos() const { return m_albumpos; } unsigned int modificationTime() const { return m_modtime; } int year() const { return m_year; } + bool loved() { return m_currentSocialActions[ "Love" ].toBool(); } + QList< Tomahawk::SocialAction > allSocialActions(); void setScore( float score ) { m_score = score; } void setId( unsigned int id ) { m_id = id; } @@ -88,12 +100,18 @@ public: void setAlbumPos( unsigned int albumpos ) { m_albumpos = albumpos; } void setModificationTime( unsigned int modtime ) { m_modtime = modtime; } void setYear( unsigned int year ) { m_year = year; } - + void setLoved( bool loved ) { m_currentSocialActions[ "Loved" ] = loved; } + void setAllSocialActions( QList< Tomahawk::SocialAction > socialActions ); + + void loadSocialActions(); QVariantMap attributes() const { return m_attributes; } void setAttributes( const QVariantMap& map ) { m_attributes = map; updateAttributes(); } unsigned int dbid() const { return m_id; } +public slots: + void onSocialActionsLoaded(); + signals: // emitted when the collection this result comes from is going offline/online: void statusChanged(); @@ -104,6 +122,7 @@ private slots: private: void updateAttributes(); + void parseSocialActions(); mutable RID m_rid; collection_ptr m_collection; @@ -126,6 +145,9 @@ private: QVariantMap m_attributes; unsigned int m_id; + + QHash< QString, QVariant > m_currentSocialActions; + QList< SocialAction > m_allSocialActions; }; }; //ns diff --git a/src/libtomahawk/sip/SipPlugin.h b/src/libtomahawk/sip/SipPlugin.h index 002f9b242..ae8ce58b7 100644 --- a/src/libtomahawk/sip/SipPlugin.h +++ b/src/libtomahawk/sip/SipPlugin.h @@ -94,8 +94,6 @@ public slots: // so plugins can clean up after themselves virtual void deletePlugin(); - virtual void reset() {} - signals: void error( int, const QString& ); void stateChanged( SipPlugin::ConnectionState state ); @@ -115,6 +113,8 @@ signals: void addMenu( QMenu* menu ); void removeMenu( QMenu* menu ); + + void dataError( bool ); private slots: void onError( int, const QString& ); diff --git a/src/libtomahawk/sourceplaylistinterface.cpp b/src/libtomahawk/sourceplaylistinterface.cpp index c8023baf2..75c67839b 100644 --- a/src/libtomahawk/sourceplaylistinterface.cpp +++ b/src/libtomahawk/sourceplaylistinterface.cpp @@ -70,7 +70,7 @@ SourcePlaylistInterface::hasNextItem() { if ( m_source.isNull() || m_source->currentTrack().isNull() || m_source->currentTrack()->results().isEmpty() ) return false; - + return m_gotNextItem; } @@ -96,7 +96,6 @@ void SourcePlaylistInterface::onSourcePlaybackStarted( const Tomahawk::query_ptr& query ) { qDebug() << Q_FUNC_INFO; - connect( query.data(), SIGNAL( resultsAdded( const QList& ) ), SLOT( resolveResultsAdded( const QList& ) ) ); connect( query.data(), SIGNAL( resolvingFinished( bool ) ), SLOT( resolvingFinished( bool ) ) ); Pipeline::instance()->resolve( query, true ); m_gotNextItem = true; @@ -104,17 +103,9 @@ SourcePlaylistInterface::onSourcePlaybackStarted( const Tomahawk::query_ptr& que void -SourcePlaylistInterface::resolveResultsAdded( const QList& results ) const -{ - qDebug() << Q_FUNC_INFO; - foreach ( Tomahawk::result_ptr ptr, results ) - { - qDebug() << "Found result: " << ptr->track(); - } -} - -void -SourcePlaylistInterface::resolvingFinished( bool hasResults ) const +SourcePlaylistInterface::resolvingFinished( bool hasResults ) { qDebug() << Q_FUNC_INFO << " and has results? : " << (hasResults ? "true" : "false"); + if ( hasResults ) + emit nextTrackReady(); } \ No newline at end of file diff --git a/src/libtomahawk/sourceplaylistinterface.h b/src/libtomahawk/sourceplaylistinterface.h index 56d8bebfa..097d1a4be 100644 --- a/src/libtomahawk/sourceplaylistinterface.h +++ b/src/libtomahawk/sourceplaylistinterface.h @@ -70,11 +70,11 @@ signals: void shuffleModeChanged( bool enabled ); void trackCountChanged( unsigned int tracks ); void sourceTrackCountChanged( unsigned int tracks ); - + void nextTrackReady(); + private slots: void onSourcePlaybackStarted( const Tomahawk::query_ptr& query ); - void resolveResultsAdded( const QList& results ) const; - void resolvingFinished( bool hasResults ) const; + void resolvingFinished( bool hasResults ); private: Tomahawk::source_ptr m_source; diff --git a/src/libtomahawk/tomahawksettings.cpp b/src/libtomahawk/tomahawksettings.cpp index bc8a5d824..f92ee98c1 100644 --- a/src/libtomahawk/tomahawksettings.cpp +++ b/src/libtomahawk/tomahawksettings.cpp @@ -420,6 +420,20 @@ TomahawkSettings::setMainWindowSplitterState( const QByteArray& state ) } +bool +TomahawkSettings::verboseNotifications() const +{ + return value( "ui/notifications/verbose", false ).toBool(); +} + + +void +TomahawkSettings::setVerboseNotifications( bool notifications ) +{ + setValue( "ui/notifications/verbose", notifications ); +} + + QByteArray TomahawkSettings::playlistColumnSizes( const QString& playlistid ) const { diff --git a/src/libtomahawk/tomahawksettings.h b/src/libtomahawk/tomahawksettings.h index aaedcbb08..1ee60c975 100644 --- a/src/libtomahawk/tomahawksettings.h +++ b/src/libtomahawk/tomahawksettings.h @@ -66,6 +66,9 @@ public: QByteArray mainWindowSplitterState() const; void setMainWindowSplitterState( const QByteArray& state ); + bool verboseNotifications() const; + void setVerboseNotifications( bool notifications ); + /// Playlist stuff QByteArray playlistColumnSizes( const QString& playlistid ) const; void setPlaylistColumnSizes( const QString& playlistid, const QByteArray& state ); diff --git a/src/libtomahawk/utils/elidedlabel.cpp b/src/libtomahawk/utils/elidedlabel.cpp index 9b4343383..f43502175 100644 --- a/src/libtomahawk/utils/elidedlabel.cpp +++ b/src/libtomahawk/utils/elidedlabel.cpp @@ -1,5 +1,5 @@ /* === This file is part of Tomahawk Player - === - * + * * Copyright 2010-2011, Christian Muehlhaeuser * * Tomahawk is free software: you can redistribute it and/or modify @@ -98,19 +98,29 @@ ElidedLabel::setElideMode( Qt::TextElideMode mode ) } } -void + +void ElidedLabel::setMargin( int margin ) { m_margin = margin; } -int + +int ElidedLabel::margin() const { return m_margin; } +void +ElidedLabel::setFont( const QFont& font ) +{ + QWidget::setFont( font ); + updateLabel(); +} + + void ElidedLabel::init( const QString& txt ) { @@ -134,8 +144,7 @@ QSize ElidedLabel::sizeHint() const { const QFontMetrics& fm = fontMetrics(); - QSize size( fm.width( m_text ), fm.height() ); - return size; + return QSize( fm.width( m_text ) + m_margin * 2, fm.height() + m_margin * 2 ); } @@ -164,7 +173,7 @@ ElidedLabel::paintEvent( QPaintEvent* event ) QPainter p( this ); QRect r = contentsRect(); 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 ); } diff --git a/src/libtomahawk/utils/elidedlabel.h b/src/libtomahawk/utils/elidedlabel.h index 2f45e1c97..00198d888 100644 --- a/src/libtomahawk/utils/elidedlabel.h +++ b/src/libtomahawk/utils/elidedlabel.h @@ -1,5 +1,5 @@ /* === This file is part of Tomahawk Player - === - * + * * Copyright 2010-2011, Christian Muehlhaeuser * * Tomahawk is free software: you can redistribute it and/or modify @@ -44,9 +44,11 @@ public: Qt::TextElideMode elideMode() const; void setElideMode( Qt::TextElideMode mode ); + void setFont( const QFont& font ); + void setMargin( int margin ); int margin() const; - + virtual QSize sizeHint() const; virtual QSize minimumSizeHint() const; @@ -65,7 +67,7 @@ protected: virtual void mousePressEvent( QMouseEvent* event ); virtual void mouseReleaseEvent( QMouseEvent* event ); virtual void paintEvent( QPaintEvent* event ); - + private: QTime m_time; QString m_text; diff --git a/src/libtomahawk/utils/tomahawkutils.cpp b/src/libtomahawk/utils/tomahawkutils.cpp index 97ce2e572..6cf01c787 100644 --- a/src/libtomahawk/utils/tomahawkutils.cpp +++ b/src/libtomahawk/utils/tomahawkutils.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -162,6 +163,7 @@ timeToString( int seconds ) .arg( secs < 10 ? "0" + QString::number( secs ) : QString::number( secs ) ); } + QString ageToString( const QDateTime& time ) { @@ -177,52 +179,50 @@ ageToString( const QDateTime& time ) if ( years ) { if ( years > 1 ) - return QString( "%1 years" ).arg( years ); + return QObject::tr( "%1 years" ).arg( years ); else - return QString( "%1 year" ).arg( years ); + return QObject::tr( "%1 year" ).arg( years ); } if ( months ) { if ( months > 1 ) - return QString( "%1 months" ).arg( months ); + return QObject::tr( "%1 months" ).arg( months ); else - return QString( "%1 month" ).arg( months ); + return QObject::tr( "%1 month" ).arg( months ); } if ( weeks ) { if ( weeks > 1 ) - return QString( "%1 weeks" ).arg( weeks ); + return QObject::tr( "%1 weeks" ).arg( weeks ); else - return QString( "%1 week" ).arg( weeks ); + return QObject::tr( "%1 week" ).arg( weeks ); } if ( days ) { if ( days > 1 ) - return QString( "%1 days" ).arg( days ); + return QObject::tr( "%1 days" ).arg( days ); else - return QString( "%1 day" ).arg( days ); + return QObject::tr( "%1 day" ).arg( days ); } if ( hours ) { if ( hours > 1 ) - return QString( "%1 hours" ).arg( hours ); + return QObject::tr( "%1 hours" ).arg( hours ); else - return QString( "%1 hour" ).arg( hours ); + return QObject::tr( "%1 hour" ).arg( hours ); } if ( mins ) { if ( mins > 1 ) - return QString( "%1 minutes" ).arg( mins ); - else - return QString( "%1 minute" ).arg( mins ); + return QObject::tr( "%1 minutes" ).arg( mins ); } - return QString(); + return QObject::tr( "1 minute" ); } @@ -339,6 +339,22 @@ createDragPixmap( int itemCount ) } +void +unmarginLayout( QLayout* layout ) +{ + layout->setContentsMargins( 0, 0, 0, 0 ); + layout->setMargin( 0 ); + layout->setSpacing( 0 ); + + for ( int i = 0; i < layout->count(); i++ ) + { + QLayout* childLayout = layout->itemAt( i )->layout(); + if ( childLayout ) + unmarginLayout( childLayout ); + } +} + + QWeakPointer< QNetworkAccessManager > s_nam; NetworkProxyFactory* s_proxyFactory = 0; @@ -397,10 +413,10 @@ NetworkProxyFactory* proxyFactory() { // Don't use this anywhere! It's provided here for access reasons, but QNAM deletes this at will! - + if ( !s_proxyFactory ) s_proxyFactory = new NetworkProxyFactory(); - + return s_proxyFactory; } diff --git a/src/libtomahawk/utils/tomahawkutils.h b/src/libtomahawk/utils/tomahawkutils.h index e4d461e99..581252bf7 100644 --- a/src/libtomahawk/utils/tomahawkutils.h +++ b/src/libtomahawk/utils/tomahawkutils.h @@ -24,7 +24,6 @@ #include #include #include -#include #define RESPATH ":/data/" @@ -33,8 +32,8 @@ class QDir; class QDateTime; class QString; class QPixmap; +class QLayout; class QNetworkAccessManager; -class QNetworkProxy; namespace TomahawkUtils { @@ -72,6 +71,8 @@ namespace TomahawkUtils DLLEXPORT QColor alphaBlend( const QColor& colorFrom, const QColor& colorTo, float opacity ); DLLEXPORT QPixmap createDragPixmap( int itemCount = 1 ); + DLLEXPORT void unmarginLayout( QLayout* layout ); + DLLEXPORT NetworkProxyFactory* proxyFactory(); DLLEXPORT QNetworkAccessManager* nam(); diff --git a/src/libtomahawk/viewmanager.cpp b/src/libtomahawk/viewmanager.cpp index 1e14db964..d558167c0 100644 --- a/src/libtomahawk/viewmanager.cpp +++ b/src/libtomahawk/viewmanager.cpp @@ -25,8 +25,6 @@ #include "utils/animatedsplitter.h" #include "infobar/infobar.h" #include "topbar/topbar.h" -#include "widgets/infowidgets/sourceinfowidget.h" -#include "widgets/welcomewidget.h" #include "treemodel.h" #include "collectionflatmodel.h" @@ -47,6 +45,7 @@ #include "widgets/welcomewidget.h" #include "widgets/infowidgets/sourceinfowidget.h" +#include "widgets/infowidgets/ArtistInfoWidget.h" #include "widgets/newplaylistwidget.h" #define FILTER_TIMEOUT 280 @@ -217,28 +216,19 @@ ViewManager::show( const Tomahawk::dynplaylist_ptr& playlist ) Tomahawk::ViewPage* ViewManager::show( const Tomahawk::artist_ptr& artist ) { - PlaylistView* view; - + ArtistInfoWidget* swidget; if ( !m_artistViews.contains( artist ) ) { - view = new PlaylistView(); - PlaylistModel* model = new PlaylistModel(); - view->setPlaylistModel( model ); - view->setFrameShape( QFrame::NoFrame ); - view->setAttribute( Qt::WA_MacShowFocusRect, 0 ); - model->append( artist ); - - m_artistViews.insert( artist, view ); + swidget = new ArtistInfoWidget( artist ); + m_artistViews.insert( artist, swidget ); } else { - view = m_artistViews.value( artist ); + swidget = m_artistViews.value( artist ); } - setPage( view ); - emit numSourcesChanged( 1 ); - - return view; + setPage( swidget ); + return swidget; } @@ -367,8 +357,6 @@ ViewManager::show( const Tomahawk::source_ptr& source ) } setPage( swidget ); - emit numSourcesChanged( 1 ); - return swidget; } @@ -617,9 +605,15 @@ ViewManager::setPage( ViewPage* page, bool trackHistory ) if( obj->metaObject()->indexOfSignal( "descriptionChanged(QString)" ) > -1 ) connect( obj, SIGNAL( descriptionChanged( QString ) ), m_infobar, SLOT( setDescription( QString ) ), Qt::UniqueConnection ); + if( obj->metaObject()->indexOfSignal( "longDescriptionChanged(QString)" ) > -1 ) + connect( obj, SIGNAL( longDescriptionChanged( QString ) ), m_infobar, SLOT( setLongDescription( QString ) ), Qt::UniqueConnection ); + if( obj->metaObject()->indexOfSignal( "nameChanged(QString)" ) > -1 ) connect( obj, SIGNAL( nameChanged( QString ) ), m_infobar, SLOT( setCaption( QString ) ), Qt::UniqueConnection ); + if( obj->metaObject()->indexOfSignal( "pixmapChanged(QPixmap)" ) > -1 ) + connect( obj, SIGNAL( pixmapChanged( QPixmap ) ), m_infobar, SLOT( setPixmap( QPixmap ) ), Qt::UniqueConnection ); + if( obj->metaObject()->indexOfSignal( "destroyed(QWidget*)" ) > -1 ) connect( obj, SIGNAL( destroyed( QWidget* ) ), SLOT( onWidgetDestroyed( QWidget* ) ), Qt::UniqueConnection ); } @@ -724,6 +718,7 @@ ViewManager::updateView() m_infobar->setCaption( currentPage()->title() ); m_infobar->setDescription( currentPage()->description() ); + m_infobar->setLongDescription( currentPage()->longDescription() ); m_infobar->setPixmap( currentPage()->pixmap() ); // turn on shuffle/repeat mode for the new playlist view if specified in config diff --git a/src/libtomahawk/viewmanager.h b/src/libtomahawk/viewmanager.h index 20919534b..9982f671b 100644 --- a/src/libtomahawk/viewmanager.h +++ b/src/libtomahawk/viewmanager.h @@ -33,6 +33,7 @@ class AnimatedSplitter; class AlbumModel; class AlbumView; +class ArtistInfoWidget; class ArtistView; class CollectionModel; class CollectionFlatModel; @@ -190,7 +191,7 @@ private: 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::artist_ptr, ArtistInfoWidget* > m_artistViews; QHash< Tomahawk::album_ptr, PlaylistView* > m_albumViews; QHash< Tomahawk::playlist_ptr, PlaylistView* > m_playlistViews; QHash< Tomahawk::source_ptr, SourceInfoWidget* > m_sourceViews; diff --git a/src/libtomahawk/viewpage.h b/src/libtomahawk/viewpage.h index e17acf2b3..c36a12f8e 100644 --- a/src/libtomahawk/viewpage.h +++ b/src/libtomahawk/viewpage.h @@ -42,6 +42,7 @@ public: virtual QString title() const = 0; virtual QString description() const = 0; + virtual QString longDescription() const { return QString(); } virtual QPixmap pixmap() const { return QPixmap( RESPATH "icons/tomahawk-icon-128x128.png" ); } virtual bool showStatsBar() const { return true; } @@ -56,6 +57,8 @@ public: /** subclasses implementing ViewPage can emit the following signals: * nameChanged( const QString& ) * descriptionChanged( const QString& ) + * longDescriptionChanged( const QString& ) + * pixmapChanged( const QPixmap& ) * destroyed( QWidget* widget ); * * See DynamicWidget for an example diff --git a/src/libtomahawk/widgets/HeaderLabel.cpp b/src/libtomahawk/widgets/HeaderLabel.cpp new file mode 100644 index 000000000..208abed60 --- /dev/null +++ b/src/libtomahawk/widgets/HeaderLabel.cpp @@ -0,0 +1,86 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2010-2011, Christian Muehlhaeuser + * + * Tomahawk is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Tomahawk is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU 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 "HeaderLabel.h" + +#include +#include + +#define FONT_SIZE 16 + + +HeaderLabel::HeaderLabel( QWidget* parent ) + : QLabel( parent ) + , m_parent( parent ) +{ + QFont f( font() ); + f.setBold( true ); + f.setPointSize( 11 ); + +#ifdef Q_WS_MAC + f.setPointSize( f.pointSize() - 2 ); +#endif + + setFont( f ); + setFixedHeight( sizeHint().height() + 8 ); +} + + +HeaderLabel::~HeaderLabel() +{ +} + + +QSize +HeaderLabel::sizeHint() const +{ + return QLabel::sizeHint(); +} + + +void +HeaderLabel::paintEvent( QPaintEvent* event ) +{ + QPainter p( this ); + QRect r = contentsRect(); + +// p.setRenderHint( QPainter::Antialiasing ); + + QRect upperHalf( 0, 0, r.width(), r.height() / 2 ); + QRect lowerHalf( 0, upperHalf.height(), r.width(), r.height() ); + p.fillRect( upperHalf, QColor( 80, 80, 80 ) ); + p.fillRect( lowerHalf, QColor( 72, 72, 72 ) ); + + { + QColor lineColor( 100, 100, 100 ); + QLine line( 0, 0, r.width(), 0 ); + p.setPen( lineColor ); + p.drawLine( line ); + } + { + QColor lineColor( 30, 30, 30 ); + QLine line( 0, r.height() - 1, r.width(), r.height() - 1 ); + p.setPen( lineColor ); + p.drawLine( line ); + } + + QTextOption to( Qt::AlignVCenter ); + r.adjust( 8, 0, -8, 0 ); + p.setPen( Qt::white ); + p.drawText( r, text(), to ); +} diff --git a/src/libtomahawk/widgets/HeaderLabel.h b/src/libtomahawk/widgets/HeaderLabel.h new file mode 100644 index 000000000..3333dabf6 --- /dev/null +++ b/src/libtomahawk/widgets/HeaderLabel.h @@ -0,0 +1,47 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2010-2011, Christian Muehlhaeuser + * + * Tomahawk is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Tomahawk is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU 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 HEADERLABEL_H +#define HEADERLABEL_H + +#include + +#include "dllmacro.h" + +class DLLEXPORT HeaderLabel : public QLabel +{ +Q_OBJECT + +public: + HeaderLabel( QWidget* parent ); + ~HeaderLabel(); + + QSize minimumSizeHint() const { return sizeHint(); } + QSize sizeHint() const; + +public slots: + +protected: +// void changeEvent( QEvent* e ); + void paintEvent( QPaintEvent* event ); + +private: + QWidget* m_parent; +}; + +#endif // HEADERLABEL_H diff --git a/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp b/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp new file mode 100644 index 000000000..2b1e7bdd0 --- /dev/null +++ b/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp @@ -0,0 +1,219 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2010-2011, Christian Muehlhaeuser + * + * Tomahawk is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Tomahawk is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU 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 "ArtistInfoWidget.h" +#include "ui_ArtistInfoWidget.h" + +#include "utils/tomahawkutils.h" + +#include "viewmanager.h" +#include "playlist/treemodel.h" +#include "playlist/playlistmodel.h" + +#include "database/databasecommand_alltracks.h" +#include "database/databasecommand_allalbums.h" + +#include "widgets/overlaywidget.h" + +static QString s_aiInfoIdentifier = QString( "ArtistInfoWidget" ); + +using namespace Tomahawk; + + +ArtistInfoWidget::ArtistInfoWidget( const Tomahawk::artist_ptr& artist, QWidget* parent ) + : QWidget( parent ) + , ui( new Ui::ArtistInfoWidget ) + , m_artist( artist ) +{ + ui->setupUi( this ); + + ui->albums->setFrameShape( QFrame::NoFrame ); + ui->albums->setAttribute( Qt::WA_MacShowFocusRect, 0 ); + ui->relatedArtists->setFrameShape( QFrame::NoFrame ); + ui->relatedArtists->setAttribute( Qt::WA_MacShowFocusRect, 0 ); + ui->topHits->setFrameShape( QFrame::NoFrame ); + ui->topHits->setAttribute( Qt::WA_MacShowFocusRect, 0 ); + + TomahawkUtils::unmarginLayout( layout() ); + + m_albumsModel = new TreeModel( ui->albums ); + ui->albums->setTreeModel( m_albumsModel ); + + m_relatedModel = new TreeModel( ui->relatedArtists ); + ui->relatedArtists->setTreeModel( m_relatedModel ); + + m_topHitsModel = new PlaylistModel( ui->topHits ); + m_topHitsModel->setStyle( TrackModel::Short ); + ui->topHits->setTrackModel( m_topHitsModel ); + + m_pixmap = QPixmap( RESPATH "images/no-album-art-placeholder.png" ).scaledToWidth( 48, Qt::SmoothTransformation ); + + connect( Tomahawk::InfoSystem::InfoSystem::instance(), + SIGNAL( info( Tomahawk::InfoSystem::InfoRequestData, QVariant ) ), + SLOT( infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData, QVariant ) ) ); + + connect( Tomahawk::InfoSystem::InfoSystem::instance(), SIGNAL( finished( QString ) ), SLOT( infoSystemFinished( QString ) ) ); + + load( artist ); +} + + +ArtistInfoWidget::~ArtistInfoWidget() +{ + delete ui; +} + + +void +ArtistInfoWidget::load( const artist_ptr& artist ) +{ + m_title = artist->name(); + m_albumsModel->addAlbums( artist, QModelIndex() ); + + Tomahawk::InfoSystem::InfoCriteriaHash artistInfo; + artistInfo["artist"] = artist->name(); + + Tomahawk::InfoSystem::InfoRequestData requestData; + requestData.caller = s_aiInfoIdentifier; + requestData.customData = QVariantMap(); + + requestData.input = artist->name(); + requestData.type = Tomahawk::InfoSystem::InfoArtistBiography; + Tomahawk::InfoSystem::InfoSystem::instance()->getInfo( requestData ); + + requestData.input = QVariant::fromValue< Tomahawk::InfoSystem::InfoCriteriaHash >( artistInfo ); + + requestData.type = Tomahawk::InfoSystem::InfoArtistImages; + Tomahawk::InfoSystem::InfoSystem::instance()->getInfo( requestData ); + + requestData.type = Tomahawk::InfoSystem::InfoArtistSimilars; + Tomahawk::InfoSystem::InfoSystem::instance()->getInfo( requestData ); + + requestData.type = Tomahawk::InfoSystem::InfoArtistSongs; + Tomahawk::InfoSystem::InfoSystem::instance()->getInfo( requestData ); +} + + +void +ArtistInfoWidget::infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData requestData, QVariant output ) +{ + if ( requestData.caller != s_aiInfoIdentifier ) + { +// qDebug() << "Info of wrong type or not with our identifier"; + return; + } + qDebug() << Q_FUNC_INFO << requestData.caller << requestData.type << s_aiInfoIdentifier; + + InfoSystem::InfoCriteriaHash trackInfo; + trackInfo = requestData.input.value< InfoSystem::InfoCriteriaHash >(); + + if ( output.canConvert< QVariantMap >() ) + { + if ( trackInfo["artist"] != m_artist->name() ) + { + qDebug() << "Returned info was for:" << trackInfo["artist"] << "- was looking for:" << m_artist->name(); + return; + } + } + + QVariantMap returnedData = output.value< QVariantMap >(); + switch ( requestData.type ) + { + case InfoSystem::InfoArtistBiography: + { + InfoSystem::InfoGenericMap bmap = output.value< Tomahawk::InfoSystem::InfoGenericMap >(); + + foreach ( const QString& source, bmap.keys() ) + { + if ( m_longDescription.isEmpty() || source == "last.fm" ) + m_longDescription = bmap[source]["text"]; + } + emit longDescriptionChanged( m_longDescription ); + break; + } + + case InfoSystem::InfoArtistSongs: + { + const QStringList tracks = returnedData["tracks"].toStringList(); + + int i = 0; + foreach ( const QString& track, tracks ) + { + query_ptr query = Query::get( m_artist->name(), track, QString(), uuid() ); + m_topHitsModel->append( query ); + + if ( ++i == 15 ) + break; + } + break; + } + + case InfoSystem::InfoArtistImages: + { + const QByteArray ba = returnedData["imgbytes"].toByteArray(); + if ( ba.length() ) + { + QPixmap pm; + pm.loadFromData( ba ); + + if ( !pm.isNull() ) + m_pixmap = pm.scaledToHeight( 48, Qt::SmoothTransformation ); + + emit pixmapChanged( m_pixmap ); + } + break; + } + + case InfoSystem::InfoArtistSimilars: + { + const QStringList artists = returnedData["artists"].toStringList(); + foreach ( const QString& artist, artists ) + { + m_relatedModel->addArtists( Artist::get( artist ) ); + } + break; + } + + default: + return; + } +} + + +void +ArtistInfoWidget::infoSystemFinished( QString target ) +{ + Q_UNUSED( target ); + qDebug() << Q_FUNC_INFO; +} + + +void +ArtistInfoWidget::changeEvent( QEvent* e ) +{ + QWidget::changeEvent( e ); + switch ( e->type() ) + { + case QEvent::LanguageChange: + ui->retranslateUi( this ); + break; + + default: + break; + } +} diff --git a/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.h b/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.h new file mode 100644 index 000000000..d8c535424 --- /dev/null +++ b/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.h @@ -0,0 +1,107 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2010-2011, Christian Muehlhaeuser + * + * Tomahawk is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Tomahawk is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Tomahawk. If not, see . + */ + +/** + * \class ArtistInfoWidget + * \brief A ViewPage class, which displays top-hits, related artists and albums. + * + * This Tomahawk ViewPage displays top-hits, related artists and known albums + * for any given artist. It is our default ViewPage when showing an artist + * via ViewManager. + * + */ + +#ifndef ARTISTINFOWIDGET_H +#define ARTISTINFOWIDGET_H + +#include + +#include "artist.h" +#include "result.h" +#include "playlistinterface.h" +#include "viewpage.h" +#include "infosystem/infosystem.h" + +#include "dllmacro.h" + +class PlaylistModel; +class TreeModel; + +namespace Ui +{ + class ArtistInfoWidget; +} + +class DLLEXPORT ArtistInfoWidget : public QWidget, public Tomahawk::ViewPage +{ +Q_OBJECT + +public: + ArtistInfoWidget( const Tomahawk::artist_ptr& artist, QWidget* parent = 0 ); + ~ArtistInfoWidget(); + + /** \brief Load information for a given artist. + * \param artist The artist that you want to load information for. + * + * Calling this method will make ArtistInfoWidget load information about + * an artist's top hits, related artists and all available albums. It is + * automatically called by the constructor, but you can use it to load + * another artist's information at any point. + */ + void load( const Tomahawk::artist_ptr& artist ); + + virtual QWidget* widget() { return this; } + virtual Tomahawk::PlaylistInterface* playlistInterface() const { return 0; } + + virtual QString title() const { return m_title; } + virtual QString description() const { return m_description; } + virtual QString longDescription() const { return m_longDescription; } + virtual QPixmap pixmap() const { if ( m_pixmap.isNull() ) return Tomahawk::ViewPage::pixmap(); else return m_pixmap; } + + virtual bool showStatsBar() const { return false; } + + virtual bool jumpToCurrentTrack() { return false; } + +signals: + void longDescriptionChanged( const QString& description ); + void descriptionChanged( const QString& description ); + void pixmapChanged( const QPixmap& pixmap ); + +protected: + void changeEvent( QEvent* e ); + +private slots: + void infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData requestData, QVariant output ); + void infoSystemFinished( QString target ); + +private: + Ui::ArtistInfoWidget *ui; + + Tomahawk::artist_ptr m_artist; + + TreeModel* m_relatedModel; + TreeModel* m_albumsModel; + PlaylistModel* m_topHitsModel; + + QString m_title; + QString m_description; + QString m_longDescription; + QPixmap m_pixmap; +}; + +#endif // SOURCEINFOWIDGET_H diff --git a/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.ui b/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.ui new file mode 100644 index 000000000..738ebffca --- /dev/null +++ b/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.ui @@ -0,0 +1,84 @@ + + + ArtistInfoWidget + + + + 0 + 0 + 902 + 696 + + + + Form + + + + + + + + + + Top Hits + + + + + + + + + + + + + + Related Artists + + + + + + + + + + + + + + + + Albums + + + + + + + + + + + + + HeaderLabel + QLabel +
widgets/HeaderLabel.h
+
+ + ArtistView + QTreeView +
artistview.h
+
+ + PlaylistView + QTreeView +
playlistview.h
+
+
+ + +
diff --git a/src/libtomahawk/widgets/infowidgets/sourceinfowidget.cpp b/src/libtomahawk/widgets/infowidgets/sourceinfowidget.cpp index 77912e92a..228284d19 100644 --- a/src/libtomahawk/widgets/infowidgets/sourceinfowidget.cpp +++ b/src/libtomahawk/widgets/infowidgets/sourceinfowidget.cpp @@ -38,6 +38,15 @@ SourceInfoWidget::SourceInfoWidget( const Tomahawk::source_ptr& source, QWidget* { ui->setupUi( this ); + ui->historyView->setFrameShape( QFrame::NoFrame ); + ui->historyView->setAttribute( Qt::WA_MacShowFocusRect, 0 ); + ui->recentAlbumView->setFrameShape( QFrame::NoFrame ); + ui->recentAlbumView->setAttribute( Qt::WA_MacShowFocusRect, 0 ); + ui->recentCollectionView->setFrameShape( QFrame::NoFrame ); + ui->recentCollectionView->setAttribute( Qt::WA_MacShowFocusRect, 0 ); + + TomahawkUtils::unmarginLayout( layout() ); + ui->historyView->overlay()->setEnabled( false ); m_recentCollectionModel = new CollectionFlatModel( ui->recentCollectionView ); diff --git a/src/libtomahawk/widgets/infowidgets/sourceinfowidget.ui b/src/libtomahawk/widgets/infowidgets/sourceinfowidget.ui index cfb416fcb..bde48ccbc 100644 --- a/src/libtomahawk/widgets/infowidgets/sourceinfowidget.ui +++ b/src/libtomahawk/widgets/infowidgets/sourceinfowidget.ui @@ -6,7 +6,7 @@ 0 0 - 985 + 831 460 @@ -14,14 +14,7 @@ - - - - 13 - 75 - true - - + Recent Albums @@ -56,14 +49,7 @@ - - - - 13 - 75 - true - - + Latest Additions to their Collection @@ -77,14 +63,7 @@ - - - - 13 - 75 - true - - + Recently played Tracks @@ -100,21 +79,26 @@ + + HeaderLabel + QLabel +
widgets/HeaderLabel.h
+
PlaylistView QTreeView
playlist/playlistview.h
- - CollectionView - QTreeView -
playlist/collectionview.h
-
AlbumView QListView
playlist/albumview.h
+ + CollectionView + QTreeView +
playlist/collectionview.h
+
diff --git a/src/libtomahawk/widgets/playlisttypeselectordlg.ui b/src/libtomahawk/widgets/playlisttypeselectordlg.ui index bc4cb8e32..3ae6c25df 100644 --- a/src/libtomahawk/widgets/playlisttypeselectordlg.ui +++ b/src/libtomahawk/widgets/playlisttypeselectordlg.ui @@ -6,7 +6,7 @@ 0 0 - 554 + 482 169 diff --git a/src/libtomahawk/widgets/welcomewidget.cpp b/src/libtomahawk/widgets/welcomewidget.cpp index fd40744fb..8ec1d4d1a 100644 --- a/src/libtomahawk/widgets/welcomewidget.cpp +++ b/src/libtomahawk/widgets/welcomewidget.cpp @@ -47,9 +47,27 @@ WelcomeWidget::WelcomeWidget( QWidget* parent ) { ui->setupUi( this ); + ui->splitter->setHandleWidth( 1 ); + ui->splitter_2->setHandleWidth( 1 ); + ui->splitter_2->setStretchFactor( 0, 2 ); + ui->splitter_2->setStretchFactor( 0, 1 ); + WelcomePlaylistModel* model = new WelcomePlaylistModel( this ); model->setMaxPlaylists( HISTORY_PLAYLIST_ITEMS ); + ui->playlistWidget->setFrameShape( QFrame::NoFrame ); + ui->playlistWidget->setAttribute( Qt::WA_MacShowFocusRect, 0 ); + ui->tracksView->setFrameShape( QFrame::NoFrame ); + ui->tracksView->setAttribute( Qt::WA_MacShowFocusRect, 0 ); + ui->additionsView->setFrameShape( QFrame::NoFrame ); + ui->additionsView->setAttribute( Qt::WA_MacShowFocusRect, 0 ); + + TomahawkUtils::unmarginLayout( layout() ); + TomahawkUtils::unmarginLayout( ui->verticalLayout->layout() ); + TomahawkUtils::unmarginLayout( ui->verticalLayout_2->layout() ); + TomahawkUtils::unmarginLayout( ui->verticalLayout_3->layout() ); + TomahawkUtils::unmarginLayout( ui->verticalLayout_4->layout() ); + ui->playlistWidget->setItemDelegate( new PlaylistDelegate() ); ui->playlistWidget->setModel( model ); ui->playlistWidget->overlay()->resize( 380, 86 ); diff --git a/src/libtomahawk/widgets/welcomewidget.ui b/src/libtomahawk/widgets/welcomewidget.ui index d55ca2ff7..489ff9fb2 100644 --- a/src/libtomahawk/widgets/welcomewidget.ui +++ b/src/libtomahawk/widgets/welcomewidget.ui @@ -6,106 +6,95 @@ 0 0 - 985 - 459 + 875 + 513 - + - + - Qt::Vertical + Qt::Horizontal - - - - - - - 14 - - - - Recent Additions - - - - - - - + + + Qt::Vertical + + + + + + + Recent Additions + + + + + + + + + + + + + + Newest Stations & Playlists + + + + + + + + - + - - - - 14 - - + - Newest Stations & Playlists + Recently Played Tracks - + + + + 320 + 0 + + + - - - - - - - 16777215 - 16777215 - - - - - 14 - - - - Recently Played Tracks - - - - - - - - 16777215 - 16777215 - - - - - -
+ + HeaderLabel + QLabel +
widgets/HeaderLabel.h
+
PlaylistView QTreeView
playlist/playlistview.h
- - PlaylistWidget - QListWidget -
widgets/welcomewidget.h
-
AlbumView QListView
playlist/albumview.h
+ + PlaylistWidget + QListWidget +
widgets/welcomewidget.h
+
diff --git a/src/musicscanner.cpp b/src/musicscanner.cpp index 3123f5ca4..c65966370 100644 --- a/src/musicscanner.cpp +++ b/src/musicscanner.cpp @@ -1,5 +1,5 @@ /* === This file is part of Tomahawk Player - === - * + * * Copyright 2010-2011, Christian Muehlhaeuser * * Tomahawk is free software: you can redistribute it and/or modify @@ -26,6 +26,7 @@ #include "database/database.h" #include "database/databasecommand_dirmtimes.h" #include "database/databasecommand_filemtimes.h" +#include "database/databasecommand_collectionstats.h" #include "database/databasecommand_addfiles.h" #include "database/databasecommand_deletefiles.h" @@ -59,7 +60,7 @@ DirLister::go() } m_newdirmtimes = m_dirmtimes; } - + foreach( QString dir, m_dirs ) { m_opcount++; @@ -76,12 +77,12 @@ DirLister::scanDir( QDir dir, int depth, DirLister::Mode mode ) m_opcount--; if ( m_opcount == 0 ) emit finished( m_newdirmtimes ); - + return; } - + //qDebug() << "DirLister::scanDir scanning: " << dir.canonicalPath() << " with mode " << mode; - + if( !dir.exists() ) { qDebug() << "Dir no longer exists, not scanning"; @@ -89,10 +90,10 @@ DirLister::scanDir( QDir dir, int depth, DirLister::Mode mode ) m_opcount--; if ( m_opcount == 0 ) emit finished( m_newdirmtimes ); - + return; } - + QFileInfoList dirs; const uint mtime = QFileInfo( dir.canonicalPath() ).lastModified().toUTC().toTime_t(); m_newdirmtimes.insert( dir.canonicalPath(), mtime ); @@ -120,7 +121,7 @@ DirLister::scanDir( QDir dir, int depth, DirLister::Mode mode ) } dir.setFilter( QDir::Dirs | QDir::Readable | QDir::NoDotAndDotDot ); dirs = dir.entryInfoList(); - + foreach( const QFileInfo& di, dirs ) { const QString canonical = di.canonicalFilePath(); @@ -132,7 +133,7 @@ DirLister::scanDir( QDir dir, int depth, DirLister::Mode mode ) QMetaObject::invokeMethod( this, "scanDir", Qt::QueuedConnection, Q_ARG( QDir, di.canonicalFilePath() ), Q_ARG( int, depth + 1 ), Q_ARG( DirLister::Mode, DirLister::Recursive ) ); } } - + m_opcount--; if ( m_opcount == 0 ) emit finished( m_newdirmtimes ); @@ -232,7 +233,7 @@ MusicScanner::scan() SLOT( commitBatch( QVariantList, QVariantList ) ), Qt::DirectConnection ); m_dirListerThreadController = new QThread( this ); - + m_dirLister = QWeakPointer< DirLister >( new DirLister( m_dirs, m_dirmtimes, m_mode, m_manualFull, m_recursive ) ); m_dirLister.data()->moveToThread( m_dirListerThreadController ); @@ -242,7 +243,7 @@ MusicScanner::scan() // queued, so will only fire after all dirs have been scanned: connect( m_dirLister.data(), SIGNAL( finished( QMap ) ), SLOT( listerFinished( QMap ) ), Qt::QueuedConnection ); - + m_dirListerThreadController->start(); QMetaObject::invokeMethod( m_dirLister.data(), "go" ); } @@ -277,12 +278,20 @@ MusicScanner::listerFinished( const QMap& newmtimes ) foreach( const QString& s, m_skippedFiles ) qDebug() << s; - // save mtimes, then quit thread - DatabaseCommand_DirMtimes* cmd = new DatabaseCommand_DirMtimes( newmtimes ); - connect( cmd, SIGNAL( finished() ), SIGNAL( finished() ) ); + { + DatabaseCommand_DirMtimes* cmd = new DatabaseCommand_DirMtimes( newmtimes ); + connect( cmd, SIGNAL( finished() ), SIGNAL( finished() ) ); + Database::instance()->enqueue( QSharedPointer(cmd) ); + } - Database::instance()->enqueue( QSharedPointer(cmd) ); + // re-calculate source stats + { + DatabaseCommand_CollectionStats* cmd = new DatabaseCommand_CollectionStats( SourceList::instance()->getLocal() ); + connect( cmd, SIGNAL( done( QVariantMap ) ), + SourceList::instance()->getLocal().data(), SLOT( setStats( QVariantMap ) ), Qt::QueuedConnection ); + Database::instance()->enqueue( QSharedPointer( cmd ) ); + } if ( !m_dirLister.isNull() ) { @@ -332,7 +341,7 @@ MusicScanner::scanFile( const QFileInfo& fi ) m_filesToDelete << m_filemtimes.value( "file://" + fi.canonicalFilePath() ).keys().first(); } - + QVariant m = readFile( fi ); if ( m.toMap().isEmpty() ) return; @@ -419,7 +428,7 @@ MusicScanner::readFile( const QFileInfo& fi ) m["albumpos"] = tag->track(); m["year"] = tag->year(); m["hash"] = ""; // TODO - + m_scanned++; return m; } diff --git a/src/resolvers/qtscriptresolver.cpp b/src/resolvers/qtscriptresolver.cpp index a069ab5cf..4ff0e0151 100644 --- a/src/resolvers/qtscriptresolver.cpp +++ b/src/resolvers/qtscriptresolver.cpp @@ -24,11 +24,87 @@ #include "sourcelist.h" #include "utils/tomahawkutils.h" +#include + +#define RESOLVER_LEGACY_CODE "var resolver = Tomahawk.resolver.instance ? Tomahawk.resolver.instance : TomahawkResolver;" + +QtScriptResolverHelper::QtScriptResolverHelper( const QString& scriptPath, QObject* parent ) + : QObject( parent ) +{ + m_scriptPath = scriptPath; +} + + +QByteArray +QtScriptResolverHelper::readRaw( const QString& fileName ) +{ + QString path = QFileInfo( m_scriptPath ).absolutePath(); + // remove directories + QString cleanedFileName = QFileInfo( fileName ).fileName(); + QString absoluteFilePath = path.append( "/" ).append( cleanedFileName ); + + QFile file( absoluteFilePath ); + if ( !file.exists() ) + { + Q_ASSERT(false); + return QByteArray(); + } + + file.open( QIODevice::ReadOnly ); + return file.readAll(); +} + + +QString +QtScriptResolverHelper::compress( const QString& data ) +{ + QByteArray comp = qCompress( data.toLatin1(), 9 ); + return comp.toBase64(); +} + + +QString +QtScriptResolverHelper::readCompressed(const QString& fileName) +{ + return compress( readRaw( fileName ) ); +} + + +QString +QtScriptResolverHelper::readBase64(const QString& fileName) +{ + return readRaw( fileName ).toBase64(); +} + + +QVariantMap +QtScriptResolverHelper::resolverData() +{ + QVariantMap resolver; + resolver["config"] = m_resolverConfig; + resolver["scriptPath"] = m_scriptPath; + return resolver; +} + + +void QtScriptResolverHelper::log(const QString& message) +{ + qDebug() << m_scriptPath << ":" << message; +} + + +void +QtScriptResolverHelper::setResolverConfig( QVariantMap config ) +{ + m_resolverConfig = config; +} + QtScriptResolver::QtScriptResolver( const QString& scriptPath ) : Tomahawk::ExternalResolver( scriptPath ) , m_ready( false ) , m_stopped( false ) + , m_resolverHelper( new QtScriptResolverHelper( scriptPath, this ) ) { qDebug() << Q_FUNC_INFO << scriptPath; @@ -42,14 +118,36 @@ QtScriptResolver::QtScriptResolver( const QString& scriptPath ) } m_engine->mainFrame()->setHtml( "" ); + + // add c++ part of tomahawk javascript library + m_engine->mainFrame()->addToJavaScriptWindowObject( "Tomahawk", m_resolverHelper ); + + // add rest of it + m_engine->setScriptPath( "tomahawk.js" ); + QFile jslib( RESPATH "js/tomahawk.js" ); + jslib.open( QIODevice::ReadOnly ); + m_engine->mainFrame()->evaluateJavaScript( jslib.readAll() ); + jslib.close(); + + // add resolver + m_engine->setScriptPath( scriptPath ); m_engine->mainFrame()->evaluateJavaScript( scriptFile.readAll() ); scriptFile.close(); - QVariantMap m = m_engine->mainFrame()->evaluateJavaScript( "getSettings();" ).toMap(); + // init resolver + resolverInit(); + + + QVariantMap m = resolverSettings(); m_name = m.value( "name" ).toString(); m_weight = m.value( "weight", 0 ).toUInt(); m_timeout = m.value( "timeout", 25 ).toUInt() * 1000; + // load config widget and apply settings + loadUi(); + QVariantMap config = resolverUserConfig(); + fillDataInWidgets( config ); + qDebug() << Q_FUNC_INFO << m_name << m_weight << m_timeout; m_ready = true; @@ -79,7 +177,7 @@ QtScriptResolver::resolve( const Tomahawk::query_ptr& query ) if ( !query->isFullTextQuery() ) { - eval = QString( "resolve( '%1', '%2', '%3', '%4' );" ) + eval = QString( RESOLVER_LEGACY_CODE "resolver.resolve( '%1', '%2', '%3', '%4' );" ) .arg( query->id().replace( "'", "\\'" ) ) .arg( query->artist().replace( "'", "\\'" ) ) .arg( query->album().replace( "'", "\\'" ) ) @@ -87,11 +185,14 @@ QtScriptResolver::resolve( const Tomahawk::query_ptr& query ) } else { - eval = QString( "resolve( '%1', '%2', '%3', '%4' );" ) + eval = QString( "if(Tomahawk.resolver.instance !== undefined) {" + " resolver.search( '%1', '%2' );" + "} else {" + " resolve( '%1', '', '', '%2' );" + "}" + ) .arg( query->id().replace( "'", "\\'" ) ) - .arg( query->fullTextQuery().replace( "'", "\\'" ) ) - .arg( QString() ) - .arg( QString() ); + .arg( query->fullTextQuery().replace( "'", "\\'" ) ); } QList< Tomahawk::result_ptr > results; @@ -152,3 +253,180 @@ QtScriptResolver::stop() m_stopped = true; emit finished(); } + + +void +QtScriptResolver::loadUi() +{ + qDebug() << Q_FUNC_INFO; + + QVariantMap m = m_engine->mainFrame()->evaluateJavaScript( RESOLVER_LEGACY_CODE "resolver.getConfigUi();" ).toMap(); + m_dataWidgets = m["fields"].toList(); + + + bool compressed = m.value( "compressed", "false" ).toBool(); + qDebug() << "Resolver has a preferences widget! compressed?" << compressed << m; + + QByteArray uiData = m[ "widget" ].toByteArray(); + + if( compressed ) + uiData = qUncompress( QByteArray::fromBase64( uiData ) ); + else + uiData = QByteArray::fromBase64( uiData ); + + QVariantMap images; + foreach(const QVariant& item, m[ "images" ].toList()) + { + QString key = item.toMap().keys().first(); + QVariant value = item.toMap().value(key); + images[key] = value; + } + + if( m.contains( "images" ) ) + uiData = fixDataImagePaths( uiData, compressed, images ); + + + + m_configWidget = QWeakPointer< QWidget >( widgetFromData( uiData, 0 ) ); + + emit changed(); +} + + +QWidget* +QtScriptResolver::configUI() const +{ + if( m_configWidget.isNull() ) + return 0; + else + return m_configWidget.data(); +} + + +void +QtScriptResolver::saveConfig() +{ + QVariant saveData = loadDataFromWidgets(); + qDebug() << Q_FUNC_INFO << saveData; + + m_resolverHelper->setResolverConfig( saveData.toMap() ); + m_engine->mainFrame()->evaluateJavaScript( RESOLVER_LEGACY_CODE "resolver.saveUserConfig();" ); +} + + +QWidget* +QtScriptResolver::findWidget(QWidget* widget, const QString& objectName) +{ + if( !widget || !widget->isWidgetType() ) + return 0; + + if( widget->objectName() == objectName ) + return widget; + + + foreach( QObject* child, widget->children() ) + { + QWidget* found = findWidget(qobject_cast< QWidget* >( child ), objectName); + + if( found ) + return found; + } + + return 0; +} + + +QVariant +QtScriptResolver::widgetData(QWidget* widget, const QString& property) +{ + for( int i = 0; i < widget->metaObject()->propertyCount(); i++ ) + { + if( widget->metaObject()->property( i ).name() == property ) + { + return widget->property( property.toLatin1() ); + } + } + + return QVariant(); +} + + +void +QtScriptResolver::setWidgetData(const QVariant& value, QWidget* widget, const QString& property) +{ + for( int i = 0; i < widget->metaObject()->propertyCount(); i++ ) + { + if( widget->metaObject()->property( i ).name() == property ) + { + widget->metaObject()->property( i ).write( widget, value); + return; + } + } +} + + +QVariantMap +QtScriptResolver::loadDataFromWidgets() +{ + QVariantMap saveData; + foreach(const QVariant& dataWidget, m_dataWidgets) + { + QVariantMap data = dataWidget.toMap(); + + QString widgetName = data["widget"].toString(); + QWidget* widget= findWidget( m_configWidget.data(), widgetName ); + + QString value = widgetData( widget, data["property"].toString() ).toString(); + + saveData[ data["name"].toString() ] = value; + } + + qDebug() << saveData; + + return saveData; +} + + +void +QtScriptResolver::fillDataInWidgets( const QVariantMap& data ) +{ + qDebug() << Q_FUNC_INFO << data; + foreach(const QVariant& dataWidget, m_dataWidgets) + { + QString widgetName = dataWidget.toMap()["widget"].toString(); + QWidget* widget= findWidget( m_configWidget.data(), widgetName ); + if( !widget ) + { + qDebug() << Q_FUNC_INFO << "widget specified in resolver was not found:" << widgetName; + Q_ASSERT(false); + return; + } + + QString propertyName = dataWidget.toMap()["property"].toString(); + QString name = dataWidget.toMap()["name"].toString(); + + setWidgetData( data[ name ], widget, propertyName ); + } +} + + +QVariantMap +QtScriptResolver::resolverSettings() +{ + return m_engine->mainFrame()->evaluateJavaScript( RESOLVER_LEGACY_CODE "if(resolver.settings) resolver.settings; else getSettings(); " ).toMap(); +} + + +QVariantMap +QtScriptResolver::resolverUserConfig() +{ + return m_engine->mainFrame()->evaluateJavaScript( RESOLVER_LEGACY_CODE "resolver.getUserConfig();" ).toMap(); +} + + +QVariantMap +QtScriptResolver::resolverInit() +{ + return m_engine->mainFrame()->evaluateJavaScript( RESOLVER_LEGACY_CODE "resolver.init();" ).toMap(); +} + diff --git a/src/resolvers/qtscriptresolver.h b/src/resolvers/qtscriptresolver.h index 51d457c1b..a988b1e9e 100644 --- a/src/resolvers/qtscriptresolver.h +++ b/src/resolvers/qtscriptresolver.h @@ -26,6 +26,7 @@ #include #include +#include #include #include #include @@ -33,6 +34,30 @@ class QtScriptResolver; +class QtScriptResolverHelper : public QObject +{ +Q_OBJECT + +public: + QtScriptResolverHelper( const QString& scriptPath, QObject* parent ); + void setResolverConfig( QVariantMap config ); + +public slots: + QByteArray readRaw( const QString& fileName ); + QString readBase64( const QString& fileName ); + QString readCompressed( const QString& fileName ); + + QString compress( const QString& data ); + QVariantMap resolverData(); + + void log( const QString& message); + bool fakeEnv() { return false; } + +private: + QString m_scriptPath; + QVariantMap m_resolverConfig; +}; + class ScriptEngine : public QWebPage { Q_OBJECT @@ -49,6 +74,11 @@ public: settings()->setAttribute( QWebSettings::LocalStorageDatabaseEnabled, true ); } + void setScriptPath( const QString& scriptPath ) + { + m_scriptPath = scriptPath; + } + public slots: bool shouldInterruptJavaScript() { @@ -57,10 +87,11 @@ public slots: protected: virtual void javaScriptConsoleMessage( const QString & message, int lineNumber, const QString & sourceID ) - { qDebug() << "JAVASCRIPT ERROR:" << message << lineNumber << sourceID; } + { qDebug() << "JAVASCRIPT:" << m_scriptPath << message << lineNumber << sourceID; Q_ASSERT(false);} private: QtScriptResolver* m_parent; + QString m_scriptPath; }; @@ -72,12 +103,13 @@ public: explicit QtScriptResolver( const QString& scriptPath ); virtual ~QtScriptResolver(); - virtual QString name() const { return m_name; } - virtual unsigned int weight() const { return m_weight; } - virtual unsigned int timeout() const { return m_timeout; } + virtual QString name() const { return m_name; } + virtual unsigned int weight() const { return m_weight; } + virtual unsigned int timeout() const { return m_timeout; } + + virtual QWidget* configUI() const; + virtual void saveConfig(); - 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(); @@ -86,12 +118,28 @@ signals: void finished(); private: + virtual void loadUi(); + QWidget* findWidget( QWidget* widget, const QString& objectName ); + void setWidgetData( const QVariant& value, QWidget* widget, const QString& property ); + QVariant widgetData( QWidget* widget, const QString& property ); + QVariantMap loadDataFromWidgets(); + void fillDataInWidgets( const QVariantMap& data ); + + // encapsulate javascript calls + QVariantMap resolverSettings(); + QVariantMap resolverUserConfig(); + QVariantMap resolverInit(); + ScriptEngine* m_engine; QString m_name; unsigned int m_weight, m_timeout; bool m_ready, m_stopped; + + QtScriptResolverHelper* m_resolverHelper; + QWeakPointer< QWidget > m_configWidget; + QList< QVariant > m_dataWidgets; }; #endif // QTSCRIPTRESOLVER_H diff --git a/src/scrobbler.cpp b/src/scrobbler.cpp index 82bf2855b..f79abe917 100644 --- a/src/scrobbler.cpp +++ b/src/scrobbler.cpp @@ -38,8 +38,8 @@ Scrobbler::Scrobbler( QObject* parent ) 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 ) ) ); + SIGNAL( info( Tomahawk::InfoSystem::InfoRequestData, QVariant ) ), + SLOT( infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData, QVariant ) ) ); connect( AudioEngine::instance(), SIGNAL( started( const Tomahawk::result_ptr& ) ), SLOT( trackStarted( const Tomahawk::result_ptr& ) ), Qt::QueuedConnection ); @@ -80,9 +80,14 @@ Scrobbler::trackStarted( const Tomahawk::result_ptr& track ) trackInfo["artist"] = track->artist()->name(); trackInfo["album"] = track->album()->name(); trackInfo["duration"] = QString::number( track->duration() ); - Tomahawk::InfoSystem::InfoSystem::instance()->pushInfo( - s_scInfoIdentifier, Tomahawk::InfoSystem::InfoSubmitNowPlaying, - QVariant::fromValue< Tomahawk::InfoSystem::InfoCriteriaHash >( trackInfo ) ); + + Tomahawk::InfoSystem::InfoRequestData requestData; + requestData.caller = s_scInfoIdentifier; + requestData.type = Tomahawk::InfoSystem::InfoArtistImages; + requestData.input = QVariant::fromValue< Tomahawk::InfoSystem::InfoCriteriaHash >( trackInfo ); + requestData.customData = QVariantMap(); + + Tomahawk::InfoSystem::InfoSystem::instance()->getInfo( requestData ); m_scrobblePoint = ScrobblePoint( track->duration() / 2 ); } @@ -135,13 +140,10 @@ Scrobbler::scrobble() void -Scrobbler::infoSystemInfo( QString caller, Tomahawk::InfoSystem::InfoType type, QVariant input, QVariant output, Tomahawk::InfoSystem::InfoCustomData customData ) +Scrobbler::infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData requestData, QVariant output ) { - Q_UNUSED( type ); - Q_UNUSED( input ); Q_UNUSED( output ); - Q_UNUSED( customData ); - if ( caller == s_scInfoIdentifier ) + if ( requestData.caller == s_scInfoIdentifier ) qDebug() << Q_FUNC_INFO; } diff --git a/src/scrobbler.h b/src/scrobbler.h index f2c484aa9..87c78290b 100644 --- a/src/scrobbler.h +++ b/src/scrobbler.h @@ -45,7 +45,7 @@ public slots: void trackStopped(); void engineTick( unsigned int secondsElapsed ); - void infoSystemInfo( QString caller, Tomahawk::InfoSystem::InfoType type, QVariant input, QVariant output, Tomahawk::InfoSystem::InfoCustomData customData ); + void infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData requestData, QVariant output ); void infoSystemFinished( QString target ); private: diff --git a/src/settingsdialog.cpp b/src/settingsdialog.cpp index 4ce763879..22e418e9a 100644 --- a/src/settingsdialog.cpp +++ b/src/settingsdialog.cpp @@ -598,11 +598,13 @@ SettingsDialog::sipFactoryClicked( SipPluginFactory* factory ) DelegateConfigWrapper* dialog = new DelegateConfigWrapper( p->configWidget(), QString("%1 Config" ).arg( p->friendlyName() ), this, Qt::Sheet ); dialog->setProperty( "sipplugin", QVariant::fromValue< QObject* >( p ) ); connect( dialog, SIGNAL( finished( int ) ), this, SLOT( sipCreateConfigClosed( int ) ) ); + connect( p, SIGNAL( datatError( bool ) ), dialog, SLOT( toggleOkButton( bool ) ) ); dialog->show(); #else DelegateConfigWrapper dialog( p->configWidget(), QString("%1 Config" ).arg( p->friendlyName() ), this ); QWeakPointer< DelegateConfigWrapper > watcher( &dialog ); + connect( p, SIGNAL( dataError( bool ) ), &dialog, SLOT( toggleOkButton( bool ) ) ); int ret = dialog.exec(); if( !watcher.isNull() && ret == QDialog::Accepted ) { // send changed config to resolver diff --git a/src/sip/jabber/CMakeLists.txt b/src/sip/jabber/CMakeLists.txt index a7cd6992e..98815219c 100644 --- a/src/sip/jabber/CMakeLists.txt +++ b/src/sip/jabber/CMakeLists.txt @@ -16,8 +16,6 @@ set( jabberSources set( jabberHeaders jabber.h - tomahawksipmessage.h - tomahawksipmessagefactory.h avatarmanager.h xmlconsole.h ) diff --git a/src/sip/jabber/configwidget.ui b/src/sip/jabber/configwidget.ui index 456f19255..cd5fd72b1 100644 --- a/src/sip/jabber/configwidget.ui +++ b/src/sip/jabber/configwidget.ui @@ -6,17 +6,14 @@ 0 0 - 433 - 260 + 451 + 335 Jabber Configuration - - 0 - @@ -185,6 +182,16 @@ + + + + An account with this name already exists! + + + Qt::AlignCenter + + +
diff --git a/src/sip/jabber/googlewrapper/CMakeLists.txt b/src/sip/jabber/googlewrapper/CMakeLists.txt index 3ebd653e0..6f9e310c7 100644 --- a/src/sip/jabber/googlewrapper/CMakeLists.txt +++ b/src/sip/jabber/googlewrapper/CMakeLists.txt @@ -3,8 +3,6 @@ set( googleHeaders ../jabber.h - ../tomahawksipmessage.h - ../tomahawksipmessagefactory.h ../avatarmanager.h ../xmlconsole.h googlewrapper.h ) diff --git a/src/sip/jabber/jabber.cpp b/src/sip/jabber/jabber.cpp index e1b239536..0575651ea 100644 --- a/src/sip/jabber/jabber.cpp +++ b/src/sip/jabber/jabber.cpp @@ -80,11 +80,13 @@ JabberPlugin::JabberPlugin( const QString& pluginId ) m_ui->jabberPassword->setText( readPassword() ); m_ui->jabberServer->setText( readServer() ); m_ui->jabberPort->setValue( readPort() ); + m_ui->jidExistsLabel->hide(); m_currentUsername = accountName(); m_currentServer = readServer(); m_currentPassword = readPassword(); m_currentPort = readPort(); + connect( m_ui->jabberUsername, SIGNAL( textChanged( QString ) ), SLOT( onCheckJidExists( QString ) ) ); // setup JID object Jreen::JID jid = Jreen::JID( accountName() ); @@ -999,6 +1001,33 @@ JabberPlugin::readServer() return TomahawkSettings::instance()->value( pluginId() + "/server" ).toString(); } + +void +JabberPlugin::onCheckJidExists( QString jid ) +{ + for ( int i=0; isipPlugins().count(); i++ ) + { + QString savedUsername = TomahawkSettings::instance()->value( + TomahawkSettings::instance()->sipPlugins().at( i ) + "/username" ).toString(); + QStringList splitUserName = TomahawkSettings::instance()->value( + TomahawkSettings::instance()->sipPlugins().at( i ) + "/username" ).toString().split("@"); + QString server = TomahawkSettings::instance()->value( + TomahawkSettings::instance()->sipPlugins().at( i ) + "/server" ).toString(); + + if ( ( savedUsername == jid || splitUserName.contains( jid ) ) && + server == m_ui->jabberServer->text() && !jid.trimmed().isEmpty() ) + { + m_ui->jidExistsLabel->show(); + // the already jid exists + emit dataError( true ); + return; + } + } + m_ui->jidExistsLabel->hide(); + emit dataError( false ); +} + + void JabberPlugin::saveConfig() { diff --git a/src/sip/jabber/jabber.h b/src/sip/jabber/jabber.h index f77d6bda3..d71bcdd5f 100644 --- a/src/sip/jabber/jabber.h +++ b/src/sip/jabber/jabber.h @@ -85,6 +85,7 @@ public: virtual void deletePlugin(); signals: + void dataError( bool exists ); void jidChanged( const QString& ); public slots: @@ -118,6 +119,7 @@ private slots: } void onNewIq( const Jreen::IQ &iq ); void onNewAvatar( const QString &jid ); + void onCheckJidExists( QString jid ); private: bool readXmlConsoleEnabled(); @@ -133,6 +135,8 @@ private: bool presenceMeansOnline( Jreen::Presence::Type p ); void handlePeerStatus( const Jreen::JID &jid, Jreen::Presence::Type presenceType ); + using SipPlugin::errorMessage; + QMenu* m_menu; XmlConsole* m_xmlConsole; QString m_currentUsername; diff --git a/src/sip/twitter/twitter.cpp b/src/sip/twitter/twitter.cpp index 4fb5befbe..83cd36169 100644 --- a/src/sip/twitter/twitter.cpp +++ b/src/sip/twitter/twitter.cpp @@ -67,6 +67,19 @@ TwitterPlugin::TwitterPlugin( const QString& pluginId ) , m_state( Disconnected ) { qDebug() << Q_FUNC_INFO; + + if ( !Database::instance() || Database::instance()->dbid() != twitterSavedDbid() ) + { + if ( !twitterSavedDbid().isEmpty() ) //remove eventually (post 0.2), here for migration purposes + { + setTwitterCachedDirectMessagesSinceId( 0 ); + setTwitterCachedFriendsSinceId( 0 ); + setTwitterCachedMentionsSinceId( 0 ); + setTwitterCachedPeers( QHash< QString, QVariant >() ); + } + setTwitterSavedDbid( Database::instance()->dbid() ); + } + m_checkTimer.setInterval( 150000 ); m_checkTimer.setSingleShot( false ); connect( &m_checkTimer, SIGNAL( timeout() ), SLOT( checkTimerFired() ) ); @@ -785,6 +798,21 @@ TwitterPlugin::checkSettings() connectPlugin( false ); } + +void +TwitterPlugin::setTwitterSavedDbid( const QString& dbid ) +{ + TomahawkSettings::instance()->setValue( pluginId() + "/saveddbid", dbid ); +} + + +QString +TwitterPlugin::twitterSavedDbid() const +{ + return TomahawkSettings::instance()->value( pluginId() + "/saveddbid", QString() ).toString(); +} + + QString TwitterPlugin::twitterScreenName() const { diff --git a/src/sip/twitter/twitter.h b/src/sip/twitter/twitter.h index 508cacceb..1846e0b8a 100644 --- a/src/sip/twitter/twitter.h +++ b/src/sip/twitter/twitter.h @@ -122,6 +122,8 @@ private: bool refreshTwitterAuth(); void parseGotTomahawk( const QRegExp ®ex, const QString &screenName, const QString &text ); // handle per-plugin config + QString twitterSavedDbid() const; + void setTwitterSavedDbid( const QString& dbid ); QString twitterScreenName() const; void setTwitterScreenName( const QString& screenName ); QString twitterOAuthToken() const; diff --git a/src/sourcetree/sourcetreeview.cpp b/src/sourcetree/sourcetreeview.cpp index 695633a29..8043624cd 100644 --- a/src/sourcetree/sourcetreeview.cpp +++ b/src/sourcetree/sourcetreeview.cpp @@ -127,7 +127,7 @@ SourceTreeView::setupMenus() m_playlistMenu.clear(); m_roPlaylistMenu.clear(); m_latchMenu.clear(); - + bool readonly = true; SourcesModel::RowType type = ( SourcesModel::RowType )model()->data( m_contextMenuIndex, SourcesModel::SourceTreeItemTypeRole ).toInt(); if ( type == SourcesModel::StaticPlaylist || type == SourcesModel::AutomaticPlaylist || type == SourcesModel::Station ) @@ -345,7 +345,7 @@ SourceTreeView::latchOn() CollectionItem* item = itemFromIndex< CollectionItem >( m_contextMenuIndex ); source_ptr source = item->source(); PlaylistInterface* pi = AudioEngine::instance()->playlist(); - + if ( pi && dynamic_cast< SourcePlaylistInterface* >( pi ) ) { SourcePlaylistInterface* sourcepi = dynamic_cast< SourcePlaylistInterface* >( pi ); @@ -414,7 +414,7 @@ SourceTreeView::onCustomContextMenu( const QPoint& pos ) else if ( model()->data( m_contextMenuIndex, SourcesModel::SourceTreeItemTypeRole ) == SourcesModel::Collection ) { CollectionItem* item = itemFromIndex< CollectionItem >( m_contextMenuIndex ); - if ( !item->source()->isLocal() ) + if ( !item->source().isNull() && !item->source()->isLocal() ) m_latchMenu.exec( mapToGlobal( pos ) ); } } diff --git a/src/tomahawkapp.cpp b/src/tomahawkapp.cpp index 10543e925..ff5cacc7b 100644 --- a/src/tomahawkapp.cpp +++ b/src/tomahawkapp.cpp @@ -204,6 +204,14 @@ TomahawkApp::init() new TomahawkSettings( this ); TomahawkSettings* s = TomahawkSettings::instance(); + #ifdef LIBLASTFM_FOUND + qDebug() << "Setting NAM."; + TomahawkUtils::setNam( lastfm::nam() ); + #else + qDebug() << "Setting NAM."; + TomahawkUtils::setNam( new QNetworkAccessManager() ); + #endif + TomahawkUtils::NetworkProxyFactory* proxyFactory = new TomahawkUtils::NetworkProxyFactory(); if( s->proxyType() != QNetworkProxy::NoProxy && @@ -212,20 +220,15 @@ TomahawkApp::init() qDebug() << "Setting proxy to saved values"; QNetworkProxy proxy( static_cast( s->proxyType() ), s->proxyHost(), s->proxyPort(), s->proxyUsername(), s->proxyPassword() ); proxyFactory->setProxy( proxy ); + //TODO: On Windows and Mac because liblastfm sets an application level proxy it may override our factory, so may need to explicitly do + //a QNetworkProxy::setApplicationProxy with our own proxy (but then also overriding our own factory :-( ) } if ( !s->proxyNoProxyHosts().isEmpty() ) proxyFactory->setNoProxyHosts( s->proxyNoProxyHosts().split( ',', QString::SkipEmptyParts ) ); - TomahawkUtils::NetworkProxyFactory::setApplicationProxyFactory( proxyFactory ); - -#ifdef LIBLASTFM_FOUND - qDebug() << "Setting NAM."; - TomahawkUtils::setNam( lastfm::nam() ); -#else - qDebug() << "Setting NAM."; - TomahawkUtils::setNam( new QNetworkAccessManager() ); -#endif - + + TomahawkUtils::setProxyFactory( proxyFactory ); + Echonest::Config::instance()->setAPIKey( "JRIHWEP6GPOER2QQ6" ); m_audioEngine = QWeakPointer( new AudioEngine ); @@ -415,9 +418,9 @@ TomahawkApp::registerMetaTypes() qRegisterMetaType< AudioErrorCode >("AudioErrorCode"); qRegisterMetaType< QMap< QString, QMap< QString, QString > > >( "Tomahawk::InfoSystem::InfoGenericMap" ); - qRegisterMetaType< QHash< QString, QVariant > >( "Tomahawk::InfoSystem::InfoCustomData" ); qRegisterMetaType< QHash< QString, QString > >( "Tomahawk::InfoSystem::InfoCriteriaHash" ); qRegisterMetaType< Tomahawk::InfoSystem::InfoType >( "Tomahawk::InfoSystem::InfoType" ); + qRegisterMetaType< Tomahawk::InfoSystem::InfoRequestData >( "Tomahawk::InfoSystem::InfoRequestData" ); qRegisterMetaType< QWeakPointer< Tomahawk::InfoSystem::InfoSystemCache > >( "QWeakPointer< Tomahawk::InfoSystem::InfoSystemCache >" ); qRegisterMetaType< DirLister::Mode >("DirLister::Mode"); diff --git a/src/xmppbot/xmppbot.cpp b/src/xmppbot/xmppbot.cpp index 404d64fd2..258384619 100644 --- a/src/xmppbot/xmppbot.cpp +++ b/src/xmppbot/xmppbot.cpp @@ -66,8 +66,8 @@ XMPPBot::XMPPBot(QObject *parent) SLOT(newTrackSlot(const Tomahawk::result_ptr &))); 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))); + SIGNAL(info(QString, Tomahawk::InfoSystem::InfoType, QVariant, QVariant, QVariantMap)), + SLOT(infoReturnedSlot(QString, Tomahawk::InfoSystem::InfoType, QVariant, QVariant, QVariantMap))); connect(InfoSystem::instance(), SIGNAL(finished(QString)), SLOT(infoFinishedSlot(QString))); @@ -201,7 +201,7 @@ void XMPPBot::handleMessage(const Message& msg, MessageSession* session) return; } - InfoMap infoMap; + InfoTypeMap infoMap; Q_FOREACH(QString token, tokens) { if (token == "biography") @@ -214,10 +214,10 @@ void XMPPBot::handleMessage(const Message& msg, MessageSession* session) infoMap[InfoArtistFamiliarity] = m_currTrack.data()->artist()->name(); if (token == "lyrics") { - InfoCustomData myhash; + QVariantMap myhash; myhash["trackName"] = QVariant::fromValue(m_currTrack.data()->track()); myhash["artistName"] = QVariant::fromValue(m_currTrack.data()->artist()->name()); - infoMap[InfoTrackLyrics] = QVariant::fromValue(myhash); + infoMap[InfoTrackLyrics] = QVariant::fromValue(myhash); } } @@ -234,12 +234,12 @@ void XMPPBot::handleMessage(const Message& msg, MessageSession* session) QString waitMsg("Please wait..."); Message retMsg(Message::Chat, JID(originatingJid.toStdString()), waitMsg.toStdString()); m_client.data()->send(retMsg); - Tomahawk::InfoSystem::InfoCustomData hash; + QVariantMap hash; hash["XMPPBotSendToJID"] = originatingJid; InfoSystem::instance()->getInfo(s_botInfoIdentifier, infoMap, hash); } -void XMPPBot::infoReturnedSlot(QString caller, Tomahawk::InfoSystem::InfoType type, QVariant input, QVariant output, Tomahawk::InfoSystem::InfoCustomData customData) +void XMPPBot::infoReturnedSlot(QString caller, Tomahawk::InfoSystem::InfoType type, QVariant input, QVariant output, QVariantMap customData) { qDebug() << Q_FUNC_INFO; @@ -363,13 +363,13 @@ void XMPPBot::infoReturnedSlot(QString caller, Tomahawk::InfoSystem::InfoType ty { qDebug() << "Lyrics requested"; if (!output.canConvert() || - !input.canConvert() + !input.canConvert() ) { qDebug() << "Variants failed to be valid"; break; } - InfoCustomData inHash = input.value(); + QVariantMap inHash = input.value(); QString artist = inHash["artistName"].toString(); QString track = inHash["trackName"].toString(); QString lyrics = output.toString(); diff --git a/src/xmppbot/xmppbot.h b/src/xmppbot/xmppbot.h index e22a564ae..9b39ae29f 100644 --- a/src/xmppbot/xmppbot.h +++ b/src/xmppbot/xmppbot.h @@ -66,7 +66,7 @@ public: 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::InfoCustomData customData); + virtual void infoReturnedSlot(QString caller, Tomahawk::InfoSystem::InfoType type, QVariant input, QVariant output, QVariantMap customData); virtual void infoFinishedSlot(QString caller); protected: @@ -87,7 +87,7 @@ private slots: private: QWeakPointer m_client; Tomahawk::result_ptr m_currTrack; - Tomahawk::InfoSystem::InfoMap m_currInfoMap; + Tomahawk::InfoSystem::InfoTypeMap m_currInfoMap; QString m_currReturnMessage; QString m_currReturnJid; }; diff --git a/thirdparty/liblastfm2/src/types/Artist.cpp b/thirdparty/liblastfm2/src/types/Artist.cpp index 242834e83..9af4303e1 100644 --- a/thirdparty/liblastfm2/src/types/Artist.cpp +++ b/thirdparty/liblastfm2/src/types/Artist.cpp @@ -116,6 +116,13 @@ Artist::getTopTags() const } +QNetworkReply* +Artist::getTopTracks() const +{ + return ws::get( params("getTopTracks") ); +} + + QNetworkReply* Artist::getSimilar() const { @@ -154,6 +161,25 @@ Artist::getSimilar( QNetworkReply* r ) } +QStringList /* static */ +Artist::getTopTracks( QNetworkReply* r ) +{ + QStringList tracks; + try + { + XmlQuery lfm = ws::parse(r); + foreach (XmlQuery e, lfm.children( "track" )) + { + tracks << e["name"].text(); + } + } + catch (ws::ParseError& e) + { + qWarning() << e.what(); + } + return tracks; +} + QList /* static */ Artist::list( QNetworkReply* r ) diff --git a/thirdparty/liblastfm2/src/types/Artist.h b/thirdparty/liblastfm2/src/types/Artist.h index 2bfd6d007..9eae719f5 100644 --- a/thirdparty/liblastfm2/src/types/Artist.h +++ b/thirdparty/liblastfm2/src/types/Artist.h @@ -86,6 +86,9 @@ namespace lastfm /** use Tag::list to get the tag list out of the finished reply */ QNetworkReply* getTags() const; QNetworkReply* getTopTags() const; + + QNetworkReply* getTopTracks() const; + static QStringList getTopTracks( QNetworkReply* ); /** Last.fm dictates that you may submit at most 10 of these */ QNetworkReply* addTags( const QStringList& ) const; diff --git a/thirdparty/qtweetlib/CMakeLists.txt b/thirdparty/qtweetlib/CMakeLists.txt index 8057ae945..952404ee2 100644 --- a/thirdparty/qtweetlib/CMakeLists.txt +++ b/thirdparty/qtweetlib/CMakeLists.txt @@ -94,17 +94,14 @@ set(TOMAHAWK_QTWEETLIB_HEADERS QTweetLib/src/oauth.h QTweetLib/src/oauthtwitter.h QTweetLib/src/qtweetfriendstimeline.h - QTweetLib/src/qtweetlib_global.h QTweetLib/src/qtweethometimeline.h QTweetLib/src/qtweetmentions.h QTweetLib/src/qtweetnetbase.h QTweetLib/src/qtweetretweetbyme.h QTweetLib/src/qtweetretweetsofme.h QTweetLib/src/qtweetretweettome.h - QTweetLib/src/qtweetstatus.h QTweetLib/src/qtweetstatusshow.h QTweetLib/src/qtweetstatusupdate.h - QTweetLib/src/qtweetuser.h QTweetLib/src/qtweetusertimeline.h QTweetLib/src/qtweetstatusdestroy.h QTweetLib/src/qtweetstatusretweet.h @@ -113,11 +110,9 @@ set(TOMAHAWK_QTWEETLIB_HEADERS QTweetLib/src/qtweetuserlookup.h QTweetLib/src/qtweetdirectmessages.h QTweetLib/src/qtweetuserstream.h - QTweetLib/src/qtweetdmstatus.h QTweetLib/src/qtweetusersearch.h QTweetLib/src/qtweetuserstatusesfriends.h QTweetLib/src/qtweetuserstatusesfollowers.h - QTweetLib/src/qtweetlist.h QTweetLib/src/qtweetlistcreate.h QTweetLib/src/qtweetlistupdate.h QTweetLib/src/qtweetlistgetlists.h @@ -145,20 +140,11 @@ set(TOMAHAWK_QTWEETLIB_HEADERS QTweetLib/src/qtweetfavoritescreate.h QTweetLib/src/qtweetfavoritesdestroy.h QTweetLib/src/qtweetsearch.h - QTweetLib/src/qtweetsearchresult.h - QTweetLib/src/qtweetsearchpageresults.h - QTweetLib/src/qtweetplace.h QTweetLib/src/qtweetgeoreversegeocode.h QTweetLib/src/qtweetgeosearch.h QTweetLib/src/qtweetgeosimilarplaces.h QTweetLib/src/qtweetgeoplaceid.h QTweetLib/src/qtweetgeoplacecreate.h - QTweetLib/src/qtweetgeocoord.h - QTweetLib/src/qtweetgeoboundingbox.h - QTweetLib/src/qtweetconvert.h - QTweetLib/src/qtweetentityurl.h - QTweetLib/src/qtweetentityhashtag.h - QTweetLib/src/qtweetentityusermentions.h QTweetLib/src/qtweetblocksblocking.h QTweetLib/src/qtweetblocksblockingids.h QTweetLib/src/qtweetblockscreate.h