1
0
mirror of https://github.com/tomahawk-player/tomahawk.git synced 2025-09-13 23:42:11 +02:00

Compare commits

...

116 Commits
vsxu ... 0.4.0

Author SHA1 Message Date
Christian Muehlhaeuser
e3a43e8ca6 * Bumped version to 0.4.0. 2012-03-07 05:07:47 +01:00
Christian Muehlhaeuser
c401ea0d91 * Update ChangeLog. 2012-03-07 05:07:23 +01:00
Christian Muehlhaeuser
2d03b8aea0 * Didn't mean to commit that. 2012-03-07 04:49:26 +01:00
Christian Muehlhaeuser
178aed9174 * Fixed TWK-474: proper fulltext search. 2012-03-07 04:47:24 +01:00
Christian Muehlhaeuser
6552deca61 * Fixed Source method being run in the wrong thread. 2012-03-07 04:45:32 +01:00
Leo Franchi
89009c09be Add a small guard to Artist::get and Album::get as they are called all over, and might be called after destruction of DB on shutdown 2012-03-06 18:47:55 -05:00
Leo Franchi
34672c7bed TWK-722: Fix quitting right after startup by ensuring chart data loaders are deleted before database 2012-03-06 18:46:48 -05:00
Leo Franchi
3caeb4642a Attempt at fixing TWK-723. 2012-03-06 18:23:17 -05:00
Christian Muehlhaeuser
80b0eacfed * Revert macdeploy back to use Qt 4.7.4. 2012-03-06 00:42:12 +01:00
Leo Franchi
aa078e86e5 Merge pull request #77 from anselmolsm/master
Play/Pause when system tray receives a mouse middle click
2012-03-05 12:20:13 -08:00
Anselmo L. S. Melo
9daf0a6089 Play/Pause when system tray receives a mouse middle click 2012-03-05 15:00:28 -03:00
Jason Herskowitz
6dead5e7e1 Fix all Super Collection text strings to SuperCollection 2012-03-04 22:46:10 -05:00
Jason Herskowitz
335868ea95 Merge branch 'master' of github.com:tomahawk-player/tomahawk 2012-03-04 22:28:56 -05:00
Jason Herskowitz
fc45646205 Update SuperCollection header text 2012-03-04 22:28:35 -05:00
Leo Franchi
dbd466d3ed Changelog++ 2012-03-04 22:21:04 -05:00
Leo Franchi
292d8c9530 Don't update playlist if no tracks have changed 2012-03-04 21:54:55 -05:00
Leo Franchi
6b68598d1d Show job status for indexing 2012-03-04 21:54:55 -05:00
Dominik Schmidt
0d9e248dac Fix lastfm export macro after my last commit. Why did it work before that?! 2012-03-05 01:09:31 +01:00
Leo Franchi
46a86fe6a5 Install vlc plugins to Frameworks/vlc/plugins instead of plugins/ 2012-03-04 16:55:57 -05:00
Dominik Schmidt
a56ca4ddfc Improve liblastfm2 export macro 2012-03-04 21:14:59 +01:00
Leo Franchi
6483c55749 Revert "Disable breakpad for an osx nightly"
This reverts commit 5a56a3e060.
2012-03-04 13:21:41 -05:00
Leo Franchi
5a56a3e060 Disable breakpad for an osx nightly 2012-03-04 12:55:05 -05:00
Dominik Schmidt
585f32c4d8 osx: fix undefined reference to bringToFront() with visibility=hidden 2012-03-04 18:44:23 +01:00
Jason Herskowitz
9ea311526c Make audio error icon match set 2012-03-03 18:45:07 -05:00
Leo Franchi
1342fdb9a7 Revert "disable break for a sec"
This reverts commit 2cc4fccd66.
2012-03-03 11:40:16 -05:00
Leo Franchi
2cc4fccd66 disable break for a sec 2012-03-03 11:39:49 -05:00
Leo Franchi
a2d0899285 Add grooveshark track parsing, and add a job notification for shortened url unwrapping 2012-03-02 22:41:44 -05:00
Dominik Schmidt
06cc52744d Enforce proper symbol exporting on all platforms 2012-03-03 01:14:21 +01:00
Leo Franchi
df758aa3f8 TWK-710: Don't expand items when clicking on headphones or lock icon 2012-03-02 00:15:16 -05:00
Leo Franchi
f79ed86b57 TWK-711: Some attica resolver icon fixes and optimizations. Don't write too much to disk 2012-03-01 23:33:27 -05:00
Leo Franchi
c9b0c92450 Bump up time for SetFile to finish 2012-03-01 23:25:49 -05:00
Leo Franchi
57d1c29d35 TWK-533: Show instructive error message if stations run out of tracks 2012-03-01 23:05:09 -05:00
Christian Muehlhaeuser
cdf7c11b29 * Fixed TWK-703: Auto expand sidebar's groups per default. 2012-03-02 04:42:32 +01:00
Leo Franchi
be7b5babe9 Show filter errors when loading a dynamic playlist for the first time too 2012-03-01 22:34:55 -05:00
Leo Franchi
f7f2c51d4e TWK-712: Don't emit track number changed when they really haven't, thus masking other errors 2012-03-01 22:11:02 -05:00
Leo Franchi
4889ed6a33 TWK-633: Don't use useless-to-us QUrl.addQueryItem, does not encode '+' 2012-03-01 21:53:13 -05:00
Leo Franchi
3557686681 Include in the right place 2012-03-01 21:53:11 -05:00
Christian Muehlhaeuser
58dfc54e00 * Fixed TWK-708: Resolve album page top-to-bottom. 2012-03-02 03:32:30 +01:00
Christian Muehlhaeuser
bddbc7a194 * Fixed TWK-642: Update Footnotes when selecting an album in AlbumView. 2012-03-02 02:45:56 +01:00
Christian Muehlhaeuser
33e0116e4b * Fixed TWK-700: Add description for SuperCollection. 2012-03-02 02:28:27 +01:00
Christian Muehlhaeuser
b07f498516 * Fixed TWK-699: Don't paint now playing icon over item decoration. 2012-03-02 02:21:08 +01:00
Christian Muehlhaeuser
c77be81e5b * Fixed TWK-641: don't write empty album covers in InfoSystem. 2012-03-02 01:55:28 +01:00
Christian Muehlhaeuser
11cf2c78d8 * Fixed ArtistPlaylistInterface. 2012-03-02 01:30:31 +01:00
Leo Franchi
77bcc7c7aa TWK-709: Fetch albums from infosystem if no tracks are found in the DB when dropping an album 2012-03-01 18:14:45 -05:00
Leo Franchi
5b97802104 Revert "Add debug for jason". Debug did its job :)
This reverts commit 25ec5cb432.
2012-03-01 09:20:41 -05:00
Leo Franchi
25ec5cb432 Add debug for jason 2012-03-01 09:12:27 -05:00
Leo Franchi
8b4b4a8f0c Add documentation 2012-03-01 00:19:59 -05:00
Leo Franchi
6448cebec6 Mostly fix TWK-701. A few bugs lined up here.
1) Tracks fetched from an album or artist in DropJob would only fetch from the db
   and never resolve w/ resolvers
2) artist* and album* objects were getting deleted too early if they didn't already exist before
   DropJob asked for them (results from db thread are queued, sharedpointer deletion happened first).
   Keep ahold of them until we're done with them.

For albums we don't know about, we still need to look up metadata track listings when nothing is found in the DB.
2012-03-01 00:14:16 -05:00
Leo Franchi
191ee259d4 TWK-671: Fix filter text being 'behind' by one step 2012-02-29 22:28:54 -05:00
Leo Franchi
dc364726c0 TWK-706: Don't auto-enable resolvers when upgrading them. 2012-02-29 18:46:58 -05:00
Hugo Lindström
6ac0f68224 Silly misstake in naming 2012-03-01 00:38:32 +01:00
Leo Franchi
3c3078f9f8 Clean up from previous test 2012-02-29 17:45:20 -05:00
Leo Franchi
b994befc58 osx /volumes tweak 2012-02-29 17:12:59 -05:00
Hugo Lindström
56cf0239da Fix soundcloudwall and pump version 2012-02-29 08:06:56 +01:00
Christian Muehlhaeuser
69d75362b3 * This should speed up resolving... a lot. 2012-02-29 02:36:33 +01:00
Leo Franchi
8db9c79e57 Add some SetFile/GetFileInfo debug 2012-02-28 18:54:22 -05:00
Hugo Lindström
e9ee617d94 Better name for soundcloudwall 2012-02-28 21:36:53 +01:00
Leo Franchi
c9c6534af5 Wait a bit longer for portfwd thread to exit to avoid crashes on exit 2012-02-28 10:37:37 -05:00
Leo Franchi
1b9767995b Make stations item drop enabled. How did this ever work!? 2012-02-28 10:37:37 -05:00
Christian Muehlhaeuser
1c561ba7a9 * Prepare macdeploy for Qt 4.8. 2012-02-28 15:49:21 +01:00
Leo Franchi
52baebc899 Delete pipeline after database, as some dbcmds create queries which access Pipeline 2012-02-26 17:35:14 -05:00
Leo Franchi
63c029554a Set default type of echonest control so an empty control is valid (and ignored) 2012-02-26 17:34:11 -05:00
Leo Franchi
3900dd27d2 TWK-686: Implement playlistinterface and jump to current track 2012-02-26 13:06:10 -05:00
Leo Franchi
e9fb17dadb TWK-604: Update Show/Hide menu entry if Tomahawk is hidden with Cmd-H 2012-02-26 12:44:19 -05:00
Leo Franchi
56c2f86381 Use SetFile/GetFileInfo from bundle 2012-02-26 10:57:00 -05:00
Leo Franchi
a8440c72e4 Fix build for gcc on mac. Hudson should use clang :D 2012-02-25 23:52:34 -05:00
Leo Franchi
cbb5bac075 TWK-624: When resolving dups in dropjob, prefer playable ones 2012-02-25 23:20:31 -05:00
Leo Franchi
b7f1f56f77 TWK-633: Don't hand-make URLs, that is just asking for trouble 2012-02-25 21:53:08 -05:00
Leo Franchi
9fe17cf6f5 Upgrade SPMediaKeyTap 2012-02-25 21:25:57 -05:00
Leo Franchi
d641f06e6d cleanups 2012-02-25 20:54:54 -05:00
Leo Franchi
7668f04c0c Cache height as sizehint is called plenty often 2012-02-25 20:13:12 -05:00
Leo Franchi
a73d1cd342 Don't calculate height by assuming rows are same height anymore, as they are not. 2012-02-25 20:07:33 -05:00
Leo Franchi
35c4a29cbe center icon in delegate for multi-line jobs 2012-02-25 20:03:01 -05:00
Leo Franchi
f6e00fbb86 Show error message if phonon spits an error, but don't block with modal dialog/audio stop 2012-02-25 20:02:38 -05:00
Leo Franchi
3f9503364c Return null qstring 2012-02-25 19:48:00 -05:00
Leo Franchi
36b2a585f9 Show errors when fetching information from the network. Fixes TWK-515 2012-02-25 19:39:35 -05:00
Leo Franchi
42220a8c95 Delete InfoSystem after pipeline as various constructors connect to InfoSystem. Fixes crash on exit. 2012-02-25 16:24:57 -05:00
Christian Muehlhaeuser
1ac4efa88a * Prepare for 0.4.0. 2012-02-25 10:18:00 +01:00
Christian Muehlhaeuser
cfe42d10ea * Minor cleanups. 2012-02-24 06:29:32 +01:00
Christian Muehlhaeuser
6ad9e820e3 * Only show top 50 loved tracks for the SuperCollection. 2012-02-24 05:52:02 +01:00
Christian Muehlhaeuser
47a403a9c8 * Update social actions everytime you open the contextual menu of a track. 2012-02-24 05:40:00 +01:00
Christian Muehlhaeuser
b2fc0935a4 * Show now playing speaker next to current playlist again. 2012-02-24 05:18:55 +01:00
Christian Muehlhaeuser
a04d384ac4 * Show default album, not artist cover on AlbumInfoWidget. 2012-02-24 04:15:28 +01:00
Christian Muehlhaeuser
325bb0bf8f * Added global common image cache. 2012-02-24 04:13:37 +01:00
Christian Muehlhaeuser
c2a79d4cbf * Don't wordwrap track names in sidebar. 2012-02-23 21:06:17 +01:00
Christian Muehlhaeuser
b17b8356ad * Fixed a few issues with controlling playback via CLI args. 2012-02-23 20:34:23 +01:00
Christian Muehlhaeuser
a41b174851 Merge pull request #76 from ubertaco/master
Initial support for TWK-595
2012-02-23 11:23:43 -08:00
ubertaco
458d32ed7c removed duplicate --stop 2012-02-23 14:05:44 -05:00
ubertaco
a0383b6664 fixed helpstrings and inconsistent spacing 2012-02-23 14:04:24 -05:00
ubertaco
9b1fc4cd24 added helpstrings 2012-02-23 13:52:11 -05:00
ubertaco
2a27ef88f6 Added initial support for TWK-595 ( command-line audio controls ) 2012-02-23 13:48:51 -05:00
Christian Muehlhaeuser
d1ecf6d748 * More work on sidebar reorganization. 2012-02-23 06:22:47 +01:00
Christian Muehlhaeuser
396b5cd6e0 * Modify Tomahawk's config file to be only accessible by the owner. 2012-02-23 04:36:30 +01:00
Christian Muehlhaeuser
55e7d6383b * Unbreak headless more. 2012-02-23 04:08:14 +01:00
Christian Muehlhaeuser
dbcbffb6c4 * Auto load covers after expanding an artist. 2012-02-23 04:05:06 +01:00
Christian Muehlhaeuser
6b51872c3e * Unbreak headless. 2012-02-23 03:30:22 +01:00
Christian Muehlhaeuser
79d8b081d7 * Speed up cover loading in TreeView. 2012-02-23 03:21:21 +01:00
Christian Muehlhaeuser
165276912f * Fixed Windows compiling. 2012-02-22 02:42:42 +01:00
Christian Muehlhaeuser
413052bf8e * Destroy Pipeline properly and deactivate it earlier during shutdown. 2012-02-22 02:09:24 +01:00
Leo Franchi
46fd72920c Remove duplicate signal/slot collection. All SourceTreeItem* signals are already hooked up for each item 2012-02-19 15:36:48 -05:00
Christian Muehlhaeuser
399b835436 * SourcePlaylistInterface shouldn't use temporary queries. 2012-02-19 11:29:03 +01:00
Alejandro Wainzinger
9f76cdf486 Fix typo. 2012-02-18 00:49:08 -08:00
Alejandro Wainzinger
881bf5dd9d Check for OS X 10.7 as well as 10.6 for delegate protocol. 2012-02-18 00:47:23 -08:00
Leo Franchi
8bb9661960 Add an index on the file mtime. For future users for now 2012-02-17 19:08:59 -05:00
Leo Franchi
a1c69b0b43 rework some startup order to load servent before UI components 2012-02-17 18:37:59 -05:00
Dominik Schmidt
5083849514 Merge branch 'master' of git://github.com/tomahawk-player/tomahawk 2012-02-14 05:28:57 +01:00
Dominik Schmidt
8d75ba4d64 Add console output on windows 2012-02-14 05:28:35 +01:00
Leo Franchi
b54b7f6455 Up webapi timeouts so resolving can finish 2012-02-12 17:15:02 -05:00
Dominik Schmidt
488eb387cb Use my new pvlc build in update-vlc.sh 2012-02-12 20:49:55 +01:00
Dominik Schmidt
99143f6148 Fix windows for qt 4.8 2012-02-12 20:23:20 +01:00
Dominik Schmidt
1d4320afea Bump year ;-) 2012-02-11 03:55:23 +01:00
Dominik Schmidt
d9c3162146 Remove now unneccesary workaround in mingw toolchain file 2012-02-10 19:36:10 +01:00
Dominik Schmidt
4a1b021753 Fix FindTaglib.cmake for windows 2012-02-10 19:33:48 +01:00
Leo Franchi
3418d5295b Merge pull request #75 from crabmanX/master
suggest playlist filename on export
2012-02-10 06:35:51 -08:00
Kilian Lackhove
bf30dc37a2 suggest playlist filename on export 2012-02-10 15:30:07 +01:00
Dominik Schmidt
6ed4902e00 Check for QtUiTools 2012-02-06 14:56:32 +01:00
135 changed files with 1994 additions and 712 deletions

View File

@@ -15,11 +15,13 @@ SET( TOMAHAWK_APPLICATION_NAME "Tomahawk" )
SET( TOMAHAWK_DESCRIPTION_SUMMARY "The social media player" )
SET( TOMAHAWK_VERSION_MAJOR 0 )
SET( TOMAHAWK_VERSION_MINOR 3 )
SET( TOMAHAWK_VERSION_PATCH 99 )
SET( TOMAHAWK_VERSION_MINOR 4 )
SET( TOMAHAWK_VERSION_PATCH 0 )
#SET( TOMAHAWK_VERSION_RC 0 )
# enforce proper symbol exporting on all platforms
add_definitions( "-fvisibility=hidden" )
# build options
option(BUILD_GUI "Build Tomahawk with GUI" ON)
@@ -72,7 +74,7 @@ IF( NOT BUILD_GUI )
MESSAGE( STATUS "Building Tomahawk ${TOMAHAWK_VERSION} in HEADLESS mode ***" )
ELSE()
MESSAGE( STATUS "Building Tomahawk ${TOMAHAWK_VERSION} full GUI version ***" )
LIST(APPEND NEEDED_QT4_COMPONENTS "QtGui" "QtWebkit" )
LIST(APPEND NEEDED_QT4_COMPONENTS "QtGui" "QtWebkit" "QtUiTools" )
ENDIF()
IF( BUILD_GUI AND UNIX AND NOT APPLE )
@@ -80,7 +82,7 @@ IF( BUILD_GUI AND UNIX AND NOT APPLE )
ENDIF()
macro_optional_find_package(Qt4 4.7.0 COMPONENTS ${NEEDED_QT4_COMPONENTS} )
macro_log_feature(QT4_FOUND "Qt" "A cross-platform application and UI framework" "http://qt.nokia.com" TRUE "" "If you see this, although libqt4-devel is installed, check whether \n the qtwebkit-devel package is installed as well")
macro_log_feature(QT4_FOUND "Qt" "A cross-platform application and UI framework" "http://qt.nokia.com" TRUE "" "If you see this, although libqt4-devel is installed, check whether the \n qtwebkit-devel package and whatever contains QtUiTools is installed too")
macro_optional_find_package(Phonon 4.5.0)
macro_log_feature(PHONON_FOUND "Phonon" "The Phonon multimedia library" "http://phonon.kde.org" TRUE "" "")

View File

@@ -56,7 +56,7 @@ ELSE()
include(FindLibraryWithDebug)
include(FindPackageHandleStandardArgs)
find_path(TAGLIB_CFLAGS
find_path(TAGLIB_INCLUDES
NAMES
tag.h
PATH_SUFFIXES taglib

View File

@@ -28,8 +28,8 @@
; We use official release plugins
; mingw32-vlc from obs misses a lot and has even broken ones probably
!define VLC_PATH "${SOURCE_PATH}\admin\win\vlc\prefix" ; SIC! ^
!define VLC_BIN "${VLC_PATH}\bin"
!define VLC_PATH "${SOURCE_PATH}\admin\win\vlc\" ; SIC! ^
!define VLC_BIN "${VLC_PATH}"
!define VLC_PLUGIN_PATH "${VLC_BIN}\plugins"
!define NSI_PATH "${SOURCE_PATH}/admin/win/nsi"
@@ -279,7 +279,6 @@ Section "Tomahawk Player" SEC_TOMAHAWK_PLAYER
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\libquazip.dll"
File "${INSTALL_PATH}\bin\libtomahawklib.dll"
File "${INSTALL_PATH}\lib\libtomahawk_sip*.dll"
!endif
@@ -294,7 +293,6 @@ Section "Tomahawk Player" SEC_TOMAHAWK_PLAYER
File "${BUILD_PATH}\libqxtweb-standalone.dll"
File "${BUILD_PATH}\libtomahawk_portfwd.dll"
File "${BUILD_PATH}\libtomahawk_lastfm2.dll"
File "${BUILD_PATH}\libquazip.dll"
File "${BUILD_PATH}\libtomahawk_sip*.dll"
!endif
@@ -307,8 +305,10 @@ Section "Tomahawk Player" SEC_TOMAHAWK_PLAYER
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}\QtScript4.dll"
File "${QT_DLL_PATH}\QtUiTools4.dll"
File "${QT_DLL_PATH}\QtWebKit4.dll"
File "${QT_DLL_PATH}\QtXml4.dll"
;SQLite driver
SetOutPath "$INSTDIR\sqldrivers"
@@ -329,13 +329,12 @@ Section "Tomahawk Player" SEC_TOMAHAWK_PLAYER
File "${MING_BIN}\libstdc++-6.dll"
;Phonon stuff
File "${VLC_BIN}\libphonon.dll"
File "${MING_BIN}\libphonon.dll"
SetOutPath "$INSTDIR\phonon_backend"
File "${VLC_BIN}\phonon_backend\phonon_vlc.dll"
SetOutPath "$INSTDIR"
;VLC
;SetOutPath "$INSTDIR\phonon_backend"
File "${VLC_BIN}\libvlc.dll"
File "${VLC_BIN}\libvlccore.dll"
SetOutPath "$INSTDIR\plugins"
@@ -351,6 +350,7 @@ Section "Tomahawk Player" SEC_TOMAHAWK_PLAYER
File "${MING_BIN}\libechonest.dll"
File "${MING_BIN}\libQTweetLib.dll"
File "${MING_BIN}\libquazip.dll"
; Jabber
File "${MING_BIN}\libjreen.dll"

View File

@@ -1,3 +1,39 @@
Version 0.4.0:
* Added visual notification for database indexing job.
* Fixed icons not appearing in resolvers list.
* Fixed various UI glitches and stray error messages in stations.
* Fixed bug where album page would resolve bottom-to-top.
* Fixed bug where Footnotes would not update when changing selected album in Album View.
* Fixed dragging albums and artists from charts, album, and artist views.
* Fixed bug where filter text would be one step behind filter value.
* Fixed bug where resolvers would enable themselves after auto-updating.
* Fixed occasional crash when dropping tracks onto New Station item.
* Added jump-to-current-track support for search results page.
* Fixed out of sync Show/Hide menu items on OS X when hidden with cmd-h.
* Fixed non-resolving tracks when dragging from album view.
* Fixed /Volumes directory not showing up on OS X.
* Fixed fetching album covers for albums with special characters.
* Show errors and continue gracefully when resolved audio is not available.
* Fixed various crashes on exit.
* Added basic command-line options for playback control.
* Bumped up web api timeouts to allow web clients to finish resolving.
* Added filename suggestion when exporting a playlist.
* Cleaned up highlighting of artist names in album view.
* Cleaned up alignment of playlist items.
* Fixed potential crash when searching.
* Added support for disc number.
* Added SoundCloudWall.com charts.
* Added ability to "lock on" to a user when listening along, to skip along.
* Fixed bug where loved tracks would be refreshed much too often.
* Fixed startup crash on OS X.
* Fixed some font size issues.
* Sped up Tomahawk startup by moving chart loading into a separate thread.
* Added support for parsing Grooveshark and Tinysong tracks and playlists.
* Reorganized sidebar to follow more logical item groupings.
* Added artist and album results to global searches.
* Fixed style and contrast issues when using GTK styles.
* Fixed paths to artwork when using MPRIS2 interface.
Version 0.3.3:
* Automatically load Super Collection tracks when no official release
information is available.

View File

@@ -491,7 +491,7 @@ def FindVLCPlugin(name):
FixBinary(binary)
for plugin in VLC_PLUGINS:
FixVLCPlugin(FindVLCPlugin(plugin), '.')
FixVLCPlugin(FindVLCPlugin(plugin), '../Frameworks/vlc/plugins')
for plugin in TOMAHAWK_PLUGINS:
FixPlugin(plugin, '../MacOS')

View File

@@ -3,9 +3,12 @@ SET(MINGW_PREFIX "i686-w64-mingw32")
# this one is important
SET(CMAKE_SYSTEM_NAME Windows)
# specify the cross compiler
SET(CMAKE_C_COMPILER ccache ${MINGW_PREFIX}-gcc)
SET(CMAKE_C_FLAGS "-fno-keep-inline-dllexport")
SET(CMAKE_CXX_COMPILER ccache ${MINGW_PREFIX}-g++)
SET(CMAKE_CXX_FLAGS ${CMAKE_C_FLAGS})
SET(CMAKE_RC_COMPILER /usr/bin/${MINGW_PREFIX}-windres)
# where is the target environment containing libraries
@@ -15,13 +18,6 @@ SET(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
SET(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
# libs with broken find modules
SET(TAGLIB_FOUND true)
SET(TAGLIB_LIBRARIES ${CMAKE_FIND_ROOT_PATH}/lib/libtag.dll.a)
SET(TAGLIB_INCLUDES ${CMAKE_FIND_ROOT_PATH}/include/taglib)
# configure qt variables
SET(QT_LIBRARY_DIR /usr/${MINGW_PREFIX}/bin)
SET(QT_PLUGINS_DIR ${CMAKE_FIND_ROOT_PATH}/lib/qt4/plugins/)
SET(QT_QTUITOOLS_LIBRARY_RELEASE ${CMAKE_FIND_ROOT_PATH}/lib/libQtUiTools.a)
SET(QT_QTUITOOLS_LIBRARY_DEBUG ${CMAKE_FIND_ROOT_PATH}/lib/libQtUiToolsd.a)
SET(QT_QTUITOOLS_LIBRARY ${QT_QTUITOOLS_LIBRARY_RELEASE})

View File

@@ -1,51 +1,52 @@
#!/bin/bash
mkdir -p vlc/
if [ "$1" = "-c" ] ; then
echo "Continuing last download.."
rm -rvf vlc/prefix/
rm -rvf vlc/
else
echo "Remove old vlc dir..."
rm -rvf vlc/*
echo "Update archive..."
fi
cd vlc/
rm -rvf vlc/
echo "Download phonon archive..."
#wget -c "http://downloads.sourceforge.net/project/vlc/1.1.9/win32/vlc-1.1.9-win32.7z?r=http%3A%2F%2Fwww.videolan.org%2Fvlc%2Fdownload-windows.html&ts=1306272584&use_mirror=leaseweb"
#wget -c "http://download.tomahawk-player.org/tomahawk-vlc-0.1.zip"
#wget -c http://people.videolan.org/~jb/phonon/phonon-vlc-last.7z
wget -c http://people.videolan.org/~jb/phonon/phonon_phonon-vlc_20111128.7z
# wget -c "http://downloads.sourceforge.net/project/vlc/1.1.9/win32/vlc-1.1.9-win32.7z?r=http%3A%2F%2Fwww.videolan.org%2Fvlc%2Fdownload-windows.html&ts=1306272584&use_mirror=leaseweb"
# wget -c "http://download.tomahawk-player.org/tomahawk-vlc-0.1.zip"
# wget -c http://people.videolan.org/~jb/phonon/phonon-vlc-last.7z
# wget -c http://people.videolan.org/~jb/phonon/phonon_phonon-vlc_20111128.7z
wget -c http://download.tomahawk-player.org/test/pvlc.tar.bz2
echo "Extract binary..."
7z x phonon*.7z
#mv -v vlc-*/ vlc/
#unzip tomahawk-vlc-0.1.zip
# 7z x phonon*.7z
# mv -v vlc-*/ vlc/
# unzip tomahawk-vlc-0.1.zip
tar xvjf pvlc.tar.bz2
echo "Download phonon_vlc_no_video.dll..."
wget -c http://people.videolan.org/~jb/phonon/phonon_vlc_no_video.dll
cp -v phonon_vlc_no_video.dll prefix/bin/phonon_backend/phonon_vlc.dll
# echo "Download phonon_vlc_no_video.dll..."
# wget -c http://people.videolan.org/~jb/phonon/phonon_vlc_no_video.dll
# cp -v phonon_vlc_no_video.dll prefix/bin/phonon_backend/phonon_vlc.dll
echo "Strip unneeded plugins from vlc/plugins..."
cd prefix/bin/plugins
rm -rvf libold* libvcd* libdvd* liblibass* libx264* libschroe* liblibmpeg2* \
libstream_out_* libmjpeg_plugin* libh264_plugin* libzvbi_plugin* lib*sub* \
*qt4* *skins2* libaccess_bd_plugin.dll \
libaudiobargraph_* libball_plugin.dll \
libdirac_plugin.dll \
libgnutls_plugin.dll \
libcaca_plugin.dll \
libfreetype_plugin.dll \
libaccess_output_shout_plugin.dll \
libremoteosd_plugin.dll \
libsdl_image_plugin.dll \
libvout_sdl_plugin.dll \
libpng_plugin.dll \
libgoom_plugin.dll \
libatmo_plugin.dll \
libmux_ts_plugin.dll \
libkate_plugin.dll \
libtaglib_plugin.dll
# echo "Strip unneeded plugins from vlc/plugins..."
# cd prefix/bin/plugins
# rm -rvf libold* libvcd* libdvd* liblibass* libx264* libschroe* liblibmpeg2* \
# libstream_out_* libmjpeg_plugin* libh264_plugin* libzvbi_plugin* lib*sub* \
# *qt4* *skins2* libaccess_bd_plugin.dll \
# libaudiobargraph_* libball_plugin.dll \
# libdirac_plugin.dll \
# libgnutls_plugin.dll \
# libcaca_plugin.dll \
# libfreetype_plugin.dll \
# libaccess_output_shout_plugin.dll \
# libremoteosd_plugin.dll \
# libsdl_image_plugin.dll \
# libvout_sdl_plugin.dll \
# libpng_plugin.dll \
# libgoom_plugin.dll \
# libatmo_plugin.dll \
# libmux_ts_plugin.dll \
# libkate_plugin.dll \
# libtaglib_plugin.dll
# this is for vlc-1.2

BIN
data/images/grooveshark.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 68 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 68 KiB

After

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 91 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

View File

@@ -133,6 +133,8 @@
<file>data/images/headphones-bigger.png</file>
<file>data/images/no-album-no-case.png</file>
<file>data/images/rdio.png</file>
<file>data/images/grooveshark.png</file>
<file>data/sql/dbmigrate-27_to_28.sql</file>
<file>data/images/process-stop.png</file>
</qresource>
</RCC>

View File

@@ -58,4 +58,7 @@ if (APPLE)
FILE(COPY ${CMAKE_SOURCE_DIR}/admin/mac/sparkle_pub.pem
DESTINATION "${CMAKE_BINARY_DIR}/tomahawk.app/Contents/Resources")
FILE(COPY /usr/bin/SetFile DESTINATION "${CMAKE_BINARY_DIR}/tomahawk.app/Contents/MacOS")
FILE(COPY /usr/bin/GetFileInfo DESTINATION "${CMAKE_BINARY_DIR}/tomahawk.app/Contents/MacOS")
endif (APPLE)

View File

@@ -100,8 +100,6 @@ AudioControls::AudioControls( QWidget* parent )
m_sliderTimeLine.setCurveShape( QTimeLine::LinearCurve );
ui->seekSlider->setTimeLine( &m_sliderTimeLine );
m_defaultCover = QPixmap( RESPATH "images/no-album-no-case.png" ).scaled( ui->coverImage->size(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation );
connect( &m_phononTickCheckTimer, SIGNAL( timeout() ), SLOT( phononTickCheckTimeout() ) );
connect( &m_sliderTimeLine, SIGNAL( frameChanged( int ) ), ui->seekSlider, SLOT( setValue( int ) ) );
@@ -264,14 +262,14 @@ AudioControls::onAlbumCoverUpdated()
void
AudioControls::setAlbumCover()
{
if ( !m_currentTrack->album()->cover().isNull() )
if ( !m_currentTrack->album()->cover( ui->coverImage->size() ).isNull() )
{
QPixmap cover;
cover.loadFromData( m_currentTrack->album()->cover() );
ui->coverImage->setPixmap( cover.scaled( ui->coverImage->size(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation ) );
cover = m_currentTrack->album()->cover( ui->coverImage->size() );
ui->coverImage->setPixmap( cover );
}
else
ui->coverImage->setPixmap( m_defaultCover );
ui->coverImage->setPixmap( TomahawkUtils::defaultPixmap( TomahawkUtils::DefaultAlbumCover, TomahawkUtils::ScaledCover, ui->coverImage->size() ) );
}
@@ -279,10 +277,16 @@ void
AudioControls::onSocialActionsLoaded()
{
Query* query = qobject_cast< Query* >( sender() );
if ( !query || query != m_currentTrack->toQuery().data() )
if ( !query )
return;
setSocialActions();
query_ptr currentQuery = m_currentTrack->toQuery();
if ( query->artist() == currentQuery->artist() &&
query->track() == currentQuery->track() &&
query->album() == currentQuery->album() )
{
setSocialActions();
}
}

View File

@@ -91,8 +91,6 @@ private:
Ui::AudioControls *ui;
QPixmap m_defaultCover;
Tomahawk::result_ptr m_currentTrack;
Tomahawk::PlaylistInterface::RepeatMode m_repeatMode;
bool m_shuffled;

View File

@@ -21,5 +21,4 @@ ADD_DEFINITIONS( ${QT_DEFINITIONS} )
ADD_EXECUTABLE( tomahawk_crash_reporter WIN32 ${crashreporter_SOURCES} ${crashreporter_HEADERS_MOC} ${crashreporter_UI_HEADERS} ${crashreporter_RC_RCC} )
TARGET_LINK_LIBRARIES( tomahawk_crash_reporter ${QT_LIBRARIES} tomahawklib )
INCLUDE(GNUInstallDirs)
install(TARGETS tomahawk_crash_reporter RUNTIME DESTINATION ${CMAKE_INSTALL_FULL_LIBEXECDIR})
install(TARGETS tomahawk_crash_reporter RUNTIME DESTINATION ${CMAKE_INSTALL_LIBEXECDIR})

View File

@@ -76,7 +76,10 @@ AtticaManager::loadPixmapsFromCache()
}
QPixmap* icon = new QPixmap( cacheDir.absoluteFilePath( file ) );
m_resolverStates[ info.baseName() ].pixmap = icon;
if ( !icon->isNull() )
{
m_resolverStates[ info.baseName() ].pixmap = icon;
}
}
}
@@ -93,14 +96,21 @@ AtticaManager::savePixmapsToCache()
foreach( const QString& id, m_resolverStates.keys() )
{
if ( !m_resolverStates[ id ].pixmap )
if ( !m_resolverStates[ id ].pixmap || !m_resolverStates[ id ].pixmapDirty )
continue;
const QString filename = cacheDir.absoluteFilePath( QString( "%1.png" ).arg( id ) );
if ( !m_resolverStates[ id ].pixmap->save( filename ) )
QFile f( filename );
if ( !f.open( QIODevice::WriteOnly ) )
{
tLog() << "Failed to open cache file for writing:" << filename;
continue;
}
else
{
if ( !m_resolverStates[ id ].pixmap->save( &f ) )
{
tLog() << "Failed to save pixmap into opened file for writing:" << filename;
}
}
}
}
@@ -247,6 +257,7 @@ AtticaManager::resolverIconFetched()
QPixmap* icon = new QPixmap;
icon->loadFromData( data );
m_resolverStates[ resolverId ].pixmap = icon;
m_resolverStates[ resolverId ].pixmapDirty = true;
}
@@ -288,7 +299,7 @@ AtticaManager::syncServerData()
void
AtticaManager::installResolver( const Content& resolver )
AtticaManager::installResolver( const Content& resolver, bool autoEnable )
{
Q_ASSERT( !resolver.id().isNull() );
@@ -302,6 +313,7 @@ AtticaManager::installResolver( const Content& resolver )
ItemJob< DownloadItem >* job = m_resolverProvider.downloadLink( resolver.id() );
connect( job, SIGNAL( finished( Attica::BaseJob* ) ), this, SLOT( resolverDownloadFinished( Attica::BaseJob* ) ) );
job->setProperty( "resolverId", resolver.id() );
job->setProperty( "autoEnable", autoEnable );
job->start();
}
@@ -316,11 +328,12 @@ AtticaManager::upgradeResolver( const Content& resolver )
if ( !m_resolverStates.contains( resolver.id() ) || m_resolverStates[ resolver.id() ].state != NeedsUpgrade )
return;
const bool enabled = TomahawkSettings::instance()->enabledScriptResolvers().contains( m_resolverStates[ resolver.id() ].scriptPath );
m_resolverStates[ resolver.id() ].state = Upgrading;
emit resolverStateChanged( resolver.id() );
uninstallResolver( resolver );
installResolver( resolver );
installResolver( resolver, enabled );
}
@@ -337,6 +350,7 @@ AtticaManager::resolverDownloadFinished ( BaseJob* j )
QNetworkReply* reply = TomahawkUtils::nam()->get( QNetworkRequest( url ) );
connect( reply, SIGNAL( finished() ), this, SLOT( payloadFetched() ) );
reply->setProperty( "resolverId", job->property( "resolverId" ) );
reply->setProperty( "autoEnable", job->property( "autoEnable" ) );
}
else
{
@@ -372,8 +386,10 @@ AtticaManager::payloadFetched()
// update with absolute, not relative, path
m_resolverStates[ resolverId ].scriptPath = resolverPath;
const bool autoEnable = reply->property( "autoEnable" ).toBool();
// Do the install / add to tomahawk
Tomahawk::Pipeline::instance()->addScriptResolver( resolverPath, true );
Tomahawk::Pipeline::instance()->addScriptResolver( resolverPath, autoEnable );
m_resolverStates[ resolverId ].state = Installed;
TomahawkSettingsGui::instanceGui()->setAtticaResolverStates( m_resolverStates );
emit resolverInstalled( resolverId );

View File

@@ -52,9 +52,12 @@ public:
ResolverState state;
QPixmap* pixmap;
// internal
bool pixmapDirty;
Resolver( const QString& v, const QString& path, int userR, ResolverState s )
: version( v ), scriptPath( path ), userRating( userR ), state( s ), pixmap( 0 ) {}
Resolver() : userRating( -1 ), state( Uninstalled ), pixmap( 0 ) {}
: version( v ), scriptPath( path ), userRating( userR ), state( s ), pixmap( 0 ), pixmapDirty( false ) {}
Resolver() : userRating( -1 ), state( Uninstalled ), pixmap( 0 ), pixmapDirty( false ) {}
};
typedef QHash< QString, AtticaManager::Resolver > StateHash;
@@ -91,7 +94,7 @@ public:
bool userHasRated( const Attica::Content& c ) const;
public slots:
void installResolver( const Attica::Content& resolver );
void installResolver( const Attica::Content& resolver, bool autoEnable = true );
void upgradeResolver( const Attica::Content& resolver );
signals:

View File

@@ -38,6 +38,8 @@ set( libGuiSources
jobview/PipelineStatusItem.cpp
jobview/TransferStatusItem.cpp
jobview/LatchedStatusItem.cpp
jobview/ErrorStatusMessage.cpp
jobview/IndexingJobItem.cpp
infobar/infobar.cpp
@@ -276,6 +278,8 @@ set( libGuiHeaders
jobview/PipelineStatusItem.h
jobview/TransferStatusItem.h
jobview/LatchedStatusItem.h
jobview/ErrorStatusMessage.h
jobview/IndexingJobItem.h
thirdparty/Qocoa/qsearchfield.h
)
@@ -648,6 +652,8 @@ IF( APPLE )
utils/tomahawkutils_mac.mm
thirdparty/Qocoa/qsearchfield_mac.mm )
SET_SOURCE_FILES_PROPERTIES(utils/tomahawkutils_mac.mm PROPERTIES COMPILE_FLAGS "-fvisibility=default")
SET( libHeaders ${libHeaders}
infosystem/infoplugins/mac/adium.h
infosystem/infoplugins/mac/adiumplugin.h )
@@ -703,6 +709,7 @@ TARGET_LINK_LIBRARIES( tomahawklib
${QT_QTSQL_LIBRARY}
${QT_QTUITOOLS_LIBRARY}
${QT_QTGUI_LIBRARY}
${QT_QTSCRIPT_LIBRARY}
${OS_SPECIFIC_LINK_LIBRARIES}
${CMAKE_THREAD_LIBS_INIT}
${LINK_LIBRARIES}

View File

@@ -22,7 +22,6 @@
#include "albumplaylistinterface.h"
#include "database/database.h"
#include "database/databaseimpl.h"
#include "database/databasecommand_alltracks.h"
#include "query.h"
#include "utils/logger.h"
@@ -32,12 +31,16 @@ using namespace Tomahawk;
Album::~Album()
{
delete m_cover;
}
album_ptr
Album::get( const Tomahawk::artist_ptr& artist, const QString& name, bool autoCreate )
{
if ( !Database::instance() || !Database::instance()->impl() )
return album_ptr();
int albid = Database::instance()->impl()->albumId( artist->id(), name, autoCreate );
if ( albid < 1 && autoCreate )
return album_ptr();
@@ -71,6 +74,7 @@ Album::Album( unsigned int id, const QString& name, const Tomahawk::artist_ptr&
, m_id( id )
, m_name( name )
, m_artist( artist )
, m_cover( 0 )
, m_infoLoaded( false )
{
connect( Tomahawk::InfoSystem::InfoSystem::instance(),
@@ -97,11 +101,14 @@ Album::artist() const
}
QByteArray
Album::cover() const
#ifndef ENABLE_HEADLESS
QPixmap
Album::cover( const QSize& size, bool forceLoad ) const
{
if ( !m_infoLoaded )
{
if ( !forceLoad )
return QPixmap();
m_uuid = uuid();
Tomahawk::InfoSystem::InfoStringHash trackInfo;
@@ -117,8 +124,31 @@ Album::cover() const
Tomahawk::InfoSystem::InfoSystem::instance()->getInfo( requestData );
}
return m_cover;
if ( !m_cover && !m_coverBuffer.isEmpty() )
{
m_cover = new QPixmap();
m_cover->loadFromData( m_coverBuffer );
}
if ( m_cover && !m_cover->isNull() && !size.isEmpty() )
{
if ( m_coverCache.contains( size.width() ) )
{
return m_coverCache.value( size.width() );
}
QPixmap scaledCover;
scaledCover = m_cover->scaled( size, Qt::KeepAspectRatio, Qt::SmoothTransformation );
m_coverCache.insert( size.width(), scaledCover );
return scaledCover;
}
if ( m_cover )
return *m_cover;
else
return QPixmap();
}
#endif
void
@@ -137,7 +167,7 @@ Album::infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData requestData, QVaria
const QByteArray ba = returnedData["imgbytes"].toByteArray();
if ( ba.length() )
{
m_cover = ba;
m_coverBuffer = ba;
}
}

View File

@@ -19,8 +19,13 @@
#ifndef TOMAHAWKALBUM_H
#define TOMAHAWKALBUM_H
#include "config.h"
#include <QtCore/QObject>
#include <QtCore/QSharedPointer>
#ifndef ENABLE_HEADLESS
#include <QtGui/QPixmap>
#endif
#include "typedefs.h"
#include "playlistinterface.h"
@@ -44,7 +49,9 @@ public:
unsigned int id() const { return m_id; }
QString name() const { return m_name; }
artist_ptr artist() const;
QByteArray cover() const;
#ifndef ENABLE_HEADLESS
QPixmap cover( const QSize& size, bool forceLoad = true ) const;
#endif
bool infoLoaded() const { return m_infoLoaded; }
Tomahawk::playlistinterface_ptr playlistInterface();
@@ -64,10 +71,13 @@ private:
unsigned int m_id;
QString m_name;
artist_ptr m_artist;
QByteArray m_cover;
QByteArray m_coverBuffer;
mutable QPixmap* m_cover;
bool m_infoLoaded;
mutable QString m_uuid;
mutable QHash< int, QPixmap > m_coverCache;
Tomahawk::playlistinterface_ptr m_playlistInterface;
};

View File

@@ -68,7 +68,6 @@ signals:
void nextTrackReady();
private:
Q_DISABLE_COPY( AlbumPlaylistInterface )
AlbumPlaylistInterface();
QList< Tomahawk::query_ptr > m_queries;

View File

@@ -31,12 +31,16 @@ using namespace Tomahawk;
Artist::~Artist()
{
delete m_cover;
}
artist_ptr
Artist::get( const QString& name, bool autoCreate )
{
if ( !Database::instance() || !Database::instance()->impl() )
return artist_ptr();
int artid = Database::instance()->impl()->artistId( name, autoCreate );
if ( artid < 1 && autoCreate )
return artist_ptr();
@@ -69,6 +73,7 @@ Artist::Artist( unsigned int id, const QString& name )
: QObject()
, m_id( id )
, m_name( name )
, m_cover( 0 )
, m_infoLoaded( false )
{
m_sortname = DatabaseImpl::sortname( name, true );
@@ -89,11 +94,14 @@ Artist::onTracksAdded( const QList<Tomahawk::query_ptr>& tracks )
}
QByteArray
Artist::cover() const
#ifndef ENABLE_HEADLESS
QPixmap
Artist::cover( const QSize& size, bool forceLoad ) const
{
if ( !m_infoLoaded )
{
if ( !forceLoad )
return QPixmap();
m_uuid = uuid();
Tomahawk::InfoSystem::InfoStringHash trackInfo;
@@ -108,8 +116,31 @@ Artist::cover() const
Tomahawk::InfoSystem::InfoSystem::instance()->getInfo( requestData );
}
return m_cover;
if ( !m_cover && !m_coverBuffer.isEmpty() )
{
m_cover = new QPixmap();
m_cover->loadFromData( m_coverBuffer );
}
if ( m_cover && !m_cover->isNull() && !size.isEmpty() )
{
if ( m_coverCache.contains( size.width() ) )
{
return m_coverCache.value( size.width() );
}
QPixmap scaledCover;
scaledCover = m_cover->scaled( size, Qt::KeepAspectRatio, Qt::SmoothTransformation );
m_coverCache.insert( size.width(), scaledCover );
return scaledCover;
}
if ( m_cover )
return *m_cover;
else
return QPixmap();
}
#endif
void
@@ -128,7 +159,7 @@ Artist::infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData requestData, QVari
const QByteArray ba = returnedData["imgbytes"].toByteArray();
if ( ba.length() )
{
m_cover = ba;
m_coverBuffer = ba;
}
}

View File

@@ -19,8 +19,13 @@
#ifndef TOMAHAWKARTIST_H
#define TOMAHAWKARTIST_H
#include "config.h"
#include <QtCore/QObject>
#include <QtCore/QSharedPointer>
#ifndef ENABLE_HEADLESS
#include <QtGui/QPixmap>
#endif
#include "typedefs.h"
#include "dllmacro.h"
@@ -43,7 +48,9 @@ public:
unsigned int id() const { return m_id; }
QString name() const { return m_name; }
QString sortname() const { return m_sortname; }
QByteArray cover() const;
#ifndef ENABLE_HEADLESS
QPixmap cover( const QSize& size, bool forceLoad = true ) const;
#endif
bool infoLoaded() const { return m_infoLoaded; }
Tomahawk::playlistinterface_ptr playlistInterface();
@@ -63,10 +70,13 @@ private:
unsigned int m_id;
QString m_name;
QString m_sortname;
QByteArray m_cover;
QByteArray m_coverBuffer;
mutable QPixmap* m_cover;
bool m_infoLoaded;
mutable QString m_uuid;
mutable QHash< int, QPixmap > m_coverCache;
Tomahawk::playlistinterface_ptr m_playlistInterface;
};

View File

@@ -89,8 +89,8 @@ ArtistPlaylistInterface::tracks()
cmd->setArtist( m_artist );
cmd->setSortOrder( DatabaseCommand_AllTracks::Album );
connect( cmd, SIGNAL( tracks( QList<Tomahawk::query_ptr>, QVariant ) ),
SLOT( onTracksAdded( QList<Tomahawk::query_ptr> ) ) );
connect( cmd, SIGNAL( tracks( QList<Tomahawk::query_ptr>, QVariant ) ),
m_artist.data(), SLOT( onTracksAdded( QList<Tomahawk::query_ptr> ) ) );
Database::instance()->enqueue( QSharedPointer< DatabaseCommand >( cmd ) );
}

View File

@@ -18,6 +18,8 @@
#include "audioengine.h"
#include "config.h"
#include <QtCore/QUrl>
#include <QtNetwork/QNetworkReply>
@@ -328,13 +330,15 @@ AudioEngine::sendWaitingNotificationSlot() const
void
AudioEngine::sendNowPlayingNotification()
{
#ifndef ENABLE_HEADLESS
if ( m_currentTrack->album().isNull() || m_currentTrack->album()->infoLoaded() )
onNowPlayingInfoReady();
else
{
connect( m_currentTrack->album().data(), SIGNAL( updated() ), SLOT( onNowPlayingInfoReady() ), Qt::UniqueConnection );
m_currentTrack->album()->cover();
m_currentTrack->album()->cover( QSize( 0, 0 ) );
}
#endif
}
@@ -357,9 +361,11 @@ AudioEngine::onNowPlayingInfoReady()
if ( !m_currentTrack->album().isNull() )
{
#ifndef ENABLE_HEADLESS
QImage cover;
cover.loadFromData( m_currentTrack->album()->cover() );
cover = m_currentTrack->album()->cover( QSize( 0, 0 ) ).toImage();
playInfo["image"] = QVariant( cover );
#endif
}
Tomahawk::InfoSystem::InfoSystem::instance()->pushInfo(
@@ -584,10 +590,10 @@ AudioEngine::onStateChanged( Phonon::State newState, Phonon::State oldState )
if ( newState == Phonon::ErrorState )
{
stop();
tLog() << "Phonon Error:" << m_mediaObject->errorString() << m_mediaObject->errorType();
emit error( UnknownError );
stop();
return;
}
if ( newState == Phonon::PlayingState )

View File

@@ -88,6 +88,7 @@ ContextMenu::setQueries( const QList<Tomahawk::query_ptr>& queries )
m_sigmap->setMapping( m_loveAction, ActionLove );
connect( queries.first().data(), SIGNAL( socialActionsLoaded() ), SLOT( onSocialActionsLoaded() ) );
m_queries.first()->loadSocialActions();
onSocialActionsLoaded();
}

View File

@@ -20,6 +20,7 @@
#include "artist.h"
#include "album.h"
#include "pipeline.h"
#include "sourcelist.h"
#include "utils/logger.h"
@@ -30,10 +31,14 @@ DatabaseCommand_Resolve::DatabaseCommand_Resolve( const query_ptr& query )
: DatabaseCommand()
, m_query( query )
{
Q_ASSERT( Pipeline::instance()->isRunning() );
}
DatabaseCommand_Resolve::~DatabaseCommand_Resolve()
{}
{
}
void
DatabaseCommand_Resolve::exec( DatabaseImpl* lib )
@@ -76,11 +81,9 @@ DatabaseCommand_Resolve::resolve( DatabaseImpl* lib )
typedef QPair<int, float> scorepair_t;
// STEP 1
QList< QPair<int, float> > artists = lib->searchTable( "artist", m_query->artist() );
QList< QPair<int, float> > tracks = lib->searchTable( "track", m_query->track() );
QList< QPair<int, float> > albums = lib->searchTable( "album", m_query->album() );
QList< QPair<int, float> > tracks = lib->search( m_query );
if ( artists.length() == 0 || tracks.length() == 0 )
if ( tracks.length() == 0 )
{
qDebug() << "No candidates found in first pass, aborting resolve" << m_query->artist() << m_query->track();
emit results( m_query->id(), res );
@@ -90,13 +93,10 @@ DatabaseCommand_Resolve::resolve( DatabaseImpl* lib )
// STEP 2
TomahawkSqlQuery files_query = lib->newquery();
QStringList artsl, trksl;
for ( int k = 0; k < artists.count(); k++ )
artsl.append( QString::number( artists.at( k ).first ) );
QStringList trksl;
for ( int k = 0; k < tracks.count(); k++ )
trksl.append( QString::number( tracks.at( k ).first ) );
QString artsToken = QString( "file_join.artist IN (%1)" ).arg( artsl.join( "," ) );
QString trksToken = QString( "file_join.track IN (%1)" ).arg( trksl.join( "," ) );
QString sql = QString( "SELECT "
@@ -119,8 +119,7 @@ DatabaseCommand_Resolve::resolve( DatabaseImpl* lib )
"artist.id = file_join.artist AND "
"track.id = file_join.track AND "
"file.id = file_join.file AND "
"(%1 AND %2)" )
.arg( artsToken )
"(%1)" )
.arg( trksToken );
files_query.prepare( sql );
@@ -196,27 +195,9 @@ DatabaseCommand_Resolve::fullTextResolve( DatabaseImpl* lib )
typedef QPair<int, float> scorepair_t;
// STEP 1
QList< QPair<int, float> > artistPairs = lib->searchTable( "artist", m_query->fullTextQuery(), 20 );
QList< QPair<int, float> > trackPairs = lib->searchTable( "track", m_query->fullTextQuery(), 20 );
QList< QPair<int, float> > albumPairs = lib->searchTable( "album", m_query->fullTextQuery(), 20 );
QList< QPair<int, float> > trackPairs = lib->search( m_query );
QList< QPair<int, float> > albumPairs = lib->searchAlbum( m_query, 20 );
foreach ( const scorepair_t& artistPair, artistPairs )
{
TomahawkSqlQuery query = lib->newquery();
QString sql = QString( "SELECT name FROM artist WHERE id = %1" ).arg( artistPair.first );
query.prepare( sql );
query.exec();
QList<Tomahawk::artist_ptr> artistList;
while ( query.next() )
{
Tomahawk::artist_ptr artist = Tomahawk::Artist::get( artistPair.first, query.value( 0 ).toString() );
artistList << artist;
}
emit artists( m_query->id(), artistList );
}
foreach ( const scorepair_t& albumPair, albumPairs )
{
TomahawkSqlQuery query = lib->newquery();
@@ -235,10 +216,10 @@ DatabaseCommand_Resolve::fullTextResolve( DatabaseImpl* lib )
emit albums( m_query->id(), albumList );
}
if ( artistPairs.length() == 0 && trackPairs.length() == 0 && albumPairs.length() == 0 )
if ( trackPairs.length() == 0 )
{
qDebug() << "No candidates found in first pass, aborting resolve" << m_query->artist() << m_query->track();
qDebug() << "No candidates found in first pass, aborting resolve" << m_query->fullTextQuery();
emit results( m_query->id(), res );
return;
}
@@ -246,18 +227,11 @@ DatabaseCommand_Resolve::fullTextResolve( DatabaseImpl* lib )
// STEP 2
TomahawkSqlQuery files_query = lib->newquery();
QStringList artsl, trksl, albsl;
for ( int k = 0; k < artistPairs.count(); k++ )
artsl.append( QString::number( artistPairs.at( k ).first ) );
QStringList trksl;
for ( int k = 0; k < trackPairs.count(); k++ )
trksl.append( QString::number( trackPairs.at( k ).first ) );
for ( int k = 0; k < albumPairs.count(); k++ )
albsl.append( QString::number( albumPairs.at( k ).first ) );
QString artsToken = QString( "file_join.artist IN (%1)" ).arg( artsl.join( "," ) );
QString trksToken = QString( "file_join.track IN (%1)" ).arg( trksl.join( "," ) );
QString albsToken = QString( "file_join.album IN (%1)" ).arg( albsl.join( "," ) );
QString sql = QString( "SELECT "
"url, mtime, size, md5, mimetype, duration, bitrate, " //0
"file_join.artist, file_join.album, file_join.track, " //7
@@ -279,7 +253,7 @@ DatabaseCommand_Resolve::fullTextResolve( DatabaseImpl* lib )
"track.id = file_join.track AND "
"file.id = file_join.file AND "
"%1" )
.arg( trackPairs.length() > 0 ? trksToken : QString( "0" ) );
.arg( trksl.length() > 0 ? trksToken : QString( "0" ) );
files_query.prepare( sql );
files_query.exec();

View File

@@ -21,32 +21,26 @@
#include "databaseimpl.h"
#include "tomahawksqlquery.h"
#include "utils/logger.h"
#include "jobview/IndexingJobItem.h"
#include "jobview/JobStatusView.h"
#include "jobview/JobStatusModel.h"
#include <QSqlRecord>
DatabaseCommand_UpdateSearchIndex::DatabaseCommand_UpdateSearchIndex()
: DatabaseCommand()
, m_statusJob( new IndexingJobItem )
{
tLog() << Q_FUNC_INFO << "Updating index.";
JobStatusView::instance()->model()->addJob( m_statusJob );
}
void
DatabaseCommand_UpdateSearchIndex::indexTable( DatabaseImpl* db, const QString& table )
DatabaseCommand_UpdateSearchIndex::~DatabaseCommand_UpdateSearchIndex()
{
qDebug() << Q_FUNC_INFO;
TomahawkSqlQuery query = db->newquery();
qDebug() << "Building index for" << table;
query.exec( QString( "SELECT id, name FROM %1" ).arg( table ) );
QMap< unsigned int, QString > fields;
while ( query.next() )
{
fields.insert( query.value( 0 ).toUInt(), query.value( 1 ).toString() );
}
db->m_fuzzyIndex->appendFields( table, fields );
qDebug() << "Building index for" << table << "finished.";
m_statusJob->done();
}
@@ -55,9 +49,35 @@ DatabaseCommand_UpdateSearchIndex::exec( DatabaseImpl* db )
{
db->m_fuzzyIndex->beginIndexing();
indexTable( db, "artist" );
indexTable( db, "album" );
indexTable( db, "track" );
QMap< unsigned int, QMap< QString, QString > > data;
TomahawkSqlQuery q = db->newquery();
q.exec( "SELECT track.id, track.name, artist.name, artist.id FROM track, artist WHERE artist.id = track.artist" );
while ( q.next() )
{
QMap< QString, QString > track;
track.insert( "track", q.value( 1 ).toString() );
track.insert( "artist", q.value( 2 ).toString() );
track.insert( "artistid", q.value( 3 ).toString() );
data.insert( q.value( 0 ).toUInt(), track );
}
db->m_fuzzyIndex->appendFields( data );
data.clear();
q.exec( "SELECT album.id, album.name FROM album" );
while ( q.next() )
{
QMap< QString, QString > album;
album.insert( "album", q.value( 1 ).toString() );
data.insert( q.value( 0 ).toUInt(), album );
}
db->m_fuzzyIndex->appendFields( data );
qDebug() << "Building index finished.";
db->m_fuzzyIndex->endIndexing();
}

View File

@@ -22,23 +22,21 @@
#include "databasecommand.h"
#include "dllmacro.h"
class IndexingJobItem;
class DLLEXPORT DatabaseCommand_UpdateSearchIndex : public DatabaseCommand
{
Q_OBJECT
public:
explicit DatabaseCommand_UpdateSearchIndex();
virtual ~DatabaseCommand_UpdateSearchIndex();
virtual QString commandname() const { return "updatesearchindex"; }
virtual bool doesMutates() const { return true; }
virtual void exec( DatabaseImpl* db );
signals:
void indexUpdated();
private:
void indexTable( DatabaseImpl* db, const QString& table );
QString table;
IndexingJobItem* m_statusJob;
};
#endif // DATABASECOMMAND_UPDATESEARCHINDEX_H

View File

@@ -79,6 +79,9 @@ DatabaseImpl::DatabaseImpl( const QString& dbname, Database* parent )
query.exec( "UPDATE source SET isonline = 'false'" );
m_fuzzyIndex = new FuzzyIndex( *this, schemaUpdated );
if ( schemaUpdated )
QTimer::singleShot( 0, this, SLOT( updateIndex() ) );
tDebug( LOGVERBOSE ) << "Loaded index:" << t.elapsed();
if ( qApp->arguments().contains( "--dumpdb" ) )
@@ -405,13 +408,36 @@ DatabaseImpl::albumId( int artistid, const QString& name_orig, bool autoCreate )
QList< QPair<int, float> >
DatabaseImpl::searchTable( const QString& table, const QString& name, uint limit )
DatabaseImpl::search( const Tomahawk::query_ptr& query, uint limit )
{
QList< QPair<int, float> > resultslist;
if ( table != "artist" && table != "track" && table != "album" )
QMap< int, float > resultsmap = m_fuzzyIndex->search( query );
foreach ( int i, resultsmap.keys() )
{
resultslist << QPair<int, float>( i, (float)resultsmap.value( i ) );
}
qSort( resultslist.begin(), resultslist.end(), DatabaseImpl::scorepairSorter );
if ( !limit )
return resultslist;
QMap< int, float > resultsmap = m_fuzzyIndex->search( table, name );
QList< QPair<int, float> > resultscapped;
for ( int i = 0; i < (int)limit && i < resultsmap.count(); i++ )
{
resultscapped << resultslist.at( i );
}
return resultscapped;
}
QList< QPair<int, float> >
DatabaseImpl::searchAlbum( const Tomahawk::query_ptr& query, uint limit )
{
QList< QPair<int, float> > resultslist;
QMap< int, float > resultsmap = m_fuzzyIndex->searchAlbum( query );
foreach ( int i, resultsmap.keys() )
{
resultslist << QPair<int, float>( i, (float)resultsmap.value( i ) );
@@ -696,3 +722,11 @@ DatabaseImpl::openDatabase( const QString& dbname )
return schemaUpdated;
}
void
DatabaseImpl::updateIndex()
{
DatabaseCommand* cmd = new DatabaseCommand_UpdateSearchIndex();
Database::instance()->enqueue( QSharedPointer<DatabaseCommand>( cmd ) );
}

View File

@@ -56,7 +56,8 @@ public:
int trackId( int artistid, const QString& name_orig, bool autoCreate );
int albumId( int artistid, const QString& name_orig, bool autoCreate );
QList< QPair<int, float> > searchTable( const QString& table, const QString& name, uint limit = 0 );
QList< QPair<int, float> > search( const Tomahawk::query_ptr& query, uint limit = 0 );
QList< QPair<int, float> > searchAlbum( const Tomahawk::query_ptr& query, uint limit = 0 );
QList< int > getTrackFids( int tid );
static QString sortname( const QString& str, bool replaceArticle = false );
@@ -79,7 +80,8 @@ public:
signals:
void indexReady();
public slots:
private slots:
void updateIndex();
private:
QString cleanSql( const QString& sql );

View File

@@ -22,12 +22,14 @@
#include <QTime>
#include <CLucene.h>
#include <CLucene/queryParser/MultiFieldQueryParser.h>
#include "databaseimpl.h"
#include "utils/tomahawkutils.h"
#include "utils/logger.h"
using namespace lucene::analysis;
using namespace lucene::analysis::standard;
using namespace lucene::document;
using namespace lucene::store;
using namespace lucene::index;
@@ -83,7 +85,7 @@ FuzzyIndex::beginIndexing()
}
qDebug() << "Creating new index writer.";
IndexWriter luceneWriter = IndexWriter( m_luceneDir, m_analyzer, true );
IndexWriter luceneWriter( m_luceneDir, m_analyzer, true );
}
catch( CLuceneError& error )
{
@@ -102,38 +104,55 @@ FuzzyIndex::endIndexing()
void
FuzzyIndex::appendFields( const QString& table, const QMap< unsigned int, QString >& fields )
FuzzyIndex::appendFields( const QMap< unsigned int, QMap< QString, QString > >& trackData )
{
try
{
qDebug() << "Appending to index:" << fields.count();
tDebug() << "Appending to index:" << trackData.count();
bool create = !IndexReader::indexExists( TomahawkUtils::appDataDir().absoluteFilePath( "tomahawk.lucene" ).toStdString().c_str() );
IndexWriter luceneWriter = IndexWriter( m_luceneDir, m_analyzer, create );
IndexWriter luceneWriter( m_luceneDir, m_analyzer, create );
Document doc;
QMapIterator< unsigned int, QString > it( fields );
QMapIterator< unsigned int, QMap< QString, QString > > it( trackData );
while ( it.hasNext() )
{
it.next();
unsigned int id = it.key();
QString name = it.value();
QMap< QString, QString > values = it.value();
if ( values.contains( "track" ) )
{
Field* field = _CLNEW Field( table.toStdWString().c_str(), DatabaseImpl::sortname( name ).toStdWString().c_str(),
Field::STORE_YES | Field::INDEX_UNTOKENIZED );
doc.add( *field );
}
doc.add( *( _CLNEW Field( _T( "fulltext" ), DatabaseImpl::sortname( QString( "%1 %2" ).arg( values.value( "artist" ) ).arg( values.value( "track" ) ) ).toStdWString().c_str(),
Field::STORE_NO | Field::INDEX_UNTOKENIZED ) ) );
{
Field* field = _CLNEW Field( _T( "id" ), QString::number( id ).toStdWString().c_str(),
Field::STORE_YES | Field::INDEX_NO );
doc.add( *field );
doc.add( *( _CLNEW Field( _T( "track" ), DatabaseImpl::sortname( values.value( "track" ) ).toStdWString().c_str(),
Field::STORE_NO | Field::INDEX_UNTOKENIZED ) ) );
doc.add( *( _CLNEW Field( _T( "artist" ), DatabaseImpl::sortname( values.value( "artist" ) ).toStdWString().c_str(),
Field::STORE_NO | Field::INDEX_UNTOKENIZED ) ) );
doc.add( *( _CLNEW Field( _T( "artistid" ), values.value( "artistid" ).toStdWString().c_str(),
Field::STORE_YES | Field::INDEX_NO ) ) );
doc.add( *( _CLNEW Field( _T( "trackid" ), QString::number( id ).toStdWString().c_str(),
Field::STORE_YES | Field::INDEX_NO ) ) );
}
else if ( values.contains( "album" ) )
{
doc.add( *( _CLNEW Field( _T( "album" ), DatabaseImpl::sortname( values.value( "album" ) ).toStdWString().c_str(),
Field::STORE_NO | Field::INDEX_UNTOKENIZED ) ) );
doc.add( *( _CLNEW Field( _T( "albumid" ), QString::number( id ).toStdWString().c_str(),
Field::STORE_YES | Field::INDEX_NO ) ) );
}
else
Q_ASSERT( false );
luceneWriter.addDocument( &doc );
doc.clear();
}
luceneWriter.optimize();
luceneWriter.close();
}
catch( CLuceneError& error )
@@ -152,7 +171,7 @@ FuzzyIndex::loadLuceneIndex()
QMap< int, float >
FuzzyIndex::search( const QString& table, const QString& name )
FuzzyIndex::search( const Tomahawk::query_ptr& query )
{
QMutexLocker lock( &m_mutex );
@@ -171,33 +190,112 @@ FuzzyIndex::search( const QString& table, const QString& name )
m_luceneSearcher = _CLNEW IndexSearcher( m_luceneReader );
}
if ( name.isEmpty() )
return resultsmap;
float minScore;
const TCHAR** fields = 0;
MultiFieldQueryParser parser( fields, m_analyzer );
BooleanQuery* qry = _CLNEW BooleanQuery();
SimpleAnalyzer analyzer;
QueryParser parser( table.toStdWString().c_str(), m_analyzer );
Hits* hits = 0;
if ( query->isFullTextQuery() )
{
QString escapedQuery = QString::fromWCharArray( parser.escape( DatabaseImpl::sortname( query->fullTextQuery() ).toStdWString().c_str() ) );
Term* term = _CLNEW Term( _T( "track" ), escapedQuery.toStdWString().c_str() );
Query* fqry = _CLNEW FuzzyQuery( term );
qry->add( fqry, true, BooleanClause::SHOULD );
FuzzyQuery* qry = _CLNEW FuzzyQuery( _CLNEW Term( table.toStdWString().c_str(), DatabaseImpl::sortname( name ).toStdWString().c_str() ) );
hits = m_luceneSearcher->search( qry );
term = _CLNEW Term( _T( "artist" ), escapedQuery.toStdWString().c_str() );
fqry = _CLNEW FuzzyQuery( term );
qry->add( fqry, true, BooleanClause::SHOULD );
term = _CLNEW Term( _T( "fulltext" ), escapedQuery.toStdWString().c_str() );
fqry = _CLNEW FuzzyQuery( term );
qry->add( fqry, true, BooleanClause::SHOULD );
minScore = 0.00;
}
else
{
QString track = QString::fromWCharArray( parser.escape( DatabaseImpl::sortname( query->track() ).toStdWString().c_str() ) );
QString artist = QString::fromWCharArray( parser.escape( DatabaseImpl::sortname( query->artist() ).toStdWString().c_str() ) );
// QString album = QString::fromWCharArray( parser.escape( query->album().toStdWString().c_str() ) );
Term* term = _CLNEW Term( _T( "track" ), track.toStdWString().c_str() );
Query* fqry = _CLNEW FuzzyQuery( term );
qry->add( fqry, true, BooleanClause::MUST );
term = _CLNEW Term( _T( "artist" ), artist.toStdWString().c_str() );
fqry = _CLNEW FuzzyQuery( term );
qry->add( fqry, true, BooleanClause::MUST );
minScore = 0.05;
}
Hits* hits = m_luceneSearcher->search( qry );
for ( uint i = 0; i < hits->length(); i++ )
{
Document* d = &hits->doc( i );
float score = hits->score( i );
int id = QString::fromWCharArray( d->get( _T( "id" ) ) ).toInt();
QString result = QString::fromWCharArray( d->get( table.toStdWString().c_str() ) );
int id = QString::fromWCharArray( d->get( _T( "trackid" ) ) ).toInt();
if ( DatabaseImpl::sortname( result ) == DatabaseImpl::sortname( name ) )
score = 1.0;
else
score = qMin( score, (float)0.99 );
if ( score > 0.05 )
if ( score > minScore )
{
resultsmap.insert( id, score );
// qDebug() << "Hitres:" << result << id << score << table << name;
// tDebug() << "Index hit:" << id << score << QString::fromWCharArray( ((Query*)qry)->toString() );
}
}
delete hits;
delete qry;
}
catch( CLuceneError& error )
{
tDebug() << "Caught CLucene error:" << error.what();
Q_ASSERT( false );
}
return resultsmap;
}
QMap< int, float >
FuzzyIndex::searchAlbum( const Tomahawk::query_ptr& query )
{
Q_ASSERT( query->isFullTextQuery() );
QMutexLocker lock( &m_mutex );
QMap< int, float > resultsmap;
try
{
if ( !m_luceneReader )
{
if ( !IndexReader::indexExists( TomahawkUtils::appDataDir().absoluteFilePath( "tomahawk.lucene" ).toStdString().c_str() ) )
{
qDebug() << Q_FUNC_INFO << "index didn't exist.";
return resultsmap;
}
m_luceneReader = IndexReader::open( m_luceneDir );
m_luceneSearcher = _CLNEW IndexSearcher( m_luceneReader );
}
QueryParser parser( _T( "album" ), m_analyzer );
QString escapedName = QString::fromWCharArray( parser.escape( DatabaseImpl::sortname( query->fullTextQuery() ).toStdWString().c_str() ) );
Query* qry = _CLNEW FuzzyQuery( _CLNEW Term( _T( "album" ), escapedName.toStdWString().c_str() ) );
Hits* hits = m_luceneSearcher->search( qry );
for ( uint i = 0; i < hits->length(); i++ )
{
Document* d = &hits->doc( i );
float score = hits->score( i );
int id = QString::fromWCharArray( d->get( _T( "albumid" ) ) ).toInt();
if ( score > 0.30 )
{
resultsmap.insert( id, score );
// tDebug() << "Index hit:" << id << score;
}
}

View File

@@ -25,6 +25,8 @@
#include <QString>
#include <QMutex>
#include "query.h"
namespace lucene
{
namespace analysis
@@ -58,7 +60,7 @@ public:
void beginIndexing();
void endIndexing();
void appendFields( const QString& table, const QMap< unsigned int, QString >& fields );
void appendFields( const QMap< unsigned int, QMap< QString, QString > >& trackData );
signals:
void indexReady();
@@ -66,7 +68,8 @@ signals:
public slots:
void loadLuceneIndex();
QMap< int, float > search( const QString& table, const QString& name );
QMap< int, float > search( const Tomahawk::query_ptr& query );
QMap< int, float > searchAlbum( const Tomahawk::query_ptr& query );
private:
DatabaseImpl& m_db;

View File

@@ -162,6 +162,7 @@ CREATE TABLE IF NOT EXISTS file (
);
CREATE UNIQUE INDEX file_url_src_uniq ON file(source, url);
CREATE INDEX file_source ON file(source);
CREATE INDEX file_mtime ON file(mtime);
-- mtime of dir when last scanned.
-- load into memory when rescanning, skip stuff that's unchanged

View File

@@ -35,6 +35,9 @@
#include "utils/xspfloader.h"
#include "jobview/JobStatusView.h"
#include "jobview/JobStatusModel.h"
#include "jobview/ErrorStatusMessage.h"
#include "pipeline.h"
#ifdef QCA2_FOUND
#include "utils/groovesharkparser.h"
#endif //QCA2_FOUND
@@ -43,7 +46,7 @@
using namespace Tomahawk;
bool DropJob::s_canParseSpotifyPlaylists = false;
static QString s_dropJobInfoId = "dropjob";
DropJob::DropJob( QObject *parent )
: QObject( parent )
@@ -124,7 +127,7 @@ DropJob::acceptsMimeData( const QMimeData* data, DropJob::DropTypes acceptedType
// Not the most elegant
if ( url.contains( "spotify" ) && url.contains( "playlist" ) && s_canParseSpotifyPlaylists )
return true;
if ( url.contains( "grooveshark.com" ) && url.contains( "playlist" ) )
return true;
}
@@ -571,7 +574,7 @@ DropJob::handleGroovesharkUrls ( const QString& urlsRaw )
#ifdef QCA2_FOUND
QStringList urls = urlsRaw.split( QRegExp( "\\s+" ), QString::SkipEmptyParts );
tDebug() << "Got Grooveshark urls!" << urls;
if ( dropAction() == Default )
setDropAction( Create );
@@ -691,6 +694,67 @@ DropJob::onTracksAdded( const QList<Tomahawk::query_ptr>& tracksList )
}
void
DropJob::tracksFromDB( const QList< query_ptr >& tracks )
{
// Tracks that we get from databasecommand_alltracks are resolved only against the database and explicitly marked
// as finished. if the source they resolve to is offline they will not resolve against any resolver.
// explicitly resolve them if they fall in that case first
foreach( const query_ptr& track, tracks )
{
if ( !track->playable() && !track->solved() && track->results().size() ) // we have offline results
{
track->setResolveFinished( false );
Pipeline::instance()->resolve( track );
}
}
album_ptr albumPtr;
artist_ptr artistPtr;
if ( Tomahawk::Album* album = qobject_cast< Tomahawk::Album* >( sender() ) )
{
foreach ( const album_ptr& ptr, m_albumsToKeep )
if ( ptr.data() == album )
{
albumPtr = ptr;
m_albumsToKeep.remove( ptr );
}
}
else if ( Tomahawk::Artist* artist = qobject_cast< Tomahawk::Artist* >( sender() ) )
{
foreach ( const artist_ptr& ptr, m_artistsToKeep )
if ( ptr.data() == artist )
{
artistPtr = ptr;
m_artistsToKeep.remove( ptr );
}
}
// If we have no tracks, this means no sources in our network have the give request (artist or album)
// Since we really do want to try to drop them, we ask the infosystem as well.
if ( tracks.isEmpty() )
{
if ( !albumPtr.isNull() && !albumPtr->artist().isNull() )
{
Q_ASSERT( artistPtr.isNull() );
--m_queryCount; // This query is done. New query is infosystem query
getAlbumFromInfoystem( albumPtr->artist()->name(), albumPtr->name() );
}
else if ( !artistPtr.isNull() )
{
Q_ASSERT( albumPtr.isNull() );
--m_queryCount;
getTopTen( artistPtr->name() );
}
}
else
{
onTracksAdded( tracks );
}
}
void
DropJob::removeDuplicates()
{
@@ -699,10 +763,18 @@ DropJob::removeDuplicates()
{
bool contains = false;
foreach( const Tomahawk::query_ptr &tmpItem, list )
{
if ( item->album() == tmpItem->album()
&& item->artist() == tmpItem->artist()
&& item->track() == tmpItem->track() )
{
if ( item->playable() && !tmpItem->playable() )
list.replace( list.indexOf( tmpItem ), item );
contains = true;
break;
}
}
if ( !contains )
list.append( item );
}
@@ -733,28 +805,31 @@ DropJob::removeRemoteSources()
void
DropJob::infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData requestData, QVariant output )
{
if ( requestData.caller == "changeme" )
if ( requestData.caller == s_dropJobInfoId )
{
Tomahawk::InfoSystem::InfoStringHash artistInfo;
const Tomahawk::InfoSystem::InfoStringHash info = requestData.input.value< Tomahawk::InfoSystem::InfoStringHash >();
artistInfo = requestData.input.value< Tomahawk::InfoSystem::InfoStringHash >();
const QString artist = info["artist"];
const QString album = info["album"];
QString artist = artistInfo["artist"];
qDebug() << "Got requestData response for artist" << artist << output;
qDebug() << "Got requestData response for artist" << artist << "and album:" << album << output;
QList< query_ptr > results;
int i = 0;
foreach ( const QVariant& title, output.toMap().value( "tracks" ).toList() )
{
qDebug() << "got title" << title;
results << Query::get( artist, title.toString(), QString(), uuid() );
if ( ++i == 10 ) // Only getting top ten for now. Would make sense to make it configurable
break;
}
if ( results.isEmpty() )
{
const QString which = album.isEmpty() ? "artist" : "album";
JobStatusView::instance()->model()->addJob( new ErrorStatusMessage( tr( "No tracks found for given %1" ).arg( which ), 5 ) );
}
onTracksAdded( results );
}
}
@@ -766,8 +841,10 @@ DropJob::getArtist( const QString &artist )
artist_ptr artistPtr = Artist::get( artist );
if ( artistPtr->playlistInterface()->tracks().isEmpty() )
{
m_artistsToKeep.insert( artistPtr );
connect( artistPtr.data(), SIGNAL( tracksAdded( QList<Tomahawk::query_ptr> ) ),
SLOT( onTracksAdded( QList<Tomahawk::query_ptr> ) ) );
SLOT( tracksFromDB( QList<Tomahawk::query_ptr> ) ) );
m_queryCount++;
return QList< query_ptr >();
}
@@ -787,9 +864,14 @@ DropJob::getAlbum(const QString &artist, const QString &album)
if ( albumPtr->playlistInterface()->tracks().isEmpty() )
{
// For albums that don't exist until this moment, we are the main shared pointer holding on.
// fetching the tracks is asynchronous, so the resulting signal is queued. when we go out of scope we delete
// the artist_ptr which means we never get the signal delivered. so we hold on to the album pointer till we're done
m_albumsToKeep.insert( albumPtr );
m_dropJob = new DropJobNotifier( QPixmap( RESPATH "images/album-icon.png" ), Album );
connect( albumPtr.data(), SIGNAL( tracksAdded( QList<Tomahawk::query_ptr> ) ),
SLOT( onTracksAdded( QList<Tomahawk::query_ptr> ) ) );
SLOT( tracksFromDB( QList<Tomahawk::query_ptr> ) ) );
JobStatusView::instance()->model()->addJob( m_dropJob );
m_queryCount++;
@@ -811,7 +893,7 @@ DropJob::getTopTen( const QString &artist )
artistInfo["artist"] = artist;
Tomahawk::InfoSystem::InfoRequestData requestData;
requestData.caller = "changeme";
requestData.caller = s_dropJobInfoId;
requestData.customData = QVariantMap();
requestData.input = QVariant::fromValue< Tomahawk::InfoSystem::InfoStringHash >( artistInfo );
@@ -821,3 +903,28 @@ DropJob::getTopTen( const QString &artist )
m_queryCount++;
}
void
DropJob::getAlbumFromInfoystem( const QString& artist, const QString& album )
{
connect( Tomahawk::InfoSystem::InfoSystem::instance(),
SIGNAL( info( Tomahawk::InfoSystem::InfoRequestData, QVariant ) ),
SLOT( infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData, QVariant ) ) );
Tomahawk::InfoSystem::InfoStringHash artistInfo;
artistInfo["artist"] = artist;
artistInfo["album"] = album;
Tomahawk::InfoSystem::InfoRequestData requestData;
requestData.caller = s_dropJobInfoId;
requestData.customData = QVariantMap();
requestData.input = QVariant::fromValue< Tomahawk::InfoSystem::InfoStringHash >( artistInfo );
requestData.type = Tomahawk::InfoSystem::InfoAlbumSongs;
Tomahawk::InfoSystem::InfoSystem::instance()->getInfo( requestData );
m_queryCount++;
}

View File

@@ -23,6 +23,8 @@
#include "query.h"
#include "infosystem/infosystem.h"
#include "utils/xspfloader.h"
#include <QObject>
#include <QStringList>
#include <QMimeData>
@@ -120,9 +122,9 @@ private slots:
void onTracksAdded( const QList<Tomahawk::query_ptr>& );
void infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData requestData, QVariant output );
void tracksFromDB( const QList< Tomahawk::query_ptr >& );
private:
/// handle parsing mime data
void handleAllUrls( const QString& urls );
void handleTrackUrls( const QString& urls );
QList< Tomahawk::query_ptr > tracksFromQueryList( const QMimeData* d );
@@ -135,6 +137,7 @@ private:
QList< Tomahawk::query_ptr > getAlbum( const QString& artist, const QString& album );
void getTopTen( const QString& artist );
void getAlbumFromInfoystem( const QString& artist, const QString& album );
void removeDuplicates();
void removeRemoteSources();
@@ -151,6 +154,8 @@ private:
Tomahawk::DropJobNotifier* m_dropJob;
QList< Tomahawk::query_ptr > m_resultList;
QSet< Tomahawk::album_ptr > m_albumsToKeep;
QSet< Tomahawk::artist_ptr > m_artistsToKeep;
static bool s_canParseSpotifyPlaylists;
};

View File

@@ -45,9 +45,9 @@ ChartsPlugin::ChartsPlugin()
, m_chartsFetchJobs( 0 )
{
/// Add resources here
m_chartResources << "billboard" << "itunes" << "rdio" << "wearehunted" << "ex.fm" << "soundcloudwall.com";
m_chartResources << "billboard" << "itunes" << "rdio" << "wearehunted" << "ex.fm" << "soundcloudwall";
/// If you add resource, update version aswell
m_chartVersion = "1.0";
m_chartVersion = "2.1";
m_supportedGetTypes << InfoChart << InfoChartCapabilities;
}
@@ -332,7 +332,9 @@ ChartsPlugin::chartTypes()
if( source == "itunes" ){
chartName = "iTunes";
}
if( source == "soundcloudwall" ){
chartName = "SoundCloudWall";
}
if( source == "wearehunted" ){
chartName = "WeAreHunted";
}

View File

@@ -451,8 +451,15 @@ LastFmPlugin::notInCacheSlot( QHash<QString, QString> criteria, Tomahawk::InfoSy
QString artistName = criteria["artist"];
QString albumName = criteria["album"];
QString imgurl = "http://ws.audioscrobbler.com/2.0/?method=album.imageredirect&artist=%1&album=%2&autocorrect=1&size=large&api_key=7a90f6672a04b809ee309af169f34b8b";
QNetworkRequest req( imgurl.arg( artistName ).arg( albumName ) );
QUrl imgurl( "http://ws.audioscrobbler.com/2.0/" );
imgurl.addQueryItem( "method", "album.imageredirect" );
imgurl.addEncodedQueryItem( "artist", QUrl::toPercentEncoding( artistName, "", "+" ) );
imgurl.addEncodedQueryItem( "album", QUrl::toPercentEncoding( albumName, "", "+" ) );
imgurl.addQueryItem( "autocorrect", QString::number( 1 ) );
imgurl.addQueryItem( "size", "large" );
imgurl.addQueryItem( "api_key", "7a90f6672a04b809ee309af169f34b8b" );
QNetworkRequest req( imgurl );
QNetworkReply* reply = TomahawkUtils::nam()->get( req );
reply->setProperty( "requestData", QVariant::fromValue< Tomahawk::InfoSystem::InfoRequestData >( requestData ) );
@@ -464,8 +471,14 @@ LastFmPlugin::notInCacheSlot( QHash<QString, QString> criteria, Tomahawk::InfoSy
{
QString artistName = criteria["artist"];
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 ) );
QUrl imgurl( "http://ws.audioscrobbler.com/2.0/" );
imgurl.addQueryItem( "method", "artist.imageredirect" );
imgurl.addEncodedQueryItem( "artist", QUrl::toPercentEncoding( artistName, "", "+" ) );
imgurl.addQueryItem( "autocorrect", QString::number( 1 ) );
imgurl.addQueryItem( "size", "large" );
imgurl.addQueryItem( "api_key", "7a90f6672a04b809ee309af169f34b8b" );
QNetworkRequest req( imgurl );
QNetworkReply* reply = TomahawkUtils::nam()->get( req );
reply->setProperty( "requestData", QVariant::fromValue< Tomahawk::InfoSystem::InfoRequestData >( requestData ) );

View File

@@ -43,7 +43,6 @@ SpotifyPlugin::SpotifyPlugin()
: InfoPlugin()
, m_chartsFetchJobs( 0 )
{
m_supportedGetTypes << InfoChart << InfoChartCapabilities;
}
@@ -71,7 +70,6 @@ SpotifyPlugin::getInfo( Tomahawk::InfoSystem::InfoRequestData requestData )
InfoStringHash hash = requestData.input.value< Tomahawk::InfoSystem::InfoStringHash >();
switch ( requestData.type )
{
case InfoChart:
@@ -87,6 +85,7 @@ SpotifyPlugin::getInfo( Tomahawk::InfoSystem::InfoRequestData requestData )
case InfoChartCapabilities:
fetchChartCapabilities( requestData );
break;
default:
dataError( requestData );
}
@@ -110,7 +109,6 @@ SpotifyPlugin::fetchChart( Tomahawk::InfoSystem::InfoRequestData requestData )
tDebug( LOGVERBOSE ) << Q_FUNC_INFO << "Hash did not contain required params!";
dataError( requestData );
return;
}
/// Set the criterias for current chart
criteria["chart_id"] = hash["chart_id"];
@@ -118,6 +116,8 @@ SpotifyPlugin::fetchChart( Tomahawk::InfoSystem::InfoRequestData requestData )
emit getCachedInfo( criteria, 86400000 /* Expire chart cache in 1 day */, requestData );
}
void
SpotifyPlugin::fetchChartCapabilities( Tomahawk::InfoSystem::InfoRequestData requestData )
{
@@ -132,12 +132,12 @@ SpotifyPlugin::fetchChartCapabilities( Tomahawk::InfoSystem::InfoRequestData req
emit getCachedInfo( criteria, 604800000, requestData );
}
void
SpotifyPlugin::notInCacheSlot( Tomahawk::InfoSystem::InfoStringHash criteria, Tomahawk::InfoSystem::InfoRequestData requestData )
{
switch ( requestData.type )
{
case InfoChart:
{
/// Fetch the chart, we need source and id
@@ -149,8 +149,6 @@ SpotifyPlugin::notInCacheSlot( Tomahawk::InfoSystem::InfoStringHash criteria, To
reply->setProperty( "requestData", QVariant::fromValue< Tomahawk::InfoSystem::InfoRequestData >( requestData ) );
connect( reply, SIGNAL( finished() ), SLOT( chartReturned() ) );
return;
}
case InfoChartCapabilities:
{
@@ -212,9 +210,8 @@ SpotifyPlugin::chartTypes()
}
QVariantMap charts;
foreach(QVariant geos, chartObj.value("Charts").toList().takeLast().toMap().value("geo").toList() )
foreach( QVariant geos, chartObj.value( "Charts" ).toList().takeLast().toMap().value( "geo" ).toList() )
{
const QString geo = geos.toMap().value( "name" ).toString();
const QString geoId = geos.toMap().value( "id" ).toString();
QString country;
@@ -225,7 +222,6 @@ SpotifyPlugin::chartTypes()
country = geo;
else
{
QLocale l( QString( "en_%1" ).arg( geo ) );
country = Tomahawk::CountryUtils::fullCountryFromCode( geo );
@@ -240,7 +236,7 @@ SpotifyPlugin::chartTypes()
}
QList< InfoStringHash > chart_types;
foreach(QVariant types, chartObj.value("Charts").toList().takeFirst().toMap().value("types").toList() )
foreach( QVariant types, chartObj.value( "Charts" ).toList().takeFirst().toMap().value( "types" ).toList() )
{
QString type = types.toMap().value( "id" ).toString();
QString label = types.toMap().value( "name" ).toString();
@@ -251,18 +247,15 @@ SpotifyPlugin::chartTypes()
c[ "type" ] = type;
chart_types.append( c );
}
charts.insert( country.toUtf8(), QVariant::fromValue<QList< InfoStringHash > >( chart_types ) );
}
QVariantMap defaultMap;
defaultMap[ "spotify" ] = QStringList() << "United States" << "Top Albums";
m_allChartsMap[ "defaults" ] = defaultMap;
m_allChartsMap.insert( "Spotify", QVariant::fromValue<QVariantMap>( charts ) );
}
else
{
@@ -281,13 +274,12 @@ SpotifyPlugin::chartTypes()
}
m_cachedRequests.clear();
}
}
void
SpotifyPlugin::chartReturned()
{
/// Chart request returned something! Woho
QNetworkReply* reply = qobject_cast<QNetworkReply*>( sender() );
QString url = reply->url().toString();
@@ -318,14 +310,13 @@ SpotifyPlugin::chartReturned()
else
setChartType( None );
foreach(QVariant result, res.value("toplist").toMap().value("result").toList() )
foreach( QVariant result, res.value( "toplist" ).toMap().value( "result" ).toList() )
{
QString title, artist;
QVariantMap chartMap = result.toMap();
if ( !chartMap.isEmpty() )
{
title = chartMap.value( "title" ).toString();
artist = chartMap.value( "artist" ).toString();
@@ -341,7 +332,6 @@ SpotifyPlugin::chartReturned()
if( chartType() == Album )
{
InfoStringHash pair;
pair["artist"] = artist;
pair["album"] = title;
@@ -351,10 +341,8 @@ SpotifyPlugin::chartReturned()
if( chartType() == Artist )
{
top_artists << chartMap.value( "name" ).toString();
qDebug() << "SpotifyChart type is artist";
}
}
}
@@ -393,5 +381,4 @@ SpotifyPlugin::chartReturned()
}
else
qDebug() << "Network error in fetching chart:" << reply->url().toString();
}

View File

@@ -42,8 +42,6 @@ MprisPlugin::MprisPlugin()
: InfoPlugin()
, m_coverTempFile( 0 )
{
qDebug() << Q_FUNC_INFO;
// init
m_playbackStatus = "Stopped";
@@ -54,79 +52,84 @@ MprisPlugin::MprisPlugin()
new MprisPluginRootAdaptor( this );
new MprisPluginPlayerAdaptor( this );
QDBusConnection dbus = QDBusConnection::sessionBus();
dbus.registerObject("/org/mpris/MediaPlayer2", this);
dbus.registerService("org.mpris.MediaPlayer2.tomahawk");
dbus.registerObject( "/org/mpris/MediaPlayer2", this );
dbus.registerService( "org.mpris.MediaPlayer2.tomahawk" );
// Listen to volume changes
connect( AudioEngine::instance(), SIGNAL( volumeChanged( int ) ),
SLOT( onVolumeChanged( int ) ) );
SLOT( onVolumeChanged( int ) ) );
// When the playlist changes, signals for several properties are sent
connect( AudioEngine::instance(), SIGNAL( playlistChanged( Tomahawk::playlistinterface_ptr ) ),
SLOT( onPlaylistChanged( Tomahawk::playlistinterface_ptr ) ) );
SLOT( onPlaylistChanged( Tomahawk::playlistinterface_ptr ) ) );
// When a track is added or removed, CanGoNext updated signal is sent
Tomahawk::playlistinterface_ptr playlist = AudioEngine::instance()->playlist();
if( !playlist.isNull() )
if ( !playlist.isNull() )
{
connect( playlist.data(), SIGNAL( trackCountChanged( unsigned int ) ),
SLOT( onTrackCountChanged( unsigned int ) ) );
SLOT( onTrackCountChanged( unsigned int ) ) );
}
// Connect to AudioEngine's seeked signal
connect( AudioEngine::instance(), SIGNAL( seeked( qint64 ) ),
SLOT( onSeeked( qint64 ) ) );
SLOT( onSeeked( qint64 ) ) );
// Connect to the InfoSystem (we need to get album covers via getInfo)
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 ) ) );
SIGNAL( info( Tomahawk::InfoSystem::InfoRequestData, QVariant ) ),
SLOT( infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData, QVariant ) ) );
connect( Tomahawk::InfoSystem::InfoSystem::instance(),
SIGNAL( finished( QString ) ),
SLOT( infoSystemFinished( QString ) ) );
}
MprisPlugin::~MprisPlugin()
{
qDebug() << Q_FUNC_INFO;
delete m_coverTempFile;
}
// org.mpris.MediaPlayer2
bool
MprisPlugin::canQuit() const
{
qDebug() << Q_FUNC_INFO;
return true;
}
bool
MprisPlugin::canRaise() const
{
qDebug() << Q_FUNC_INFO;
return false;
}
bool
MprisPlugin::hasTrackList() const
{
qDebug() << Q_FUNC_INFO;
return false;
}
QString
MprisPlugin::identity() const
{
return QString("Tomahawk");
return QString( "Tomahawk" );
}
QString
MprisPlugin::desktopEntry() const
{
return QString("tomahawk");
return QString( "tomahawk" );
}
QStringList
MprisPlugin::supportedUriSchemes() const
{
@@ -135,23 +138,27 @@ MprisPlugin::supportedUriSchemes() const
return uriSchemes;
}
QStringList
MprisPlugin::supportedMimeTypes() const
{
return QStringList();
}
void
MprisPlugin::Raise()
{
}
void
MprisPlugin::Quit()
{
QApplication::quit();
}
// org.mpris.MediaPlayer2.Player
bool
@@ -160,24 +167,28 @@ MprisPlugin::canControl() const
return true;
}
bool
MprisPlugin::canGoNext() const
{
return AudioEngine::instance()->canGoNext();
}
bool
MprisPlugin::canGoPrevious() const
{
return AudioEngine::instance()->canGoPrevious();
}
bool
MprisPlugin::canPause() const
{
return AudioEngine::instance()->currentTrack();
}
bool
MprisPlugin::canPlay() const
{
@@ -186,6 +197,7 @@ MprisPlugin::canPlay() const
return AudioEngine::instance()->currentTrack() || ( !p.isNull() && p->trackCount() );
}
bool
MprisPlugin::canSeek() const
{
@@ -196,6 +208,7 @@ MprisPlugin::canSeek() const
}
QString
MprisPlugin::loopStatus() const
{
@@ -215,39 +228,42 @@ MprisPlugin::loopStatus() const
return "None";
break;
default:
return QString("None");
return "None";
break;
}
return QString("None");
return QString( "None" );
}
void
MprisPlugin::setLoopStatus( const QString &value )
MprisPlugin::setLoopStatus( const QString& value )
{
Tomahawk::playlistinterface_ptr p = AudioEngine::instance()->playlist();
if ( p.isNull() )
return;
if( value == "Track")
if ( value == "Track" )
p->setRepeatMode( PlaylistInterface::RepeatOne );
else if( value == "Playlist" )
else if ( value == "Playlist" )
p->setRepeatMode( PlaylistInterface::RepeatAll );
else if( value == "None" )
else if ( value == "None" )
p->setRepeatMode( PlaylistInterface::NoRepeat );
}
double
MprisPlugin::maximumRate() const
{
return 1.0;
}
QVariantMap
MprisPlugin::metadata() const
{
QVariantMap metadataMap;
Tomahawk::result_ptr track = AudioEngine::instance()->currentTrack();
if( track )
if ( track )
{
metadataMap.insert( "mpris:trackid", QString( "/track/" ) + track->id().replace( "-", "" ) );
metadataMap.insert( "mpris:length", track->duration() );
@@ -256,9 +272,11 @@ MprisPlugin::metadata() const
metadataMap.insert( "xesam:title", track->track() );
// Only return art if tempfile exists, and if its name contains the same "artist_album_tomahawk_cover.png"
if( m_coverTempFile && m_coverTempFile->exists() &&
m_coverTempFile->fileName().contains( track->artist()->name() + "_" + track->album()->name() + "_tomahawk_cover.png" ) )
if ( m_coverTempFile && m_coverTempFile->exists() &&
m_coverTempFile->fileName().contains( track->artist()->name() + "_" + track->album()->name() + "_tomahawk_cover.png" ) )
{
metadataMap.insert( "mpris:artUrl", QString( QUrl::fromLocalFile( QFileInfo( *m_coverTempFile ).absoluteFilePath() ).toEncoded() ) );
}
else
{
// Need to fetch the album cover
@@ -280,18 +298,21 @@ MprisPlugin::metadata() const
return metadataMap;
}
double
MprisPlugin::minimumRate() const
{
return 1.0;
}
QString
MprisPlugin::playbackStatus() const
{
return m_playbackStatus;
}
qlonglong
MprisPlugin::position() const
{
@@ -299,18 +320,21 @@ MprisPlugin::position() const
return (qlonglong) ( AudioEngine::instance()->currentTime() * 1000 );
}
double
MprisPlugin::rate() const
{
return 1.0;
}
void
MprisPlugin::setRate( double value )
{
Q_UNUSED( value );
}
bool
MprisPlugin::shuffle() const
{
@@ -320,6 +344,7 @@ MprisPlugin::shuffle() const
return p->shuffled();
}
void
MprisPlugin::setShuffle( bool value )
{
@@ -329,70 +354,76 @@ MprisPlugin::setShuffle( bool value )
return p->setShuffled( value );
}
double
MprisPlugin::volume() const
{
return AudioEngine::instance()->volume();
}
void
MprisPlugin::setVolume( double value )
{
AudioEngine::instance()->setVolume( value );
}
void
MprisPlugin::Next()
{
AudioEngine::instance()->next();
}
void
MprisPlugin::OpenUri( const QString &Uri )
MprisPlugin::OpenUri( const QString& Uri )
{
if( Uri.contains( "tomahawk://" ) )
if ( Uri.contains( "tomahawk://" ) )
GlobalActionManager::instance()->parseTomahawkLink( Uri );
else if( Uri.contains( "spotify:" ) )
else if ( Uri.contains( "spotify:" ) )
GlobalActionManager::instance()->openSpotifyLink( Uri );
}
void
MprisPlugin::Pause()
{
AudioEngine::instance()->pause();
}
void
MprisPlugin::Play()
{
AudioEngine::instance()->play();
}
void
MprisPlugin::PlayPause()
{
AudioEngine::instance()->playPause();
}
void
MprisPlugin::Previous()
{
AudioEngine::instance()->previous();
}
void
MprisPlugin::Seek( qlonglong Offset )
{
qDebug() << Q_FUNC_INFO;
if( !canSeek() )
if ( !canSeek() )
return;
qlonglong seekTime = position() + Offset;
qDebug() << "seekTime: " << seekTime;
if( seekTime < 0 )
if ( seekTime < 0 )
AudioEngine::instance()->seek( 0 );
else if( seekTime > AudioEngine::instance()->currentTrackTotalTime()*1000 )
else if ( seekTime > AudioEngine::instance()->currentTrackTotalTime()*1000 )
Next();
// seekTime is in microseconds, but we work internally in milliseconds
else
@@ -400,50 +431,46 @@ MprisPlugin::Seek( qlonglong Offset )
}
void
MprisPlugin::SetPosition( const QDBusObjectPath &TrackId, qlonglong Position )
MprisPlugin::SetPosition( const QDBusObjectPath& TrackId, qlonglong Position )
{
qDebug() << Q_FUNC_INFO;
if( !canSeek() )
if ( !canSeek() )
return;
qDebug() << "path: " << TrackId.path();
qDebug() << "position: " << Position;
if( TrackId.path() != QString("/track/") + AudioEngine::instance()->currentTrack()->id().replace( "-", "" ) )
if ( TrackId.path() != QString( "/track/" ) + AudioEngine::instance()->currentTrack()->id().replace( "-", "" ) )
return;
if( ( Position < 0) || ( Position > AudioEngine::instance()->currentTrackTotalTime()*1000 ) )
if ( ( Position < 0) || ( Position > AudioEngine::instance()->currentTrackTotalTime()*1000 ) )
return;
qDebug() << "seeking to: " << Position/1000 << "ms";
AudioEngine::instance()->seek( (qint64) (Position / 1000 ) );
}
void
MprisPlugin::Stop()
{
AudioEngine::instance()->stop();
}
// InfoPlugin Methods
void
MprisPlugin::getInfo( Tomahawk::InfoSystem::InfoRequestData requestData )
{
Q_UNUSED( requestData );
qDebug() << Q_FUNC_INFO;
return;
}
void
MprisPlugin::pushInfo( QString caller, Tomahawk::InfoSystem::InfoType type, QVariant input )
{
Q_UNUSED( caller );
qDebug() << Q_FUNC_INFO;
bool isPlayingInfo = false;
switch ( type )
@@ -469,11 +496,11 @@ MprisPlugin::pushInfo( QString caller, Tomahawk::InfoSystem::InfoType type, QVar
break;
}
if( isPlayingInfo )
notifyPropertyChanged( "org.mpris.MediaPlayer2.Player", "PlaybackStatus");
if ( isPlayingInfo )
notifyPropertyChanged( "org.mpris.MediaPlayer2.Player", "PlaybackStatus" );
}
void
MprisPlugin::stateChanged( AudioState newState, AudioState oldState )
{
@@ -481,12 +508,11 @@ MprisPlugin::stateChanged( AudioState newState, AudioState oldState )
Q_UNUSED( oldState );
}
/** Audio state slots */
void
MprisPlugin::audioStarted( const QVariant &input )
MprisPlugin::audioStarted( const QVariant& input )
{
qDebug() << Q_FUNC_INFO;
if ( !input.canConvert< Tomahawk::InfoSystem::InfoStringHash >() )
return;
@@ -495,64 +521,55 @@ MprisPlugin::audioStarted( const QVariant &input )
return;
m_playbackStatus = "Playing";
notifyPropertyChanged( "org.mpris.MediaPlayer2.Player", "Metadata");
//hash["artist"];
//hash["title"];
//QString nowPlaying = "";
//qDebug() << "nowPlaying: " << nowPlaying;
notifyPropertyChanged( "org.mpris.MediaPlayer2.Player", "Metadata" );
}
void
MprisPlugin::audioFinished( const QVariant &input )
MprisPlugin::audioFinished( const QVariant& input )
{
Q_UNUSED( input );
//qDebug() << Q_FUNC_INFO;
}
void
MprisPlugin::audioStopped()
{
qDebug() << Q_FUNC_INFO;
m_playbackStatus = "Stopped";
}
void
MprisPlugin::audioPaused()
{
qDebug() << Q_FUNC_INFO;
m_playbackStatus = "Paused";
}
void
MprisPlugin::audioResumed( const QVariant &input )
MprisPlugin::audioResumed( const QVariant& input )
{
qDebug() << Q_FUNC_INFO;
audioStarted( input );
}
void
MprisPlugin::onVolumeChanged( int volume )
{
Q_UNUSED( volume );
notifyPropertyChanged( "org.mpris.MediaPlayer2.Player", "Volume");
notifyPropertyChanged( "org.mpris.MediaPlayer2.Player", "Volume" );
}
void
MprisPlugin::onPlaylistChanged( Tomahawk::playlistinterface_ptr playlist )
{
qDebug() << Q_FUNC_INFO;
disconnect( this, SLOT( onTrackCountChanged( unsigned int ) ) );
qDebug() << "disconnected";
if( !playlist.isNull() )
qDebug() << "playlist not null";
if( !playlist.isNull() )
if ( !playlist.isNull() )
connect( playlist.data(), SIGNAL( trackCountChanged( unsigned int ) ),
SLOT( onTrackCountChanged( unsigned int ) ) );
qDebug() << "connected new playlist";
// Notify relevant changes
notifyPropertyChanged( "org.mpris.MediaPlayer2.Player", "LoopStatus" );
notifyPropertyChanged( "org.mpris.MediaPlayer2.Player", "Shuffle" );
@@ -560,6 +577,7 @@ MprisPlugin::onPlaylistChanged( Tomahawk::playlistinterface_ptr playlist )
onTrackCountChanged( 0 );
}
void
MprisPlugin::onTrackCountChanged( unsigned int tracks )
{
@@ -569,12 +587,14 @@ MprisPlugin::onTrackCountChanged( unsigned int tracks )
}
void
MprisPlugin::onSeeked( qint64 ms )
{
void
MprisPlugin::onSeeked( qint64 ms )
{
qlonglong us = (qlonglong) ( ms*1000 );
emit Seeked( us );
}
}
void
MprisPlugin::infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData requestData, QVariant output )
@@ -603,7 +623,7 @@ MprisPlugin::infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData requestData,
image.loadFromData( ba );
// Pull out request data for album+artist
if( !requestData.input.canConvert< Tomahawk::InfoSystem::InfoStringHash >() )
if ( !requestData.input.canConvert< Tomahawk::InfoSystem::InfoStringHash >() )
{
qDebug() << "Cannot convert metadata input to album cover retrieval";
return;
@@ -612,41 +632,33 @@ MprisPlugin::infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData requestData,
Tomahawk::InfoSystem::InfoStringHash hash = requestData.input.value< Tomahawk::InfoSystem::InfoStringHash>();
// delete the old tempfile and make new one, to avoid caching of filename by mpris clients
if( m_coverTempFile )
if ( m_coverTempFile )
{
delete m_coverTempFile;
m_coverTempFile = new QTemporaryFile( QDir::toNativeSeparators(
QDir::tempPath() + "/" + hash["artist"] + "_" + hash["album"] + "_tomahawk_cover.png" ) );
if( !m_coverTempFile->open() )
m_coverTempFile = 0;
}
if ( image.isNull() )
return;
m_coverTempFile = new QTemporaryFile( QDir::toNativeSeparators( QDir::tempPath() + "/" + hash["artist"] + "_" + hash["album"] + "_tomahawk_cover.png" ) );
if ( !m_coverTempFile->open() )
{
qDebug() << "WARNING: could not write temporary file for cover art!";
}
// Finally, save the image to the new temp file
//if( image.save( QFileInfo( *m_coverTempFile ).absoluteFilePath(), "PNG" ) )
if( image.save( m_coverTempFile, "PNG") )
if ( image.save( m_coverTempFile, "PNG" ) )
{
qDebug() << Q_FUNC_INFO << "Image saving successful, notifying";
qDebug() << "Saving to: " << QFileInfo( *m_coverTempFile ).absoluteFilePath();
qDebug() << "Saving cover image to:" << QFileInfo( *m_coverTempFile ).absoluteFilePath();
m_coverTempFile->close();
notifyPropertyChanged( "org.mpris.MediaPlayer2.Player", "Metadata" );
}
else
{
qDebug() << Q_FUNC_INFO << " failed to save image!";
tDebug() << Q_FUNC_INFO << "failed to save cover image!";
m_coverTempFile->close();
}
/*
if( m_coverTempFile->open() )
{
QTextStream out( m_coverTempFile );
out << ba;
m_coverTempFile->close();
notifyPropertyChanged( "org.mpris.MediaPlayer2.Player", "Metadata" );
}
*/
}
}
@@ -657,14 +669,14 @@ MprisPlugin::infoSystemFinished( QString target )
Q_UNUSED( target );
}
void
MprisPlugin::notifyPropertyChanged( const QString& interface,
const QString& propertyName )
MprisPlugin::notifyPropertyChanged( const QString& interface, const QString& propertyName )
{
QDBusMessage signal = QDBusMessage::createSignal(
"/org/mpris/MediaPlayer2",
"org.freedesktop.DBus.Properties",
"PropertiesChanged");
"PropertiesChanged" );
signal << interface;
QVariantMap changedProps;
changedProps.insert(propertyName, property(propertyName.toAscii()));

View File

@@ -87,7 +87,7 @@ public:
Q_PROPERTY( QString LoopStatus READ loopStatus WRITE setLoopStatus )
QString loopStatus() const;
void setLoopStatus( const QString &value );
void setLoopStatus( const QString& value );
Q_PROPERTY( double MaximumRate READ maximumRate )
double maximumRate() const;
@@ -129,16 +129,15 @@ public slots:
// org.mpris.MediaPlayer2.Player
void Next();
void OpenUri( const QString &Uri );
void OpenUri( const QString& Uri );
void Pause();
void Play();
void PlayPause();
void Previous();
void Seek( qlonglong Offset );
void SetPosition( const QDBusObjectPath &TrackId, qlonglong Position );
void SetPosition( const QDBusObjectPath& TrackId, qlonglong Position );
void Stop();
protected slots:
void getInfo( Tomahawk::InfoSystem::InfoRequestData requestData );
void pushInfo( QString caller, Tomahawk::InfoSystem::InfoType type, QVariant input );
@@ -160,16 +159,16 @@ private:
// Get Info
// Push Info
void audioStarted( const QVariant &input );
void audioFinished( const QVariant &input );
void audioStarted( const QVariant& input );
void audioFinished( const QVariant& input );
void audioStopped();
void audioPaused();
void audioResumed( const QVariant &input );
void audioResumed( const QVariant& input );
// DBus
void notifyPropertyChanged( const QString& interface, const QString& propertyName );
QString m_playbackStatus;
QTemporaryFile *m_coverTempFile;
QTemporaryFile* m_coverTempFile;
};
};

View File

@@ -0,0 +1,56 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2012, Leo Franchi <lfranchi@kde.org>
*
* Tomahawk is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Tomahawk is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Tomahawk. If not, see <http://www.gnu.org/licenses/>.
*/
#include "ErrorStatusMessage.h"
#include "utils/tomahawkutils.h"
#include <QTimer>
QPixmap* ErrorStatusMessage::s_pixmap = 0;
ErrorStatusMessage::ErrorStatusMessage( const QString& message, int timeoutSecs )
: JobStatusItem()
, m_message( message )
{
m_timer = new QTimer( this );
m_timer->setInterval( timeoutSecs * 1000 );
m_timer->setSingleShot( true );
connect( m_timer, SIGNAL( timeout() ), this, SIGNAL( finished() ) );
if ( !s_pixmap )
s_pixmap = new QPixmap( RESPATH "images/process-stop.png" );
m_timer->start();
}
QPixmap
ErrorStatusMessage::icon() const
{
Q_ASSERT( s_pixmap );
return *s_pixmap;
}
QString
ErrorStatusMessage::mainText() const
{
return m_message;
}

View File

@@ -0,0 +1,48 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2012, Leo Franchi <lfranchi@kde.org>
*
* Tomahawk is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Tomahawk is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Tomahawk. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef ERRORSTATUSMESSAGE_H
#define ERRORSTATUSMESSAGE_H
#include "JobStatusItem.h"
#include "dllmacro.h"
class QTimer;
class QPixmap;
class DLLEXPORT ErrorStatusMessage : public JobStatusItem
{
Q_OBJECT
public:
explicit ErrorStatusMessage( const QString& errorMessage, int defaultTimeoutSecs = 8 );
QString type() const { return "errormessage"; }
QString rightColumnText() const { return QString(); }
QPixmap icon() const;
QString mainText() const;
bool allowMultiLine() const { return true; }
private:
QString m_message;
QTimer* m_timer;
static QPixmap* s_pixmap;
};
#endif // ERRORSTATUSMESSAGE_H

View File

@@ -0,0 +1,47 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2010-2011, Leo Franchi <lfranchi@kde.org>
*
* Tomahawk is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Tomahawk is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Tomahawk. If not, see <http://www.gnu.org/licenses/>.
*/
#include "IndexingJobItem.h"
#include "utils/tomahawkutils.h"
static QPixmap* s_indexIcon = 0;
QString
IndexingJobItem::mainText() const
{
return tr( "Indexing database" );
}
QPixmap
IndexingJobItem::icon() const
{
if ( s_indexIcon == 0 )
s_indexIcon = new QPixmap( RESPATH "images/view-refresh.png" );
return *s_indexIcon;
}
void IndexingJobItem::done()
{
emit finished();
}

View File

@@ -0,0 +1,39 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2010-2011, Leo Franchi <lfranchi@kde.org>
*
* Tomahawk is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Tomahawk is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Tomahawk. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef INDEXINGJOBITEM_H
#define INDEXINGJOBITEM_H
#include <jobview/JobStatusItem.h>
class IndexingJobItem : public JobStatusItem
{
Q_OBJECT
public:
explicit IndexingJobItem() {}
void done();
virtual QString rightColumnText() const { return QString(); }
virtual QString mainText() const;
virtual QPixmap icon() const;
virtual QString type() const { return "indexerjob"; }
};
#endif // INDEXINGJOBITEM_H

View File

@@ -23,14 +23,16 @@
#include <QPainter>
#include <QApplication>
#include <QListView>
#define ROW_HEIGHT 20
#define ICON_PADDING 1
#define PADDING 2
JobStatusDelegate::JobStatusDelegate( QObject* parent )
: QStyledItemDelegate ( parent )
, m_parentView( qobject_cast< QListView* >( parent ) )
{
Q_ASSERT( m_parentView );
}
JobStatusDelegate::~JobStatusDelegate()
@@ -45,6 +47,7 @@ JobStatusDelegate::paint( QPainter* painter, const QStyleOptionViewItem& option,
QStyleOptionViewItemV4 opt = option;
initStyleOption( &opt, index );
QFontMetrics fm( opt.font );
const bool allowMultiLine = index.data( JobStatusModel::AllowMultiLineRole ).toBool();
opt.state &= ~QStyle::State_MouseOver;
QApplication::style()->drawPrimitive( QStyle::PE_PanelItemViewItem, &opt, painter, opt.widget );
@@ -52,7 +55,9 @@ JobStatusDelegate::paint( QPainter* painter, const QStyleOptionViewItem& option,
// painter->drawLine( opt.rect.topLeft(), opt.rect.topRight() );
painter->setRenderHint( QPainter::Antialiasing );
const QRect iconRect( ICON_PADDING, ICON_PADDING + opt.rect.y(), ROW_HEIGHT - 2*ICON_PADDING, ROW_HEIGHT - 2*ICON_PADDING );
QRect iconRect( ICON_PADDING, ICON_PADDING + opt.rect.y(), ROW_HEIGHT - 2*ICON_PADDING, ROW_HEIGHT - 2*ICON_PADDING );
if ( allowMultiLine )
iconRect.moveTop( opt.rect.top() + opt.rect.height() / 2 - iconRect.height() / 2);
QPixmap p = index.data( Qt::DecorationRole ).value< QPixmap >();
p = p.scaledToHeight( iconRect.height(), Qt::SmoothTransformation );
painter->drawPixmap( iconRect, p );
@@ -71,15 +76,34 @@ JobStatusDelegate::paint( QPainter* painter, const QStyleOptionViewItem& option,
const int mainW = rightEdge - 3*PADDING - iconRect.right();
QString mainText = index.data( Qt::DisplayRole ).toString();
mainText = fm.elidedText( mainText, Qt::ElideRight, mainW );
painter->drawText( QRect( iconRect.right() + 2*PADDING, PADDING + opt.rect.y(), mainW, opt.rect.height() - 2*PADDING ), Qt::AlignLeft | Qt::AlignVCenter, mainText );
QTextOption to( Qt::AlignLeft | Qt::AlignVCenter );
if ( !allowMultiLine )
mainText = fm.elidedText( mainText, Qt::ElideRight, mainW );
else
to.setWrapMode( QTextOption::WrapAtWordBoundaryOrAnywhere );
painter->drawText( QRect( iconRect.right() + 2*PADDING, PADDING + opt.rect.y(), mainW, opt.rect.height() - 2*PADDING ), mainText, to );
}
QSize
JobStatusDelegate::sizeHint( const QStyleOptionViewItem& option, const QModelIndex& index ) const
{
// return QStyledItemDelegate::sizeHint( option, index );
const int w = QStyledItemDelegate::sizeHint ( option, index ).width();
return QSize( w, ROW_HEIGHT );
const bool allowMultiLine = index.data( JobStatusModel::AllowMultiLineRole ).toBool();
if ( !allowMultiLine )
return QSize( QStyledItemDelegate::sizeHint ( option, index ).width(), ROW_HEIGHT );
else if ( m_cachedMultiLineHeights.contains( index ) )
return QSize( QStyledItemDelegate::sizeHint ( option, index ).width(), m_cachedMultiLineHeights[ index ] );
// Don't elide, but stretch across as many rows as required
QStyleOptionViewItemV4 opt = option;
initStyleOption( &opt, index );
const QString text = index.data( Qt::DisplayRole ).toString();
const int leftEdge = ICON_PADDING + ROW_HEIGHT + 2*PADDING;
const QRect rect = opt.fontMetrics.boundingRect( leftEdge, opt.rect.top(), m_parentView->width() - leftEdge, 200, Qt::AlignLeft | Qt::AlignTop | Qt::TextWordWrap, text );
m_cachedMultiLineHeights.insert( index, rect.height() + 4*PADDING );
return QSize( QStyledItemDelegate::sizeHint ( option, index ).width(), rect.height() + 4*PADDING );
}

View File

@@ -22,6 +22,7 @@
#include <QStyledItemDelegate>
class QPainter;
class QListView;
class JobStatusDelegate : public QStyledItemDelegate
{
@@ -33,6 +34,10 @@ public:
virtual void paint( QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index ) const;
virtual QSize sizeHint( const QStyleOptionViewItem& option, const QModelIndex& index ) const;
private:
mutable QHash< QPersistentModelIndex, int > m_cachedMultiLineHeights;
QListView* m_parentView;
};
#endif // JOBSTATUSDELEGATE_H

View File

@@ -53,6 +53,7 @@ public:
* and a count will be shown instead.
*/
virtual bool collapseItem() const { return false; }
virtual bool allowMultiLine() const { return false; }
signals:
/// Ask for an update

View File

@@ -100,6 +100,8 @@ JobStatusModel::data( const QModelIndex& index, int role ) const
else
return item->rightColumnText();
}
case AllowMultiLineRole:
return item->allowMultiLine();
}
return QVariant();

View File

@@ -31,7 +31,8 @@ public:
enum JobRoles {
// DecorationRole is icon
// DisplayRole is main col
RightColumnRole = Qt::UserRole + 1
RightColumnRole = Qt::UserRole + 1,
AllowMultiLineRole = Qt::UserRole + 2
};
explicit JobStatusModel( QObject* parent = 0 );

View File

@@ -40,6 +40,7 @@ JobStatusView* JobStatusView::s_instance = 0;
JobStatusView::JobStatusView( AnimatedSplitter* parent )
: AnimatedWidget( parent )
, m_parent( parent )
, m_cachedHeight( -1 )
{
s_instance = this;
@@ -56,9 +57,7 @@ JobStatusView::JobStatusView( AnimatedSplitter* parent )
m_view->setFrameShape( QFrame::NoFrame );
m_view->setAttribute( Qt::WA_MacShowFocusRect, 0 );
// new QTreeWidgetItem( m_tree );
m_view->setUniformItemSizes( true );
m_view->setUniformItemSizes( false );
#ifndef Q_WS_WIN
QFont f = font();
@@ -86,12 +85,14 @@ JobStatusView::setModel( JobStatusModel* m )
connect( m_view->model(), SIGNAL( rowsInserted( QModelIndex, int, int ) ), this, SLOT( checkCount() ) );
connect( m_view->model(), SIGNAL( rowsRemoved( QModelIndex, int, int ) ), this, SLOT( checkCount() ) );
connect( m_view->model(), SIGNAL( modelReset() ), this, SLOT( checkCount() ) );
}
void
JobStatusView::checkCount()
{
m_cachedHeight = -1;
if ( m_view->model()->rowCount() == 0 && !isHidden() )
emit hideWidget();
else
@@ -102,15 +103,21 @@ JobStatusView::checkCount()
QSize
JobStatusView::sizeHint() const
{
if ( m_cachedHeight >= 0 )
return QSize( 0, m_cachedHeight );
unsigned int y = 0;
// y += m_tree->header()->height();
y += m_view->contentsMargins().top() + m_view->contentsMargins().bottom();
if ( m_view->model()->rowCount() )
{
unsigned int rowheight = m_view->sizeHintForRow( 0 );
y += rowheight * m_view->model()->rowCount() + 2;
for ( int i = 0; i < m_view->model()->rowCount(); i++ )
{
y += m_view->sizeHintForRow( i );
}
y += 2; // some padding
}
m_cachedHeight = y;
return QSize( 0, y );
}

View File

@@ -56,6 +56,7 @@ private:
QListView* m_view;
JobStatusModel* m_model;
AnimatedSplitter* m_parent;
mutable int m_cachedHeight;
static JobStatusView* s_instance;
};

View File

@@ -43,7 +43,7 @@ PortFwdThread::~PortFwdThread()
{
qDebug() << Q_FUNC_INFO << "waiting for event loop to finish...";
quit();
wait( 1000 );
wait( 6000 );
delete m_portfwd;
}

View File

@@ -61,6 +61,7 @@ Pipeline::Pipeline( QObject* parent )
Pipeline::~Pipeline()
{
tDebug() << Q_FUNC_INFO;
m_running = false;
// stop script resolvers
@@ -412,10 +413,11 @@ Pipeline::shunt( const query_ptr& q )
r->resolve( q );
emit resolving( q );
m_qidsTimeout.insert( q->id(), true );
if ( r->timeout() > 0 )
{
m_qidsTimeout.insert( q->id(), true );
new FuncTimeout( r->timeout(), boost::bind( &Pipeline::timeoutShunt, this, q ), this );
}
}
else
{

View File

@@ -48,6 +48,8 @@ public:
explicit Pipeline( QObject* parent = 0 );
virtual ~Pipeline();
bool isRunning() const { return m_running; }
unsigned int pendingQueryCount() const { return m_queries_pending.count(); }
unsigned int activeQueryCount() const { return m_qidsState.count(); }

View File

@@ -71,7 +71,11 @@ XspfUpdater::playlistLoaded()
foreach ( const plentry_ptr ple, playlist()->entries() )
tracks << ple->query();
QList< query_ptr > mergedTracks = TomahawkUtils::mergePlaylistChanges( tracks, loader->entries() );
bool changed = false;
QList< query_ptr > mergedTracks = TomahawkUtils::mergePlaylistChanges( tracks, loader->entries(), changed );
if ( !changed )
return;
QList<Tomahawk::plentry_ptr> el = playlist()->entriesFromQueries( mergedTracks, true );
playlist()->createNewRevision( uuid(), playlist()->currentrevision(), el );

View File

@@ -40,7 +40,6 @@ AlbumItemDelegate::AlbumItemDelegate( QAbstractItemView* parent, AlbumProxyModel
, m_view( parent )
, m_model( proxy )
{
m_defaultCover = QPixmap( RESPATH "images/no-album-art-placeholder.png" );
}
@@ -89,21 +88,19 @@ AlbumItemDelegate::paint( QPainter* painter, const QStyleOptionViewItem& option,
painter->drawLine( shadowRect.bottomLeft() + QPoint( 0, 4 ), shadowRect.bottomRight() + QPoint( 0, 4 ) );
}
QRect r = option.rect.adjusted( 6, 5, -6, -41 );
QPixmap cover;
if ( !item->album().isNull() )
{
cover.loadFromData( item->album()->cover() );
cover = item->album()->cover( r.size() );
if ( cover.isNull() )
cover = TomahawkUtils::defaultPixmap( TomahawkUtils::DefaultAlbumCover, TomahawkUtils::CoverInCase, r.size() );
}
else if ( !item->artist().isNull() )
{
cover.loadFromData( item->artist()->cover() );
cover = item->artist()->cover( r.size() );
}
if ( cover.isNull() )
cover = m_defaultCover;
QRect r = option.rect.adjusted( 6, 5, -6, -41 );
if ( option.state & QStyle::State_Selected )
{
#if defined(Q_WS_MAC) || defined(Q_WS_WIN)
@@ -123,17 +120,7 @@ AlbumItemDelegate::paint( QPainter* painter, const QStyleOptionViewItem& option,
#endif
}
QPixmap scover;
if ( m_cache.contains( cover.cacheKey() ) )
{
scover = m_cache.value( cover.cacheKey() );
}
else
{
scover = cover.scaled( r.size(), Qt::KeepAspectRatio, Qt::SmoothTransformation );
m_cache.insert( cover.cacheKey(), scover );
}
painter->drawPixmap( r, scover );
painter->drawPixmap( r, cover );
painter->setPen( opt.palette.color( QPalette::Text ) );
QTextOption to;

View File

@@ -49,12 +49,10 @@ private:
QAbstractItemView* m_view;
AlbumProxyModel* m_model;
mutable QHash< qint64, QPixmap > m_cache;
mutable QHash< QPersistentModelIndex, QRect > m_artistNameRects;
QPersistentModelIndex m_hoveringOver;
QPixmap m_shadowPixmap;
QPixmap m_defaultCover;
};
#endif // ALBUMITEMDELEGATE_H

View File

@@ -304,7 +304,18 @@ AlbumModel::addAlbums( const QList<Tomahawk::album_ptr>& albums )
if ( m_overwriteOnAdd )
clear();
if ( !albums.count() )
QList<Tomahawk::album_ptr> trimmedAlbums;
foreach ( const album_ptr& album, albums )
{
if ( !album.isNull() && album->name().length() )
{
if ( findItem( album ) || trimmedAlbums.contains( album ) )
continue;
trimmedAlbums << album;
}
}
if ( !trimmedAlbums.count() )
{
emit itemCountChanged( rowCount( QModelIndex() ) );
return;
@@ -313,12 +324,12 @@ AlbumModel::addAlbums( const QList<Tomahawk::album_ptr>& albums )
int c = rowCount( QModelIndex() );
QPair< int, int > crows;
crows.first = c;
crows.second = c + albums.count() - 1;
crows.second = c + trimmedAlbums.count() - 1;
emit beginInsertRows( QModelIndex(), crows.first, crows.second );
AlbumItem* albumitem;
foreach( const album_ptr& album, albums )
foreach( const album_ptr& album, trimmedAlbums )
{
albumitem = new AlbumItem( album, m_rootItem );
albumitem->index = createIndex( m_rootItem->children.count() - 1, 0, albumitem );
@@ -339,7 +350,18 @@ AlbumModel::addArtists( const QList<Tomahawk::artist_ptr>& artists )
if ( m_overwriteOnAdd )
clear();
if ( !artists.count() )
QList<Tomahawk::artist_ptr> trimmedArtists;
foreach ( const artist_ptr& artist, artists )
{
if ( !artist.isNull() && artist->name().length() )
{
if ( findItem( artist ) || trimmedArtists.contains( artist ) )
continue;
trimmedArtists << artist;
}
}
if ( !trimmedArtists.count() )
{
emit itemCountChanged( rowCount( QModelIndex() ) );
return;
@@ -348,12 +370,12 @@ AlbumModel::addArtists( const QList<Tomahawk::artist_ptr>& artists )
int c = rowCount( QModelIndex() );
QPair< int, int > crows;
crows.first = c;
crows.second = c + artists.count() - 1;
crows.second = c + trimmedArtists.count() - 1;
emit beginInsertRows( QModelIndex(), crows.first, crows.second );
AlbumItem* albumitem;
foreach( const artist_ptr& artist, artists )
foreach ( const artist_ptr& artist, trimmedArtists )
{
albumitem = new AlbumItem( artist, m_rootItem );
albumitem->index = createIndex( m_rootItem->children.count() - 1, 0, albumitem );
@@ -396,3 +418,35 @@ AlbumModel::onDataChanged()
AlbumItem* p = (AlbumItem*)sender();
emit dataChanged( p->index, p->index.sibling( p->index.row(), columnCount( QModelIndex() ) - 1 ) );
}
AlbumItem*
AlbumModel::findItem( const artist_ptr& artist ) const
{
for ( int i = 0; i < rowCount( QModelIndex() ); i++ )
{
AlbumItem* item = itemFromIndex( index( i, 0, QModelIndex() ) );
if ( !item->artist().isNull() && item->artist() == artist )
{
return item;
}
}
return 0;
}
AlbumItem*
AlbumModel::findItem( const album_ptr& album ) const
{
for ( int i = 0; i < rowCount( QModelIndex() ); i++ )
{
AlbumItem* item = itemFromIndex( index( i, 0, QModelIndex() ) );
if ( !item->album().isNull() && item->album() == album )
{
return item;
}
}
return 0;
}

View File

@@ -71,6 +71,9 @@ public:
virtual void setTitle( const QString& title ) { m_title = title; }
virtual void setDescription( const QString& description ) { m_description = description; }
AlbumItem* findItem( const Tomahawk::artist_ptr& artist ) const;
AlbumItem* findItem( const Tomahawk::album_ptr& album ) const;
AlbumItem* itemFromIndex( const QModelIndex& index ) const
{
if ( index.isValid() )

View File

@@ -25,6 +25,7 @@
#include <qmath.h>
#include "audio/audioengine.h"
#include "context/ContextWidget.h"
#include "tomahawksettings.h"
#include "artist.h"
#include "albumitem.h"
@@ -113,6 +114,20 @@ AlbumView::setAlbumModel( AlbumModel* model )
}
void
AlbumView::currentChanged( const QModelIndex& current, const QModelIndex& previous )
{
QListView::currentChanged( current, previous );
AlbumItem* item = m_model->itemFromIndex( m_proxyModel->mapToSource( current ) );
if ( item )
{
if ( !item->album().isNull() )
ViewManager::instance()->context()->setAlbum( item->album() );
}
}
void
AlbumView::onItemActivated( const QModelIndex& index )
{

View File

@@ -71,6 +71,9 @@ protected:
void paintEvent( QPaintEvent* event );
void resizeEvent( QResizeEvent* event );
protected slots:
virtual void currentChanged( const QModelIndex& current, const QModelIndex& previous );
private slots:
void onItemCountChanged( unsigned int items );

View File

@@ -80,6 +80,11 @@ ArtistView::ArtistView( QWidget* parent )
setFont( f );
#endif
m_timer.setInterval( SCROLL_TIMEOUT );
connect( verticalScrollBar(), SIGNAL( rangeChanged( int, int ) ), SLOT( onViewChanged() ) );
connect( verticalScrollBar(), SIGNAL( valueChanged( int ) ), SLOT( onViewChanged() ) );
connect( &m_timer, SIGNAL( timeout() ), SLOT( onScrollTimeout() ) );
connect( this, SIGNAL( doubleClicked( QModelIndex ) ), SLOT( onItemActivated( QModelIndex ) ) );
connect( this, SIGNAL( customContextMenuRequested( const QPoint& ) ), SLOT( onCustomContextMenu( const QPoint& ) ) );
connect( m_contextMenu, SIGNAL( triggered( int ) ), SLOT( onMenuTriggered( int ) ) );
@@ -128,7 +133,8 @@ ArtistView::setTreeModel( TreeModel* model )
connect( m_proxyModel, SIGNAL( filteringFinished() ), m_loadingSpinner, SLOT( fadeOut() ) );
connect( m_model, SIGNAL( itemCountChanged( unsigned int ) ), SLOT( onItemCountChanged( unsigned int ) ) );
connect( m_proxyModel, SIGNAL( filterChanged( QString ) ), SLOT( onFilterChanged( QString ) ) );
connect( m_proxyModel, SIGNAL( filteringFinished() ), SLOT( onFilterChangeFinished() ) );
connect( m_proxyModel, SIGNAL( rowsInserted( QModelIndex, int, int ) ), SLOT( onViewChanged() ) );
guid(); // this will set the guid on the header
@@ -145,6 +151,44 @@ ArtistView::setTreeModel( TreeModel* model )
}
void
ArtistView::onViewChanged()
{
if ( m_timer.isActive() )
m_timer.stop();
m_timer.start();
}
void
ArtistView::onScrollTimeout()
{
if ( m_timer.isActive() )
m_timer.stop();
QModelIndex left = indexAt( viewport()->rect().topLeft() );
while ( left.isValid() && left.parent().isValid() )
left = left.parent();
QModelIndex right = indexAt( viewport()->rect().bottomLeft() );
while ( right.isValid() && right.parent().isValid() )
right = right.parent();
int max = m_proxyModel->playlistInterface()->trackCount();
if ( right.isValid() )
max = right.row() + 1;
if ( !max )
return;
for ( int i = left.row(); i < max; i++ )
{
m_model->getCover( m_proxyModel->mapToSource( m_proxyModel->index( i, 0 ) ) );
}
}
void
ArtistView::currentChanged( const QModelIndex& current, const QModelIndex& previous )
{
@@ -236,7 +280,7 @@ ArtistView::onItemCountChanged( unsigned int items )
void
ArtistView::onFilterChanged( const QString& )
ArtistView::onFilterChangeFinished()
{
if ( selectedIndexes().count() )
scrollTo( selectedIndexes().at( 0 ), QAbstractItemView::PositionAtCenter );

View File

@@ -93,8 +93,10 @@ protected slots:
private slots:
void onItemCountChanged( unsigned int items );
void onFilterChanged( const QString& filter );
void onFilterChangeFinished();
void onFilteringStarted();
void onViewChanged();
void onScrollTimeout();
void onCustomContextMenu( const QPoint& pos );
void onMenuTriggered( int action );
@@ -113,6 +115,7 @@ private:
Tomahawk::ContextMenu* m_contextMenu;
bool m_showModes;
QTimer m_timer;
mutable QString m_guid;
};

View File

@@ -42,7 +42,7 @@ CustomPlaylistView::CustomPlaylistView( CustomPlaylistView::PlaylistType type, c
if ( m_type == SourceLovedTracks )
connect( m_source.data(), SIGNAL( socialAttributesChanged( QString ) ), this, SLOT( socialAttributesChanged( QString ) ) );
else if ( m_type == AllLovedTracks )
else if ( m_type == TopLovedTracks )
{
connect( SourceList::instance()->getLocal().data(), SIGNAL( socialAttributesChanged( QString ) ), this, SLOT( socialAttributesChanged( QString ) ) );
foreach ( const source_ptr& s, SourceList::instance()->sources( true ) )
@@ -86,12 +86,12 @@ CustomPlaylistView::generateTracks()
"GROUP BY track.id "
"ORDER BY counter DESC, social_attributes.timestamp DESC " ).arg( m_source->isLocal() ? "IS NULL" : QString( "= %1" ).arg( m_source->id() ) );
break;
case AllLovedTracks:
case TopLovedTracks:
sql = QString( "SELECT track.name, artist.name, source, COUNT(*) as counter "
"FROM social_attributes, track, artist "
"WHERE social_attributes.id = track.id AND artist.id = track.artist AND social_attributes.k = 'Love' AND social_attributes.v = 'true'"
"WHERE social_attributes.id = track.id AND artist.id = track.artist AND social_attributes.k = 'Love' AND social_attributes.v = 'true' "
"GROUP BY track.id "
"ORDER BY counter DESC, social_attributes.timestamp DESC " );
"ORDER BY counter DESC, social_attributes.timestamp DESC LIMIT 0, 50" );
break;
}
@@ -104,7 +104,11 @@ CustomPlaylistView::generateTracks()
void
CustomPlaylistView::tracksGenerated( QList< query_ptr > tracks )
{
QList< query_ptr > newTracks = TomahawkUtils::mergePlaylistChanges( m_model->queries(), tracks );
bool changed = false;
QList< query_ptr > newTracks = TomahawkUtils::mergePlaylistChanges( m_model->queries(), tracks, changed);
if ( !changed )
return;
m_model->clear();
m_model->append( newTracks );

View File

@@ -33,7 +33,7 @@ class DLLEXPORT CustomPlaylistView : public PlaylistView
public:
enum PlaylistType {
SourceLovedTracks,
AllLovedTracks
TopLovedTracks
};
explicit CustomPlaylistView( PlaylistType type, const source_ptr& s, QWidget* parent = 0 );

View File

@@ -54,19 +54,22 @@ DynamicModel::loadPlaylist( const Tomahawk::dynplaylist_ptr& playlist, bool load
{
Q_UNUSED( loadEntries );
if( !m_playlist.isNull() ) {
if ( !m_playlist.isNull() )
{
disconnect( m_playlist->generator().data(), SIGNAL( nextTrackGenerated( Tomahawk::query_ptr ) ), this, SLOT( newTrackGenerated( Tomahawk::query_ptr ) ) );
}
const int oldCount = rowCount( QModelIndex() );
m_playlist = playlist;
m_deduper.clear();
if( m_playlist->mode() == OnDemand )
if ( m_playlist->mode() == OnDemand )
setFilterUnresolvable( true );
connect( m_playlist->generator().data(), SIGNAL( nextTrackGenerated( Tomahawk::query_ptr ) ), this, SLOT( newTrackGenerated( Tomahawk::query_ptr ) ) );
PlaylistModel::loadPlaylist( m_playlist, m_playlist->mode() == Static );
if( m_playlist->mode() == OnDemand )
if ( m_playlist->mode() == OnDemand && oldCount != rowCount( QModelIndex() ) )
emit trackCountChanged( rowCount( QModelIndex() ) );
}
@@ -74,7 +77,7 @@ DynamicModel::loadPlaylist( const Tomahawk::dynplaylist_ptr& playlist, bool load
QString
DynamicModel::description() const
{
if( !m_playlist.isNull() && !m_playlist->generator().isNull() )
if ( !m_playlist.isNull() && !m_playlist->generator().isNull() )
return m_playlist->generator()->sentenceSummary();
else
return QString();
@@ -95,7 +98,8 @@ DynamicModel::startOnDemand()
void
DynamicModel::newTrackGenerated( const Tomahawk::query_ptr& query )
{
if( m_onDemandRunning ) {
if ( m_onDemandRunning )
{
bool isDuplicate = false;
for ( int i = 0; i < m_deduper.size(); i++ )
{
@@ -125,7 +129,7 @@ void
DynamicModel::stopOnDemand( bool stopPlaying )
{
m_onDemandRunning = false;
if( stopPlaying )
if ( stopPlaying )
AudioEngine::instance()->stop();
disconnect( AudioEngine::instance(), SIGNAL( loading( Tomahawk::result_ptr ) ), this, SLOT( newTrackLoading() ) );
@@ -135,7 +139,7 @@ DynamicModel::stopOnDemand( bool stopPlaying )
void
DynamicModel::changeStation()
{
if( m_onDemandRunning )
if ( m_onDemandRunning )
m_changeOnNext = true;
else // if we're not running, just start
m_playlist->generator()->startOnDemand();
@@ -171,7 +175,7 @@ DynamicModel::trackResolveFinished( bool success )
{
qDebug() << "Got successful resolved track:" << q->track() << q->artist() << m_lastResolvedRow << m_currentAttempts;
if( m_currentAttempts > 0 ) {
if ( m_currentAttempts > 0 ) {
qDebug() << "EMITTING AN ASK FOR COLLAPSE:" << m_lastResolvedRow << m_currentAttempts;
emit collapseFromTo( m_lastResolvedRow, m_currentAttempts );
}
@@ -188,11 +192,14 @@ void
DynamicModel::newTrackLoading()
{
qDebug() << "Got NEW TRACK LOADING signal";
if( m_changeOnNext ) { // reset instead of getting the next one
if ( m_changeOnNext )
{ // reset instead of getting the next one
m_lastResolvedRow = rowCount( QModelIndex() );
m_searchingForNext = true;
m_playlist->generator()->startOnDemand();
} else if( m_onDemandRunning && m_currentAttempts == 0 && !m_searchingForNext ) { // if we're in dynamic mode and we're also currently idle
}
else if ( m_onDemandRunning && m_currentAttempts == 0 && !m_searchingForNext )
{ // if we're in dynamic mode and we're also currently idle
m_lastResolvedRow = rowCount( QModelIndex() );
m_searchingForNext = true;
qDebug() << "IDLE fetching new track!";
@@ -204,13 +211,17 @@ DynamicModel::newTrackLoading()
void
DynamicModel::tracksGenerated( const QList< query_ptr > entries, int limitResolvedTo )
{
if( m_filterUnresolvable && m_playlist->mode() == OnDemand ) { // wait till we get them resolved (for previewing stations)
if ( m_filterUnresolvable && m_playlist->mode() == OnDemand )
{ // wait till we get them resolved (for previewing stations)
m_limitResolvedTo = limitResolvedTo;
filterUnresolved( entries );
} else {
}
else
{
addToPlaylist( entries, m_playlist->mode() == OnDemand ); // if ondemand, we're previewing, so clear old
if( m_playlist->mode() == OnDemand ) {
if ( m_playlist->mode() == OnDemand )
{
m_lastResolvedRow = rowCount( QModelIndex() );
}
}
@@ -224,9 +235,9 @@ DynamicModel::filterUnresolved( const QList< query_ptr >& entries )
{
m_toResolveList = entries;
foreach( const query_ptr& q, entries ) {
foreach ( const query_ptr& q, entries )
connect( q.data(), SIGNAL( resolvingFinished( bool ) ), this, SLOT( filteringTrackResolved( bool ) ) );
}
Pipeline::instance()->resolve( entries, true );
}
@@ -240,7 +251,8 @@ DynamicModel::filteringTrackResolved( bool successful )
// if meantime the user began the station, abort
qDebug() << "Got filtering resolved finished for track, was it successful?:" << q->track() << q->artist() << successful << q->playable();
if( m_onDemandRunning ) {
if ( m_onDemandRunning )
{
m_toResolveList.clear();
m_resolvedList.clear();
@@ -248,8 +260,10 @@ DynamicModel::filteringTrackResolved( bool successful )
}
query_ptr realptr;
foreach( const query_ptr& qptr, m_toResolveList ) {
if( qptr.data() == q ) {
foreach ( const query_ptr& qptr, m_toResolveList )
{
if ( qptr.data() == q )
{
realptr = qptr;
break;
}
@@ -259,25 +273,30 @@ DynamicModel::filteringTrackResolved( bool successful )
m_toResolveList.removeAll( realptr );
if( realptr->playable() ) {
if ( realptr->playable() )
{
m_resolvedList << realptr;
// append and update internal lastResolvedRow
addToPlaylist( QList< query_ptr >() << realptr, false );
if( m_playlist->mode() == OnDemand ) {
if ( m_playlist->mode() == OnDemand )
{
m_lastResolvedRow = rowCount( QModelIndex() );
}
if( m_toResolveList.isEmpty() || m_resolvedList.size() == m_limitResolvedTo ) { // done
if ( m_toResolveList.isEmpty() || m_resolvedList.size() == m_limitResolvedTo )
{ // done
m_toResolveList.clear();
m_resolvedList.clear();
}
} else {
}
else
{
qDebug() << "Got unsuccessful resolve request for this track" << realptr->track() << realptr->artist();
}
if( m_toResolveList.isEmpty() && rowCount( QModelIndex() ) == 0 ) // we failed
if ( m_toResolveList.isEmpty() && rowCount( QModelIndex() ) == 0 ) // we failed
emit trackGenerationFailure( tr( "Could not find a playable track.\n\nPlease change the filters or try again." ) );
}
@@ -285,16 +304,20 @@ DynamicModel::filteringTrackResolved( bool successful )
void
DynamicModel::addToPlaylist( const QList< query_ptr >& entries, bool clearFirst )
{
if( clearFirst )
if ( clearFirst )
clear();
foreach ( const query_ptr& q, entries )
m_deduper.append( QPair< QString, QString >( q->track(), q->artist() ) );
if( m_playlist->author()->isLocal() && m_playlist->mode() == Static ) {
if ( m_playlist->author()->isLocal() && m_playlist->mode() == Static )
{
m_playlist->addEntries( entries, m_playlist->currentrevision() );
} else { // read-only, so add tracks only in the GUI, not to the playlist itself
foreach( const query_ptr& query, entries ) {
}
else
{ // read-only, so add tracks only in the GUI, not to the playlist itself
foreach ( const query_ptr& query, entries )
{
append( query );
}
}
@@ -310,12 +333,15 @@ DynamicModel::remove(const QModelIndex& idx, bool moreToCome)
return;
qDebug() << Q_FUNC_INFO << "DYNAMIC MODEL REMOVIN!" << moreToCome << ( idx == index( rowCount( QModelIndex() ) - 1, 0, QModelIndex() ) );
if( m_playlist->mode() == OnDemand ) {
if( !moreToCome && idx == index( rowCount( QModelIndex() ) - 1, 0, QModelIndex() ) ) { // if the user is manually removing the last one, re-add as we're a station
if ( m_playlist->mode() == OnDemand )
{
if ( !moreToCome && idx == index( rowCount( QModelIndex() ) - 1, 0, QModelIndex() ) )
{ // if the user is manually removing the last one, re-add as we're a station
newTrackLoading();
}
TrackModel::remove( idx );
} else
}
else
PlaylistModel::remove( idx, moreToCome );
// don't call onPlaylistChanged.

View File

@@ -1,6 +1,6 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2010-2011, Christian Muehlhaeuser <muesli@tomahawk-player.org>
* Copyright 2010-2011, Leo Franchi <lfranchi@kde.org>
*
* Tomahawk is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by

View File

@@ -1,6 +1,6 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2010-2011, Christian Muehlhaeuser <muesli@tomahawk-player.org>
*
* Copyright 2010-2011, Leo Franchi <lfranchi@kde.org>
*
* Tomahawk is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -32,33 +32,33 @@ namespace Tomahawk
class DynamicModel;
class DynamicView : public PlaylistView
{
Q_OBJECT
public:
explicit DynamicView( QWidget* parent = 0 );
virtual ~DynamicView();
virtual void setDynamicModel( DynamicModel* model );
void setOnDemand( bool onDemand );
void setReadOnly( bool readOnly );
void setDynamicWorking( bool working );
virtual void paintEvent( QPaintEvent* event );
public slots:
void showMessageTimeout( const QString& title, const QString& body );
void showMessage( const QString& message );
// collapse and animate the transition
// there MUST be a row *after* startRow + num. that is, you can't collapse
// entries unless there is at least one entry after the last collapsed row
// optionally you can specify how many rows are past the block of collapsed rows
void collapseEntries( int startRow, int num, int numToKeep = 1 );
private slots:
void onTrackCountChanged( unsigned int );
void checkForOverflow();
@@ -70,7 +70,7 @@ private:
DynamicModel* m_model;
QString m_title;
QString m_body;
bool m_onDemand;
bool m_readOnly;
bool m_checkOnCollapse;
@@ -88,7 +88,7 @@ private:
QTimeLine m_fadeOutAnim;
QTimeLine m_slideAnim;
};
};

View File

@@ -181,6 +181,7 @@ Tomahawk::EchonestControl::updateWidgets()
input->hide();
m_match = QWeakPointer< QWidget >( match );
m_input = QWeakPointer< QWidget >( input );
m_data.first = m_currentType;
} else if( selectedType() == "Artist Description" ) {
m_currentType = Echonest::DynamicPlaylist::Description;
@@ -199,6 +200,7 @@ Tomahawk::EchonestControl::updateWidgets()
input->hide();
m_match = QWeakPointer< QWidget >( match );
m_input = QWeakPointer< QWidget >( input );
m_data.first = m_currentType;
} else if( selectedType() == "User Radio" ) {
m_currentType = Echonest::DynamicPlaylist::SourceCatalog;
@@ -246,6 +248,7 @@ Tomahawk::EchonestControl::updateWidgets()
input->hide();
m_match = QWeakPointer< QWidget >( match );
m_input = QWeakPointer< QWidget >( input );
m_data.first = m_currentType;
} else if( selectedType() == "Variety" ) {
m_currentType = Echonest::DynamicPlaylist::Variety;
@@ -266,6 +269,7 @@ Tomahawk::EchonestControl::updateWidgets()
input->hide();
m_match = QWeakPointer< QWidget >( match );
m_input = QWeakPointer< QWidget >( input );
m_data.first = m_currentType;
} else if( selectedType() == "Adventurousness" ) {
m_currentType = Echonest::DynamicPlaylist::Adventurousness;
@@ -287,6 +291,7 @@ Tomahawk::EchonestControl::updateWidgets()
input->hide();
m_match = QWeakPointer< QWidget >( match );
m_input = QWeakPointer< QWidget >( input );
m_data.first = m_currentType;
} else if( selectedType() == "Tempo" ) {
m_currentType = Echonest::DynamicPlaylist::MinTempo;

View File

@@ -118,32 +118,34 @@ DynamicWidget::loadDynamicPlaylist( const Tomahawk::dynplaylist_ptr& playlist )
{
// special case: if we have launched multiple setRevision calls, and the number of controls is different, it means that we're getting an intermediate setRevision
// called after the user has already created more revisions. ignore in that case.
if( m_playlist.data() == playlist.data() && m_seqRevLaunched > 0
if ( m_playlist.data() == playlist.data() && m_seqRevLaunched > 0
&& m_controls->controls().size() != playlist->generator()->controls().size() // different number of controls
&& qAbs( m_playlist->generator()->controls().size() - playlist->generator()->controls().size() ) < m_seqRevLaunched ) { // difference in controls has to be less than how many revisions we launched
&& qAbs( m_playlist->generator()->controls().size() - playlist->generator()->controls().size() ) < m_seqRevLaunched )
{ // difference in controls has to be less than how many revisions we launched
return;
}
m_seqRevLaunched = 0;
// if we're being told to load the same dynamic playlist over again, only do it if the controls have a different number
if( !m_playlist.isNull() && ( m_playlist.data() == playlist.data() ) // same playlist pointer
&& m_playlist->generator()->controls().size() == playlist->generator()->controls().size() ) {
if ( !m_playlist.isNull() && ( m_playlist.data() == playlist.data() ) // same playlist pointer
&& m_playlist->generator()->controls().size() == playlist->generator()->controls().size() )
{
// we can skip our work. just let the dynamiccontrollist show the difference
m_controls->setControls( m_playlist, m_playlist->author()->isLocal() );
m_playlist = playlist;
if( !m_runningOnDemand ) {
if ( !m_runningOnDemand )
m_model->loadPlaylist( m_playlist );
} else if( !m_controlsChanged ) { // if the controls changed, we already dealt with that and don't want to change station yet
else if ( !m_controlsChanged ) // if the controls changed, we already dealt with that and don't want to change station yet
m_model->changeStation();
}
m_controlsChanged = false;
return;
}
if( !m_playlist.isNull() ) {
if ( !m_playlist.isNull() )
{
disconnect( m_playlist->generator().data(), SIGNAL( generated( QList<Tomahawk::query_ptr> ) ), this, SLOT( tracksGenerated( QList<Tomahawk::query_ptr> ) ) );
disconnect( m_playlist.data(), SIGNAL( dynamicRevisionLoaded( Tomahawk::DynamicPlaylistRevision) ), this, SLOT(onRevisionLoaded( Tomahawk::DynamicPlaylistRevision) ) );
disconnect( m_playlist->generator().data(), SIGNAL( error( QString, QString ) ), this, SLOT( generatorError( QString, QString ) ) );
@@ -160,23 +162,22 @@ DynamicWidget::loadDynamicPlaylist( const Tomahawk::dynplaylist_ptr& playlist )
m_setup->setPlaylist( m_playlist );
if( !m_playlist->author()->isLocal() ) { // hide controls, as we show the description in the summary
if ( !m_playlist->author()->isLocal() ) // hide controls, as we show the description in the summary
m_layout->removeWidget( m_controls );
} else if( m_layout->indexOf( m_controls ) == -1 ) {
else if ( m_layout->indexOf( m_controls ) == -1 )
m_layout->insertWidget( 0, m_controls );
}
if( m_playlist->mode() == OnDemand && !m_playlist->generator()->controls().isEmpty() )
showPreview();
if( !m_playlist.isNull() )
m_controls->setControls( m_playlist, m_playlist->author()->isLocal() );
connect( m_playlist->generator().data(), SIGNAL( generated( QList<Tomahawk::query_ptr> ) ), this, SLOT( tracksGenerated( QList<Tomahawk::query_ptr> ) ) );
connect( m_playlist.data(), SIGNAL( dynamicRevisionLoaded( Tomahawk::DynamicPlaylistRevision ) ), this, SLOT( onRevisionLoaded( Tomahawk::DynamicPlaylistRevision ) ) );
connect( m_playlist->generator().data(), SIGNAL( error( QString, QString ) ), this, SLOT( generatorError( QString, QString ) ) );
connect( m_playlist.data(), SIGNAL( deleted( Tomahawk::dynplaylist_ptr ) ), this, SLOT( onDeleted() ) );
connect( m_playlist.data(), SIGNAL( changed() ), this, SLOT( onChanged() ) );
if ( m_playlist->mode() == OnDemand && !m_playlist->generator()->controls().isEmpty() )
showPreview();
if ( !m_playlist.isNull() )
m_controls->setControls( m_playlist, m_playlist->author()->isLocal() );
}
@@ -226,12 +227,15 @@ DynamicWidget::resizeEvent(QResizeEvent* )
void
DynamicWidget::layoutFloatingWidgets()
{
if( !m_runningOnDemand ) {
if ( !m_runningOnDemand )
{
int x = ( width() / 2 ) - ( m_setup->size().width() / 2 );
int y = height() - m_setup->size().height() - 40; // padding
m_setup->move( x, y );
} else if( m_runningOnDemand && m_steering ) {
}
else if( m_runningOnDemand && m_steering )
{
int x = ( width() / 2 ) - ( m_steering->size().width() / 2 );
int y = height() - m_steering->size().height() - 40; // padding
@@ -243,13 +247,15 @@ DynamicWidget::layoutFloatingWidgets()
void
DynamicWidget::playlistChanged( Tomahawk::playlistinterface_ptr pl )
{
if( pl == m_view->proxyModel()->playlistInterface() ) { // same playlist
if ( pl == m_view->proxyModel()->playlistInterface() ) // same playlist
m_activePlaylist = true;
} else {
else
{
m_activePlaylist = false;
// user started playing something somewhere else, so give it a rest
if( m_runningOnDemand ) {
if ( m_runningOnDemand )
{
stopStation( false );
}
}
@@ -259,9 +265,8 @@ DynamicWidget::playlistChanged( Tomahawk::playlistinterface_ptr pl )
void
DynamicWidget::showEvent(QShowEvent* )
{
if( !m_playlist.isNull() && !m_runningOnDemand ) {
if ( !m_playlist.isNull() && !m_runningOnDemand )
m_setup->fadeIn();
}
}
@@ -289,8 +294,9 @@ DynamicWidget::stationFailed( const QString& msg )
void
DynamicWidget::trackStarted()
{
if( m_activePlaylist && !m_playlist.isNull() &&
m_playlist->mode() == OnDemand && !m_runningOnDemand ) {
if ( m_activePlaylist && !m_playlist.isNull() &&
m_playlist->mode() == OnDemand && !m_runningOnDemand )
{
startStation();
}
@@ -300,7 +306,7 @@ DynamicWidget::trackStarted()
void
DynamicWidget::tracksAdded()
{
if( m_playlist->mode() == OnDemand && m_runningOnDemand && m_setup->isVisible() )
if ( m_playlist->mode() == OnDemand && m_runningOnDemand && m_setup->isVisible() )
m_setup->fadeOut();
}
@@ -325,7 +331,8 @@ DynamicWidget::startStation()
m_setup->fadeOut();
// show the steering controls
if( m_playlist->generator()->onDemandSteerable() ) {
if ( m_playlist->generator()->onDemandSteerable() )
{
// position it horizontally centered, above the botton.
m_steering = m_playlist->generator()->steeringWidget();
Q_ASSERT( m_steering );
@@ -361,7 +368,7 @@ DynamicWidget::tracksGenerated( const QList< query_ptr >& queries )
{
m_resolveOnNextLoad = true;
}
else if( m_playlist->mode() == OnDemand )
else if ( m_playlist->mode() == OnDemand )
{
limit = 5;
}
@@ -380,7 +387,7 @@ DynamicWidget::controlsChanged( bool added )
// when playing a station just ignore it till we're ready and get a controlChanged()
m_controlsChanged = true;
if( !m_playlist->author()->isLocal() )
if ( !m_playlist->author()->isLocal() )
return;
m_playlist->createNewRevision();
m_seqRevLaunched++;
@@ -396,7 +403,7 @@ void
DynamicWidget::controlChanged( const Tomahawk::dyncontrol_ptr& control )
{
Q_UNUSED( control );
if( !m_playlist->author()->isLocal() )
if ( !m_playlist->author()->isLocal() )
return;
m_playlist->createNewRevision();
m_seqRevLaunched++;
@@ -443,8 +450,7 @@ DynamicWidget::steeringChanged()
void
DynamicWidget::showPreview()
{
if ( m_playlist->mode() == OnDemand &&
!m_runningOnDemand )
if ( m_playlist->mode() == OnDemand && !m_runningOnDemand )
{
// if this is a not running station, preview matching tracks
m_model->clear();
@@ -456,12 +462,16 @@ DynamicWidget::showPreview()
void
DynamicWidget::generatorError( const QString& title, const QString& content )
{
if( m_runningOnDemand ) {
stopStation( false );
}
m_view->setDynamicWorking( false );
m_loading->fadeOut();
m_view->showMessageTimeout( title, content );
if ( m_runningOnDemand )
{
stopStation( false );
m_view->showMessage( tr( "Station ran out of tracks!\n\nTry tweaking the filters for a new set of songs to play." ) );
}
else
m_view->showMessageTimeout( title, content );
}
@@ -528,7 +538,7 @@ DynamicWidget::onDeleted()
void
DynamicWidget::onChanged()
{
if( !m_playlist.isNull() &&
ViewManager::instance()->currentPage() == this )
emit nameChanged( m_playlist->title() );
if ( !m_playlist.isNull() &&
ViewManager::instance()->currentPage() == this )
emit nameChanged( m_playlist->title() );
}

View File

@@ -155,32 +155,18 @@ TreeItemDelegate::paint( QPainter* painter, const QStyleOptionViewItem& option,
QPixmap cover;
if ( !item->album().isNull() )
{
cover.loadFromData( item->album()->cover() );
cover = item->album()->cover( r.size(), false );
if ( cover.isNull() )
cover = TomahawkUtils::defaultPixmap( TomahawkUtils::DefaultAlbumCover, TomahawkUtils::ScaledCover, r.size() );
}
else if ( !item->artist().isNull() )
{
cover.loadFromData( item->artist()->cover() );
cover = item->artist()->cover( r.size(), false );
if ( cover.isNull() )
cover = TomahawkUtils::defaultPixmap( TomahawkUtils::DefaultArtistImage, TomahawkUtils::ScaledCover, r.size() );
}
QPixmap scover;
if ( cover.isNull() )
{
if ( !item->artist().isNull() )
cover = m_defaultArtistImage;
else
cover = m_defaultAlbumCover;
}
if ( m_cache.contains( cover.cacheKey() ) )
{
scover = m_cache.value( cover.cacheKey() );
}
else
{
scover = cover.scaled( r.size(), Qt::KeepAspectRatio, Qt::SmoothTransformation );
m_cache.insert( cover.cacheKey(), scover );
}
painter->drawPixmap( r, scover );
painter->drawPixmap( r, cover );
QTextOption to;
to.setAlignment( Qt::AlignVCenter );

View File

@@ -43,8 +43,6 @@ private:
ArtistView* m_view;
TreeProxyModel* m_model;
mutable QHash< qint64, QPixmap > m_cache;
QPixmap m_nowPlayingIcon;
QPixmap m_defaultAlbumCover;
QPixmap m_defaultArtistImage;

View File

@@ -20,6 +20,7 @@
#include <QMimeData>
#include "pipeline.h"
#include "source.h"
#include "sourcelist.h"
#include "audio/audioengine.h"
@@ -87,6 +88,18 @@ TreeModel::collection() const
}
void
TreeModel::getCover( const QModelIndex& index )
{
TreeModelItem* item = itemFromIndex( index );
if ( !item->artist().isNull() && !item->artist()->infoLoaded() )
item->artist()->cover( QSize( 0, 0 ) );
else if ( !item->album().isNull() && !item->album()->infoLoaded() )
item->album()->cover( QSize( 0, 0 ) );
}
void
TreeModel::setCurrentItem( const QModelIndex& index )
{
@@ -783,6 +796,8 @@ TreeModel::onAlbumsAdded( const QList<Tomahawk::album_ptr>& albums, const QModel
albumitem = new TreeModelItem( album, parentItem );
albumitem->index = createIndex( parentItem->children.count() - 1, 0, albumitem );
connect( albumitem, SIGNAL( dataChanged() ), SLOT( onDataChanged() ) );
getCover( albumitem->index );
}
emit endInsertRows();
@@ -915,10 +930,11 @@ TreeModel::infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData requestData, QV
foreach ( const QString& trackName, tracks )
{
query_ptr query = Query::get( inputInfo[ "artist" ], trackName, inputInfo[ "album" ], uuid() );
query_ptr query = Query::get( inputInfo[ "artist" ], trackName, inputInfo[ "album" ] );
query->setAlbumPos( trackNo++ );
ql << query;
}
Pipeline::instance()->resolve( ql );
onTracksAdded( ql, idx );
}

View File

@@ -98,6 +98,8 @@ public:
void addAlbums( const Tomahawk::artist_ptr& artist, const QModelIndex& parent, bool autoRefetch = false );
void addTracks( const Tomahawk::album_ptr& album, const QModelIndex& parent, bool autoRefetch = false );
void getCover( const QModelIndex& index );
ColumnStyle columnStyle() const { return m_columnStyle; }
void setColumnStyle( ColumnStyle style );

View File

@@ -104,6 +104,7 @@ Query::Query( const QString& query, const QID& qid )
Query::~Query()
{
QMutexLocker lock( &m_mutex );
m_ownRef.clear();
}
@@ -459,7 +460,15 @@ Query::howSimilar( const Tomahawk::result_ptr& r )
if ( isFullTextQuery() )
{
const QString artistTrackname = DatabaseImpl::sortname( fullTextQuery() );
const QString rArtistTrackname = DatabaseImpl::sortname( r->artist()->name() + " " + r->track() );
int atrdist = levenshtein( artistTrackname, rArtistTrackname );
int mlatr = qMax( artistTrackname.length(), rArtistTrackname.length() );
float dcatr = (float)( mlatr - atrdist ) / mlatr;
float res = qMax( dcart, dcalb );
res = qMax( res, dcatr );
return qMax( res, dctrk );
}
else
@@ -489,7 +498,7 @@ Query::loadSocialActions()
query_ptr q = m_ownRef.toStrongRef();
DatabaseCommand_LoadSocialActions* cmd = new DatabaseCommand_LoadSocialActions( q );
connect( cmd, SIGNAL( finished() ), SLOT( onSocialActionsLoaded() ));
connect( cmd, SIGNAL( finished() ), SLOT( onSocialActionsLoaded() ) );
Database::instance()->enqueue( QSharedPointer<DatabaseCommand>(cmd) );
}

View File

@@ -350,6 +350,12 @@ Source::trackTimerFired()
void
Source::addCommand( const QSharedPointer<DatabaseCommand>& command )
{
if ( QThread::currentThread() != thread() )
{
QMetaObject::invokeMethod( this, "addCommand", Qt::QueuedConnection, Q_ARG( const QSharedPointer<DatabaseCommand>, command ) );
return;
}
m_cmds << command;
if ( !command->singletonCmd() )
m_lastCmdGuid = command->guid();
@@ -363,7 +369,6 @@ Source::executeCommands()
{
if ( QThread::currentThread() != thread() )
{
tDebug() << "Reinvoking in correct thread:" << Q_FUNC_INFO;
QMetaObject::invokeMethod( this, "executeCommands", Qt::QueuedConnection );
return;
}

View File

@@ -129,9 +129,9 @@ private slots:
void trackTimerFired();
void executeCommands();
void addCommand( const QSharedPointer<DatabaseCommand>& command );
private:
void addCommand( const QSharedPointer<DatabaseCommand>& command );
void updateTracks();
void reportSocialAttributesChanged( DatabaseCommand_SocialAction* action );

View File

@@ -137,7 +137,7 @@ SourcePlaylistInterface::onSourcePlaybackStarted( const Tomahawk::query_ptr& que
{
tDebug( LOGEXTRA ) << Q_FUNC_INFO;
connect( query.data(), SIGNAL( resolvingFinished( bool ) ), SLOT( resolvingFinished( bool ) ) );
Pipeline::instance()->resolve( query, true );
Pipeline::instance()->resolve( query );
m_gotNextItem = false;
}

View File

@@ -29,7 +29,7 @@
#include "database/databasecommand_updatesearchindex.h"
#include "database/database.h"
#define VERSION 5
#define VERSION 6
using namespace Tomahawk;
@@ -47,19 +47,24 @@ TomahawkSettings::TomahawkSettings( QObject* parent )
{
s_instance = this;
if( !contains( "configversion") )
#ifdef Q_OS_LINUX
QFile file( fileName() );
file.setPermissions( file.permissions() & ~(QFile::ReadGroup | QFile::WriteGroup | QFile::ExeGroup | QFile::ReadOther | QFile::WriteOther | QFile::ExeOther ) );
#endif
if ( !contains( "configversion" ) )
{
setValue( "configversion", VERSION );
doInitialSetup();
}
else if( value( "configversion" ).toUInt() != VERSION )
else if ( value( "configversion" ).toUInt() != VERSION )
{
qDebug() << "Config version outdated, old:" << value( "configversion" ).toUInt()
<< "new:" << VERSION
<< "Doing upgrade, if any...";
int current = value( "configversion" ).toUInt();
while( current < VERSION )
while ( current < VERSION )
{
doUpgrade( current, current + 1 );
@@ -68,7 +73,6 @@ TomahawkSettings::TomahawkSettings( QObject* parent )
// insert upgrade code here as required
setValue( "configversion", VERSION );
}
}
@@ -91,22 +95,23 @@ TomahawkSettings::doUpgrade( int oldVersion, int newVersion )
{
Q_UNUSED( newVersion );
if( oldVersion == 1 )
if ( oldVersion == 1 )
{
qDebug() << "Migrating config from verson 1 to 2: script resolver config name";
if( contains( "script/resolvers" ) ) {
setValue( "script/loadedresolvers", value( "script/resolvers" ) );
remove( "script/resolvers" );
}
} else if( oldVersion == 2 )
}
else if ( oldVersion == 2 )
{
qDebug() << "Migrating config from version 2 to 3: Converting jabber and twitter accounts to new SIP Factory approach";
// migrate old accounts to new system. only jabber and twitter, and max one each. create a new plugin for each if needed
// not pretty as we hardcode a plugin id and assume that we know how the config layout is, but hey, this is migration after all
if( contains( "jabber/username" ) && contains( "jabber/password" ) )
if ( contains( "jabber/username" ) && contains( "jabber/password" ) )
{
QString sipName = "sipjabber";
if( value( "jabber/username" ).toString().contains( "@gmail" ) )
if ( value( "jabber/username" ).toString().contains( "@gmail" ) )
sipName = "sipgoogle";
setValue( QString( "%1_legacy/username" ).arg( sipName ), value( "jabber/username" ) );
@@ -123,7 +128,7 @@ TomahawkSettings::doUpgrade( int oldVersion, int newVersion )
remove( "jabber/server" );
remove( "jabber/port" );
}
if( contains( "twitter/ScreenName" ) && contains( "twitter/OAuthToken" ) )
if ( contains( "twitter/ScreenName" ) && contains( "twitter/OAuthToken" ) )
{
setValue( "siptwitter_legacy/ScreenName", value( "twitter/ScreenName" ) );
setValue( "siptwitter_legacy/OAuthToken", value( "twitter/OAuthToken" ) );
@@ -144,7 +149,8 @@ TomahawkSettings::doUpgrade( int oldVersion, int newVersion )
}
// create a zeroconf plugin too
addSipPlugin( "sipzeroconf_legacy" );
} else if ( oldVersion == 3 )
}
else if ( oldVersion == 3 )
{
if ( contains( "script/atticaresolverstates" ) )
{
@@ -183,7 +189,8 @@ TomahawkSettings::doUpgrade( int oldVersion, int newVersion )
tDebug() << "UPGRADING AND DELETING:" << resolverDir.absolutePath();
TomahawkUtils::removeDirectory( resolverDir.absolutePath() );
}
} else if ( oldVersion == 4 )
}
else if ( oldVersion == 4 || oldVersion == 5 )
{
// 0.3.0 contained a bug which prevent indexing local files. Force a reindex.
QTimer::singleShot( 0, this, SLOT( updateIndex() ) );
@@ -935,6 +942,7 @@ TomahawkSettings::setNowPlayingEnabled( bool enable )
setValue( "adium/enablenowplaying", enable );
}
TomahawkSettings::PrivateListeningMode
TomahawkSettings::privateListeningMode() const
{

View File

@@ -27,6 +27,7 @@
#include "dropjob.h"
#include "jobview/JobStatusView.h"
#include "jobview/JobStatusModel.h"
#include "jobview/ErrorStatusMessage.h"
#include "dropjobnotifier.h"
#include "viewmanager.h"
@@ -38,6 +39,10 @@
#include <QtNetwork/QNetworkAccessManager>
#include <QtNetwork/QNetworkReply>
#include <QWebPage>
#include <QWebFrame>
#include <QWebElement>
using namespace Tomahawk;
QPixmap* GroovesharkParser::s_pixmap = 0;
@@ -71,15 +76,17 @@ GroovesharkParser::~GroovesharkParser()
void
GroovesharkParser::lookupUrl( const QString& link )
{
if( link.contains( "playlist" ) )
if ( link.contains( "playlist" ) )
{
if( !m_createNewPlaylist )
if ( !m_createNewPlaylist )
m_trackMode = true;
else
m_trackMode = false;
lookupGroovesharkPlaylist( link );
}
else if ( link.contains( "grooveshark.com/s/" ) || link.contains( "grooveshark.com/#/s/" ) )
lookupGroovesharkTrack( link );
else
return;
@@ -96,7 +103,7 @@ GroovesharkParser::lookupGroovesharkPlaylist( const QString& linkRaw )
tDebug() << "no fragment, setting fragment to path";
urlFragment = QUrl(linkRaw).path();
}
tDebug() << urlFragment;
int paramStartingPostition = urlFragment.indexOf( "?" );
@@ -107,22 +114,22 @@ GroovesharkParser::lookupGroovesharkPlaylist( const QString& linkRaw )
bool ok;
QStringList urlParts = urlFragment.split( "/", QString::SkipEmptyParts );
tDebug() << urlParts;
int playlistID = urlParts.at( 2 ).toInt( &ok, 10 );
if (!ok)
{
tDebug() << "incorrect grooveshark url";
return;
}
m_title = urlParts.at( 1 );
tDebug() << "should get playlist " << playlistID;
DropJob::DropType type;
type = DropJob::Playlist;
@@ -152,6 +159,53 @@ GroovesharkParser::lookupGroovesharkPlaylist( const QString& linkRaw )
m_queries.insert( reply );
}
void
GroovesharkParser::lookupGroovesharkTrack( const QString& track )
{
tLog() << "Parsing Grooveshark Track Page:" << track;
QNetworkReply* reply = TomahawkUtils::nam()->get( QNetworkRequest( QUrl( track ) ) );
connect( reply, SIGNAL( finished() ), this, SLOT( trackPageFetchFinished() ) );
m_browseJob = new DropJobNotifier( pixmap(), "Grooveshark", DropJob::Track, reply );
JobStatusView::instance()->model()->addJob( m_browseJob );
m_queries << reply;
}
void
GroovesharkParser::trackPageFetchFinished()
{
QNetworkReply* r = qobject_cast< QNetworkReply* >( sender() );
Q_ASSERT( r );
m_queries.remove( r );
r->deleteLater();
QWebPage page;
page.settings()->setAttribute( QWebSettings::JavascriptEnabled, false );
page.settings()->setAttribute( QWebSettings::PluginsEnabled, false );
page.settings()->setAttribute( QWebSettings::JavaEnabled, false );
page.settings()->setAttribute( QWebSettings::AutoLoadImages, false );
page.mainFrame()->setHtml( QString::fromUtf8( r->readAll() ) );
QWebElement title = page.mainFrame()->findFirstElement("span[itemprop='name']");
QWebElement artist = page.mainFrame()->findFirstElement("noscript span[itemprop='byArtist']");
QWebElement album = page.mainFrame()->findFirstElement("noscript span[itemprop='inAlbum']");
if ( !title.toPlainText().isEmpty() && !artist.toPlainText().isEmpty() )
{
tDebug() << "Got track info from grooveshark, enough to create a query:" << title.toPlainText() << artist.toPlainText() << album.toPlainText();
Tomahawk::query_ptr q = Tomahawk::Query::get( artist.toPlainText(), title.toPlainText(), album.toPlainText(), uuid(), true );
m_tracks << q;
}
checkTrackFinished();
}
void
GroovesharkParser::groovesharkLookupFinished()
{
@@ -178,7 +232,7 @@ GroovesharkParser::groovesharkLookupFinished()
foreach (const QVariant& var, list)
{
QVariantMap trackResult = var.toMap();
QString title, artist, album;
title = trackResult.value( "SongName", QString() ).toString();
@@ -194,10 +248,11 @@ GroovesharkParser::groovesharkLookupFinished()
Tomahawk::query_ptr q = Tomahawk::Query::get( artist, title, album, uuid(), m_trackMode );
m_tracks << q;
}
} else
{
JobStatusView::instance()->model()->addJob( new ErrorStatusMessage( tr( "Error fetching Grooveshark information from the network!" ) ) );
tLog() << "Error in network request to grooveshark for track decoding:" << r->errorString();
}
@@ -229,7 +284,7 @@ GroovesharkParser::checkPlaylistFinished()
return;
}
emit tracks( m_tracks );
deleteLater();

View File

@@ -42,7 +42,7 @@ class QNetworkReply;
namespace Tomahawk
{
class DropJobNotifier;
class DLLEXPORT GroovesharkParser : public QObject
@@ -58,6 +58,7 @@ signals:
private slots:
void groovesharkLookupFinished();
void trackPageFetchFinished();
void playlistCreated();
private:
@@ -65,6 +66,8 @@ private:
void lookupUrl( const QString& url );
void lookupGroovesharkPlaylist( const QString& playlist );
void lookupGroovesharkTrack( const QString& track );
void checkTrackFinished();
void checkPlaylistFinished();
int m_limit;
@@ -79,7 +82,7 @@ private:
QCA::SymmetricKey m_apiKey;
static QPixmap* s_pixmap;
};
}

View File

@@ -25,6 +25,7 @@
#include "sourcelist.h"
#include "jobview/JobStatusView.h"
#include "jobview/JobStatusModel.h"
#include "jobview/ErrorStatusMessage.h"
#include <qjson/parser.h>
@@ -166,6 +167,7 @@ ItunesParser::itunesResponseLookupFinished()
} else
{
JobStatusView::instance()->model()->addJob( new ErrorStatusMessage( tr( "Error fetching iTunes information from the network!" ) ) );
tLog() << "Error in network request to Itunes for track decoding:" << r->errorString();
}

View File

@@ -26,6 +26,7 @@
#include "dropjob.h"
#include "jobview/JobStatusView.h"
#include "jobview/JobStatusModel.h"
#include "jobview/ErrorStatusMessage.h"
#include "dropjobnotifier.h"
#include "viewmanager.h"
#include "sourcelist.h"
@@ -189,6 +190,7 @@ RdioParser::rdioReturned()
} else
{
JobStatusView::instance()->model()->addJob( new ErrorStatusMessage( tr( "Error fetching Rdio information from the network!" ) ) );
tLog() << "Error in network request to Rdio for track decoding:" << r->errorString();
}

View File

@@ -21,7 +21,11 @@
#include "utils/logger.h"
#include "utils/tomahawkutils.h"
#include "dropjobnotifier.h"
#include "query.h"
#include "jobview/ErrorStatusMessage.h"
#include "jobview/JobStatusModel.h"
#include "jobview/JobStatusView.h"
#include <qjson/parser.h>
@@ -30,6 +34,7 @@
using namespace Tomahawk;
QPixmap* ShortenedLinkParser::s_pixmap = 0;
ShortenedLinkParser::ShortenedLinkParser ( const QStringList& urls, QObject* parent )
: QObject( parent )
@@ -58,6 +63,8 @@ ShortenedLinkParser::handlesUrl( const QString& url )
url.contains( "itun.es" ) ||
url.contains( "tinyurl.com" ) ||
url.contains( "tinysong.com" ) ||
url.contains( "grooveshark.com/s/~/" ) || // These redirect to the 'real' grooveshark track url
url.contains( "grooveshark.com/#/s/~/" ) ||
url.contains( "rd.io" ) );
}
@@ -65,11 +72,18 @@ void
ShortenedLinkParser::lookupUrl ( const QString& url )
{
tDebug() << "Looking up..." << url;
QString cleaned = url;
if ( cleaned.contains( "/#/s/" ) )
cleaned.replace( "/#", "" );
QNetworkReply* reply = TomahawkUtils::nam()->get( QNetworkRequest( QUrl( url ) ) );
QNetworkReply* reply = TomahawkUtils::nam()->get( QNetworkRequest( QUrl( cleaned ) ) );
connect( reply, SIGNAL( finished() ), this, SLOT( lookupFinished() ) );
m_queries.insert( reply );
m_expandJob = new DropJobNotifier( pixmap(), "shortened", DropJob::Track, reply );
JobStatusView::instance()->model()->addJob( m_expandJob );
}
void
@@ -78,6 +92,9 @@ ShortenedLinkParser::lookupFinished()
QNetworkReply* r = qobject_cast< QNetworkReply* >( sender() );
Q_ASSERT( r );
if ( r->error() != QNetworkReply::NoError )
JobStatusView::instance()->model()->addJob( new ErrorStatusMessage( tr( "Network error parsing shortened link!" ) ) );
QVariant redir = r->attribute( QNetworkRequest::RedirectionTargetAttribute );
if ( redir.isValid() && !redir.toUrl().isEmpty() )
{
@@ -89,7 +106,7 @@ ShortenedLinkParser::lookupFinished()
else
{
tLog() << "Got a redirected url:" << r->url().toString();
m_links << r->url().toString();
m_links << r->url().toString();
m_queries.remove( r );
r->deleteLater();
checkFinished();
@@ -108,3 +125,13 @@ ShortenedLinkParser::checkFinished()
deleteLater();
}
}
QPixmap
ShortenedLinkParser::pixmap()
{
if ( !s_pixmap )
s_pixmap = new QPixmap( RESPATH "images/add.png" );
return *s_pixmap;
}

View File

@@ -26,12 +26,15 @@
#include <QObject>
#include <QSet>
#include <QStringList>
#include <QPixmap>
class QNetworkReply;
namespace Tomahawk
{
class DropJobNotifier;
/**
* Small class to parse whitelisted shortened links into the redirected urls
*
@@ -58,8 +61,13 @@ private:
void lookupUrl( const QString& url );
void checkFinished();
static QPixmap pixmap();
QStringList m_links;
QSet< QNetworkReply* > m_queries;
DropJobNotifier* m_expandJob;
static QPixmap* s_pixmap;
};
}

View File

@@ -26,6 +26,7 @@
#include "dropjob.h"
#include "jobview/JobStatusView.h"
#include "jobview/JobStatusModel.h"
#include "jobview/ErrorStatusMessage.h"
#include "dropjobnotifier.h"
#include "viewmanager.h"
@@ -220,6 +221,7 @@ SpotifyParser::spotifyBrowseFinished()
} else
{
JobStatusView::instance()->model()->addJob( new ErrorStatusMessage( tr( "Error fetching Spotify information from the network!" ) ) );
tLog() << "Error in network request to Spotify for track decoding:" << r->errorString();
}

View File

@@ -533,10 +533,12 @@ newerVersion( const QString& oldVersion, const QString& newVersion )
QList< Tomahawk::query_ptr >
mergePlaylistChanges( const QList< Tomahawk::query_ptr >& orig, const QList< Tomahawk::query_ptr >& newTracks )
mergePlaylistChanges( const QList< Tomahawk::query_ptr >& orig, const QList< Tomahawk::query_ptr >& newTracks, bool& changed )
{
int sameCount = 0;
QList< Tomahawk::query_ptr > tosave = newTracks;
changed = false;
foreach ( const Tomahawk::query_ptr& newquery, newTracks )
{
foreach ( const Tomahawk::query_ptr& oldq, orig )
@@ -558,6 +560,7 @@ mergePlaylistChanges( const QList< Tomahawk::query_ptr >& orig, const QList< Tom
if ( orig.size() == newTracks.size() && sameCount == orig.size() )
return orig;
changed = true;
return tosave;
}

View File

@@ -42,6 +42,18 @@ namespace TomahawkUtils
MediaTypeTrack
};
enum ImageType
{
DefaultAlbumCover,
DefaultArtistImage
};
enum ImageMode
{
NoDefaultCover,
CoverInCase,
ScaledCover
};
class DLLEXPORT NetworkProxyFactory : public QNetworkProxyFactory
{
public:
@@ -98,7 +110,7 @@ namespace TomahawkUtils
*
* \return true if some changes were made, false if the new tracks are the same as the current tracks in \param orig
*/
DLLEXPORT QList< Tomahawk::query_ptr > mergePlaylistChanges( const QList< Tomahawk::query_ptr >& orig, const QList< Tomahawk::query_ptr >& newTracks );
DLLEXPORT QList< Tomahawk::query_ptr > mergePlaylistChanges( const QList< Tomahawk::query_ptr >& orig, const QList< Tomahawk::query_ptr >& newTracks, bool& changed );
DLLEXPORT void crash();
}

View File

@@ -36,6 +36,8 @@
#include <windowsx.h>
#endif
#include "logger.h"
namespace TomahawkUtils
{
static int s_headerHeight = 0;
@@ -83,15 +85,15 @@ createDragPixmap( MediaType type, int itemCount )
QPixmap pixmap;
switch ( type )
{
case MediaTypeArtist:
pixmap = QPixmap( ":/data/images/artist-icon.png" ).scaledToWidth( size, Qt::SmoothTransformation );
break;
case MediaTypeAlbum:
pixmap = QPixmap( ":/data/images/album-icon.png" ).scaledToWidth( size, Qt::SmoothTransformation );
break;
case MediaTypeTrack:
pixmap = QPixmap( QString( ":/data/images/track-icon-%2x%2.png" ).arg( size ) );
break;
case MediaTypeArtist:
pixmap = QPixmap( ":/data/images/artist-icon.png" ).scaledToWidth( size, Qt::SmoothTransformation );
break;
case MediaTypeAlbum:
pixmap = QPixmap( ":/data/images/album-icon.png" ).scaledToWidth( size, Qt::SmoothTransformation );
break;
case MediaTypeTrack:
pixmap = QPixmap( QString( ":/data/images/track-icon-%2x%2.png" ).arg( size ) );
break;
}
int x = 0;
@@ -303,4 +305,59 @@ alphaBlend( const QColor& colorFrom, const QColor& colorTo, float opacity )
return QColor( r, g, b );
}
QPixmap
defaultPixmap( ImageType type, ImageMode mode, const QSize& size )
{
QPixmap pixmap;
QHash< int, QPixmap > subsubcache;
QHash< int, QHash< int, QPixmap > > subcache;
static QHash< int, QHash< int, QHash< int, QPixmap > > > cache;
if ( cache.contains( type ) )
{
subcache = cache.value( type );
if ( subcache.contains( mode ) )
{
subsubcache = subcache.value( mode );
if ( subsubcache.contains( size.width() ) )
return subsubcache.value( size.width() );
}
}
switch ( type )
{
case DefaultAlbumCover:
if ( mode == CoverInCase )
pixmap = QPixmap( RESPATH "images/no-album-art-placeholder.png" );
else
pixmap = QPixmap( RESPATH "images/no-album-no-case.png" );
break;
case DefaultArtistImage:
pixmap = QPixmap( RESPATH "images/no-artist-image-placeholder.png" );
break;
default:
break;
}
if ( pixmap.isNull() )
{
Q_ASSERT( false );
return QPixmap();
}
if ( !size.isNull() )
pixmap = pixmap.scaled( size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation );
subsubcache.insert( size.width(), pixmap );
subcache.insert( mode, subsubcache );
cache.insert( type, subcache );
return pixmap;
}
} // ns

View File

@@ -19,6 +19,8 @@
#ifndef TOMAHAWKUTILSGUI_H
#define TOMAHAWKUTILSGUI_H
#include <QSize>
#include "tomahawkutils.h"
#include "dllmacro.h"
@@ -46,6 +48,8 @@ namespace TomahawkUtils
DLLEXPORT int headerHeight();
DLLEXPORT void setHeaderHeight( int height );
DLLEXPORT QPixmap defaultPixmap( ImageType type, ImageMode mode, const QSize& size = QSize( 0, 0 ) );
}
#endif // TOMAHAWKUTILSGUI_H

View File

@@ -1,6 +1,7 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2010-2011, Christian Muehlhaeuser <muesli@tomahawk-player.org>
* Copyright 2011-2012, Leo Franchi <lfranchi@kde.org>
*
* Tomahawk is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -25,6 +26,12 @@
#include "utils/tomahawkutils.h"
#include "utils/logger.h"
#ifndef ENABLE_HEADLESS
#include "jobview/JobStatusView.h"
#include "jobview/JobStatusModel.h"
#include "jobview/ErrorStatusMessage.h"
#endif
#include "sourcelist.h"
#include "playlist.h"
#include <XspfUpdater.h>
@@ -32,6 +39,22 @@
using namespace Tomahawk;
QString
XSPFLoader::errorToString( XSPFErrorCode error )
{
switch ( error )
{
case ParseError:
return tr( "Failed to parse contents of XSPF playlist" );
case InvalidTrackError:
return tr( "Some playlist entries were found without artist and track name, they will be omitted");
case FetchError:
return tr( "Failed to fetch the desired playlist from the network, or the desired file does not exist" );
default:
return QString();
}
}
XSPFLoader::XSPFLoader( bool autoCreate, bool autoUpdate, QObject *parent )
: QObject( parent )
, m_autoCreate( autoCreate )
@@ -97,6 +120,9 @@ void
XSPFLoader::reportError()
{
emit error( FetchError );
#ifndef ENABLE_HEADLESS
JobStatusView::instance()->model()->addJob( new ErrorStatusMessage( errorToString( FetchError) ) );
#endif
deleteLater();
}

View File

@@ -1,6 +1,7 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2010-2011, Christian Muehlhaeuser <muesli@tomahawk-player.org>
* Copyright 2011-2012, Leo Franchi <lfranchi@kde.org>
*
* Tomahawk is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -48,6 +49,8 @@ public:
void setOverrideTitle( const QString& newTitle );
void setAutoResolveTracks( bool autoResolve ) { m_autoResolve = autoResolve; }
static QString errorToString( XSPFErrorCode error );
signals:
void error( XSPFLoader::XSPFErrorCode error );
void ok( const Tomahawk::playlist_ptr& );

View File

@@ -74,6 +74,7 @@ ViewManager::ViewManager( QObject* parent )
, m_whatsHotWidget( new WhatsHotWidget() )
, m_topLovedWidget( 0 )
, m_currentMode( PlaylistInterface::Tree )
, m_loaded( false )
{
s_instance = this;
@@ -112,6 +113,10 @@ ViewManager::ViewManager( QObject* parent )
connect( &m_filterTimer, SIGNAL( timeout() ), SLOT( applyFilter() ) );
connect( m_infobar, SIGNAL( filterTextChanged( QString ) ), SLOT( setFilter( QString ) ) );
connect( m_infobar, SIGNAL( autoUpdateChanged( int ) ), SLOT( autoUpdateChanged( int ) ) );
connect( this, SIGNAL( tomahawkLoaded() ), m_whatsHotWidget, SLOT( fetchData() ) );
connect( this, SIGNAL( tomahawkLoaded() ), m_welcomeWidget, SLOT( loadData() ) );
/* connect( m_infobar, SIGNAL( flatMode() ), SLOT( setTableMode() ) );
connect( m_infobar, SIGNAL( artistMode() ), SLOT( setTreeMode() ) );
connect( m_infobar, SIGNAL( albumMode() ), SLOT( setAlbumMode() ) );*/
@@ -121,6 +126,10 @@ ViewManager::ViewManager( QObject* parent )
ViewManager::~ViewManager()
{
saveCurrentPlaylistSettings();
delete m_whatsHotWidget;
delete m_welcomeWidget;
delete m_topLovedWidget;
delete m_contextWidget;
delete m_widget;
}
@@ -366,7 +375,8 @@ ViewManager::showSuperCollection()
}
}
m_superCollectionModel->setTitle( tr( "All available tracks" ) );
m_superCollectionModel->setTitle( tr( "SuperCollection" ) );
m_superCollectionModel->setDescription( tr( "Combined libraries of all your online friends" ) );
m_superAlbumModel->setTitle( tr( "All available albums" ) );
ViewPage* shown = 0;
@@ -427,7 +437,7 @@ Tomahawk::ViewPage*
ViewManager::showTopLovedPage()
{
if ( !m_topLovedWidget )
m_topLovedWidget = new CustomPlaylistView( CustomPlaylistView::AllLovedTracks, source_ptr(), m_widget );
m_topLovedWidget = new CustomPlaylistView( CustomPlaylistView::TopLovedTracks, source_ptr(), m_widget );
return show( m_topLovedWidget );
}
@@ -811,6 +821,15 @@ ViewManager::createDynamicPlaylist( const Tomahawk::source_ptr& src, const QVari
}
void
ViewManager::setTomahawkLoaded()
{
m_loaded = true;
emit tomahawkLoaded();
}
ViewPage*
ViewManager::pageForCollection( const collection_ptr& col ) const
{

View File

@@ -106,6 +106,8 @@ public:
// linked to the sidebar. call it right after creating the playlist
PlaylistView* createPageForPlaylist( const Tomahawk::playlist_ptr& pl );
bool isTomahawkLoaded() const { return m_loaded; }
signals:
void numSourcesChanged( unsigned int sources );
void numTracksChanged( unsigned int tracks );
@@ -130,6 +132,8 @@ signals:
void showQueueRequested();
void hideQueueRequested();
void tomahawkLoaded();
public slots:
Tomahawk::ViewPage* showSuperCollection();
Tomahawk::ViewPage* showWelcomePage();
@@ -164,6 +168,8 @@ public slots:
void createPlaylist( const Tomahawk::source_ptr& src, const QVariant& contents );
void createDynamicPlaylist( const Tomahawk::source_ptr& src, const QVariant& contents );
void setTomahawkLoaded();
private slots:
void setFilter( const QString& filter );
void applyFilter();
@@ -217,6 +223,8 @@ private:
QTimer m_filterTimer;
QString m_filter;
bool m_loaded;
static ViewManager* s_instance;
};

View File

@@ -22,6 +22,7 @@
#include "utils/logger.h"
#include "tomahawksettings.h"
#include <QCoreApplication>
#include <QProcess>
static QString s_macVolumePath = "/Volumes";
@@ -30,40 +31,49 @@ CheckDirModel::CheckDirModel( QWidget* parent )
: QFileSystemModel( parent )
, m_shownVolumes( false )
{
#ifdef Q_WS_MAC
#ifdef Q_OS_MAC
m_setFilePath = QString( "%1/SetFile" ) .arg( QCoreApplication::applicationDirPath() );
m_getFileInfoPath = QString( "%1/GetFileInfo" ).arg( QCoreApplication::applicationDirPath() );
QProcess* checkVolumeVisible = new QProcess( this );
connect( checkVolumeVisible, SIGNAL( readyReadStandardOutput() ), this, SLOT( getFileInfoResult() ) );
checkVolumeVisible->start( "GetFileInfo", QStringList() << "-aV" << s_macVolumePath );
qDebug() << "Running GetFileInfo:" << m_getFileInfoPath << "-aV" << s_macVolumePath;
checkVolumeVisible->start( m_getFileInfoPath, QStringList() << "-aV" << s_macVolumePath );
#endif
}
CheckDirModel::~CheckDirModel()
{
#ifdef Q_WS_MAC
#ifdef Q_OS_MAC
// reset to previous state
if ( m_shownVolumes )
QProcess::startDetached( QString( "SetFile -a V %1" ).arg( s_macVolumePath ) );
QProcess::startDetached( QString( "%1 -a V %2" ).arg( m_setFilePath).arg( s_macVolumePath ) );
#endif
}
void
CheckDirModel::getFileInfoResult()
{
#ifdef Q_WS_MAC
#ifdef Q_OS_MAC
QProcess* p = qobject_cast< QProcess* >( sender() );
Q_ASSERT( p );
QByteArray res = p->readAll().trimmed();
qDebug() << "Got output from GetFileInfo:" << res;
// 1 means /Volumes is hidden, so we show it while the dialog is visible
if ( res == "1" )
{
// Remove the hidden flag for the /Volumnes folder so all mount points are visible in the default (Q)FileSystemModel
QProcess* p = new QProcess( this );
connect( p, SIGNAL( finished( int, QProcess::ExitStatus ) ), this, SLOT( volumeShowFinished() ) );
p->start( QString( "SetFile -a v %1" ).arg( s_macVolumePath ) );
QProcess* showProcess = new QProcess( this );
qDebug() << "Running SetFile:" << QString( "%1 -a v %2" ).arg( m_setFilePath ).arg( s_macVolumePath );
showProcess->start( QString( "%1 -a v %2" ).arg( m_setFilePath ).arg( s_macVolumePath ) );
connect( showProcess, SIGNAL( readyReadStandardError() ), this, SLOT( processErrorOutput() ) );
m_shownVolumes = true;
QTimer::singleShot( 500, this, SLOT( volumeShowFinished() ) );
}
p->terminate();
p->deleteLater();
#endif
}
@@ -74,6 +84,16 @@ CheckDirModel::volumeShowFinished()
reset();
}
void
CheckDirModel::processErrorOutput()
{
QProcess* p = qobject_cast< QProcess* >( sender() );
Q_ASSERT( p );
qDebug() << "Got ERROR OUTPUT from subprocess in CheckDirModel:" << p->readAll();
}
Qt::ItemFlags
CheckDirModel::flags( const QModelIndex& index ) const
{

View File

@@ -46,11 +46,13 @@ signals:
private slots:
void getFileInfoResult();
void volumeShowFinished();
void processErrorOutput();
private:
QHash<QPersistentModelIndex, Qt::CheckState> m_checkTable;
bool m_shownVolumes;
QString m_setFilePath;
QString m_getFileInfoPath;
};

View File

@@ -61,20 +61,20 @@ AlbumInfoWidget::AlbumInfoWidget( const Tomahawk::album_ptr& album, ModelMode st
ui->tracksView->setTreeModel( m_tracksModel );
ui->tracksView->setRootIsDecorated( false );
m_pixmap = QPixmap( RESPATH "images/no-album-art-placeholder.png" ).scaledToWidth( 48, Qt::SmoothTransformation );
m_pixmap = TomahawkUtils::defaultPixmap( TomahawkUtils::DefaultAlbumCover, TomahawkUtils::ScaledCover, QSize( 48, 48 ) );
m_button = new OverlayButton( ui->tracksView );
m_button->setCheckable( true );
m_button->setChecked( m_tracksModel->mode() == InfoSystemMode );
if ( m_button->isChecked() )
m_button->setText( tr( "Click to show Super Collection Tracks" ) );
m_button->setText( tr( "Click to show SuperCollection Tracks" ) );
else
m_button->setText( tr( "Click to show Official Tracks" ) );
m_buttonAlbums = new OverlayButton( ui->albumsView );
m_buttonAlbums->setCheckable( true );
m_buttonAlbums->setChecked( true );
m_buttonAlbums->setText( tr( "Click to show Super Collection Albums" ) );
m_buttonAlbums->setText( tr( "Click to show SuperCollection Albums" ) );
m_buttonAlbums->show();
connect( m_button, SIGNAL( clicked() ), SLOT( onModeToggle() ) );
@@ -113,7 +113,7 @@ AlbumInfoWidget::setMode( ModelMode mode )
onModeToggle();
if ( mode == InfoSystemMode )
m_button->setText( tr( "Click to show Super Collection Tracks" ) );
m_button->setText( tr( "Click to show SuperCollection Tracks" ) );
else
m_button->setText( tr( "Click to show Official Tracks" ) );
}
@@ -131,7 +131,7 @@ void
AlbumInfoWidget::onAlbumsModeToggle()
{
if ( m_buttonAlbums->isChecked() )
m_buttonAlbums->setText( tr( "Click to show Super Collection Albums" ) );
m_buttonAlbums->setText( tr( "Click to show SuperCollection Albums" ) );
else
m_buttonAlbums->setText( tr( "Click to show Official Albums" ) );
@@ -243,10 +243,10 @@ AlbumInfoWidget::loadAlbums( bool autoRefetch )
void
AlbumInfoWidget::onAlbumCoverUpdated()
{
if ( m_album->cover().isNull() )
if ( m_album->cover( QSize( 0, 0 ) ).isNull() )
return;
m_pixmap.loadFromData( m_album->cover() );
m_pixmap = m_album->cover( QSize( 0, 0 ) );
emit pixmapChanged( m_pixmap );
}

Some files were not shown because too many files have changed in this diff Show More