1
0
mirror of https://github.com/tomahawk-player/tomahawk.git synced 2025-09-06 12:10:47 +02:00

Compare commits

...

151 Commits

Author SHA1 Message Date
Dominik Schmidt
34448b1b46 DownloadButton: Share click handling code between primitive and widget mode 2016-04-16 01:16:35 +02:00
Dominik Schmidt
4408ba275c Add localUrlForDownload method 2016-04-16 00:19:36 +02:00
Dominik Schmidt
dfd71d7010 Add DownloadButton to column view 2016-04-15 21:44:34 +02:00
Dominik Schmidt
170e2e1c86 Allow to use DownloadButton as a real standalone widget 2016-04-15 21:42:28 +02:00
Dominik Schmidt
0047763188 Unify DownloadButtons 2016-04-15 18:48:30 +02:00
Christian Muehlhaeuser
85657e40a6 Update editor geometry correctly for download buttons in GridItemDelegate. 2016-04-14 16:56:56 +02:00
Dominik Schmidt
af459afb0b Show actual DropDownButton (with Format selector) for DownloadFormats in GridView 2016-04-14 16:56:56 +02:00
Dominik Schmidt
5bbc39e6a5 Fix broken button state after download from GridView 2016-04-14 16:56:56 +02:00
Dominik Schmidt
c88640268a Fix DropDownButton activation in GridView 2016-04-14 16:56:56 +02:00
Dominik Schmidt
8749a04e23 Fix album folders not being opened correctly if the path contained whitespaces 2016-04-14 16:56:55 +02:00
Dominik Schmidt
d45917d14a Show View In Folder button when album is completely downloaded 2016-04-14 16:56:39 +02:00
Dominik Schmidt
c87b1390e5 Add Result::isLocal helper method 2016-04-14 16:53:30 +02:00
Christian Muehlhaeuser
bf28f6856e Fixed seeking for HTTP streams without headers, e.g. Spotify (see #453). 2016-04-14 00:57:00 +02:00
Dominik Schmidt
ceab0f9cf2 Fix RTMP streams 2016-04-13 20:03:02 +02:00
Christian Muehlhaeuser
a562e75937 Fixed copy/paste job-gone-wrong: GPL2->GPL3 header updates. 2016-04-13 15:34:27 +02:00
Christian Muehlhaeuser
81eb226146 Abort event-handling when TomahawkWindow's event-filter caught and handled a mouse event. 2016-04-13 14:40:08 +02:00
Dominik Schmidt
ec88cf27f3 Fix getResponseHeader to correctly return values for case-insensitive headers 2016-04-13 13:59:35 +02:00
Dominik Schmidt
931562fbe5 Fix getResponseHeader for native requests. Needs discussion what's the cross platform way forward 2016-04-13 13:17:38 +02:00
Christian Muehlhaeuser
77593e32a7 Don't refresh collection every single time we're about to show the page. 2016-04-12 20:05:13 +02:00
Christian Muehlhaeuser
bac38e4555 Coding style in SeekSlider.cpp. 2016-04-12 15:55:33 +02:00
Christian Muehlhaeuser
980ee97c97 Merge pull request #332 from tmwoz/master
Fix for next track not getting triggered when seeking to/past the end with a mouse
2016-04-12 15:45:07 +02:00
Christian Muehlhaeuser
26ad51f4b8 Removed Qt5 warning. It's our default now. 2016-04-12 14:31:08 +02:00
Christian Muehlhaeuser
21bdb0bc89 Show up to 50 top tracks per artist. 2016-04-12 14:23:18 +02:00
Christian Muehlhaeuser
0a560f2f27 Fixed compiler warnings for QMetaType comparison. 2016-04-12 14:11:41 +02:00
Christian Muehlhaeuser
9c9f5e4dae Added Anton Romanov and Stefan Ahlers to our about-dialog. Well deserved guys, thanks for all the amazing work. 2016-04-05 11:37:44 +02:00
Christian Muehlhaeuser
fb38735936 Style fix in Pipeline. 2016-04-05 11:34:45 +02:00
Dominik Schmidt
c62ae52bfe Fix Zeroconf with recent Qt5 2016-04-02 13:45:08 +02:00
Dominik Schmidt
0341492069 Fix #458: Completely disable BUY buttons in GridItemDelegate instead of only making them invisible 2016-04-02 13:28:56 +02:00
Dominik Schmidt
879df894c3 Merge pull request #465 from mrmaffen/master
Fixed a bug in fake xhr's getResponseHeader function. Plus some compat fixes for th android
2016-04-02 11:59:35 +02:00
Dominik Schmidt
0c9f1be2c8 Prevent users from compiling Tomahawk with Qt4 2016-03-27 12:07:08 +02:00
Teo Mrnjavac
f7a3d441a1 Update libcrashreporter-qt submodule for build fix. 2016-03-25 11:46:19 +01:00
Teo Mrnjavac
e9a1135525 Update libcrashreporter-qt submodule. 2016-03-25 10:08:45 +01:00
Teo Mrnjavac
bdce4f7636 Probably shouldn't be inline. 2016-03-21 11:29:23 +01:00
Teo Mrnjavac
de65e19cb7 Use a sans serif antialiased font in the artist bio view. 2016-03-09 14:34:20 +01:00
Teo Mrnjavac
ce3647a4a6 Scale up artist bio font size with DpiScaler to make it always readable 2016-03-09 13:44:45 +01:00
Enno Gottschalk
f498570a0e Fixed a bug in fake xhr's getResponseHeader function. Plus some compat fixes for th android 2016-03-04 19:34:03 +01:00
Enno Gottschalk
9a741cd2db Czeched, polished and finnished 2016-03-02 17:11:01 +01:00
Enno Gottschalk
102c9c6846 Make testConfig great again! 2016-03-02 16:14:27 +01:00
Dominik Schmidt
1fa35ae827 Go back to former VLC build as the new build is crashy 2016-02-25 23:10:07 +01:00
Dominik Schmidt
26c0f35d21 Wrap error message in account config 2016-02-25 03:46:44 +01:00
Dominik Schmidt
9acbb48659 If sign_step.sh exists, require it to succeed 2016-02-25 02:52:25 +01:00
Dominik Schmidt
cc9f287b41 Don't rely on target names for export macros 2016-02-23 17:20:31 +01:00
Dominik Schmidt
99abd7434b Don't hardcode app id for vlc 2016-02-23 16:14:10 +01:00
Dominik Schmidt
f38f3ca367 Linux: Fix icon installation 2016-02-23 16:14:10 +01:00
Christian Muehlhaeuser
773ecea0da Be less verbose in SourceTreeView. 2016-02-23 02:24:10 +01:00
Christian Muehlhaeuser
620d3fe3fc Style fixes in SourceTreeView. 2016-02-23 02:23:47 +01:00
Christian Muehlhaeuser
8679fd5055 Style fix in CollectionViewPage. 2016-02-23 02:23:19 +01:00
Christian Muehlhaeuser
d7eccea4c0 Remove obsolete method in SourceTreeView. 2016-02-23 02:22:53 +01:00
Christian Muehlhaeuser
2ab9418a9e Don't paint a hover background during drag & drop, when you can't drop something on a sidebar item, e.g. groups, labels etc. 2016-02-23 02:15:45 +01:00
Christian Muehlhaeuser
285333d43b Blur the collection header background, so the labels stand out a bit more. 2016-02-23 01:20:35 +01:00
Christian Muehlhaeuser
3bcf854a4f Updated ChangeLog. 2016-02-22 22:32:54 +01:00
Christian Muehlhaeuser
61a15cafac Speed up drag & drop moves over sidebar. 2016-02-22 22:31:34 +01:00
Christian Muehlhaeuser
2d141ccb5e Style fix in SoureTreeView. 2016-02-22 22:31:34 +01:00
Christian Muehlhaeuser
4c023b7d2f Style fix in XspfUpdater. 2016-02-22 22:31:34 +01:00
Christian Muehlhaeuser
ceb5dc6aa4 Style fix in main.cpp. 2016-02-22 22:31:34 +01:00
Christian Muehlhaeuser
13fa9c7c53 Merge pull request #448 from tomahawk-player/vlc-volume-fix
Fix #441: Handle VLC volume and muted state properly
2016-02-22 22:31:04 +01:00
Dominik Schmidt
4c946b304c Fix #451: Can't select crash ID in the crash report UI 2016-02-22 22:02:54 +01:00
Dominik Schmidt
5075c9ddbf Fix compilation with libvlc < 2.2.2 2016-02-22 16:10:47 +01:00
Dominik Schmidt
8b3a916aa6 Update VLC for Windows 2016-02-22 16:10:47 +01:00
Dominik Schmidt
cd412add8f Persist muted state accross restarts 2016-02-22 16:10:47 +01:00
Dominik Schmidt
68851843f5 Unmute AudioOutput on start up 2016-02-22 16:10:47 +01:00
Dominik Schmidt
af8811af30 Fix volume on external (un)muting 2016-02-22 16:10:47 +01:00
Dominik Schmidt
fa38ed5e91 Sync muted state as well 2016-02-22 16:10:47 +01:00
Dominik Schmidt
b7ff630808 Sync audio volume if it's changed externally in vlc 2016-02-22 16:10:47 +01:00
Dominik Schmidt
196256d77b Fix startup volume for good 2016-02-22 16:10:47 +01:00
Dominik Schmidt
79cd3a7f0a Revert "Fix startup volume for good"
This reverts commit 86cd7c1be0.
2016-02-22 16:10:20 +01:00
Dominik Schmidt
f0f45673b1 Fix #419: Fix HLS streams on Windows (e.g. Amazon Prime Music) 2016-02-22 16:09:11 +01:00
Dominik Schmidt
86cd7c1be0 Fix startup volume for good 2016-02-22 00:10:17 +01:00
Dominik Schmidt
f74c6ae429 Fix #446: Fix compilation with Qt4 2016-02-21 18:06:14 +01:00
Dominik Schmidt
015b24479c Fix crash in context menu 2016-02-21 11:12:17 +01:00
Jason Herskowitz
8b904cf806 Make folder icon match other context menu icons 2016-02-19 10:04:32 -05:00
Christian Muehlhaeuser
175e498c43 Use folder icon for file manager in context menu. 2016-02-19 04:48:42 +01:00
Jason Herskowitz
65908f9dfa Add folder icon 2016-02-18 22:35:42 -05:00
Christian Muehlhaeuser
3c9deb6389 A few improvements regarding VLC's weird mute state on startup. 2016-02-19 04:11:11 +01:00
Christian Muehlhaeuser
978c1e58de Fixed crash in ContextMenu and moved 'open file manager' action up. 2016-02-19 03:50:17 +01:00
Christian Muehlhaeuser
0722dd7812 Merge pull request #442 from tomahawk-player/open-in-file-manager
Add "Open File in File Manager..." context menu
2016-02-19 03:37:50 +01:00
Dominik Schmidt
ff34afb4e3 Fix #425: add "Open in Folder in File Manager..." context menu to local results 2016-02-18 16:02:16 +01:00
Dominik Schmidt
bd607b6c54 DRY in ContextMenu 2016-02-18 15:40:08 +01:00
Dominik Schmidt
81f7e3f8f4 consistency++: Pass track to linkGenerator instead of title 2016-02-18 14:14:21 +01:00
Christian Muehlhaeuser
798ca62190 When waiting for a query to resolve for playback, accept the earliest online result, preventing further playback delay. 2016-02-18 07:03:17 +01:00
Christian Muehlhaeuser
f2631cda74 Don't wait for resolving to finish if we want to play a track and already got some results. 2016-02-18 06:40:16 +01:00
Dominik Schmidt
4813698a9a Allow creating basic data types from QtConcurrent::run
When QObjects are created from QtConcurrent::run they live in a pooled thread that doesn't handle events/signals/slots properly
2016-02-18 00:52:48 +01:00
Christian Muehlhaeuser
dc80d19a08 Merge pull request #399 from tomahawk-player/things
Various download and branding improvements
2016-02-17 20:32:06 +01:00
Dominik Schmidt
93745b19ef Deployment scripts branding update 2016-02-17 19:37:46 +01:00
Dominik Schmidt
1b7b7ca4b3 Bump snorenotify requirement 2016-02-17 19:37:46 +01:00
Dominik Schmidt
3808e230d4 Make result more thread safe 2016-02-17 19:37:46 +01:00
Dominik Schmidt
0bdb8061e2 Do not set empty mimetype on results 2016-02-17 19:37:46 +01:00
Dominik Schmidt
87278456f4 Add download context menu to all playlist views but database collections 2016-02-17 19:37:46 +01:00
Dominik Schmidt
a5693d6663 Workaround preview widget flicker in column view 2016-02-17 19:37:46 +01:00
Dominik Schmidt
6416b34e8c Don't reset column sort order on collection updated 2016-02-17 19:37:45 +01:00
Dominik Schmidt
f95bd4aab8 Correctly paint hover state in DropDownButton 2016-02-17 19:37:45 +01:00
Dominik Schmidt
996d112ebb Fix DropDownButton font size and boldness 2016-02-17 19:37:45 +01:00
Dominik Schmidt
fc413ac103 Fix download all assert/crash 2016-02-17 19:37:45 +01:00
Dominik Schmidt
2020aad725 Download and branding improvements 2016-02-17 19:37:33 +01:00
Christian Muehlhaeuser
198201aec2 Don't try to reset hover-state if there is none. This speeds up collection browsing. 2016-02-17 12:15:24 +01:00
Christian Muehlhaeuser
6b84630ba1 Reset GridView's hover-state on wheel event. 2016-02-17 12:15:24 +01:00
Christian Muehlhaeuser
205dfca60d Don't use native windows for each widget. This is insane. Thanks Domme for figuring this out. 2016-02-17 01:57:39 +01:00
Dominik Schmidt
ca2da81132 Don't parse artists/albums for non-fulltext queries 2016-02-16 17:11:52 +01:00
Dominik Schmidt
08004ddbd4 Allow returning arrays or maps from resolve/search in JS 2016-02-16 16:47:58 +01:00
Dominik Schmidt
4dadbb5acd Support album and artist results in JSResolvers 2016-02-16 14:53:39 +01:00
Christian Muehlhaeuser
dee504c997 Use correct units for filesize. 2016-02-16 14:40:25 +01:00
Christian Muehlhaeuser
81dae62349 Merge pull request #438 from tomahawk-player/makeresolverdevelopmentgreatagain-wip
Add "Open Account Debugger" menu to ScriptAccounts in SettingsDialog
2016-02-16 02:00:41 +01:00
Dominik Schmidt
3c00f36df6 Add ScriptAccount debugger menu to SettingsDialog/AccountListWidget 2016-02-16 01:59:45 +01:00
Christian Muehlhaeuser
f13ea2f6df Merge pull request #436 from tomahawk-player/makeresolverdevelopmentgreatagain
Show debugger for script errors
2016-02-15 17:04:25 +01:00
Dominik Schmidt
2f4eb75745 Show debugger (WebInspector for JS) when clicking script errors 2016-02-14 13:50:05 +01:00
Dominik Schmidt
942eded60d Make JobStatusItems clickable 2016-02-14 13:41:20 +01:00
Dominik Schmidt
1b2ec05ea3 Move infoplugin wrong thread log message above assert 2016-02-12 22:16:42 +01:00
Dominik Schmidt
3875b10bb4 Don't leak spotify info plugin 2016-02-12 22:13:58 +01:00
Dominik Schmidt
f396f59922 Fix xmpp info plugin 2016-02-12 22:13:39 +01:00
Dominik Schmidt
7271a95942 Add friendlyName to XMPP info plugin 2016-02-12 22:12:54 +01:00
Dominik Schmidt
9737b7a8bc Make InfoPluginPtr a QSharedPointer 2016-02-12 21:37:47 +01:00
Dominik Schmidt
d87f966d9e Merge pull request #429 from midzer/patch1
erasing in an iteration and pass by reference
2016-02-10 12:11:23 +01:00
Dominik Schmidt
cb3ccd5a76 Protect m_formats access with mutex in Result 2016-02-10 00:21:31 +01:00
Dennis
f81858e28a pass by reference 2016-02-10 00:07:09 +01:00
Dennis
8fbc0319ba Erase elements from QStringList safely 2016-02-09 23:59:43 +01:00
Christian Muehlhaeuser
e7d83835d2 What a typo. 2016-02-07 11:39:04 +01:00
Christian Muehlhaeuser
6035889b90 Coding style. 2016-02-06 07:07:53 +01:00
Christian Muehlhaeuser
2bb0113515 Make sure Query's resultSorter provides stable sorting. 2016-02-06 07:07:53 +01:00
Christian Muehlhaeuser
8ee8721dda Merge pull request #423 from cash/patch-1
fixed a typo in PlaydarApi documentation
2016-02-02 00:30:35 +01:00
Cash Costello
293b3b1b4f fixed a typo in PlaydarApi documentation 2016-02-01 18:01:29 -05:00
Dominik Schmidt
ba0cc1a00c Don't assert for linkParser plugins, placeholder for real implementation 2016-02-01 15:18:06 +01:00
Dominik Schmidt
759bfbe63b Merge pull request #388 from theli-ua/nonalphanum
Improve track matching with non-alphanum character differences
2016-01-29 00:58:20 +01:00
Anton Romanov
e3877bf0bf Improve track matching with non-alphanum character differences 2016-01-28 15:52:15 -08:00
Dominik Schmidt
f1cf645f31 Ship D3DCompiler_43.dll to make QtDeclarative work 2016-01-28 22:16:11 +01:00
Dominik Schmidt
62dea3a196 Merge pull request #416 from theli-ua/resolver_sort
only use resolvers to compare if there are resolvers tied to results
2016-01-28 00:44:50 +01:00
Dominik Schmidt
09f68fa2f5 Use v09 synchrotron 2016-01-28 00:34:57 +01:00
Anton Romanov
944827c48c only use resolvers to compare if there are resolvers tied to results 2016-01-27 15:01:45 -08:00
Dominik Schmidt
771b2f5ae1 Add resolve default implementation in Tomahawk.Resolver 2016-01-27 18:03:40 +01:00
Dominik Schmidt
d50c16740a Fix crash in parseResultVariantList for broken track data 2016-01-27 17:31:48 +01:00
Dominik Schmidt
a5968b58e5 Update dll names 2016-01-26 02:11:10 +00:00
Dominik Schmidt
5f20308177 Go back to lowercase installer names on windows 2016-01-26 02:10:53 +00:00
Dominik Schmidt
1710813dc8 Correct indentation in NSIS.template.in 2016-01-25 16:03:27 +01:00
Dominik Schmidt
7fd306ca07 Don't use processEvents and still don't block ui for loading a script collection 2016-01-25 16:03:27 +01:00
Christian Muehlhaeuser
bdaf1ef2f4 Merge pull request #405 from theli-ua/resolver_sort
Sort equal results by resolver's weight
2016-01-20 22:18:26 +01:00
Anton Romanov
c068d8c9e3 Left goes to the left, right to the right 2016-01-20 12:45:55 -08:00
Anton Romanov
1e6b8b3747 Fix sorting order 2016-01-19 16:35:38 -08:00
Anton Romanov
0535d09f08 Sort equal results by resolver's weight 2016-01-19 11:11:11 -08:00
Christian Muehlhaeuser
c93302c563 Revert "Process events while parsing potentially very long script responses"
This reverts commit 1025e77d3e.
2016-01-18 11:04:19 +01:00
Christian Muehlhaeuser
1192c668b0 Too much whitespace in debug output. 2016-01-18 11:03:09 +01:00
Dominik Schmidt
53b27d0ea1 Fix isDown state in ImageButton 2016-01-18 04:55:48 +01:00
Dominik Schmidt
9ebe155313 Clean up PlayableProxyModel::columnCount 2016-01-18 03:19:01 +01:00
Dominik Schmidt
ddf030037e Fix artist colum not being rearrangable 2016-01-18 03:15:00 +01:00
Dominik Schmidt
5d950bd139 Add colum mapping to PlayableProxyModel 2016-01-18 02:50:19 +01:00
Dominik Schmidt
eab1ccee74 Fix #381: Show scrollbars for biography as needed 2016-01-18 00:21:26 +01:00
Dominik Schmidt
1025e77d3e Process events while parsing potentially very long script responses 2016-01-18 00:21:09 +01:00
Christian Muehlhaeuser
53de4319b6 Merge pull request #400 from tomahawk-player/fix-dropdownbutton
Fix DropDownButton in Playlist view
2016-01-15 18:26:57 +01:00
Dominik Schmidt
4eee01d8bf Fix DropDownButton in Playlist view 2016-01-15 18:10:28 +01:00
Dominik Schmidt
5bd69fe09c Automatically link against libc++ if found and compiler is clang 2016-01-15 04:12:12 +01:00
Tomasz Woźniak
1b0517efc5 moved the fix to include seeking via position 2015-10-05 22:01:50 +02:00
Tomasz Woźniak
e6a8a41c31 added quick fix for libvlc not seeking correctly to an end for some tracks 2015-10-05 20:04:17 +02:00
Tomasz Woźniak
193beda2bc handle case when user seeks past the slider's end 2015-10-05 00:47:26 +02:00
175 changed files with 2520 additions and 1114 deletions

View File

@@ -179,7 +179,7 @@ if( NOT BUILD_WITH_QT4 )
set(HAVE_X11 FALSE)
endif()
message(STATUS "Found Qt5! Be aware that Qt5-support is still experimental and not officially supported!")
message(STATUS "Found Qt5!")
if( UNIX AND NOT APPLE )
# We need this to find the paths to qdbusxml2cpp and co
@@ -228,6 +228,10 @@ endif()
if( NOT Qt5Core_DIR )
message(STATUS "Could not find Qt5, searching for Qt4 instead...")
if(NOT FORCE_QT4)
message(FATAL_ERROR "Qt4 support is broken, if you plan to fix it, add -DFORCE_QT4=ON to your cmake arguments otherwise you should compile Tomahawk with Qt5")
endif()
set(NEEDED_QT4_COMPONENTS "QtCore" "QtXml" "QtNetwork")
if( BUILD_GUI )
list(APPEND NEEDED_QT4_COMPONENTS "QtGui" "QtWebkit" "QtUiTools" "QtSvg")
@@ -307,11 +311,10 @@ else()
message(STATUS "${CMAKE_CXX_COMPILER} does not support C++11, please use a
different compiler")
endif()
if(LIBCPP_FOUND AND APPLE)
if(("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang" OR APPLE) AND LIBCPP_FOUND)
tomahawk_add_cxx_flags( "-stdlib=libc++" )
endif()
macro_optional_find_package(Echonest 2.3.0)
macro_log_feature(ECHONEST_FOUND "Echonest" "Qt library for communicating with The Echo Nest" "http://projects.kde.org/libechonest" TRUE "" "libechonest 2.3.0 is needed for dynamic playlists and the infosystem")
@@ -404,7 +407,7 @@ if( WIN32 )
endif( WIN32 )
if( WIN32 OR APPLE )
macro_optional_find_package(LibsnoreQt5 0.5.70 QUIET)
macro_optional_find_package(LibsnoreQt5 0.6.0 QUIET)
macro_log_feature(LibsnoreQt5_FOUND "Libsnore" "Library for notifications" "https://projects.kde.org/projects/playground/libs/snorenotify" FALSE "" "")
endif()

View File

@@ -18,11 +18,10 @@
!ifndef MINGW_ROOT
!define MINGW_ROOT "/usr/i686-w64-mingw32/sys-root/mingw"
!endif
!define APPLICATION_NAME "Tomahawk"
!define TARGET_NAME "tomahawk"
!define APPLICATION_NAME "@CPACK_PACKAGE_NAME@"
!define TARGET_NAME "@CPACK_PACKAGE_TARGET_NAME@"
;define app id needed for Windows 8 notifications
!define AppUserModelId @TOMAHAWK_APPLICATION_PACKAGE_NAME@
!define AppUserModelId "@TOMAHAWK_APPLICATION_PACKAGE_NAME@"
!define MINGW_BIN "${MINGW_ROOT}/bin"
!define MINGW_LIB "${MINGW_ROOT}/lib"
@@ -347,105 +346,108 @@ Section "${APPLICATION_NAME}" SEC_TOMAHAWK_PLAYER
File "${QT_DLL_PATH}\icudata56.dll"
File "${QT_DLL_PATH}\icui18n56.dll"
;SQLite driver
SetOutPath "$INSTDIR\sqldrivers"
File "${SQLITE_DLL_PATH}\qsqlite.dll"
SetOutPath "$INSTDIR"
File "${MINGW_BIN}\libsqlite3-0.dll"
;SQLite driver
SetOutPath "$INSTDIR\sqldrivers"
File "${SQLITE_DLL_PATH}\qsqlite.dll"
SetOutPath "$INSTDIR"
File "${MINGW_BIN}\libsqlite3-0.dll"
;Qt platform plugins
SetOutPath "$INSTDIR\platforms"
File "${MINGW_LIB}/qt5/plugins/platforms/qwindows.dll"
SetOutPath "$INSTDIR"
;Qt platform plugins
SetOutPath "$INSTDIR\platforms"
File "${MINGW_LIB}/qt5/plugins/platforms/qwindows.dll"
SetOutPath "$INSTDIR"
;Image plugins
SetOutPath "$INSTDIR\imageformats"
File "${IMAGEFORMATS_DLL_PATH}\qgif.dll"
File "${IMAGEFORMATS_DLL_PATH}\qjpeg.dll"
File "${IMAGEFORMATS_DLL_PATH}\qsvg.dll"
SetOutPath "$INSTDIR"
;Image plugins
SetOutPath "$INSTDIR\imageformats"
File "${IMAGEFORMATS_DLL_PATH}\qgif.dll"
File "${IMAGEFORMATS_DLL_PATH}\qjpeg.dll"
File "${IMAGEFORMATS_DLL_PATH}\qsvg.dll"
SetOutPath "$INSTDIR"
;Qt qml plugins
SetOutPath "$INSTDIR\QtQuick.2"
File /r /x *.debug "${QT_QML_PATH}\QtQuick.2\*"
SetOutPath "$INSTDIR\QtQuick\Window.2"
File /r /x *.debug "${QT_QML_PATH}\QtQuick\Window.2\*"
SetOutPath "$INSTDIR"
;Qt qml plugins
SetOutPath "$INSTDIR\QtQuick.2"
File /r /x *.debug "${QT_QML_PATH}\QtQuick.2\*"
SetOutPath "$INSTDIR\QtQuick\Window.2"
File /r /x *.debug "${QT_QML_PATH}\QtQuick\Window.2\*"
SetOutPath "$INSTDIR"
;Cygwin/c++ stuff
File "${MINGW_BIN}\libgcc_s_sjlj-1.dll"
File "${MINGW_BIN}\libstdc++-6.dll"
;Cygwin/c++ stuff
File "${MINGW_BIN}\libgcc_s_sjlj-1.dll"
File "${MINGW_BIN}\libstdc++-6.dll"
;VLC
File "${VLC_BIN}\libvlc.dll"
File "${VLC_BIN}\libvlccore.dll"
SetOutPath "$INSTDIR\plugins"
File /r "${VLC_PLUGIN_PATH}\*.dll"
SetOutPath "$INSTDIR"
;VLC
File "${VLC_BIN}\libvlc.dll"
File "${VLC_BIN}\libvlccore.dll"
SetOutPath "$INSTDIR\plugins"
File /r "${VLC_PLUGIN_PATH}\*.dll"
SetOutPath "$INSTDIR"
; Other
File "${MINGW_BIN}\libtag.dll"
File "${MINGW_BIN}\libpng16-16.dll"
File "${MINGW_BIN}\libjpeg-8.dll"
File "${MINGW_BIN}\zlib1.dll"
File "${MINGW_BIN}\libfreetype-6.dll"
File "${MINGW_BIN}\libglib-2.0-0.dll"
File "${MINGW_BIN}\libharfbuzz-0.dll"
; Other
File "${MINGW_BIN}\libtag.dll"
File "${MINGW_BIN}\libpng16-16.dll"
File "${MINGW_BIN}\libjpeg-8.dll"
File "${MINGW_BIN}\zlib1.dll"
File "${MINGW_BIN}\libfreetype-6.dll"
File "${MINGW_BIN}\libglib-2.0-0.dll"
File "${MINGW_BIN}\libharfbuzz-0.dll"
File "${MINGW_BIN}\libechonest5.dll"
File "${MINGW_BIN}\liblastfm5.dll"
File "${MINGW_BIN}\libquazip5.dll"
File "${MINGW_BIN}\libqt5keychain.dll"
; ANGLE
File "${MINGW_BIN}\D3DCompiler_43.dll"
; GnuTLS
File "${MINGW_BIN}\libgnutls-28.dll"
File "${MINGW_BIN}\libtasn1-6.dll"
File "${MINGW_BIN}\libgmp-10.dll"
File "${MINGW_BIN}\libhogweed-2-4.dll"
File "${MINGW_BIN}\libintl-8.dll"
File "${MINGW_BIN}\libnettle-4-6.dll"
File "${MINGW_BIN}\libp11-kit-0.dll"
File "${MINGW_BIN}\libffi-6.dll"
File "${MINGW_BIN}\libechonest5.dll"
File "${MINGW_BIN}\liblastfm5.dll"
File "${MINGW_BIN}\libquazip5.dll"
File "${MINGW_BIN}\libqt5keychain.dll"
; Snorenotify
File "${MINGW_BIN}\SnoreToast.exe"
File "${MINGW_BIN}\libsnore-qt5.dll"
File "${MINGW_BIN}\libsnoresettings-qt5.dll"
File "${MINGW_BIN}\snoresettings.exe"
File "${MINGW_LIB}\plugins\libsnore-qt5\libsnore_backend_growl.dll"
File "${MINGW_LIB}\plugins\libsnore-qt5\libsnore_settings_backend_growl.dll"
File "${MINGW_LIB}\plugins\libsnore-qt5\libsnore_backend_snarl.dll"
File "${MINGW_LIB}\plugins\libsnore-qt5\libsnore_settings_backend_snarl.dll"
File "${MINGW_LIB}\plugins\libsnore-qt5\libsnore_backend_snore.dll"
File "${MINGW_LIB}\plugins\libsnore-qt5\libsnore_settings_backend_snore.dll"
File "${MINGW_LIB}\plugins\libsnore-qt5\libsnore_backend_windowstoast.dll"
; GnuTLS
File "${MINGW_BIN}\libgnutls-28.dll"
File "${MINGW_BIN}\libtasn1-6.dll"
File "${MINGW_BIN}\libgmp-10.dll"
File "${MINGW_BIN}\libhogweed-4-1.dll"
File "${MINGW_BIN}\libintl-8.dll"
File "${MINGW_BIN}\libnettle-6-1.dll"
File "${MINGW_BIN}\libp11-kit-0.dll"
File "${MINGW_BIN}\libffi-6.dll"
; Snoregrowl
File "${MINGW_BIN}\libsnoregrowl++.dll"
File "${MINGW_BIN}\libsnoregrowl.dll"
; Snorenotify
File "${MINGW_BIN}\SnoreToast.exe"
File "${MINGW_BIN}\libsnore-qt5.dll"
File "${MINGW_BIN}\libsnoresettings-qt5.dll"
File "${MINGW_BIN}\snoresettings.exe"
File "${MINGW_LIB}\plugins\libsnore-qt5\libsnore_backend_growl.dll"
File "${MINGW_LIB}\plugins\libsnore-qt5\libsnore_settings_backend_growl.dll"
File "${MINGW_LIB}\plugins\libsnore-qt5\libsnore_backend_snarl.dll"
File "${MINGW_LIB}\plugins\libsnore-qt5\libsnore_settings_backend_snarl.dll"
File "${MINGW_LIB}\plugins\libsnore-qt5\libsnore_backend_snore.dll"
File "${MINGW_LIB}\plugins\libsnore-qt5\libsnore_settings_backend_snore.dll"
File "${MINGW_LIB}\plugins\libsnore-qt5\libsnore_backend_windowstoast.dll"
; Snoregrowl
File "${MINGW_BIN}\libsnoregrowl++.dll"
File "${MINGW_BIN}\libsnoregrowl.dll"
; Jabber
File "${MINGW_BIN}\libjreen-qt5.dll"
File "${MINGW_BIN}\libidn-11.dll"
File "${MINGW_BIN}\libgsasl-7.dll"
File "${MINGW_BIN}\libqca-qt5.dll"
SetOutPath "$INSTDIR\crypto"
File "${MINGW_LIB}\qca-qt5\crypto\libqca-ossl.dll"
SetOutPath "$INSTDIR"
File "${MINGW_BIN}\libssl-10.dll"
File "${MINGW_BIN}\libcrypto-10.dll"
File "${MINGW_BIN}\libjreen-qt5.dll"
File "${MINGW_BIN}\libidn-11.dll"
File "${MINGW_BIN}\libgsasl-7.dll"
File "${MINGW_BIN}\libqca-qt5.dll"
SetOutPath "$INSTDIR\crypto"
File "${MINGW_LIB}\qca-qt5\crypto\libqca-ossl.dll"
SetOutPath "$INSTDIR"
File "${MINGW_BIN}\libssl-10.dll"
File "${MINGW_BIN}\libcrypto-10.dll"
; LucenePlusPlus
File "${MINGW_BIN}\liblucene++.dll"
File "${MINGW_BIN}\libboost_system-mt.dll"
File "${MINGW_BIN}\libboost_filesystem-mt.dll"
File "${MINGW_BIN}\libboost_iostreams-mt.dll"
File "${MINGW_BIN}\libboost_regex-mt.dll"
File "${MINGW_BIN}\libboost_thread-mt.dll"
File "${MINGW_BIN}\libbz2-1.dll"
; LucenePlusPlus
File "${MINGW_BIN}\liblucene++.dll"
File "${MINGW_BIN}\libboost_system-mt.dll"
File "${MINGW_BIN}\libboost_filesystem-mt.dll"
File "${MINGW_BIN}\libboost_iostreams-mt.dll"
File "${MINGW_BIN}\libboost_regex-mt.dll"
File "${MINGW_BIN}\libboost_thread-mt.dll"
File "${MINGW_BIN}\libbz2-1.dll"
File "${MINGW_BIN}\libqtsparkle-qt5.dll"
File "${MINGW_BIN}\libKF5Attica.dll"
File "${MINGW_BIN}\libqtsparkle-qt5.dll"
File "${MINGW_BIN}\libKF5Attica.dll"
SectionEnd
SectionGroup "Shortcuts"

View File

@@ -1,6 +1,7 @@
Version 0.9.0:
* Resolved various playback issues by switching to a new audio engine.
* Fixed collection sorting.
* Fixed volume/mute state not being reset correctly on startup.
Version 0.8.4:
* Fixed drag & drop issues on sidebar.

View File

@@ -2,7 +2,7 @@ INCLUDE( InstallRequiredSystemLibraries )
SET( CPACK_PACKAGE_CONTACT "Dominik Schmidt <domme@tomahawk-player.org>" )
SET( CPACK_PACKAGE_FILE_NAME "${TOMAHAWK_APPLICATION_NAME}-${TOMAHAWK_VERSION}" ) # Package file name without extension. Also a directory of installer cmake-2.5.0-Linux-i686
SET( CPACK_PACKAGE_FILE_NAME "${TOMAHAWK_TARGET_NAME}-${TOMAHAWK_VERSION}" ) # Package file name without extension. Also a directory of installer cmake-2.5.0-Linux-i686
# CPACK_GENERATOR CPack generator to be used STGZ;TGZ;TZ
# CPACK_INCLUDE_TOPLEVEL_DIRECTORY Controls whether CPack adds a top-level directory, usually of the form ProjectName-Version-OS, to the top of package tree. 0 to disable, 1 to enable
@@ -13,7 +13,9 @@ SET( CPACK_PACKAGE_DESCRIPTION_SUMMARY ${TOMAHAWK_DESCRIPTION_SUMMARY} ) # Des
SET( CPACK_PACKAGE_INSTALL_DIRECTORY ${TOMAHAWK_APPLICATION_NAME} ) # Installation directory on the target system -> C:\Program Files\fellody
SET( CPACK_PACKAGE_INSTALL_REGISTRY_KEY ${TOMAHAWK_APPLICATION_NAME} ) # Registry key used when installing this project CMake 2.5.0
SET( CPACK_PACKAGE_NAME ${TOMAHAWK_APPLICATION_NAME} ) # Package name, defaults to the project name
SET( CPACK_PACKAGE_TARGET_NAME ${TOMAHAWK_TARGET_NAME} ) # Used to build library and executable names
SET( CPACK_PACKAGE_VENDOR ${TOMAHAWK_ORGANIZATION_NAME} ) # Package vendor name
SET( TOMAHAWK_APPLICATION_PACKAGE_NAME ${TOMAHAWK_APPLICATION_PACKAGE_NAME} )
SET( CPACK_PACKAGE_VERSION_MAJOR ${TOMAHAWK_VERSION_MAJOR} )
SET( CPACK_PACKAGE_VERSION_MINOR ${TOMAHAWK_VERSION_MINOR} )
SET( CPACK_PACKAGE_VERSION_PATCH ${TOMAHAWK_VERSION_PATCH} )

View File

@@ -3,8 +3,6 @@
set( TOMAHAWK_QT5 @TOMAHAWK_QT5@ )
if(TOMAHAWK_QT5)
message(STATUS "Found Qt5! Be aware that Qt5-support is still experimental and not officially supported!")
# CMAKE 2.8.13+/3.0.0+ requires these for IMPORTed targets
find_package(Qt5Core REQUIRED)
find_package(Qt5Concurrent REQUIRED)

View File

@@ -46,7 +46,7 @@ CERT_SIGNER=$2
cd ..
if [ -f ~/sign_step.sh ];
then
~/sign_step.sh "$CERT_SIGNER" "${TARGET_NAME}.app" || true
~/sign_step.sh "$CERT_SIGNER" "${TARGET_NAME}.app"
fi
header "Creating DMG"

View File

@@ -9,19 +9,20 @@ fi
rm -rvf vlc/
VLC_TARBALL="vlc.tar.bz2"
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://download.tomahawk-player.org/test/vlc.tar.bz2
wget -c "http://download.tomahawk-player.org/test/$VLC_TARBALL"
echo "Extract binary..."
# 7z x phonon*.7z
# mv -v vlc-*/ vlc/
# unzip tomahawk-vlc-0.1.zip
tar xvjf vlc.tar.bz2
tar xvjf "$VLC_TARBALL"
# echo "Download phonon_vlc_no_video.dll..."
# wget -c http://people.videolan.org/~jb/phonon/phonon_vlc_no_video.dll
@@ -72,7 +73,9 @@ rm -rvf \
**/libi420* \
**/libi422* \
mux/ \
stream_filter/ \
stream_filter/*dash* \
stream_filter/*smooth* \
stream_filter/*record* \
**/libtheora_plugin* \
**/liblibbluray_plugin* \
**/libdtv_plugin* \

13
data/images/folder.svg Normal file
View File

@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg width="86px" height="86px" viewBox="0 0 86 86" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns">
<title>folder</title>
<description>Created with Sketch (http://www.bohemiancoding.com/sketch)</description>
<defs></defs>
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
<g id="folder" sketch:type="MSLayerGroup" transform="translate(0.000000, 13.000000)" fill="#000000">
<g id="Page-1" sketch:type="MSShapeGroup">
<path d="M84.1450038,10.4833376 C82.8248024,9.1092149 80.9427764,8.3195294 78.9789985,8.3195294 L72.233622,8.3195294 L34.4730823,8.3195294 L34.4730823,7.0184614 C34.4730823,3.1483061 31.3247762,0 27.4563602,0 L10.1441555,0 C6.27226058,0 3.12569415,3.1500456 3.12569415,7.0184614 L3.12569415,9.4918821 C1.2088801,10.761641 0,12.9237099 0,15.3362515 L0,54.4987445 C0,54.5057021 0,54.5178779 0.0017394,54.5265748 L0,54.5439688 C0,58.3897726 3.14656684,61.5154666 7.0184614,61.5154666 L72.2336242,61.5154666 C72.4736608,61.5154666 72.7102186,61.5050302 72.9432977,61.4789393 C72.9850432,61.4771999 73.028528,61.4650241 73.0702735,61.4615453 C73.2616071,61.4389332 73.4529406,61.4145816 73.6390559,61.3745755 C73.6686257,61.3693573 73.6947166,61.3606604 73.7242863,61.3519634 C73.9225774,61.3119573 74.1208685,61.2597754 74.3139414,61.200636 C74.3243778,61.1954178 74.3348142,61.191939 74.3452506,61.1884602 C76.756053,60.4300837 78.5998126,58.4123847 79.1059765,55.9198305 C79.1077159,55.912873 79.1077159,55.9024366 79.1077159,55.895479 C79.1512008,55.6937091 79.18251,55.4919392 79.2051221,55.2797329 C79.2103403,55.2240723 79.2155585,55.1684116 79.2190373,55.1092721 C79.2277343,55.0136054 79.2433888,54.9266356 79.2451282,54.8309688 L85.9539776,15.7937099 L85.9939837,15.4127822 C86.0705187,13.5587047 85.4147685,11.8086925 84.1450038,10.4833376 C84.1450038,10.4833376 85.4147685,11.8086925 84.1450038,10.4833376 L84.1450038,10.4833376" id="Shape"></path>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

@@ -42,6 +42,8 @@ Tomahawk.apiVersion = "0.2.2";
//Statuses considered a success for HTTP request
var httpSuccessStatuses = [200, 201];
Tomahawk.error = console.error;
// install RSVP error handler for uncaught(!) errors
RSVP.on('error', function (reason) {
var resolverName = "";
@@ -49,9 +51,9 @@ RSVP.on('error', function (reason) {
resolverName = Tomahawk.resolver.instance.settings.name + " - ";
}
if (reason) {
console.error(resolverName + 'Uncaught error:', reason);
Tomahawk.error(resolverName + 'Uncaught error:', reason);
} else {
console.error(resolverName + 'Uncaught error: error thrown from RSVP but it was empty');
Tomahawk.error(resolverName + 'Uncaught error: error thrown from RSVP but it was empty');
}
});
@@ -273,30 +275,42 @@ Tomahawk.Resolver = {
getStreamUrl: function (params) {
return params;
},
resolve: function() {
},
_adapter_resolve: function (params) {
return RSVP.Promise.resolve(this.resolve(params)).then(function (results) {
return {
'tracks': results
};
if(Array.isArray(results)) {
return {
'tracks': results
};
}
return results;
});
},
_adapter_search: function (params) {
return RSVP.Promise.resolve(this.search(params)).then(function (results) {
return {
'tracks': results
};
if(Array.isArray(results)) {
return {
'tracks': results
};
}
return results;
});
},
_adapter_testConfig: function (config) {
return RSVP.Promise.resolve(this.testConfig(config)).then(function () {
return {result: Tomahawk.ConfigTestResultType.Success};
return RSVP.Promise.resolve(this.testConfig(config)).then(function (results) {
results = results || Tomahawk.ConfigTestResultType.Success;
return results;
}, function (error) {
return error;
});
}
};
// help functions
Tomahawk.valueForSubNode = function (node, tag) {
@@ -367,48 +381,6 @@ Tomahawk.retrievedMetadata = function (metadataId, metadata, error) {
delete Tomahawk.retrieveMetadataCallbacks[metadataId];
};
/**
* Internal counter used to identify asyncRequest callback from native code.
*/
Tomahawk.asyncRequestIdCounter = 0;
/**
* Internal map used to map asyncRequestIds to the respective javascript
* callback functions.
*/
Tomahawk.asyncRequestCallbacks = {};
/**
* Pass the natively retrieved reply back to the javascript callback
* and augment the fake XMLHttpRequest object.
*
* Internal use only!
*/
Tomahawk.nativeAsyncRequestDone = function (reqId, xhr) {
// Check that we have a matching callback stored.
if (!Tomahawk.asyncRequestCallbacks.hasOwnProperty(reqId)) {
return;
}
// Call the real callback
if (xhr.readyState == 4 && httpSuccessStatuses.indexOf(xhr.status) != -1) {
// Call the real callback
if (Tomahawk.asyncRequestCallbacks[reqId].callback) {
Tomahawk.asyncRequestCallbacks[reqId].callback(xhr);
}
} else if (xhr.readyState === 4) {
Tomahawk.log("Failed to do nativeAsyncRequest");
Tomahawk.log("Status Code was: " + xhr.status);
if (Tomahawk.asyncRequestCallbacks[reqId].errorHandler) {
Tomahawk.asyncRequestCallbacks[reqId].errorHandler(xhr);
}
}
// Callbacks are only used once.
delete Tomahawk.asyncRequestCallbacks[reqId];
};
/**
* This method is externalized from Tomahawk.asyncRequest, so that other clients
* (like tomahawk-android) can inject their own logic that determines whether or not to do a request
@@ -423,9 +395,9 @@ var shouldDoNativeRequest = function (options) {
|| extraHeaders.hasOwnProperty("User-Agent")));
};
/**
* Possible options:
* - url: The URL to call
* - method: The HTTP request method (default: GET)
* - username: The username for HTTP Basic Auth
* - password: The password for HTTP Basic Auth
@@ -443,7 +415,12 @@ var doRequest = function(options) {
return this.responseHeaders;
};
xhr.getResponseHeader = function (header) {
return this.responseHeaders[header];
for(key in xhr.responseHeaders) {
if(key.toLowerCase() === header.toLowerCase()) {
return xhr.responseHeaders[key];
}
}
return null;
};
return xhr;
@@ -735,7 +712,6 @@ Tomahawk.base64Encode = function (b) {
return window.btoa(b);
};
Tomahawk.PluginManager = {
wrapperPrefix: '_adapter_',
objects: {},
@@ -766,8 +742,6 @@ Tomahawk.PluginManager = {
methodName = this.wrapperPrefix + methodName;
}
var pluginManager = this;
if (!this.objects[objectId]) {
Tomahawk.log("Object not found! objectId: " + objectId + " methodName: " + methodName);
} else {
@@ -788,20 +762,21 @@ Tomahawk.PluginManager = {
invoke: function (requestId, objectId, methodName, params) {
RSVP.Promise.resolve(this.invokeSync(requestId, objectId, methodName, params))
.then(function (result) {
Tomahawk.reportScriptJobResults({
var params = {
requestId: requestId,
data: result
});
};
Tomahawk.reportScriptJobResults(encodeParamsToNativeFunctions(params));
}, function (error) {
Tomahawk.reportScriptJobResults({
var params = {
requestId: requestId,
error: error
});
};
Tomahawk.reportScriptJobResults(encodeParamsToNativeFunctions(params));
});
}
};
var encodeParamsToNativeFunctions = function(param) {
return param;
};
@@ -815,7 +790,7 @@ Tomahawk.NativeScriptJobManager = {
var requestId = this.idCounter++;
var deferred = RSVP.defer();
this.deferreds[requestId] = deferred;
Tomahawk.invokeNativeScriptJob(requestId, methodName, encodeParamsToNativeFunctions(params));;
Tomahawk.invokeNativeScriptJob(requestId, methodName, encodeParamsToNativeFunctions(params));
return deferred.promise;
},
reportNativeScriptJobResult: function(requestId, result) {
@@ -1484,9 +1459,7 @@ Tomahawk.Collection = {
return new RSVP.Promise(function (resolve, reject) {
that.cachedDbs[id].changeVersion(that.cachedDbs[id].version, "", null,
function (err) {
if (console.error) {
console.error("Error!: %o", err);
}
Tomahawk.error("Error trying to change db version!", err);
reject();
}, function () {
delete that.cachedDbs[id];

BIN
data/sounds/silence.ogg Normal file

Binary file not shown.

View File

@@ -3,6 +3,7 @@
<file>data/images/collection_background.png</file>
<file>data/images/playlist_background.png</file>
<file>data/images/filter.svg</file>
<file>data/images/folder.svg</file>
<file>data/images/loved.svg</file>
<file>data/images/love.svg</file>
<file>data/images/not-loved.svg</file>
@@ -169,5 +170,6 @@
<file>data/images/downloadbutton.svg</file>
<file>data/images/nav-back.svg</file>
<file>data/images/nav-forward.svg</file>
<file>data/sounds/silence.ogg</file>
</qresource>
</RCC>

View File

@@ -24,13 +24,6 @@ include_directories(
add_definitions(-D_WEBSOCKETPP_CPP11_STL_)
if(APPLE)
# http://stackoverflow.com/questions/7226753/osx-lion-xcode-4-1-how-do-i-setup-a-c0x-project/7236451#7236451
add_definitions(-std=c++11 -stdlib=libc++ -U__STRICT_ANSI__)
set(PLATFORM_SPECIFIC_LINK_LIBRARIES "/usr/lib/libc++.dylib")
else()
add_definitions(-std=c++0x)
endif()
tomahawk_add_plugin(hatchet
TYPE account

View File

@@ -69,6 +69,13 @@ Tomahawk::InfoSystem::XmppInfoPlugin::init()
}
const QString
Tomahawk::InfoSystem::XmppInfoPlugin::friendlyName() const
{
return "xmpp";
}
void
Tomahawk::InfoSystem::XmppInfoPlugin::pushInfo( Tomahawk::InfoSystem::InfoPushData pushData )
{

View File

@@ -38,6 +38,8 @@ namespace Tomahawk {
XmppInfoPlugin(XmppSipPlugin* parent);
virtual ~XmppInfoPlugin();
const QString friendlyName() const override;
signals:
void publishTune( QUrl url, Tomahawk::InfoSystem::InfoStringHash trackInfo );

View File

@@ -194,9 +194,9 @@ InfoSystem::InfoPluginPtr
XmppSipPlugin::infoPlugin()
{
if ( m_infoPlugin.isNull() )
m_infoPlugin = QPointer< Tomahawk::InfoSystem::XmppInfoPlugin >( new Tomahawk::InfoSystem::XmppInfoPlugin( this ) );
m_infoPlugin = QSharedPointer< Tomahawk::InfoSystem::XmppInfoPlugin >( new Tomahawk::InfoSystem::XmppInfoPlugin( this ) );
return InfoSystem::InfoPluginPtr( m_infoPlugin.data() );
return m_infoPlugin;
}
@@ -285,7 +285,7 @@ XmppSipPlugin::onConnect()
// load XmppInfoPlugin
if ( infoPlugin() && Tomahawk::InfoSystem::InfoSystem::instance()->workerThread() )
{
infoPlugin().data()->moveToThread( Tomahawk::InfoSystem::InfoSystem::instance()->workerThread().data() );
infoPlugin()->moveToThread( Tomahawk::InfoSystem::InfoSystem::instance()->workerThread().data() );
Tomahawk::InfoSystem::InfoSystem::instance()->addInfoPlugin( infoPlugin() );
}
@@ -359,7 +359,6 @@ XmppSipPlugin::onDisconnect( Jreen::Client::DisconnectReason reason )
if ( !m_infoPlugin.isNull() )
{
Tomahawk::InfoSystem::InfoSystem::instance()->removeInfoPlugin( infoPlugin() );
delete m_infoPlugin;
}
}

View File

@@ -128,7 +128,7 @@ private:
int m_currentPort;
QString m_currentResource;
QPointer< Tomahawk::InfoSystem::XmppInfoPlugin > m_infoPlugin;
QSharedPointer< Tomahawk::InfoSystem::XmppInfoPlugin > m_infoPlugin;
Tomahawk::Accounts::Account::ConnectionState m_state;
// sort out

View File

@@ -24,6 +24,7 @@ tomahawk_add_library(${TOMAHAWK_PLAYDARAPI_LIBRARY_TARGET}
qtcertificateaddon
${GNUTLS_LIBRARIES}
EXPORT TomahawkLibraryDepends
EXPORT_MACRO TOMAHAWK_WIDGETS_EXPORT_PRO
VERSION ${TOMAHAWK_VERSION_SHORT}
)

View File

@@ -23,7 +23,7 @@
#include <QtCore/qglobal.h>
#ifndef TOMAHAWK_PLAYDARAPI_EXPORT
# if defined (tomahawk_playdarapi_EXPORTS)
# if defined (TOMAHAWK_WIDGETS_EXPORT_PRO)
# define TOMAHAWK_PLAYDARAPI_EXPORT Q_DECL_EXPORT
# else
# define TOMAHAWK_PLAYDARAPI_EXPORT Q_DECL_IMPORT

View File

@@ -34,7 +34,7 @@ public:
* Creates a Playdar HTTP interface
* @param ha Address to listen on
* @param port Port to listen on with HTTP
* @param sport Pot to listen on with HTTPS
* @param sport Port to listen on with HTTPS
* @param parent
*/
explicit PlaydarApi( QHostAddress ha, qint16 port, qint16 sport, QObject *parent = 0 );

View File

@@ -13,5 +13,6 @@ tomahawk_add_library(${TOMAHAWK_WIDGETS_LIBRARY_TARGET}
SOURCES ${${TOMAHAWK_WIDGETS_LIBRARY_TARGET}_SOURCES}
UI ${${TOMAHAWK_WIDGETS_LIBRARY_TARGET}_UI}
EXPORT TomahawkLibraryDepends
EXPORT_MACRO TOMAHAWK_WIDGETS_EXPORT_PRO
VERSION ${TOMAHAWK_VERSION_SHORT}
)

View File

@@ -23,7 +23,7 @@
#include <QtCore/qglobal.h>
#ifndef TOMAHAWK_WIDGETS_EXPORT
# if defined (tomahawk_widgets_EXPORTS)
# if defined (TOMAHAWK_WIDGETS_EXPORT_PRO)
# define TOMAHAWK_WIDGETS_EXPORT Q_DECL_EXPORT
# else
# define TOMAHAWK_WIDGETS_EXPORT Q_DECL_IMPORT

View File

@@ -33,6 +33,7 @@
#include <QReadWriteLock>
#include <QPixmapCache>
#include <QCoreApplication>
using namespace Tomahawk;
@@ -75,6 +76,7 @@ Album::get( const Tomahawk::artist_ptr& artist, const QString& name, bool autoCr
}
album_ptr album = album_ptr( new Album( name, artist ), &Album::deleteLater );
album->moveToThread( QCoreApplication::instance()->thread() );
album->setWeakRef( album.toWeakRef() );
album->loadId( autoCreate );
s_albumsByName.insert( key, album );

View File

@@ -36,6 +36,7 @@
#include <QReadWriteLock>
#include <QPixmapCache>
#include <QCoreApplication>
using namespace Tomahawk;
@@ -109,6 +110,7 @@ Artist::get( unsigned int id, const QString& name )
}
artist_ptr a = artist_ptr( new Artist( id, name ), &Artist::deleteLater );
a->moveToThread( QCoreApplication::instance()->thread() );
a->setWeakRef( a.toWeakRef() );
s_artistsByName.insert( key, a );

View File

@@ -67,7 +67,7 @@ AtticaManager::AtticaManager( QObject* parent )
connect( &m_manager, SIGNAL( providerAdded( Attica::Provider ) ), this, SLOT( providerAdded( Attica::Provider ) ) );
// resolvers
// m_manager.addProviderFile( QUrl( "http://bakery.tomahawk-player.org/resolvers/providers.xml" ) );
// m_manager.addProviderFile( QUrl( "http://v09.bakery.tomahawk-player.org/resolvers/providers.xml" ) );
const QString url = QString( "%1/resolvers/providers.xml?version=%2" ).arg( hostname() ).arg( TomahawkUtils::appFriendlyVersion() );
QNetworkReply* reply = Tomahawk::Utils::nam()->get( QNetworkRequest( QUrl( url ) ) );
@@ -116,7 +116,7 @@ AtticaManager::fetchMissingIcons()
QString
AtticaManager::hostname() const
{
return "http://bakery.tomahawk-player.org";
return "http://v09.bakery.tomahawk-player.org";
}

View File

@@ -37,6 +37,7 @@ set( libGuiSources
jobview/ErrorStatusMessage.cpp
jobview/IndexingJobItem.cpp
jobview/InboxJobItem.cpp
jobview/ScriptErrorStatusMessage.cpp
playlist/InboxModel.cpp
playlist/InboxView.cpp
@@ -145,6 +146,7 @@ set( libGuiSources
widgets/ClickableLabel.cpp
widgets/ComboBox.cpp
widgets/DropDownButton.cpp
widgets/DownloadButton.cpp
widgets/ElidedLabel.cpp
widgets/FilterHeader.cpp
widgets/CaptionLabel.cpp
@@ -417,7 +419,7 @@ include_directories(
${Boost_INCLUDE_DIR}
${LIBPORTFWD_INCLUDE_DIR}
${QUAZIP_INCLUDE_DIR}
${QUAZIP_INCLUDE_DIRS}
${QTKEYCHAIN_INCLUDE_DIRS}
)

View File

@@ -37,6 +37,9 @@
#include "utils/ImageRegistry.h"
#include "utils/Logger.h"
#include <QDesktopServices>
#include <QFileInfo>
using namespace Tomahawk;
@@ -51,7 +54,7 @@ ContextMenu::ContextMenu( QWidget* parent )
m_sigmap = new QSignalMapper( this );
connect( m_sigmap, SIGNAL( mapped( int ) ), SLOT( onTriggered( int ) ) );
m_supportedActions = ActionPlay | ActionQueue | ActionPlaylist | ActionCopyLink | ActionLove | ActionStopAfter | ActionPage | ActionEditMetadata | ActionSend;
clear();
}
@@ -69,7 +72,7 @@ ContextMenu::clear()
m_albums.clear();
m_artists.clear();
m_supportedActions = ActionPlay | ActionQueue | ActionPlaylist | ActionCopyLink | ActionLove | ActionStopAfter | ActionPage | ActionEditMetadata | ActionSend;
m_supportedActions = ActionPlay | ActionQueue | ActionPlaylist | ActionCopyLink | ActionLove | ActionStopAfter | ActionPage | ActionEditMetadata | ActionSend | ActionOpenFileManager;
}
@@ -216,10 +219,23 @@ ContextMenu::setQueries( const QList<Tomahawk::query_ptr>& queries )
addSeparator();
if ( m_supportedActions & ActionCopyLink && itemCount() == 1 )
{
m_sigmap->setMapping( addAction( tr( "&Copy Track Link" ) ), ActionCopyLink );
}
if ( m_supportedActions & ActionOpenFileManager &&
queries.length() == 1 &&
queries.first()->numResults() &&
queries.first()->results().first()->isLocal() )
{
m_sigmap->setMapping( addAction( ImageRegistry::instance()->icon( RESPATH "images/folder.svg" ),
tr( "Open Folder in File Manager..." ) ), ActionOpenFileManager );
}
if ( m_supportedActions & ActionEditMetadata && itemCount() == 1 )
{
m_sigmap->setMapping( addAction( tr( "Properties..." ) ), ActionEditMetadata );
}
addSeparator();
@@ -239,6 +255,8 @@ ContextMenu::setQueries( const QList<Tomahawk::query_ptr>& queries )
m_sigmap->setMapping( addAction( tr( "Mark as &Listened" ) ), ActionMarkListened );
}
addSeparator();
if ( m_supportedActions & ActionDelete )
m_sigmap->setMapping( addAction( queries.count() > 1 ? tr( "&Remove Items" ) : tr( "&Remove Item" ) ), ActionDelete );
@@ -394,6 +412,15 @@ ContextMenu::onTriggered( int action )
}
break;
case ActionOpenFileManager:
{
result_ptr result = m_queries.first()->results().first();
QString path = QFileInfo( result->url() ).path();
tLog() << Q_FUNC_INFO << "open directory" << path;
QDesktopServices::openUrl( path );
}
break;
default:
emit triggered( action );
}

View File

@@ -37,21 +37,22 @@ Q_OBJECT
public:
enum MenuActions
{
ActionPlay = 1,
ActionQueue = 2,
ActionDelete = 4,
ActionCopyLink = 8,
ActionLove = 16,
ActionStopAfter = 32,
ActionPage = 64,
ActionTrackPage = 128,
ActionArtistPage = 256,
ActionAlbumPage = 512,
ActionEditMetadata = 1024,
ActionPlaylist = 2048,
ActionSend = 4096,
ActionMarkListened = 8192,
ActionDownload = 16384
ActionPlay = 1,
ActionQueue = 2,
ActionDelete = 4,
ActionCopyLink = 8,
ActionLove = 16,
ActionStopAfter = 32,
ActionPage = 64,
ActionTrackPage = 128,
ActionArtistPage = 256,
ActionAlbumPage = 512,
ActionEditMetadata = 1024,
ActionPlaylist = 2048,
ActionSend = 4096,
ActionMarkListened = 8192,
ActionDownload = 16384,
ActionOpenFileManager = 32768
};
explicit ContextMenu( QWidget* parent = 0 );

View File

@@ -118,7 +118,7 @@ DownloadJob::localFile() const
QString
DownloadJob::localPath() const
DownloadJob::localPath( const Tomahawk::album_ptr& album )
{
QDir dir = TomahawkSettings::instance()->downloadsPath();
@@ -127,7 +127,7 @@ DownloadJob::localPath() const
dir.mkpath( "." );
}
QString path = QString( "%1/%2" ).arg( safeEncode( m_track->artist(), true ) ).arg( safeEncode( m_track->album(), true ) );
QString path = QString( "%1/%2" ).arg( safeEncode( album->artist()->name(), true ) ).arg( safeEncode( album->name(), true ) );
dir.mkpath( path );
return QString( dir.path() + "/" + path ).replace( "//", "/" );
@@ -138,7 +138,7 @@ QUrl
DownloadJob::prepareFilename()
{
QString filename = QString( "%1. %2.%3" ).arg( m_track->albumpos() ).arg( safeEncode( m_track->track() ) ).arg( m_format.extension );
QString path = localPath();
QString path = localPath( m_track->albumPtr() );
QString localFile = QString( path + "/" + filename );
if ( !m_tryResuming )
@@ -444,7 +444,7 @@ DownloadJob::checkForResumedFile()
QString
DownloadJob::safeEncode( const QString& filename, bool removeTrailingDots ) const
DownloadJob::safeEncode( const QString& filename, bool removeTrailingDots )
{
//FIXME: make it a regexp
QString res = QString( filename ).toLatin1().replace( "/", "_" ).replace( "\\", "_" )

View File

@@ -58,7 +58,7 @@ public:
long receivedSize() const { return m_rcvdSize; }
long fileSize() const { return m_fileSize; }
QString localPath() const;
static QString localPath( const Tomahawk::album_ptr& album );
QString localFile() const;
DownloadFormat format() const;
@@ -90,7 +90,7 @@ private slots:
private:
void storeState();
QString safeEncode( const QString& filename, bool removeTrailingDots = false ) const;
static QString safeEncode( const QString& filename, bool removeTrailingDots = false );
bool checkForResumedFile();
QUrl prepareFilename();

View File

@@ -24,6 +24,8 @@
#include "TomahawkSettings.h"
#include "infosystem/InfoSystem.h"
#include "utils/Logger.h"
#include "Result.h"
#include "Query.h"
DownloadManager* DownloadManager::s_instance = 0;
@@ -85,6 +87,36 @@ DownloadManager::localFileForDownload( const QString& url ) const
}
QUrl
DownloadManager::localUrlForDownload( const Tomahawk::query_ptr& query ) const
{
Tomahawk::result_ptr result = query->numResults( true ) ? query->results().first() : Tomahawk::result_ptr();
if ( result )
{
return localUrlForDownload( result );
}
return QUrl();
}
QUrl
DownloadManager::localUrlForDownload( const Tomahawk::result_ptr& result ) const
{
if ( result && !result->downloadFormats().isEmpty() &&
!localFileForDownload( result->downloadFormats().first().url.toString() ).isEmpty() )
{
return QUrl::fromLocalFile( QFileInfo( DownloadManager::instance()->localFileForDownload( result->downloadFormats().first().url.toString() ) ).absolutePath() );
}
else if ( result && result->downloadJob() && result->downloadJob()->state() == DownloadJob::Finished )
{
return QUrl::fromLocalFile( QFileInfo( result->downloadJob()->localFile() ).absolutePath() );
}
return QUrl();
}
void
DownloadManager::storeJobs( const QList<downloadjob_ptr>& jobs )
{

View File

@@ -45,6 +45,8 @@ public:
void storeJobs( const QList<downloadjob_ptr>& jobs );
QString localFileForDownload( const QString& url ) const;
QUrl localUrlForDownload( const Tomahawk::result_ptr& result ) const;
QUrl localUrlForDownload( const Tomahawk::query_ptr& query ) const;
public slots:
bool addJob( const downloadjob_ptr& job );

View File

@@ -263,9 +263,14 @@ bool
DropJob::validateLocalFiles(const QString &paths, const QString &suffix)
{
QStringList filePaths = paths.split( QRegExp( "\\s+" ), QString::SkipEmptyParts );
for ( QStringList::iterator it = filePaths.begin(); it != filePaths.end(); ++it )
QStringList::iterator it = filePaths.begin();
while ( it != filePaths.end() )
{
if ( !validateLocalFile( *it, suffix ) )
filePaths.erase( it );
it = filePaths.erase( it );
else
++it;
}
return !filePaths.isEmpty();
}
@@ -975,7 +980,7 @@ DropJob::removeRemoteSources()
foreach ( const Tomahawk::result_ptr& result, item->results() )
{
if ( !result->resolvedByCollection().isNull() && !result->resolvedByCollection()->isLocal() )
if ( !result->isLocal() )
{
list.append( item );
break;

View File

@@ -1,14 +1,14 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright (C) 2011 Leo Franchi <lfranchi@kde.org>
* Copyright (C) 2011, Jeff Mitchell <jeff@tomahawk-player.org>
* Copyright (C) 2011-2014, Christian Muehlhaeuser <muesli@tomahawk-player.org>
* Copyright (C) 2013, Uwe L. Korn <uwelk@xhochy.com>
* Copyright (C) 2013, Teo Mrnjavac <teo@kde.org>
* Copyright 2011, Leo Franchi <lfranchi@kde.org>
* Copyright 2011-2016, Christian Muehlhaeuser <muesli@tomahawk-player.org>
* Copyright 2011, Jeff Mitchell <jeff@tomahawk-player.org>
* Copyright 2013, Uwe L. Korn <uwelk@xhochy.com>
* Copyright 2013, Teo Mrnjavac <teo@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 2 of the License, or
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Tomahawk is distributed in the hope that it will be useful,
@@ -20,7 +20,6 @@
* along with Tomahawk. If not, see <http://www.gnu.org/licenses/>.
*/
#include "GlobalActionManager.h"
#include "accounts/AccountManager.h"

View File

@@ -1,21 +1,21 @@
/*
Copyright (C) 2011 Leo Franchi <lfranchi@kde.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2011, Leo Franchi <lfranchi@kde.org>
* Copyright 2011-2016, Christian Muehlhaeuser <muesli@tomahawk-player.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 GLOBALACTIONMANAGER_H
#define GLOBALACTIONMANAGER_H

View File

@@ -36,7 +36,7 @@
#define MAX_CONCURRENT_QUERIES 16
#define CLEANUP_TIMEOUT 5 * 60 * 1000
#define MINSCORE 0.5
#define DEFAULT_RESOLVER_TIMEOUT 5000 //5 seconds
#define DEFAULT_RESOLVER_TIMEOUT 5000 // 5 seconds
using namespace Tomahawk;
@@ -542,12 +542,12 @@ Pipeline::shuntNext()
q->setCurrentResolver( 0 );
}
//Zero-patient, a stub so that query is not resolved until we go through
//all resolvers
//As query considered as 'finished trying to resolve' when there are no
//more qid entries in qidsState we'll put one as sort of 'keep this until
//we kick off all our resolvers' entry
//once we kick off all resolvers we'll remove this entry
// Zero-patient, a stub so that query is not resolved until we go through
// all resolvers
// As query considered as 'finished trying to resolve' when there are no
// more qid entries in qidsState we'll put one as sort of 'keep this until
// we kick off all our resolvers' entry
// once we kick off all resolvers we'll remove this entry
incQIDState( q, nullptr );
checkQIDState( q );
}
@@ -595,8 +595,8 @@ Pipeline::shunt( const query_ptr& q )
// we get here if we disable a resolver while a query is resolving
// OR we are just out of resolvers while query is still resolving
//since we seem to at least tried to kick off all of the resolvers,
//remove the '.keep' entry
// since we seem to at least tried to kick off all of the resolvers,
// remove the '.keep' entry
decQIDState( q, nullptr );
return;
}
@@ -636,7 +636,7 @@ Pipeline::checkQIDState( const Tomahawk::query_ptr& query )
Q_D( Pipeline );
QMutexLocker lock( &d->mut );
tDebug() << Q_FUNC_INFO << " " << query->id() << " " << d->qidsState.count( query->id() );
tDebug() << Q_FUNC_INFO << query->id() << d->qidsState.count( query->id() );
if ( d->qidsState.contains( query->id() ) )
{
@@ -673,7 +673,7 @@ Pipeline::decQIDState( const Tomahawk::query_ptr& query, Tomahawk::Resolver* r )
{
{
QMutexLocker lock( &d->mut );
d->qidsState.remove( query->id(), r );//Removes all matching pairs
d->qidsState.remove( query->id(), r ); // Removes all matching pairs
}
checkQIDState( query );

View File

@@ -33,6 +33,7 @@
#include <QtAlgorithms>
#include <QDebug>
#include <QCoreApplication>
using namespace Tomahawk;
@@ -47,6 +48,7 @@ Query::get( const QString& artist, const QString& track, const QString& album, c
autoResolve = false;
query_ptr q = query_ptr( new Query( Track::get( artist, track, album ), qid, autoResolve ), &QObject::deleteLater );
q->moveToThread( QCoreApplication::instance()->thread() );
q->setWeakRef( q.toWeakRef() );
if ( autoResolve )
@@ -399,16 +401,21 @@ Query::resultSorter( const result_ptr& left, const result_ptr& right )
if ( ls == rs )
{
if ( right->resolvedByCollection() && right->resolvedByCollection()->isLocal() )
if ( right->isLocal() )
{
return false;
}
if ( !right->isPreview() )
if ( left->isPreview() != right->isPreview() )
{
return false;
return !left->isPreview();
}
return true;
if ( left->resolvedBy() != nullptr && right->resolvedBy() != nullptr )
{
return left->resolvedBy()->weight() > right->resolvedBy()->weight();
}
return left->id() > right->id();
}
if ( left->isPreview() != right->isPreview() )
@@ -636,6 +643,11 @@ Query::howSimilar( const Tomahawk::result_ptr& r )
qTrackname = queryTrack()->trackSortname();
}
//Cleanup symbols for minor naming differences
qArtistname.remove(QRegExp(QString::fromUtf8("[-`~!@#$%^&*()_—+=|:;<>«»,.?/{}\'\"\\[\\]\\\\]")));
qTrackname.remove(QRegExp(QString::fromUtf8("[-`~!@#$%^&*()_—+=|:;<>«»,.?/{}\'\"\\[\\]\\\\]")));
qAlbumname.remove(QRegExp(QString::fromUtf8("[-`~!@#$%^&*()_—+=|:;<>«»,.?/{}\'\"\\[\\]\\\\]")));
// normal edit distance
const int artdist = TomahawkUtils::levenshtein( qArtistname, rArtistname );
const int trkdist = TomahawkUtils::levenshtein( qTrackname, rTrackname );

View File

@@ -35,6 +35,8 @@
#include "Track.h"
#include "Typedefs.h"
#include <QCoreApplication>
using namespace Tomahawk;
static QHash< QString, result_wptr > s_results;
@@ -69,6 +71,7 @@ Result::get( const QString& url, const track_ptr& track )
}
result_ptr r = result_ptr( new Result( url, track ), &Result::deleteLater );
r->moveToThread( QCoreApplication::instance()->thread() );
r->setWeakRef( r.toWeakRef() );
s_results.insert( url, r );
@@ -132,11 +135,19 @@ Result::deleteLater()
void
Result::onResolverRemoved( Tomahawk::Resolver* resolver )
{
m_mutex.lock();
if ( m_resolver.data() == resolver )
{
m_resolver = 0;
m_mutex.unlock();
emit statusChanged();
}
else
{
m_mutex.unlock();
}
}
@@ -150,6 +161,8 @@ Result::resolvedByCollection() const
QString
Result::url() const
{
QMutexLocker lock( &m_mutex );
return m_url;
}
@@ -157,6 +170,8 @@ Result::url() const
bool
Result::checked() const
{
QMutexLocker lock( &m_mutex );
return m_checked;
}
@@ -164,6 +179,8 @@ Result::checked() const
bool
Result::isPreview() const
{
QMutexLocker lock( &m_mutex );
return m_isPreview;
}
@@ -171,6 +188,8 @@ Result::isPreview() const
QString
Result::mimetype() const
{
QMutexLocker lock( &m_mutex );
return m_mimetype;
}
@@ -178,6 +197,8 @@ Result::mimetype() const
RID
Result::id() const
{
QMutexLocker lock( &m_mutex );
if ( m_rid.isEmpty() )
m_rid = uuid();
@@ -194,6 +215,8 @@ Result::isOnline() const
}
else
{
QMutexLocker lock( &m_mutex );
return !m_resolver.isNull();
}
}
@@ -211,27 +234,36 @@ Result::playable() const
}
bool
Result::isLocal() const
{
return resolvedByCollection().isNull() ? false : resolvedByCollection()->isLocal();
}
QVariant
Result::toVariant() const
{
track_ptr t = track();
QVariantMap m;
m.insert( "artist", m_track->artist() );
m.insert( "album", m_track->album() );
m.insert( "track", m_track->track() );
m.insert( "artist", t->artist() );
m.insert( "album", t->album() );
m.insert( "track", t->track() );
m.insert( "source", friendlySource() );
m.insert( "mimetype", mimetype() );
m.insert( "size", size() );
m.insert( "bitrate", bitrate() );
m.insert( "duration", m_track->duration() );
m.insert( "duration", t->duration() );
// m.insert( "score", score() );
m.insert( "sid", id() );
m.insert( "discnumber", m_track->discnumber() );
m.insert( "albumpos", m_track->albumpos() );
m.insert( "discnumber", t->discnumber() );
m.insert( "albumpos", t->albumpos() );
m.insert( "preview", isPreview() );
m.insert( "purchaseUrl", purchaseUrl() );
if ( !m_track->composer().isEmpty() )
m.insert( "composer", m_track->composer() );
if ( !t->composer().isEmpty() )
m.insert( "composer", t->composer() );
return m;
}
@@ -240,20 +272,25 @@ Result::toVariant() const
QString
Result::toString() const
{
if ( m_track )
m_mutex.lock();
track_ptr track = m_track;
QString url = m_url;
m_mutex.unlock();
if ( track )
{
return QString( "Result(%1) %2 - %3%4 (%5)" )
.arg( id() )
.arg( m_track->artist() )
.arg( m_track->track() )
.arg( m_track->album().isEmpty() ? QString() : QString( " on %1" ).arg( m_track->album() ) )
.arg( m_url );
.arg( track->artist() )
.arg( track->track() )
.arg( track->album().isEmpty() ? QString() : QString( " on %1" ).arg( track->album() ) )
.arg( url );
}
else
{
return QString( "Result(%1) (%2)" )
.arg( id() )
.arg( m_url );
.arg( url );
}
}
@@ -261,6 +298,8 @@ Result::toString() const
Tomahawk::query_ptr
Result::toQuery()
{
QMutexLocker l( &m_mutex );
if ( m_query.isNull() )
{
query_ptr query = Tomahawk::Query::get( m_track );
@@ -270,12 +309,15 @@ Result::toQuery()
m_query = query->weakRef();
QList<Tomahawk::result_ptr> rl;
rl << weakRef().toStrongRef();
rl << m_ownRef.toStrongRef();
m_mutex.unlock();
query->addResults( rl );
m_mutex.lock();
query->setResolveFinished( true );
return query;
}
return m_query.toStrongRef();
}
@@ -295,9 +337,10 @@ Result::onOffline()
void
Result::setResolvedByCollection( const Tomahawk::collection_ptr& collection , bool emitOnlineEvents )
Result::setResolvedByCollection( const Tomahawk::collection_ptr& collection, bool emitOnlineEvents )
{
m_collection = collection;
if ( emitOnlineEvents )
{
Q_ASSERT( !collection.isNull() );
@@ -311,6 +354,8 @@ Result::setResolvedByCollection( const Tomahawk::collection_ptr& collection , bo
void
Result::setFriendlySource( const QString& s )
{
QMutexLocker lock( &m_mutex );
m_friendlySource = s;
}
@@ -318,6 +363,8 @@ Result::setFriendlySource( const QString& s )
void
Result::setPreview( bool isPreview )
{
QMutexLocker lock( &m_mutex );
m_isPreview = isPreview;
}
@@ -325,6 +372,8 @@ Result::setPreview( bool isPreview )
void
Result::setPurchaseUrl( const QString& u )
{
QMutexLocker lock( &m_mutex );
m_purchaseUrl = u;
}
@@ -332,6 +381,8 @@ Result::setPurchaseUrl( const QString& u )
void
Result::setLinkUrl( const QString& u )
{
QMutexLocker lock( &m_mutex );
m_linkUrl = u;
}
@@ -339,6 +390,8 @@ Result::setLinkUrl( const QString& u )
void
Result::setChecked( bool checked )
{
QMutexLocker lock( &m_mutex );
m_checked = checked;
}
@@ -346,6 +399,8 @@ Result::setChecked( bool checked )
void
Result::setMimetype( const QString& mimetype )
{
QMutexLocker lock( &m_mutex );
m_mimetype = mimetype;
}
@@ -353,6 +408,8 @@ Result::setMimetype( const QString& mimetype )
void
Result::setBitrate( unsigned int bitrate )
{
QMutexLocker lock( &m_mutex );
m_bitrate = bitrate;
}
@@ -360,6 +417,8 @@ Result::setBitrate( unsigned int bitrate )
void
Result::setSize( unsigned int size )
{
QMutexLocker lock( &m_mutex );
m_size = size;
}
@@ -367,6 +426,8 @@ Result::setSize( unsigned int size )
void
Result::setModificationTime( unsigned int modtime )
{
QMutexLocker lock( &m_mutex );
m_modtime = modtime;
}
@@ -374,6 +435,8 @@ Result::setModificationTime( unsigned int modtime )
void
Result::setTrack( const track_ptr& track )
{
QMutexLocker lock( &m_mutex );
m_track = track;
}
@@ -381,6 +444,8 @@ Result::setTrack( const track_ptr& track )
unsigned int
Result::fileId() const
{
QMutexLocker lock( &m_mutex );
return m_fileId;
}
@@ -390,6 +455,8 @@ Result::friendlySource() const
{
if ( resolvedByCollection().isNull() )
{
QMutexLocker lock( &m_mutex );
return m_friendlySource;
}
else
@@ -400,6 +467,8 @@ Result::friendlySource() const
QString
Result::purchaseUrl() const
{
QMutexLocker lock( &m_mutex );
return m_purchaseUrl;
}
@@ -407,6 +476,8 @@ Result::purchaseUrl() const
QString
Result::linkUrl() const
{
QMutexLocker lock( &m_mutex );
return m_linkUrl;
}
@@ -416,6 +487,8 @@ Result::sourceIcon( TomahawkUtils::ImageMode style, const QSize& desiredSize ) c
{
if ( resolvedByCollection().isNull() )
{
//QMutexLocker lock( &m_mutex );
const ExternalResolver* resolver = qobject_cast< ExternalResolver* >( m_resolver.data() );
if ( !resolver )
{
@@ -466,6 +539,8 @@ Result::sourceIcon( TomahawkUtils::ImageMode style, const QSize& desiredSize ) c
unsigned int
Result::bitrate() const
{
QMutexLocker lock( &m_mutex );
return m_bitrate;
}
@@ -473,6 +548,8 @@ Result::bitrate() const
unsigned int
Result::size() const
{
QMutexLocker lock( &m_mutex );
return m_size;
}
@@ -480,6 +557,8 @@ Result::size() const
unsigned int
Result::modificationTime() const
{
QMutexLocker lock( &m_mutex );
return m_modtime;
}
@@ -487,6 +566,8 @@ Result::modificationTime() const
void
Result::setFileId( unsigned int id )
{
QMutexLocker lock( &m_mutex );
m_fileId = id;
}
@@ -494,6 +575,8 @@ Result::setFileId( unsigned int id )
Tomahawk::Resolver*
Result::resolvedBy() const
{
QMutexLocker lock( &m_mutex );
if ( !m_collection.isNull() )
return m_collection.data();
@@ -504,12 +587,16 @@ Result::resolvedBy() const
void
Result::setResolvedByResolver( Tomahawk::Resolver* resolver )
{
QMutexLocker lock( &m_mutex );
m_resolver = QPointer< Tomahawk::Resolver >( resolver );
}
QPointer< Resolver > Result::resolvedByResolver() const
{
QMutexLocker lock( &m_mutex );
return m_resolver;
}
@@ -525,16 +612,29 @@ Result::doneEditing()
track_ptr
Result::track() const
{
QMutexLocker lock( &m_mutex );
return m_track;
}
QList< DownloadFormat >
Result::downloadFormats() const
{
QMutexLocker lock( &m_mutex );
return m_formats;
}
void
Result::setDownloadFormats( const QList<DownloadFormat>& formats )
{
if ( formats.isEmpty() )
return;
QMutexLocker lock( &m_mutex );
m_formats.clear();
foreach ( const DownloadFormat& format, formats )
{
@@ -562,7 +662,7 @@ Result::setDownloadFormats( const QList<DownloadFormat>& formats )
void
Result::onSettingsChanged()
{
if ( TomahawkSettings::instance()->downloadsPreferredFormat().toLower() != m_formats.first().extension.toLower() )
if ( TomahawkSettings::instance()->downloadsPreferredFormat().toLower() != downloadFormats().first().extension.toLower() )
{
setDownloadFormats( downloadFormats() );
emit updated();
@@ -599,6 +699,8 @@ Result::onDownloadJobStateChanged( DownloadJob::TrackState newState, DownloadJob
QWeakPointer<Result>
Result::weakRef()
{
QMutexLocker lock( &m_mutex );
return m_ownRef;
}
@@ -606,5 +708,7 @@ Result::weakRef()
void
Result::setWeakRef( QWeakPointer<Result> weakRef )
{
QMutexLocker lock( &m_mutex );
m_ownRef = weakRef;
}

View File

@@ -31,6 +31,7 @@
#include <QPixmap>
#include <QPointer>
#include <QVariant>
#include <QMutex>
class MetadataEditor;
@@ -93,6 +94,12 @@ public:
bool isOnline() const;
bool playable() const;
/**
* @brief whether this result isLocal, i.e. resolved by a local collection
* @return isLocal
*/
bool isLocal() const;
QString url() const;
/**
* Has the given url been checked that it is accessible/valid.
@@ -131,7 +138,7 @@ public:
track_ptr track() const;
QList<DownloadFormat> downloadFormats() const { return m_formats; }
QList< DownloadFormat > downloadFormats() const;
void setDownloadFormats( const QList<DownloadFormat>& formats );
downloadjob_ptr downloadJob() const { return m_downloadJob; }
@@ -161,6 +168,8 @@ private:
explicit Result( const QString& url, const Tomahawk::track_ptr& track );
explicit Result();
mutable QMutex m_mutex;
mutable RID m_rid;
collection_wptr m_collection;
QPointer< Tomahawk::Resolver > m_resolver;

View File

@@ -929,6 +929,20 @@ TomahawkSettings::setVolume( unsigned int volume )
}
bool
TomahawkSettings::muted() const
{
return value( "audio/muted" ).toBool();
}
void
TomahawkSettings::setMuted( bool muted )
{
setValue( "audio/muted", muted );
}
QString
TomahawkSettings::proxyHost() const
{

View File

@@ -107,6 +107,9 @@ public:
unsigned int volume() const;
void setVolume( unsigned int volume );
bool muted() const;
void setMuted( bool muted );
/// Playlist stuff
QByteArray playlistColumnSizes( const QString& playlistid ) const;
void setPlaylistColumnSizes( const QString& playlistid, const QByteArray& state );

View File

@@ -37,6 +37,7 @@
#include <QtAlgorithms>
#include <QDateTime>
#include <QReadWriteLock>
#include <QCoreApplication>
using namespace Tomahawk;
@@ -92,6 +93,7 @@ Track::get( const QString& artist, const QString& track, const QString& album, c
}
track_ptr t = track_ptr( new Track( artist, track, album, albumArtist, duration, composer, albumpos, discnumber ), &Track::deleteLater );
t->moveToThread( QCoreApplication::instance()->thread() );
t->setWeakRef( t.toWeakRef() );
s_tracksByName.insert( key, t );

View File

@@ -19,8 +19,7 @@
#include "TrackData.h"
#include <QtAlgorithms>
#include <QReadWriteLock>
#include "audio/AudioEngine.h"
#include "collection/Collection.h"
@@ -41,6 +40,10 @@
#include "PlaylistEntry.h"
#include "SourceList.h"
#include <QtAlgorithms>
#include <QReadWriteLock>
#include <QCoreApplication>
using namespace Tomahawk;
QHash< QString, trackdata_wptr > TrackData::s_trackDatasByName = QHash< QString, trackdata_wptr >();
@@ -84,6 +87,7 @@ TrackData::get( unsigned int id, const QString& artist, const QString& track )
}
trackdata_ptr t = trackdata_ptr( new TrackData( id, artist, track ), &TrackData::deleteLater );
t->moveToThread( QCoreApplication::instance()->thread() );
t->setWeakRef( t.toWeakRef() );
s_trackDatasByName.insert( key, t );

View File

@@ -253,7 +253,7 @@ namespace Tomahawk
typedef QHash< QString, QString > InfoStringHash;
typedef QPair< QVariantMap, QVariant > PushInfoPair;
typedef QPointer< InfoPlugin > InfoPluginPtr;
typedef QSharedPointer< InfoPlugin > InfoPluginPtr;
}
namespace Network

View File

@@ -167,7 +167,7 @@ signals:
void connectionStateChanged( Tomahawk::Accounts::Account::ConnectionState state );
void configurationChanged();
void configTestResult( Tomahawk::Accounts::ConfigTestResultType );
void configTestResult( int, const QString& = QString() );
protected:
virtual void loadFromConfig( const QString &accountId );

View File

@@ -68,6 +68,7 @@ DelegateConfigWrapper::DelegateConfigWrapper( Tomahawk::Accounts::Account* accou
h->setContentsMargins( m_widget->contentsMargins() );
m_errorLabel->setAlignment( Qt::AlignCenter );
m_errorLabel->setWordWrap( true );
v->addWidget( m_errorLabel );
v->addLayout( h );
@@ -81,7 +82,7 @@ DelegateConfigWrapper::DelegateConfigWrapper( Tomahawk::Accounts::Account* accou
if ( m_widget->metaObject()->indexOfSignal( "sizeHintChanged()" ) > -1 )
connect( m_widget, SIGNAL( sizeHintChanged() ), this, SLOT( updateSizeHint() ) );
connect( m_account, SIGNAL( configTestResult( Tomahawk::Accounts::ConfigTestResultType ) ), SLOT( onConfigTestResult( Tomahawk::Accounts::ConfigTestResultType ) ) );
connect( m_account, SIGNAL( configTestResult( int, const QString& ) ), SLOT( onConfigTestResult( int, const QString& ) ) );
}
@@ -192,11 +193,11 @@ DelegateConfigWrapper::aboutClicked( bool )
void
DelegateConfigWrapper::onConfigTestResult( Tomahawk::Accounts::ConfigTestResultType result )
DelegateConfigWrapper::onConfigTestResult( int code, const QString& message )
{
tLog() << Q_FUNC_INFO << result;
tLog() << Q_FUNC_INFO << code << ": " << message;
if( result == Tomahawk::Accounts::ConfigTestResultSuccess )
if( code == Tomahawk::Accounts::ConfigTestResultSuccess )
{
m_invalidData = QVariantMap();
closeDialog( QDialog::Accepted );
@@ -226,8 +227,25 @@ DelegateConfigWrapper::onConfigTestResult( Tomahawk::Accounts::ConfigTestResultT
m_invalidData = m_widget->readData();
// TODO: generate message based on status code
m_errorLabel->setText( QString( "<font color='red'>%1</font>" ).arg( tr( "Your config is invalid." ) ) );
QString msg = !message.isEmpty() ? message : getTestConfigMessage( code );
m_errorLabel->setText( QString( "<font color='red'>%1</font>" ).arg( msg ) );
}
}
QString
DelegateConfigWrapper::getTestConfigMessage( int code )
{
switch(code) {
case Tomahawk::Accounts::ConfigTestResultCommunicationError:
return tr( "Unable to authenticate. Please check your connection." );
case Tomahawk::Accounts::ConfigTestResultInvalidCredentials:
return tr( "Username or password incorrect." );
case Tomahawk::Accounts::ConfigTestResultInvalidAccount:
return tr( "Account rejected by server." );
case Tomahawk::Accounts::ConfigTestResultPlayingElsewhere:
return tr( "Action not allowed, account is in use elsewhere." );
case Tomahawk::Accounts::ConfigTestResultAccountExpired:
return tr( "Your account has expired." );
}
}

View File

@@ -60,10 +60,11 @@ protected:
private slots:
void aboutClicked( bool );
void onConfigTestResult( Tomahawk::Accounts::ConfigTestResultType );
void onConfigTestResult( int, const QString& );
private:
void closeDialog( QDialog::DialogCode code );
QString getTestConfigMessage( int code );
Tomahawk::Accounts::Account* m_account;
AccountConfigWidget* m_widget;

View File

@@ -79,7 +79,7 @@ ResolverAccountFactory::createFromPath( const QString& path )
}
Account*
ResolverAccount*
ResolverAccountFactory::createFromPath( const QString& path, const QString& factory, bool isAttica )
{
qDebug() << "Creating ResolverAccount from path:" << path << "is attica" << isAttica;
@@ -110,57 +110,8 @@ ResolverAccountFactory::createFromPath( const QString& path, const QString& fact
if ( pathInfo.suffix() == "axe" )
{
QString uniqueName = uuid();
QDir dir( TomahawkUtils::extractScriptPayload( pathInfo.filePath(),
uniqueName,
MANUALRESOLVERS_DIR ) );
if ( !( dir.exists() && dir.isReadable() ) ) //decompression fubar
if ( !installAxe( realPath, configuration ) )
{
displayError( tr( "Resolver installation error: cannot open bundle." ) );
return 0;
}
if ( !dir.cd( "content" ) ) //more fubar
{
displayError( tr( "Resolver installation error: incomplete bundle." ) );
return 0;
}
QString metadataFilePath = dir.absoluteFilePath( "metadata.json" );
configuration = metadataFromJsonFile( metadataFilePath );
configuration[ "bundleDir" ] = uniqueName;
if ( !configuration[ "pluginName" ].isNull() && !configuration[ "pluginName" ].toString().isEmpty() )
{
dir.cdUp();
if ( !dir.cdUp() ) //we're in MANUALRESOLVERS_DIR
return 0;
QString name = configuration[ "pluginName" ].toString();
QString namePath = dir.absoluteFilePath( name );
QFileInfo npI( namePath );
if ( npI.exists() && npI.isDir() )
{
TomahawkUtils::removeDirectory( namePath );
}
dir.rename( uniqueName, name );
configuration[ "bundleDir" ] = name;
if ( !dir.cd( QString( "%1/content" ).arg( name ) ) ) //should work if it worked once
return 0;
}
expandPaths( dir, configuration );
realPath = configuration[ "path" ].toString();
if ( realPath.isEmpty() )
{
displayError( tr( "Resolver installation error: bad metadata in bundle." ) );
return 0;
}
}
@@ -221,6 +172,71 @@ ResolverAccountFactory::createFromPath( const QString& path, const QString& fact
}
bool
ResolverAccountFactory::installAxe( QString& realPath, QVariantHash& configuration )
{
const QFileInfo pathInfo( realPath );
QString uniqueName = uuid();
QDir dir( TomahawkUtils::extractScriptPayload( pathInfo.filePath(),
uniqueName,
MANUALRESOLVERS_DIR ) );
if ( !( dir.exists() && dir.isReadable() ) ) //decompression fubar
{
JobStatusView::instance()->model()->addJob( new ErrorStatusMessage(
tr( "Resolver installation error: cannot open bundle." ) ) );
return 0;
}
if ( !dir.cd( "content" ) ) //more fubar
{
JobStatusView::instance()->model()->addJob( new ErrorStatusMessage(
tr( "Resolver installation error: incomplete bundle." ) ) );
return 0;
}
QString metadataFilePath = dir.absoluteFilePath( "metadata.json" );
configuration = metadataFromJsonFile( metadataFilePath );
configuration[ "bundleDir" ] = uniqueName;
if ( !configuration[ "pluginName" ].isNull() && !configuration[ "pluginName" ].toString().isEmpty() )
{
dir.cdUp();
if ( !dir.cdUp() ) //we're in MANUALRESOLVERS_DIR
return 0;
QString name = configuration[ "pluginName" ].toString();
QString namePath = dir.absoluteFilePath( name );
QFileInfo npI( namePath );
if ( npI.exists() && npI.isDir() )
{
TomahawkUtils::removeDirectory( namePath );
}
dir.rename( uniqueName, name );
configuration[ "bundleDir" ] = name;
if ( !dir.cd( QString( "%1/content" ).arg( name ) ) ) //should work if it worked once
return 0;
}
expandPaths( dir, configuration );
realPath = configuration[ "path" ].toString();
if ( realPath.isEmpty() )
{
JobStatusView::instance()->model()->addJob( new ErrorStatusMessage(
tr( "Resolver installation error: bad metadata in bundle." ) ) );
return 0;
}
return true;
}
QVariantHash
ResolverAccountFactory::metadataFromJsonFile( const QString& path )
{
@@ -517,7 +533,8 @@ ResolverAccount::removeBundle()
}
void ResolverAccount::testConfig()
void
ResolverAccount::testConfig()
{
// HACK: move to JSAccount once we have that properly
JSResolver* resolver = qobject_cast< Tomahawk::JSResolver* >( m_resolver );
@@ -525,7 +542,7 @@ void ResolverAccount::testConfig()
{
QVariantMap data = resolver->loadDataFromWidgets();
ScriptJob* job = resolver->scriptObject()->invoke( "testConfig", data );
connect( job, SIGNAL( done( QVariantMap ) ), SLOT( onTestConfig( QVariantMap ) ) );
connect( job, SIGNAL( done( QVariant ) ), SLOT( onTestConfig( QVariant ) ) );
job->start();
}
else
@@ -535,19 +552,25 @@ void ResolverAccount::testConfig()
}
ExternalResolverGui*
ResolverAccount::resolver() const
{
return m_resolver;
}
void
ResolverAccount::onTestConfig( const QVariantMap& result )
ResolverAccount::onTestConfig( const QVariant& result )
{
tLog() << Q_FUNC_INFO << result;
int resultCode = result[ "result" ].toInt();
if ( resultCode == 1 )
if ( result.type() == QVariant::String )
{
emit configTestResult( Accounts::ConfigTestResultSuccess );
emit configTestResult( Accounts::ConfigTestResultOther, result.toString() );
}
else
{
emit configTestResult( Accounts::ConfigTestResultOther );
emit configTestResult( result.toInt() );
}
sender()->deleteLater();
@@ -566,6 +589,7 @@ AtticaResolverAccount::AtticaResolverAccount( const QString& accountId )
}
AtticaResolverAccount::AtticaResolverAccount( const QString& accountId, const QString& path, const QString& atticaId, const QVariantHash& initialConfiguration )
: ResolverAccount( accountId, path, initialConfiguration )
, m_atticaId( atticaId )
@@ -595,8 +619,6 @@ AtticaResolverAccount::init()
loadIcon();
else
connect( AtticaManager::instance(), SIGNAL( resolversLoaded( Attica::Content::List ) ), this, SLOT( loadIcon() ) );
}

View File

@@ -33,6 +33,8 @@ class ExternalResolverGui;
namespace Accounts {
class ResolverAccount;
class DLLEXPORT ResolverAccountFactory : public AccountFactory
{
Q_OBJECT
@@ -53,7 +55,10 @@ public:
Account* createFromPath( const QString& path ) override;
// Internal use
static Account* createFromPath( const QString& path, const QString& factoryId, bool isAttica );
static ResolverAccount* createFromPath( const QString& path, const QString& factoryId, bool isAttica );
// YES, non const parameters!
static bool installAxe( QString& realPath, QVariantHash& configuration );
private:
static void displayError( const QString& error );
@@ -101,9 +106,11 @@ public:
void testConfig() override;
ExternalResolverGui* resolver() const;
private slots:
void resolverChanged();
void onTestConfig( const QVariantMap& result );
void onTestConfig( const QVariant& result );
protected:
// Created by factory, when user installs a new resolver

View File

@@ -71,7 +71,7 @@ LastFmAccount::LastFmAccount( const QString& accountId )
if ( infoPlugin() && Tomahawk::InfoSystem::InfoSystem::instance()->workerThread() )
{
infoPlugin().data()->moveToThread( Tomahawk::InfoSystem::InfoSystem::instance()->workerThread().data() );
infoPlugin()->moveToThread( Tomahawk::InfoSystem::InfoSystem::instance()->workerThread().data() );
Tomahawk::InfoSystem::InfoSystem::instance()->addInfoPlugin( infoPlugin() );
}
}
@@ -82,7 +82,6 @@ LastFmAccount::~LastFmAccount()
if ( m_infoPlugin )
{
Tomahawk::InfoSystem::InfoSystem::instance()->removeInfoPlugin( infoPlugin() );
delete m_infoPlugin;
}
delete m_resolver.data();
@@ -168,9 +167,11 @@ InfoPluginPtr
LastFmAccount::infoPlugin()
{
if ( m_infoPlugin.isNull() )
m_infoPlugin = QPointer< LastFmInfoPlugin >( new LastFmInfoPlugin( this ) );
{
m_infoPlugin = QSharedPointer< LastFmInfoPlugin>( new LastFmInfoPlugin( this ) );
}
return InfoPluginPtr( m_infoPlugin.data() );
return m_infoPlugin;
}
bool

View File

@@ -105,7 +105,7 @@ private:
void hookupResolver();
QPointer<Tomahawk::ExternalResolverGui> m_resolver;
QPointer<Tomahawk::InfoSystem::LastFmInfoPlugin> m_infoPlugin;
QSharedPointer<Tomahawk::InfoSystem::LastFmInfoPlugin> m_infoPlugin;
QPointer<LastFmConfig> m_configWidget;
};

View File

@@ -116,6 +116,12 @@ SpotifyAccount::~SpotifyAccount()
{
clearUser();
if ( m_infoPlugin )
{
Tomahawk::InfoSystem::InfoSystem::instance()->removeInfoPlugin( infoPlugin() );
}
if ( m_spotifyResolver.isNull() )
return;
@@ -135,7 +141,7 @@ SpotifyAccount::init()
if ( infoPlugin() && Tomahawk::InfoSystem::InfoSystem::instance()->workerThread() )
{
infoPlugin().data()->moveToThread( Tomahawk::InfoSystem::InfoSystem::instance()->workerThread().data() );
infoPlugin()->moveToThread( Tomahawk::InfoSystem::InfoSystem::instance()->workerThread().data() );
Tomahawk::InfoSystem::InfoSystem::instance()->addInfoPlugin( infoPlugin() );
}
@@ -366,10 +372,10 @@ SpotifyAccount::infoPlugin()
{
if ( m_infoPlugin.isNull() )
{
m_infoPlugin = QPointer< InfoSystem::SpotifyInfoPlugin >( new InfoSystem::SpotifyInfoPlugin( this ) );
m_infoPlugin = QSharedPointer< InfoSystem::SpotifyInfoPlugin >( new InfoSystem::SpotifyInfoPlugin( this ) );
}
return InfoSystem::InfoPluginPtr( m_infoPlugin.data() );
return m_infoPlugin;
}

View File

@@ -170,7 +170,7 @@ private:
QPointer<SpotifyAccountConfig> m_configWidget;
QPointer<QWidget> m_aboutWidget;
QPointer<ScriptResolver> m_spotifyResolver;
QPointer< InfoSystem::SpotifyInfoPlugin > m_infoPlugin;
QSharedPointer< InfoSystem::SpotifyInfoPlugin > m_infoPlugin;
QMap<QString, QPair<QObject*, QString> > m_qidToSlotMap;
QMap<QString, QVariant > m_qidToExtraData;

View File

@@ -277,7 +277,7 @@ SpotifyAccountConfig::showLoggedIn()
m_ui->verticalLayout->insertWidget( 1, m_loggedInUser, 0, Qt::AlignCenter );
}
qDebug() << "Showing logged in withuserame:" << m_verifiedUsername;
qDebug() << "Showing logged in with username:" << m_verifiedUsername;
m_loggedInUser->show();
m_loggedInUser->setText( tr( "Logged in as %1" ).arg( m_verifiedUsername ) );

View File

@@ -1,6 +1,6 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2010-2015, Christian Muehlhaeuser <muesli@tomahawk-player.org>
* Copyright 2010-2016, Christian Muehlhaeuser <muesli@tomahawk-player.org>
* Copyright 2010-2012, Jeff Mitchell <jeff@tomahawk-player.org>
* Copyright 2013, Teo Mrnjavac <teo@kde.org>
*
@@ -165,12 +165,19 @@ AudioEngine::AudioEngine()
d->s_instance = this;
tDebug() << "Init AudioEngine";
d->audioOutput = new AudioOutput(this);
d->audioOutput = new AudioOutput( this );
connect( d->audioOutput, SIGNAL( initialized() ), this, SIGNAL( initialized() ) );
connect( d->audioOutput, SIGNAL( stateChanged( AudioOutput::AudioState, AudioOutput::AudioState ) ), d_func(), SLOT( onStateChanged( AudioOutput::AudioState, AudioOutput::AudioState ) ) );
connect( d->audioOutput, SIGNAL( tick( qint64 ) ), SLOT( timerTriggered( qint64 ) ) );
connect( d->audioOutput, SIGNAL( positionChanged( float ) ), SLOT( onPositionChanged( float ) ) );
connect( d->audioOutput, SIGNAL( volumeChanged( qreal ) ), SLOT( onVolumeChanged( qreal ) ) );
connect( d->audioOutput, SIGNAL( mutedChanged( bool ) ), SIGNAL( mutedChanged( bool ) ) );
if ( TomahawkSettings::instance()->muted() )
{
mute();
}
setVolume( TomahawkSettings::instance()->volume() );
qRegisterMetaType< AudioErrorCode >("AudioErrorCode");
@@ -183,6 +190,7 @@ AudioEngine::~AudioEngine()
tDebug() << Q_FUNC_INFO;
TomahawkSettings::instance()->setVolume( volume() );
TomahawkSettings::instance()->setMuted( isMuted() );
delete d_ptr;
}
@@ -289,8 +297,11 @@ AudioEngine::stop( AudioErrorCode errorCode )
if ( d->waitingOnNewTrack )
sendWaitingNotification();
Tomahawk::InfoSystem::InfoPushData pushData( s_aeInfoIdentifier, Tomahawk::InfoSystem::InfoNowStopped, QVariant(), Tomahawk::InfoSystem::PushNoFlag );
Tomahawk::InfoSystem::InfoSystem::instance()->pushInfo( pushData );
if ( d->audioOutput->isInitialized() )
{
Tomahawk::InfoSystem::InfoPushData pushData( s_aeInfoIdentifier, Tomahawk::InfoSystem::InfoNowStopped, QVariant(), Tomahawk::InfoSystem::PushNoFlag );
Tomahawk::InfoSystem::InfoSystem::instance()->pushInfo( pushData );
}
}
@@ -455,6 +466,8 @@ AudioEngine::mute()
{
Q_D( AudioEngine );
d->audioOutput->setMuted( true );
emit volumeChanged( volume() );
}
@@ -463,6 +476,8 @@ AudioEngine::toggleMute()
{
Q_D( AudioEngine );
d->audioOutput->setMuted( !d->audioOutput->isMuted() );
emit volumeChanged( volume() );
}
@@ -562,6 +577,12 @@ AudioEngine::loadTrack( const Tomahawk::result_ptr& result )
Q_D( AudioEngine );
tDebug( LOGEXTRA ) << Q_FUNC_INFO << ( result.isNull() ? QString() : result->url() );
if ( !d->audioOutput->isInitialized() )
{
return;
}
if ( !result )
{
stop();
@@ -583,6 +604,7 @@ AudioEngine::loadTrack( const Tomahawk::result_ptr& result )
job->start();
}
void
AudioEngine::gotStreamUrl( const QVariantMap& data )
{
@@ -590,14 +612,17 @@ AudioEngine::gotStreamUrl( const QVariantMap& data )
QVariantMap headers = data[ "headers" ].toMap();
Tomahawk::result_ptr result = sender()->property( "result" ).value<result_ptr>();
if ( streamUrl.isEmpty() || !( TomahawkUtils::isHttpResult( streamUrl ) || TomahawkUtils::isHttpsResult( streamUrl ) || TomahawkUtils::isRtmpResult( streamUrl ) ) )
if ( streamUrl.isEmpty() || headers.isEmpty() ||
!( TomahawkUtils::isHttpResult( streamUrl ) || TomahawkUtils::isHttpsResult( streamUrl ) ) )
{
// Not an http(s) or RTMP URL, get IO device
// We can't supply custom headers to VLC - but prefer using its HTTP streaming due to improved seeking ability
// Not an RTMP or HTTP-with-headers URL, get IO device
QSharedPointer< QIODevice > sp;
performLoadIODevice( result, streamUrl );
}
else
{
// We need our own QIODevice for streaming
// TODO: just make this part of the http(s) IoDeviceFactory (?)
QUrl url = QUrl::fromEncoded( streamUrl.toUtf8() );
QNetworkRequest req( url );
@@ -619,12 +644,13 @@ AudioEngine::gotStreamUrl( const QVariantMap& data )
tDebug() << "Creating a QNetworkReply with url:" << req.url().toString();
NetworkReply* reply = new NetworkReply( Tomahawk::Utils::nam()->get( req ) );
NewClosure( reply, SIGNAL( finalUrlReached() ), this, SLOT( gotRedirectedStreamUrl( Tomahawk::result_ptr, NetworkReply* )), result, reply );
NewClosure( reply, SIGNAL( finalUrlReached() ), this, SLOT( gotRedirectedStreamUrl( Tomahawk::result_ptr, NetworkReply* ) ), result, reply );
}
sender()->deleteLater();
}
void
AudioEngine::gotRedirectedStreamUrl( const Tomahawk::result_ptr& result, NetworkReply* reply )
{
@@ -669,7 +695,7 @@ AudioEngine::performLoadIODevice( const result_ptr& result, const QString& url )
void
AudioEngine::performLoadTrack( const Tomahawk::result_ptr result, const QString url, QSharedPointer< QIODevice > io )
AudioEngine::performLoadTrack( const Tomahawk::result_ptr result, const QString& url, QSharedPointer< QIODevice > io )
{
if ( QThread::currentThread() != thread() )
{
@@ -951,9 +977,9 @@ AudioEngine::playItem( Tomahawk::playlistinterface_ptr playlist, const Tomahawk:
void
AudioEngine::playItem( Tomahawk::playlistinterface_ptr playlist, const Tomahawk::query_ptr& query )
{
if ( query->resolvingFinished() )
if ( query->resolvingFinished() || query->numResults( true ) )
{
if ( query->numResults() && query->results().first()->isOnline() )
if ( query->numResults( true ) )
{
playItem( playlist, query->results().first(), query );
return;
@@ -969,7 +995,7 @@ AudioEngine::playItem( Tomahawk::playlistinterface_ptr playlist, const Tomahawk:
{
Pipeline::instance()->resolve( query );
NewClosure( query.data(), SIGNAL( resolvingFinished( bool ) ),
NewClosure( query.data(), SIGNAL( resultsChanged() ),
const_cast<AudioEngine*>(this), SLOT( playItem( Tomahawk::playlistinterface_ptr, Tomahawk::query_ptr ) ), playlist, query );
}
}

View File

@@ -149,6 +149,8 @@ public slots:
void setShuffled( bool enabled );
signals:
void initialized();
void loading( const Tomahawk::result_ptr track );
void started( const Tomahawk::result_ptr track );
void finished( const Tomahawk::result_ptr track );
@@ -186,7 +188,7 @@ private slots:
void performLoadIODevice( const Tomahawk::result_ptr& result, const QString& url ); //only call from loadTrack kthxbi
void performLoadTrack( const Tomahawk::result_ptr result, const QString url, QSharedPointer< QIODevice > io ); //only call from loadTrack or performLoadIODevice kthxbi
void performLoadTrack( const Tomahawk::result_ptr result, const QString& url, QSharedPointer< QIODevice > io ); //only call from loadTrack or performLoadIODevice kthxbi
void loadPreviousTrack();
void loadNextTrack();

View File

@@ -25,11 +25,13 @@
#include "audio/MediaStream.h"
#include "utils/Logger.h"
#include "utils/TomahawkUtils.h"
#include <QApplication>
#include <QVarLengthArray>
#include <QFile>
#include <QDir>
#include <QTimer>
#include <vlc/libvlc.h>
#include <vlc/libvlc_media.h>
@@ -58,6 +60,7 @@ AudioOutput::AudioOutput( QObject* parent )
, m_currentTime( 0 )
, m_totalTime( 0 )
, m_justSeeked( false )
, m_initialized( false )
, dspPluginCallback( nullptr )
, m_vlcInstance( nullptr )
, m_vlcPlayer( nullptr )
@@ -95,8 +98,8 @@ AudioOutput::AudioOutput( QObject* parent )
TOMAHAWK_APPLICATION_NAME "/" TOMAHAWK_VERSION );
// FIXME: icon is named tomahawk, so we need the lowercase application name
#if (LIBVLC_VERSION_INT >= LIBVLC_VERSION(2, 1, 0, 0))
libvlc_set_app_id( m_vlcInstance, "org.tomahawk-player.desktop",
TOMAHAWK_VERSION, "tomahawk" );
libvlc_set_app_id( m_vlcInstance, TOMAHAWK_APPLICATION_PACKAGE_NAME,
TOMAHAWK_VERSION, TOMAHAWK_TARGET_NAME );
#endif
m_vlcPlayer = libvlc_media_player_new( m_vlcInstance );
@@ -120,6 +123,11 @@ AudioOutput::AudioOutput( QObject* parent )
libvlc_MediaPlayerTitleChanged,
libvlc_MediaPlayerSnapshotTaken,
//libvlc_MediaPlayerLengthChanged,
#if (LIBVLC_VERSION_INT >= LIBVLC_VERSION(2, 2, 2, 0))
libvlc_MediaPlayerAudioVolume,
libvlc_MediaPlayerMuted,
libvlc_MediaPlayerUnmuted,
#endif
libvlc_MediaPlayerVout
};
const int eventCount = sizeof(events) / sizeof( *events );
@@ -128,7 +136,24 @@ AudioOutput::AudioOutput( QObject* parent )
libvlc_event_attach( manager, events[ i ], &AudioOutput::vlcEventCallback, this );
}
tDebug() << Q_FUNC_INFO << "Init OK";
// HACK: play silent ogg file and set volume on that to workaround vlc not allowing to set volume before a file is played
m_silenceFile.setFileName( RESPATH "sounds/silence.ogg" );
Q_ASSERT( m_silenceFile.exists() );
Q_ASSERT( m_silenceFile.open( QIODevice::ReadOnly ) );
setCurrentSource( new MediaStream( &m_silenceFile, true ) );
libvlc_media_player_play( m_vlcPlayer );
#if QT_VERSION >= QT_VERSION_CHECK(5,4,0)
// if the silence file did not play for 15 secs, we pretend the AudioOutput is initialized, to allow proper error reporting
QTimer::singleShot( 15000, [&]()
{
if ( !m_initialized ) {
m_initialized = true;
emit initialized();
}
} );
#endif
}
@@ -154,6 +179,28 @@ AudioOutput::~AudioOutput()
}
void
AudioOutput::onInitVlcEvent( const libvlc_event_t* event )
{
switch ( event->type )
{
case libvlc_MediaPlayerTimeChanged:
setVolume( volume() );
setMuted( isMuted() );
m_initialized = true;
m_silenceFile.close();
tDebug() << Q_FUNC_INFO << "Init OK";
emit initialized();
break;
default:
break;
}
}
void
AudioOutput::setAutoDelete( bool ad )
{
@@ -302,6 +349,13 @@ AudioOutput::setCurrentSource( MediaStream* stream )
}
bool
AudioOutput::isInitialized() const
{
return m_initialized;
}
AudioOutput::AudioState
AudioOutput::state() const
{
@@ -427,16 +481,19 @@ AudioOutput::seek( qint64 milliseconds )
return;
}
qint64 duration = AudioEngine::instance()->currentTrackTotalTime();
// for some tracks, seeking to an end seems not to work correctly with libvlc
// (tracks enter a random and infinite loop) - this is a temporary fix for that
if (duration == milliseconds)
milliseconds -= 1;
if ( m_seekable )
{
// tDebug() << Q_FUNC_INFO << "AudioOutput:: seeking" << milliseconds << "msec";
libvlc_media_player_set_time( m_vlcPlayer, milliseconds );
setCurrentTime( milliseconds );
}
else
{
qint64 duration = AudioEngine::instance()->currentTrackTotalTime();
float position = float(float(milliseconds) / duration);
libvlc_media_player_set_position(m_vlcPlayer, position);
tDebug() << Q_FUNC_INFO << "AudioOutput:: seeking via position" << position << "pos";
@@ -466,11 +523,9 @@ AudioOutput::setMuted( bool m )
tDebug() << Q_FUNC_INFO;
m_muted = m;
if ( m_muted )
{
libvlc_audio_set_volume( m_vlcPlayer, 0 );
}
else
libvlc_audio_set_mute( m_vlcPlayer, m );
if ( !m_muted )
{
libvlc_audio_set_volume( m_vlcPlayer, m_volume * 100.0 );
}
@@ -487,7 +542,7 @@ AudioOutput::volume() const
void
AudioOutput::setVolume( qreal vol )
{
tDebug() << Q_FUNC_INFO;
tDebug() << Q_FUNC_INFO << vol << m_muted;
m_volume = vol;
if ( !m_muted )
@@ -531,6 +586,20 @@ AudioOutput::onVlcEvent( const libvlc_event_t* event )
// Don't call stop() here - it will deadlock libvlc
setState( Error );
break;
#if (LIBVLC_VERSION_INT >= LIBVLC_VERSION(2, 2, 2, 0))
case libvlc_MediaPlayerAudioVolume:
m_volume = event->u.media_player_audio_volume.volume;
emit volumeChanged( volume() );
break;
case libvlc_MediaPlayerMuted:
m_muted = true;
emit mutedChanged( true );
break;
case libvlc_MediaPlayerUnmuted:
m_muted = false;
emit mutedChanged( false );
break;
#endif
case libvlc_MediaPlayerNothingSpecial:
case libvlc_MediaPlayerOpening:
case libvlc_MediaPlayerBuffering:
@@ -556,7 +625,14 @@ AudioOutput::vlcEventCallback( const libvlc_event_t* event, void* opaque )
AudioOutput* that = reinterpret_cast < AudioOutput * > ( opaque );
Q_ASSERT( that );
that->onVlcEvent( event );
if ( !that->isInitialized() )
{
that->onInitVlcEvent( event );
}
else
{
that->onVlcEvent( event );
}
}

View File

@@ -25,6 +25,8 @@
#include "DllMacro.h"
#include "Typedefs.h"
#include <QFile>
#include <functional>
struct libvlc_instance_t;
@@ -44,6 +46,7 @@ public:
explicit AudioOutput( QObject* parent = nullptr );
~AudioOutput();
bool isInitialized() const;
AudioState state() const;
void setCurrentSource( const QUrl& stream );
@@ -72,11 +75,16 @@ public:
public slots:
signals:
void initialized();
void stateChanged( AudioOutput::AudioState, AudioOutput::AudioState );
void tick( qint64 );
void positionChanged( float );
void volumeChanged( qreal volume );
void mutedChanged( bool );
private:
void onInitVlcEvent( const libvlc_event_t* event );
void setState( AudioState state );
void setCurrentTime( qint64 time );
void setCurrentPosition( float position );
@@ -99,6 +107,9 @@ private:
qint64 m_totalTime;
bool m_justSeeked;
bool m_initialized;
QFile m_silenceFile;
std::function< void( int state, int frameNumber, float* samples, int nb_channels, int nb_samples ) > dspPluginCallback;
libvlc_instance_t* m_vlcInstance;

View File

@@ -41,12 +41,16 @@ MediaStream::MediaStream( const QUrl &url )
}
MediaStream::MediaStream( QIODevice* device )
MediaStream::MediaStream( QIODevice* device, bool bufferingFinished )
: QObject( nullptr )
, m_type( IODevice )
, m_ioDevice ( device )
, m_bufferingFinished( bufferingFinished )
{
QObject::connect( m_ioDevice, SIGNAL( readChannelFinished() ), this, SLOT( bufferingFinished() ) );
if ( !bufferingFinished )
{
QObject::connect( m_ioDevice, SIGNAL( readChannelFinished() ), this, SLOT( bufferingFinished() ) );
}
}

View File

@@ -42,7 +42,7 @@ public:
MediaStream( QObject* parent = nullptr );
explicit MediaStream( const QUrl &url );
explicit MediaStream( QIODevice* device );
explicit MediaStream( QIODevice* device, bool bufferingFinished = false );
virtual ~MediaStream();
MediaType type() const;

View File

@@ -1,21 +1,20 @@
/*
Copyright (C) 2011 Leo Franchi <lfranchi@kde.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2011, Leo Franchi <lfranchi@kde.org>
*
* Tomahawk is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Tomahawk is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Tomahawk. If not, see <http://www.gnu.org/licenses/>.
*/
#include "LocalCollection.h"

View File

@@ -1,21 +1,20 @@
/*
Copyright (C) 2011 Leo Franchi <lfranchi@kde.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2011, Leo Franchi <lfranchi@kde.org>
*
* Tomahawk is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Tomahawk is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Tomahawk. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef LOCALCOLLECTION_H
#define LOCALCOLLECTION_H

View File

@@ -240,7 +240,7 @@ MetadataEditor::loadResult( const Tomahawk::result_ptr& result )
return;
m_result = result;
setEditable( result->resolvedByCollection() && result->resolvedByCollection()->isLocal() );
setEditable( result->isLocal() );
setTitle( result->track()->track() );
setArtist( result->track()->artist() );
@@ -250,7 +250,7 @@ MetadataEditor::loadResult( const Tomahawk::result_ptr& result )
setYear( result->track()->year() );
setBitrate( result->bitrate() );
if ( result->resolvedByCollection() && result->resolvedByCollection()->isLocal() )
if ( result->isLocal() )
{
QString furl = m_result->url();
if ( furl.startsWith( "file://" ) )

View File

@@ -277,8 +277,8 @@ InfoSystem::addInfoPlugin( Tomahawk::InfoSystem::InfoPluginPtr plugin )
if ( plugin.data()->thread() != m_infoSystemWorkerThreadController->worker()->thread() )
{
tLog() << Q_FUNC_INFO << "The object must be moved to the worker thread first, see InfoSystem::workerThread(): " << plugin->friendlyName();
Q_ASSERT( false );
tDebug() << Q_FUNC_INFO << "The object must be moved to the worker thread first, see InfoSystem::workerThread()";
return;
}

View File

@@ -58,13 +58,7 @@ InfoSystemWorker::InfoSystemWorker()
InfoSystemWorker::~InfoSystemWorker()
{
tDebug() << Q_FUNC_INFO << " beginning";
Q_FOREACH( InfoPluginPtr plugin, m_plugins )
{
if( plugin )
delete plugin.data();
}
tDebug() << Q_FUNC_INFO << " finished";
tDebug() << Q_FUNC_INFO;
}
@@ -136,29 +130,6 @@ InfoSystemWorker::addInfoPlugin( Tomahawk::InfoSystem::InfoPluginPtr plugin )
emit updatedSupportedGetTypes( QSet< InfoType >::fromList( m_infoGetMap.keys() ) );
emit updatedSupportedPushTypes( QSet< InfoType >::fromList( m_infoPushMap.keys() ) );
connect( plugin.data(), SIGNAL( destroyed( QObject* ) ), SLOT( onInfoPluginDeleted() ) );
}
void
InfoSystemWorker::onInfoPluginDeleted()
{
foreach( const InfoPluginPtr& plugin, m_plugins )
{
if ( plugin.isNull() )
{
m_plugins.removeOne( plugin );
foreach( InfoType type, m_infoGetMap.keys() )
{
m_infoGetMap[type].removeOne( plugin );
}
foreach( InfoType type, m_infoPushMap.keys() )
{
m_infoPushMap[type].removeOne( plugin );
}
}
}
}

View File

@@ -72,7 +72,6 @@ public slots:
private slots:
void checkTimeoutsTimerFired();
void onInfoPluginDeleted();
private:
void registerInfoTypes( const InfoPluginPtr &plugin, const QSet< InfoType > &getTypes, const QSet< InfoType > &pushTypes );

View File

@@ -33,6 +33,12 @@ JobStatusItem::~JobStatusItem()
}
void
JobStatusItem::activated()
{
}
bool
JobStatusItem::allowMultiLine() const
{

View File

@@ -53,6 +53,8 @@ public:
virtual QString mainText() const = 0;
virtual QString rightColumnText() const { return QString(); };
virtual void activated();
/**
* If collapse item is true, sending multiple items of the same type will "collapse" them into one
* instead of showing each individually. In this case, the right column from the item will be ignored

View File

@@ -118,6 +118,7 @@ JobStatusView::setModel( JobStatusSortModel* m )
connect( m_view->model(), SIGNAL( customDelegateJobInserted( int, JobStatusItem* ) ), this, SLOT( customDelegateJobInserted( int, JobStatusItem* ) ) );
connect( m_view->model(), SIGNAL( customDelegateJobRemoved( int ) ), this, SLOT( customDelegateJobRemoved( int ) ) );
connect( m_view->model(), SIGNAL( refreshDelegates() ), this, SLOT( refreshDelegates() ) );
connect( m_view, SIGNAL( activated( QModelIndex ) ), this, SLOT( onItemActivated( QModelIndex ) ) );
foreach ( const QPointer<JobStatusItem> item, s_jobItems )
{
@@ -184,6 +185,21 @@ JobStatusView::refreshDelegates()
}
void
JobStatusView::onItemActivated( const QModelIndex& index )
{
QVariant itemVar = index.data( JobStatusModel::JobDataRole );
if ( !itemVar.canConvert< JobStatusItem* >() || !itemVar.value< JobStatusItem* >() )
{
tLog() << Q_FUNC_INFO << "unable to fetch JobStatusItem*";
return;
}
JobStatusItem* item = itemVar.value< JobStatusItem* >();
item->activated();
}
void
JobStatusView::checkCount()
{

View File

@@ -59,6 +59,7 @@ private slots:
void customDelegateJobInserted( int row, JobStatusItem* item );
void customDelegateJobRemoved( int row );
void refreshDelegates();
void onItemActivated( const QModelIndex& index );
private:
QListView* m_view;

View File

@@ -0,0 +1,37 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2016, Dominik Schmidt <domme@tomahawk-player.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 "ScriptErrorStatusMessage.h"
#include "../utils/Logger.h"
ScriptErrorStatusMessage::ScriptErrorStatusMessage( const QString& message, Tomahawk::ScriptAccount* account )
: ErrorStatusMessage( tr( "Script Error: %1" ).arg( message ) )
, m_account( account )
{
}
void
ScriptErrorStatusMessage::activated()
{
if ( m_account.isNull() )
return;
tDebug() << "ScriptErrorStatusMessage clicked: " << mainText() << m_account->name();
m_account->showDebugger();
}

View File

@@ -0,0 +1,39 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2016, Dominik Schmidt <domme@tomahawk-player.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 SCRIPTERRORSTATUSMESSAGE_H
#define SCRIPTERRORSTATUSMESSAGE_H
#include "ErrorStatusMessage.h"
#include "../resolvers/ScriptAccount.h"
#include "DllMacro.h"
class DLLEXPORT ScriptErrorStatusMessage : public ErrorStatusMessage
{
Q_OBJECT
public:
explicit ScriptErrorStatusMessage( const QString& scriptErrorMessage, Tomahawk::ScriptAccount* );
void activated() override;
private:
QPointer< Tomahawk::ScriptAccount > m_account;
};
#endif // SCRIPTERRORSTATUSMESSAGE_H

View File

@@ -1371,8 +1371,7 @@ Servent::isIPWhitelisted( QHostAddress ip )
}
}
#if QT_VERSION < QT_VERSION_CHECK( 5, 0, 0 )
// Qt4 cannot cope correctly with IPv4 addresses mapped into the IPv6
// Qt cannot cope correctly with IPv4 addresses mapped into the IPv6
// address space
if ( ip.protocol() == QAbstractSocket::IPv6Protocol )
{
@@ -1397,7 +1396,6 @@ Servent::isIPWhitelisted( QHostAddress ip )
return isIPWhitelisted( addr );
}
}
#endif
tDebug( LOGVERBOSE ) << Q_FUNC_INFO << "failure";
return false;

View File

@@ -118,6 +118,10 @@ ColumnView::setModel( QAbstractItemModel* model )
void
ColumnView::setTreeModel( TreeModel* model )
{
// HACK: without setting a new preview widget, the old preview widget is shown in the first column while loading the artists.
m_previewWidget = new ColumnViewPreviewWidget( this );
setPreviewWidget( m_previewWidget );
m_model = model;
if ( m_proxyModel )

View File

@@ -28,6 +28,7 @@
#include "utils/TomahawkUtilsGui.h"
#include "utils/DpiScaler.h"
#include "ViewManager.h"
#include "widgets/DownloadButton.h"
#include <QLabel>
#include <QVBoxLayout>
@@ -164,6 +165,10 @@ ColumnViewPreviewWidget::ColumnViewPreviewWidget( ColumnView* parent )
m_ageValue->setAlignment( Qt::AlignLeft | Qt::AlignVCenter );
gridLayout->addWidget( m_ageValue, 4, 1 );
m_downloadButton = new DownloadButton( this );
mainLayout->addWidget( m_downloadButton );
mainLayout->addStretch();
TomahawkUtils::unmarginLayout( mainLayout );
@@ -217,6 +222,7 @@ ColumnViewPreviewWidget::setQuery( const Tomahawk::query_ptr& query )
m_bitrateValue->setText( tr( "%1 kbps" ).arg( query->results().first()->bitrate() ) );
m_durationValue->setText( TomahawkUtils::timeToString( query->track()->duration() ) );
m_ageValue->setText( TomahawkUtils::ageToString( QDateTime::fromTime_t( query->results().first()->modificationTime() ) ) );
m_downloadButton->setQuery( query );
m_yearValue->setVisible( query->track()->year() > 0 );
m_yearLabel->setVisible( query->track()->year() > 0 );
@@ -226,6 +232,7 @@ ColumnViewPreviewWidget::setQuery( const Tomahawk::query_ptr& query )
m_durationValue->setVisible( query->track()->duration() > 0 );
m_ageLabel->setVisible( query->results().first()->modificationTime() > 0 );
m_ageValue->setVisible( query->results().first()->modificationTime() > 0 );
m_downloadButton->setVisible( true );
}
else
{
@@ -237,6 +244,7 @@ ColumnViewPreviewWidget::setQuery( const Tomahawk::query_ptr& query )
m_durationValue->setVisible( false );
m_ageLabel->setVisible( false );
m_ageValue->setVisible( false );
m_downloadButton->setVisible( false );
}
setMinimumHeight( sizeHint().height() );

View File

@@ -30,6 +30,7 @@ class QueryLabel;
class PlayableCover;
class QLabel;
class ScrollingLabel;
class DownloadButton;
class DLLEXPORT ColumnViewPreviewWidget : public QWidget
{
@@ -71,6 +72,8 @@ private:
ScrollingLabel* m_trackLabel;
QueryLabel* m_artistLabel;
DownloadButton* m_downloadButton;
};
#endif // COLUMNVIEWPREVIEWWIDGET_H

View File

@@ -96,8 +96,10 @@ ContextView::ContextView( QWidget* parent, const QString& caption )
connect( m_captionLabel, SIGNAL( clicked() ), SIGNAL( closeClicked() ) );
connect( m_trackView, SIGNAL( querySelected( Tomahawk::query_ptr ) ), SLOT( onQuerySelected( Tomahawk::query_ptr ) ) );
connect( m_trackView, SIGNAL( querySelected( Tomahawk::query_ptr ) ), m_detailView, SLOT( setQuery( Tomahawk::query_ptr ) ) );
connect( m_trackView, SIGNAL( modelChanged() ), SLOT( onModelChanged() ) );
connect( m_trackView, SIGNAL( querySelected( Tomahawk::query_ptr ) ), m_detailView, SLOT( setQuery( Tomahawk::query_ptr ) ) );
connect( m_detailView, SIGNAL( downloadAll() ), SLOT( onDownloadAll() ) );
connect( m_detailView, SIGNAL( downloadCancel() ), SLOT( onDownloadCancel() ) );
TomahawkUtils::fixMargins( this );
}

View File

@@ -19,12 +19,6 @@
#include "GridItemDelegate.h"
#include <QApplication>
#include <QPainter>
#include <QAbstractItemView>
#include <QMouseEvent>
#include <QTimeLine>
#include "Artist.h"
#include "Query.h"
#include "Result.h"
@@ -35,6 +29,7 @@
#include "playlist/PlayableItem.h"
#include "playlist/PlayableProxyModel.h"
#include "widgets/HoverControls.h"
#include "widgets/DownloadButton.h"
#include "widgets/ImageButton.h"
#include "utils/TomahawkStyle.h"
#include "utils/TomahawkUtilsGui.h"
@@ -44,6 +39,12 @@
#include "utils/DpiScaler.h"
#include "utils/Logger.h"
#include <QApplication>
#include <QPainter>
#include <QAbstractItemView>
#include <QMouseEvent>
#include <QTimeLine>
namespace {
static const int FADE_DURATION = 400;
};
@@ -55,6 +56,7 @@ GridItemDelegate::GridItemDelegate( QAbstractItemView* parent, PlayableProxyMode
, m_model( proxy )
, m_itemWidth( 0 )
, m_showPosition( false )
, m_showBuyButtons( false )
, m_wordWrapping( false )
, m_margin( TomahawkUtils::DpiScaler::scaledY( parent, 32 ) )
{
@@ -93,7 +95,8 @@ GridItemDelegate::sizeHint( const QStyleOptionViewItem& option, const QModelInde
if ( !m_wordWrapping )
return QSize( m_itemWidth, m_itemWidth + fm.height() + m_margin * 0.8 );
return QSize( m_itemWidth, m_itemWidth + fm.height() + fms.height() + m_margin * 0.8 );
const int buyButtonHeight = m_showBuyButtons ? 40 : 0;
return QSize( m_itemWidth, m_itemWidth + fm.height() + fms.height() + buyButtonHeight + m_margin * 0.8 );
}
}
@@ -275,6 +278,11 @@ GridItemDelegate::paint( QPainter* painter, const QStyleOptionViewItem& option,
painter->setFont( f );
}
if ( m_showBuyButtons && !item->query().isNull() )
{
textRect.adjust( 0, 0, 0, -40 );
}
to.setAlignment( Qt::AlignLeft | Qt::AlignTop );
text = painter->fontMetrics().elidedText( top, Qt::ElideRight, textRect.width() - m_margin / 4 );
painter->drawText( textRect, text, to );
@@ -286,6 +294,7 @@ GridItemDelegate::paint( QPainter* painter, const QStyleOptionViewItem& option,
}
painter->restore();
painter->save();
painter->setOpacity( 0.6 );
painter->setFont( m_smallFont );
@@ -304,6 +313,19 @@ GridItemDelegate::paint( QPainter* painter, const QStyleOptionViewItem& option,
// Calculate rect of artist on-hover button click area
m_artistNameRects[ index ] = painter->fontMetrics().boundingRect( textRect, Qt::AlignLeft | Qt::AlignBottom, text );
painter->restore();
if ( m_showBuyButtons && !item->query().isNull() )
{
QRect r = textRect;
r.setY( textRect.y() + textRect.height() + 8 );
r.setHeight( 32 );
if( DownloadButton::drawPrimitive(painter, r, item->query(), m_hoveringOverBuyButton == index ) )
{
m_buyButtonRects[ index ] = r;
}
}
}
painter->restore();
@@ -362,6 +384,7 @@ GridItemDelegate::editorEvent( QEvent* event, QAbstractItemModel* model, const Q
const QMouseEvent* ev = static_cast< QMouseEvent* >( event );
bool hoveringArtist = false;
bool hoveringAlbum = false;
bool hoveringBuyButton = false;
if ( m_artistNameRects.contains( index ) )
{
const QRect artistNameRect = m_artistNameRects[ index ];
@@ -372,6 +395,11 @@ GridItemDelegate::editorEvent( QEvent* event, QAbstractItemModel* model, const Q
const QRect albumNameRect = m_albumNameRects[ index ];
hoveringAlbum = albumNameRect.contains( ev->pos() );
}
if ( m_buyButtonRects.contains( index ) )
{
const QRect buyButtonRect = m_buyButtonRects[ index ];
hoveringBuyButton = buyButtonRect.contains( ev->pos() );
}
QRect coverRect = m_view->visualRect( index );
coverRect.setHeight( coverRect.width() );
@@ -379,7 +407,7 @@ GridItemDelegate::editorEvent( QEvent* event, QAbstractItemModel* model, const Q
if ( event->type() == QEvent::MouseMove )
{
if ( hoveringArtist || hoveringAlbum )
if ( hoveringArtist || hoveringAlbum || hoveringBuyButton )
m_view->setCursor( Qt::PointingHandCursor );
else
m_view->setCursor( Qt::ArrowCursor );
@@ -435,6 +463,17 @@ GridItemDelegate::editorEvent( QEvent* event, QAbstractItemModel* model, const Q
emit updateIndex( index );
}
if ( m_hoveringOverBuyButton != index || ( !hoveringBuyButton && m_hoveringOverBuyButton.isValid() ) )
{
emit updateIndex( m_hoveringOverBuyButton );
if ( hoveringBuyButton )
m_hoveringOverBuyButton = index;
else
m_hoveringOverBuyButton = QPersistentModelIndex();
emit updateIndex( index );
}
if ( m_hoverIndex != index || !hoveringCover )
{
@@ -544,6 +583,11 @@ GridItemDelegate::editorEvent( QEvent* event, QAbstractItemModel* model, const Q
}
}
if ( hoveringBuyButton )
{
return DownloadButton::handleEditorEvent( event, m_view, m_model, index );
}
return false;
}
@@ -555,6 +599,7 @@ GridItemDelegate::modelChanged()
m_albumNameRects.clear();
m_hoveringOverArtist = QPersistentModelIndex();
m_hoveringOverAlbum = QPersistentModelIndex();
m_hoveringOverBuyButton = QPersistentModelIndex();
m_hoverIndex = QPersistentModelIndex();
clearButtons();
@@ -687,6 +732,10 @@ GridItemDelegate::resetHoverIndex()
idx = m_hoveringOverAlbum;
m_hoveringOverAlbum = QPersistentModelIndex();
doUpdateIndex( idx );
idx = m_hoveringOverBuyButton;
m_hoveringOverBuyButton = QPersistentModelIndex();
doUpdateIndex( idx );
}
@@ -758,3 +807,41 @@ GridItemDelegate::eventFilter( QObject* obj, QEvent* event )
else
return QObject::eventFilter( obj, event );
}
QWidget*
GridItemDelegate::createEditor( QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index ) const
{
PlayableItem* item = m_model->itemFromIndex( m_model->mapToSource( index ) );
Q_ASSERT( item );
return DownloadButton::handleCreateEditor( parent, item->query(), m_view, index );
}
void
GridItemDelegate::updateEditorGeometry( QWidget* editor, const QStyleOptionViewItem& option, const QModelIndex& index ) const
{
QStyledItemDelegate::updateEditorGeometry( editor, option, index );
DownloadButton* comboBox = static_cast<DownloadButton*>(editor);
comboBox->resize( option.rect.size() - QSize( 8, 0 ) );
comboBox->move( option.rect.x() + 4, option.rect.y() );
if ( m_buyButtonRects.contains( index ) )
{
editor->setGeometry( m_buyButtonRects.value( index ) );
}
if ( !comboBox->property( "shownPopup" ).toBool() )
{
comboBox->showPopup();
comboBox->setProperty( "shownPopup", true );
}
}
void
GridItemDelegate::setModelData( QWidget* editor, QAbstractItemModel* model, const QModelIndex& index ) const
{
}

View File

@@ -48,6 +48,7 @@ public:
QSize itemSize() const;
void setItemWidth( int width ) { m_itemWidth = width; }
void setShowBuyButtons( bool enabled ) { m_showBuyButtons = enabled; }
public slots:
void resetHoverIndex();
@@ -61,6 +62,10 @@ protected:
bool editorEvent( QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option, const QModelIndex& index );
bool eventFilter( QObject* obj, QEvent* event );
QWidget* createEditor( QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index ) const;
void updateEditorGeometry( QWidget* editor, const QStyleOptionViewItem& option, const QModelIndex& index ) const;
void setModelData( QWidget* editor, QAbstractItemModel* model, const QModelIndex& index ) const;
signals:
void updateIndex( const QModelIndex& idx );
@@ -90,15 +95,18 @@ private:
PlayableProxyModel* m_model;
int m_itemWidth;
bool m_showPosition;
bool m_showBuyButtons;
bool m_wordWrapping;
mutable QHash< QPersistentModelIndex, QRect > m_artistNameRects;
mutable QHash< QPersistentModelIndex, QRect > m_albumNameRects;
mutable QHash< QPersistentModelIndex, QRect > m_buyButtonRects;
mutable QHash< QPersistentModelIndex, QSharedPointer< Tomahawk::PixmapDelegateFader > > m_covers;
QPersistentModelIndex m_hoverIndex;
QPersistentModelIndex m_hoveringOverArtist;
QPersistentModelIndex m_hoveringOverAlbum;
QPersistentModelIndex m_hoveringOverBuyButton;
mutable QHash< QPersistentModelIndex, QWidget* > m_spinner;
mutable QHash< QPersistentModelIndex, HoverControls* > m_hoverControls;

View File

@@ -255,6 +255,8 @@ void GridView::wheelEvent( QWheelEvent* e )
// ^ scroll step is 1/8 of the estimated row height
QListView::wheelEvent( e );
m_delegate->resetHoverIndex();
}

View File

@@ -119,8 +119,9 @@ int
PlayableModel::columnCount( const QModelIndex& parent ) const
{
Q_UNUSED( parent );
Q_D( const PlayableModel );
return 13;
return d->header.length();
}
@@ -444,14 +445,21 @@ PlayableModel::flags( const QModelIndex& index ) const
if ( index.isValid() )
{
Qt::ItemFlags returnFlags = defaultFlags;
if ( index.column() == 0 )
{
return Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled | defaultFlags;
returnFlags |= Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled;
}
else if ( index.column() == PlayableModel::Download )
{
return Qt::ItemIsEditable | defaultFlags;
returnFlags |= Qt::ItemIsEditable | defaultFlags;
}
if ( areAllColumnsEditable() ) {
returnFlags |= Qt::ItemIsEditable;
}
return returnFlags;
}
return Qt::ItemIsDropEnabled | defaultFlags;
@@ -1019,7 +1027,7 @@ PlayableModel::appendAlbums( const QList< Tomahawk::album_ptr >& albums )
void
PlayableModel::appendAlbums( const Tomahawk::collection_ptr& collection )
{
emit loadingStarted();
startLoading();
insertAlbums( collection, rowCount( QModelIndex() ) );
}
@@ -1034,7 +1042,7 @@ PlayableModel::appendQueries( const QList< Tomahawk::query_ptr >& queries )
void
PlayableModel::appendTracks( const QList< Tomahawk::track_ptr >& tracks, const QList< Tomahawk::PlaybackLog >& logs )
{
emit loadingStarted();
startLoading();
QList< Tomahawk::query_ptr > queries;
foreach ( const track_ptr& track, tracks )
{
@@ -1048,7 +1056,7 @@ PlayableModel::appendTracks( const QList< Tomahawk::track_ptr >& tracks, const Q
void
PlayableModel::appendTracks( const Tomahawk::collection_ptr& collection )
{
emit loadingStarted();
startLoading();
insertTracks( collection, rowCount( QModelIndex() ) );
}
@@ -1171,6 +1179,22 @@ PlayableModel::setIcon( const QPixmap& pixmap )
}
void
PlayableModel::setAllColumnsEditable( bool editable )
{
Q_D( PlayableModel );
d->areAllColumnsEditable = editable;
}
bool
PlayableModel::areAllColumnsEditable() const
{
Q_D( const PlayableModel );
return d->areAllColumnsEditable;
}
int
PlayableModel::trackCount() const
{

View File

@@ -96,6 +96,11 @@ public:
virtual QPixmap icon() const;
virtual void setIcon( const QPixmap& pixmap );
// HACK: we need to set column 0 editable for DropDownButton in TrackView
void setAllColumnsEditable( bool editable );
bool areAllColumnsEditable() const;
virtual int trackCount() const;
virtual int itemCount() const;

View File

@@ -38,6 +38,7 @@ public:
, rootItem( new PlayableItem( 0 ) )
, readOnly( true )
, loading( _loading )
, areAllColumnsEditable( false )
{
}
@@ -58,6 +59,7 @@ private:
QStringList header;
bool loading;
bool areAllColumnsEditable;
};
#endif // PLAYABLEMODEL_P_H

View File

@@ -607,25 +607,7 @@ PlayableProxyModel::columnCount( const QModelIndex& parent ) const
{
Q_UNUSED( parent );
switch ( m_style )
{
case SingleColumn:
return 1;
break;
case Collection:
return 10;
break;
case Locker:
return 11;
break;
case Detailed:
default:
return 12;
break;
}
return m_headerStyle[ m_style ].length();
}
@@ -733,6 +715,13 @@ PlayableProxyModel::setFilter( const QString& pattern )
}
int
PlayableProxyModel::mapSourceColumnToColumn( PlayableModel::Columns column )
{
return m_headerStyle[ m_style ].indexOf( column );
}
void
PlayableProxyModel::setCurrentIndex( const QModelIndex& index )
{

View File

@@ -98,6 +98,8 @@ public:
virtual void setFilter( const QString& pattern );
virtual void updateDetailedInfo( const QModelIndex& index );
int mapSourceColumnToColumn( PlayableModel::Columns column );
signals:
void filterChanged( const QString& filter );

View File

@@ -20,14 +20,6 @@
#include "PlaylistItemDelegate.h"
#include <QAbstractTextDocumentLayout>
#include <QApplication>
#include <QDateTime>
#include <QMouseEvent>
#include <QPainter>
#include <QDesktopServices>
#include <QToolTip>
#include "Query.h"
#include "Result.h"
#include "Artist.h"
@@ -35,8 +27,6 @@
#include "Source.h"
#include "SourceList.h"
#include "DownloadManager.h"
#include "DownloadJob.h"
#include "PlayableModel.h"
#include "PlayableItem.h"
#include "PlayableProxyModel.h"
@@ -44,7 +34,7 @@
#include "ViewHeader.h"
#include "ViewManager.h"
#include "widgets/DropDownButton.h"
#include "widgets/DownloadButton.h"
#include "audio/AudioEngine.h"
#include "utils/ImageRegistry.h"
#include "utils/PixmapDelegateFader.h"
@@ -53,6 +43,13 @@
#include "utils/TomahawkUtilsGui.h"
#include "utils/Logger.h"
#include <QAbstractTextDocumentLayout>
#include <QApplication>
#include <QDateTime>
#include <QMouseEvent>
#include <QPainter>
#include <QToolTip>
using namespace Tomahawk;
@@ -123,63 +120,7 @@ PlaylistItemDelegate::createEditor( QWidget* parent, const QStyleOptionViewItem&
PlayableItem* item = m_model->itemFromIndex( m_model->mapToSource( index ) );
Q_ASSERT( item );
if ( index.column() == PlayableModel::Download && item->result() && !item->result()->downloadFormats().isEmpty() &&
!DownloadManager::instance()->localFileForDownload( item->result()->downloadFormats().first().url.toString() ).isEmpty() )
{
QDesktopServices::openUrl( QUrl::fromLocalFile( QFileInfo( DownloadManager::instance()->localFileForDownload( item->result()->downloadFormats().first().url.toString() ) ).absolutePath() ) );
}
else if ( index.column() == PlayableModel::Download && item->result() && item->result()->downloadJob() && item->result()->downloadJob()->state() == DownloadJob::Finished )
{
QDesktopServices::openUrl( QUrl::fromLocalFile( QFileInfo( item->result()->downloadJob()->localFile() ).absolutePath() ) );
}
else if ( index.column() == PlayableModel::Download && item->result() &&
!item->result()->downloadFormats().isEmpty() && !item->result()->downloadJob() )
{
QStringList formats;
foreach ( const DownloadFormat& format, item->result()->downloadFormats() )
{
formats << tr( "Download %1" ).arg( format.extension );
}
DropDownButton* editor = new DropDownButton( parent );
editor->addItems( formats );
NewClosure( editor, SIGNAL( clicked() ),
const_cast<PlaylistItemDelegate*>(this), SLOT( addDownloadJob( const QModelIndex&, QWidget* ) ), index, (QWidget*)editor );
NewClosure( editor, SIGNAL( activated( int ) ),
const_cast<PlaylistItemDelegate*>(this), SLOT( addDownloadJob( const QModelIndex&, QWidget* ) ), index, (QWidget*)editor );
return editor;
}
return 0;
}
void
PlaylistItemDelegate::addDownloadJob( const QModelIndex& index, QWidget* editor )
{
PlayableItem* item = m_model->itemFromIndex( m_model->mapToSource( index ) );
Q_ASSERT( item );
m_view->closePersistentEditor( index );
DropDownButton* cb = static_cast< DropDownButton* >(editor);
if ( !item->result()->downloadFormats().isEmpty() )
DownloadManager::instance()->addJob( item->result()->toDownloadJob( item->result()->downloadFormats().at( cb->currentIndex() ) ) );
}
void
PlaylistItemDelegate::closeEditor( const QModelIndex& index, QWidget* editor )
{
PlayableItem* item = m_model->itemFromIndex( m_model->mapToSource( index ) );
Q_ASSERT( item );
m_view->closePersistentEditor( index );
DropDownButton* cb = static_cast< DropDownButton* >(editor);
editor->deleteLater();
return DownloadButton::handleCreateEditor( parent, item->query(), m_view, index );
}
@@ -188,13 +129,18 @@ PlaylistItemDelegate::updateEditorGeometry( QWidget* editor, const QStyleOptionV
{
QStyledItemDelegate::updateEditorGeometry( editor, option, index );
DropDownButton* comboBox = static_cast<DropDownButton*>(editor);
DownloadButton* comboBox = static_cast<DownloadButton*>(editor);
comboBox->resize( option.rect.size() - QSize( 8, 0 ) );
comboBox->move( option.rect.x() + 4, option.rect.y() );
if ( m_downloadDropDownRects.contains( index ) )
{
editor->setGeometry( m_downloadDropDownRects.value( index ) );
}
if ( !comboBox->property( "shownPopup" ).toBool() )
{
// comboBox->showPopup();
comboBox->showPopup();
comboBox->setProperty( "shownPopup", true );
}
}
@@ -270,68 +216,7 @@ PlaylistItemDelegate::paintDetailed( QPainter* painter, const QStyleOptionViewIt
else */
if ( m_view->proxyModel()->style() == PlayableProxyModel::Locker && index.column() == PlayableModel::Download )
{
if ( item->result() && !item->result()->downloadFormats().isEmpty() )
{
QStyleOptionComboBox optc;
optc.rect = opt.rect.adjusted( 4, 0, -4, 0 );
optc.editable = false;
optc.currentText = tr( "Download %1" ).arg( item->result()->downloadFormats().first().extension );
optc.palette = m_view->palette();
if ( option.state & QStyle::State_Selected && option.state & QStyle::State_Active )
optc.state = QStyle::State_Active | QStyle::State_Selected | QStyle::State_Enabled;
else
optc.state = QStyle::State_Active | QStyle::State_Enabled;
if ( !DownloadManager::instance()->localFileForDownload( item->result()->downloadFormats().first().url.toString() ).isEmpty() )
{
painter->setPen( opt.palette.text().color() );
const QString text = painter->fontMetrics().elidedText( tr( "View in Finder" ), Qt::ElideRight, opt.rect.width() - 3 );
painter->drawText( opt.rect, text, textOption );
}
else if ( !item->result()->downloadJob() )
{
DropDownButton::drawPrimitive( painter, optc.rect, optc.currentText );
/* QApplication::style()->drawComplexControl( QStyle::CC_ComboBox, &optc, painter, 0 );
optc.rect.adjust( 4, 0, 0, 0 );
QApplication::style()->drawControl( QStyle::CE_ComboBoxLabel, &optc, painter, 0 );*/
}
else
{
if ( item->result()->downloadJob()->state() == DownloadJob::Finished )
{
painter->setPen( opt.palette.text().color() );
const QString text = painter->fontMetrics().elidedText( tr( "View in Finder" ), Qt::ElideRight, opt.rect.width() - 3 );
painter->drawText( opt.rect, text, textOption );
}
else
{
painter->setPen( TomahawkStyle::PLAYLIST_PROGRESS_FOREGROUND.darker() );
painter->setBrush( TomahawkStyle::PLAYLIST_PROGRESS_BACKGROUND );
painter->drawRect( optc.rect.adjusted( 2, 2, -2, -2 ) );
painter->setPen( TomahawkStyle::PLAYLIST_PROGRESS_FOREGROUND );
painter->setBrush( TomahawkStyle::PLAYLIST_PROGRESS_FOREGROUND );
QRect fillp = optc.rect.adjusted( 3, 3, -3, -3 );
fillp.setWidth( float(fillp.width()) * ( float(item->result()->downloadJob()->progressPercentage()) / 100.0 ) );
painter->drawRect( fillp );
/* QStyleOptionProgressBarV2 optp;
optp.rect = optc.rect;
optp.minimum = 0;
optp.maximum = 100;
optp.progress = item->result()->downloadJob()->progressPercentage();
optp.palette = m_view->palette();
optp.palette.setColor( QPalette::Highlight, QColor( "#E61878" ) );
if ( option.state & QStyle::State_Selected && option.state & QStyle::State_Active )
optp.state = QStyle::State_Active | QStyle::State_Selected | QStyle::State_Enabled;
else
optp.state = QStyle::State_Active | QStyle::State_Enabled;
QApplication::style()->drawControl( QStyle::CE_ProgressBar, &optp, painter, 0 );*/
}
}
}
DownloadButton::drawPrimitive( painter, opt.rect.adjusted( 4, 0, -4, 0 ), item->query(), hoveringOver() == index );
}
else if ( item->isPlaying() )
{
@@ -604,6 +489,7 @@ QRect
PlaylistItemDelegate::drawTrack( QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index, const QRect& rect, PlayableItem* item ) const
{
const track_ptr track = item->query()->track();
const bool hasOnlineResults = ( item->query()->numResults( true ) > 0 );
painter->save();
painter->setRenderHint( QPainter::TextAntialiasing );
@@ -630,21 +516,34 @@ PlaylistItemDelegate::drawTrack( QPainter* painter, const QStyleOptionViewItem&
QRect numberRect = QRect( r.x(), r.y(), numberWidth, r.height() );
QRect extraRect = QRect( r.x() + r.width() - durationWidth, r.y(), durationWidth, r.height() );
QRect stateRect;
if ( option.state & QStyle::State_Selected || hoveringOver() == index )
QRect stateRect = extraRect.adjusted( 0, 0, 0, 0 );
if ( option.state & QStyle::State_Selected || hoveringOver() == index )
{
int h = extraRect.height() / 3;
if ( track->loved() )
{
painter->save();
painter->setOpacity( 0.5 );
int h = extraRect.height() / 3;
stateRect = extraRect.adjusted( -16, extraRect.height() / 2 - h / 2, 0, 0 );
stateRect.setHeight( h );
stateRect.setWidth( stateRect.height() );
painter->drawPixmap( stateRect, ImageRegistry::instance()->pixmap( RESPATH "images/love.svg", stateRect.size() ) );
QRect r = stateRect.adjusted( -16, extraRect.height() / 2 - h / 2, 0, 0 );
r.setHeight( h );
r.setWidth( r.height() );
painter->drawPixmap( r, ImageRegistry::instance()->pixmap( RESPATH "images/love.svg", r.size() ) );
painter->restore();
stateWidth = stateRect.width() + 16;
stateWidth += r.width() + 16;
}
}
QRect downloadButtonRect = stateRect.adjusted( -stateWidth -144, 6, 0, -6 );
downloadButtonRect.setWidth( 144 );
stateWidth += downloadButtonRect.width() + 16;
if ( DownloadButton::drawPrimitive( painter, downloadButtonRect, item->query(), m_hoveringOverDownloadButton == index ) )
{
m_downloadDropDownRects[ index ] = downloadButtonRect;
}
const int remWidth = r.width() - numberWidth - durationWidth;
QRect titleRect = QRect( numberRect.x() + numberRect.width(), r.y(), (double)remWidth * 0.5, r.height() );
@@ -708,6 +607,7 @@ PlaylistItemDelegate::drawTrack( QPainter* painter, const QStyleOptionViewItem&
int h = extraRect.height() / 2;
QRect playIconRect = extraRect.adjusted( extraRect.width() - h - 8, h / 2, -8, -h / 2 );
playIconRect.setWidth( playIconRect.height() );
painter->drawPixmap( playIconRect, ImageRegistry::instance()->pixmap( RESPATH "images/play.svg", playIconRect.size() ) );
double duration = (double)AudioEngine::instance()->currentTrackTotalTime();
@@ -759,8 +659,10 @@ PlaylistItemDelegate::editorEvent( QEvent* event, QAbstractItemModel* model, con
bool hoveringArtist = false;
bool hoveringInfo = false;
bool hoveringLove = false;
bool hoveringDownloadDropDown = false;
Tomahawk::source_ptr hoveredAvatar;
QRect hoveredAvatarRect;
if ( m_infoButtonRects.contains( index ) )
{
const QRect infoRect = m_infoButtonRects[ index ];
@@ -779,6 +681,12 @@ PlaylistItemDelegate::editorEvent( QEvent* event, QAbstractItemModel* model, con
const QMouseEvent* ev = static_cast< QMouseEvent* >( event );
hoveringLove = loveRect.contains( ev->pos() );
}
if ( m_downloadDropDownRects.contains( index ) )
{
const QRect downloadDropDownRect = m_downloadDropDownRects[ index ];
const QMouseEvent* ev = static_cast< QMouseEvent* >( event );
hoveringDownloadDropDown = downloadDropDownRect.contains( ev->pos() );
}
if ( m_avatarBoxRects.contains( index ) )
{
const QMouseEvent* ev = static_cast< QMouseEvent* >( event );
@@ -796,7 +704,7 @@ PlaylistItemDelegate::editorEvent( QEvent* event, QAbstractItemModel* model, con
if ( event->type() == QEvent::MouseMove )
{
if ( hoveringInfo || hoveringLove || hoveringArtist )
if ( hoveringInfo || hoveringLove || hoveringArtist || hoveringDownloadDropDown )
m_view->setCursor( Qt::PointingHandCursor );
else
m_view->setCursor( Qt::ArrowCursor );
@@ -820,6 +728,23 @@ PlaylistItemDelegate::editorEvent( QEvent* event, QAbstractItemModel* model, con
emit updateIndex( m_hoveringOverArtist );
m_hoveringOverArtist = QModelIndex();
}
if ( hoveringDownloadDropDown && m_hoveringOverDownloadButton != index )
{
QPersistentModelIndex ti = m_hoveringOverDownloadButton;
m_hoveringOverDownloadButton = index;
PlayableItem* item = m_model->sourceModel()->itemFromIndex( m_model->mapToSource( ti ) );
item->requestRepaint();
emit updateIndex( m_hoveringOverDownloadButton );
}
if ( !hoveringDownloadDropDown && m_hoveringOverDownloadButton.isValid() )
{
QPersistentModelIndex ti = m_hoveringOverDownloadButton;
m_hoveringOverDownloadButton = QModelIndex();
PlayableItem* item = m_model->sourceModel()->itemFromIndex( m_model->mapToSource( ti ) );
item->requestRepaint();
}
if ( m_hoveringOver != index )
{
@@ -849,6 +774,11 @@ PlaylistItemDelegate::editorEvent( QEvent* event, QAbstractItemModel* model, con
{
item->query()->queryTrack()->setLoved( !item->query()->queryTrack()->loved() );
}
else if ( hoveringDownloadDropDown || ( m_view->proxyModel()->style() == PlayableProxyModel::Locker && index.column() == PlayableModel::Download ) )
{
if ( DownloadButton::handleEditorEvent( event , m_view, m_model, index ) )
return true;
}
else if ( hoveringInfo )
{
if ( m_model->style() == PlayableProxyModel::SingleColumn )
@@ -883,11 +813,6 @@ PlaylistItemDelegate::editorEvent( QEvent* event, QAbstractItemModel* model, con
}
}
}
else if ( m_view->proxyModel()->style() == PlayableProxyModel::Locker && index.column() == PlayableModel::Download )
{
m_view->edit( index );
return true;
}
event->accept();
return true;
@@ -900,13 +825,14 @@ PlaylistItemDelegate::editorEvent( QEvent* event, QAbstractItemModel* model, con
void
PlaylistItemDelegate::resetHoverIndex()
{
if ( !m_model )
if ( !m_model || !m_hoveringOver.isValid() )
return;
QPersistentModelIndex idx = m_hoveringOver;
m_hoveringOver = QModelIndex();
m_hoveringOverArtist = QModelIndex();
m_hoveringOverDownloadButton = QModelIndex();
m_infoButtonRects.clear();
m_loveButtonRects.clear();
m_artistNameRects.clear();
@@ -920,7 +846,6 @@ PlaylistItemDelegate::resetHoverIndex()
}
emit updateIndex( idx );
m_view->repaint();
}

View File

@@ -57,8 +57,6 @@ signals:
private slots:
void doUpdateIndex( const QPersistentModelIndex& index );
void closeEditor( const QModelIndex& index, QWidget* editor );
void addDownloadJob( const QModelIndex& index, QWidget* editor );
protected:
void prepareStyleOption( QStyleOptionViewItemV4* option, const QModelIndex& index, PlayableItem* item ) const;
@@ -116,10 +114,12 @@ private:
mutable QHash< QPersistentModelIndex, QSharedPointer< Tomahawk::PixmapDelegateFader > > m_pixmaps;
mutable QHash< QPersistentModelIndex, QRect > m_infoButtonRects;
mutable QHash< QPersistentModelIndex, QRect > m_loveButtonRects;
mutable QHash< QPersistentModelIndex, QRect > m_downloadDropDownRects;
mutable QHash< QPersistentModelIndex, QRect > m_artistNameRects;
mutable QHash< QPersistentModelIndex, QHash< Tomahawk::source_ptr, QRect > > m_avatarBoxRects;
QPersistentModelIndex m_hoveringOver;
QPersistentModelIndex m_hoveringOverArtist;
QPersistentModelIndex m_hoveringOverDownloadButton;
mutable QPersistentModelIndex m_nowPlaying;
TrackView* m_view;

View File

@@ -18,11 +18,6 @@
#include "TrackDetailView.h"
#include <QLabel>
#include <QScrollArea>
#include <QSizePolicy>
#include <QVBoxLayout>
#include "Album.h"
#include "Track.h"
#include "audio/AudioEngine.h"
@@ -35,13 +30,22 @@
#include "utils/ImageRegistry.h"
#include "utils/TomahawkUtilsGui.h"
#include "utils/Closure.h"
#include "utils/WebPopup.h"
#include "utils/Logger.h"
#include <QLabel>
#include <QPushButton>
#include <QScrollArea>
#include <QSizePolicy>
#include <QVBoxLayout>
#include <QDesktopServices>
using namespace Tomahawk;
TrackDetailView::TrackDetailView( QWidget* parent )
: QWidget( parent )
, DpiScaler( this )
, m_buyButtonVisible( false )
{
setFixedWidth( scaledX( 200 ) );
setContentsMargins( 0, 0, 0, 0 );
@@ -118,11 +122,21 @@ TrackDetailView::TrackDetailView( QWidget* parent )
TomahawkStyle::styleScrollBar( m_resultsScrollArea->verticalScrollBar() );
m_resultsScrollArea->hide();
m_buyButton = new QPushButton;
m_buyButton->setStyleSheet( "QPushButton:hover { font-size: 12px; color: #ffffff; background: #000000; border-style: solid; border-radius: 0px; border-width: 2px; border-color: #2b2b2b; }"
"QPushButton { font-size: 12px; color: #ffffff; background-color: #000000; border-style: solid; border-radius: 0px; border-width: 0px; }" );
m_buyButton->setMinimumHeight( 30 );
m_buyButton->setText( tr( "Buy Album" ) );
m_buyButton->setVisible( false );
connect( m_buyButton, SIGNAL( clicked() ), SLOT( onBuyButtonClicked() ) );
QVBoxLayout* layout = new QVBoxLayout;
TomahawkUtils::unmarginLayout( layout );
layout->addWidget( m_playableCover );
layout->addSpacerItem( new QSpacerItem( 0, 8, QSizePolicy::Minimum, QSizePolicy::Fixed ) );
layout->addWidget( m_nameLabel );
layout->addSpacerItem( new QSpacerItem( 0, 4, QSizePolicy::Minimum, QSizePolicy::Fixed ) );
layout->addWidget( m_buyButton );
layout->addWidget( m_dateLabel );
layout->addWidget( m_infoBox );
layout->addSpacerItem( new QSpacerItem( 0, 32, QSizePolicy::Minimum, QSizePolicy::Fixed ) );
@@ -134,6 +148,9 @@ TrackDetailView::TrackDetailView( QWidget* parent )
setLayout( layout );
setQuery( query_ptr() );
connect( DownloadManager::instance(), SIGNAL( stateChanged( DownloadManager::DownloadManagerState, DownloadManager::DownloadManagerState ) ),
SLOT( onDownloadManagerStateChanged( DownloadManager::DownloadManagerState, DownloadManager::DownloadManagerState ) ) );
}
@@ -154,6 +171,10 @@ TrackDetailView::setQuery( const Tomahawk::query_ptr& query )
{
if ( m_query )
{
if ( m_query->track()->albumPtr() && !m_query->track()->albumPtr()->name().isEmpty() )
{
disconnect( m_query->track()->albumPtr().data(), SIGNAL( updated() ), this, SLOT( onAlbumUpdated() ) );
}
disconnect( m_query->track().data(), SIGNAL( updated() ), this, SLOT( onCoverUpdated() ) );
disconnect( m_query->track().data(), SIGNAL( socialActionsLoaded() ), this, SLOT( onSocialActionsLoaded() ) );
disconnect( m_query.data(), SIGNAL( resultsChanged() ), this, SLOT( onResultsChanged() ) );
@@ -164,6 +185,7 @@ TrackDetailView::setQuery( const Tomahawk::query_ptr& query )
onResultsChanged();
setSocialActions();
onCoverUpdated();
onAlbumUpdated();
if ( !query )
{
@@ -174,20 +196,128 @@ TrackDetailView::setQuery( const Tomahawk::query_ptr& query )
m_dateLabel->setText( tr( "Unknown Release-Date" ) );
connect( m_query->track().data(), SIGNAL( updated() ), SLOT( onCoverUpdated() ) );
connect( m_query->track().data(), SIGNAL( socialActionsLoaded() ), SLOT( onSocialActionsLoaded() ) );
connect( m_query.data(), SIGNAL( resultsChanged() ), SLOT( onResultsChanged() ) );
connect( m_query.data(), SIGNAL( resultsChanged() ), SLOT( onAlbumUpdated() ) );
}
void
TrackDetailView::onAlbumUpdated()
{
if ( !m_query )
return;
if ( m_query->track()->albumPtr() && !m_query->track()->albumPtr()->name().isEmpty() )
{
m_nameLabel->setType( QueryLabel::Album );
m_nameLabel->setAlbum( m_query->track()->albumPtr() );
connect( m_query->track()->albumPtr().data(), SIGNAL( updated() ), SLOT( onAlbumUpdated() ), Qt::UniqueConnection );
if ( m_buyButtonVisible )
{
if ( m_query->track()->albumPtr()->purchased() )
{
m_allTracksAvailableLocally = true;
foreach( const query_ptr& currentQuery, m_playlistInterface->tracks() )
{
if ( currentQuery->results().isEmpty() )
{
m_allTracksAvailableLocally = false;
break;
}
else
{
m_allTracksAvailableLocally = false;
foreach ( const result_ptr& currentResult, currentQuery->results() )
{
QList< DownloadFormat > formats = currentResult->downloadFormats();
bool isDownloaded = formats.isEmpty() ? false : !DownloadManager::instance()->localFileForDownload( currentResult->downloadFormats().first().url.toString() ).isEmpty();
if ( currentResult->isLocal() || isDownloaded )
{
m_allTracksAvailableLocally = true;
break;
}
}
if ( !m_allTracksAvailableLocally )
{
break;
}
}
}
if ( m_allTracksAvailableLocally )
{
m_buyButton->setText( tr( "View in Folder" ) );
m_buyButton->setVisible( true );
}
else
{
m_buyButton->setText( tr( "Download Album" ) );
m_buyButton->setVisible( true );
}
}
else
{
m_buyButton->setText( tr( "Buy Album" ) );
m_buyButton->setVisible( !m_query->track()->albumPtr()->purchaseUrl().isEmpty() );
}
}
}
else
{
m_nameLabel->setType( QueryLabel::Artist );
m_nameLabel->setArtist( m_query->track()->artistPtr() );
m_buyButton->setVisible( false );
}
}
void
TrackDetailView::onBuyButtonClicked()
{
if ( DownloadManager::instance()->state() == DownloadManager::Running )
{
emit downloadCancel();
return;
}
connect( m_query->track().data(), SIGNAL( updated() ), SLOT( onCoverUpdated() ) );
connect( m_query->track().data(), SIGNAL( socialActionsLoaded() ), SLOT( onSocialActionsLoaded() ) );
connect( m_query.data(), SIGNAL( resultsChanged() ), SLOT( onResultsChanged() ) );
if ( m_query && m_query->track()->albumPtr() )
{
if ( m_query->track()->albumPtr()->purchased() )
{
if ( m_allTracksAvailableLocally )
{
QDesktopServices::openUrl( QUrl::fromLocalFile( DownloadJob::localPath( m_query->track()->albumPtr() ) ) );
}
else
{
emit downloadAll();
}
}
else
{
WebPopup* popup = new WebPopup( m_query->track()->albumPtr()->purchaseUrl(), QSize( 400, 800 ) );
connect( m_query->track()->albumPtr().data(), SIGNAL( destroyed() ), popup, SLOT( close() ) );
}
}
}
void
TrackDetailView::onDownloadManagerStateChanged( DownloadManager::DownloadManagerState newState, DownloadManager::DownloadManagerState oldState )
{
tDebug() << Q_FUNC_INFO;
if ( newState == DownloadManager::Running )
{
m_buyButton->setText( tr( "Cancel Download" ) );
}
else
{
onAlbumUpdated();
}
}
@@ -304,3 +434,10 @@ TrackDetailView::onResultsChanged()
m_resultsScrollArea->hide();
}
}
void
TrackDetailView::setBuyButtonVisible( bool visible )
{
m_buyButtonVisible = visible;
}

View File

@@ -23,6 +23,7 @@
#include "Query.h"
#include "utils/DpiScaler.h"
#include "DownloadManager.h"
#include "DllMacro.h"
class QLabel;
@@ -30,6 +31,7 @@ class CaptionLabel;
class PlayableCover;
class QueryLabel;
class QScrollArea;
class QPushButton;
class DLLEXPORT TrackDetailView : public QWidget, private TomahawkUtils::DpiScaler
{
@@ -39,21 +41,29 @@ public:
explicit TrackDetailView( QWidget* parent = 0 );
~TrackDetailView();
void setBuyButtonVisible( bool visible );
public slots:
virtual void setQuery( const Tomahawk::query_ptr& query );
void setPlaylistInterface( const Tomahawk::playlistinterface_ptr& playlistInterface );
signals:
void downloadAll();
void downloadCancel();
protected:
protected slots:
private slots:
void onAlbumUpdated();
void onCoverUpdated();
void onSocialActionsLoaded();
void onResultsChanged();
void onBuyButtonClicked();
void onDownloadManagerStateChanged( DownloadManager::DownloadManagerState newState, DownloadManager::DownloadManagerState oldState );
private:
void setSocialActions();
@@ -65,6 +75,9 @@ private:
QLabel* m_lovedIcon;
QLabel* m_lovedLabel;
CaptionLabel* m_resultsBoxLabel;
QPushButton* m_buyButton;
bool m_buyButtonVisible;
bool m_allTracksAvailableLocally;
QWidget* m_infoBox;
QWidget* m_resultsBox;

View File

@@ -23,7 +23,9 @@
#include "PlayableModel.h"
#include "PlayableProxyModel.h"
#include "PlayableItem.h"
#include "DownloadManager.h"
#include "DropJob.h"
#include "Result.h"
#include "Source.h"
#include "TomahawkSettings.h"
#include "audio/AudioEngine.h"
@@ -45,6 +47,9 @@
#include <QScrollBar>
#include <QDrag>
// HACK
#include <QTableView>
#define SCROLL_TIMEOUT 280
using namespace Tomahawk;
@@ -83,6 +88,13 @@ TrackView::TrackView( QWidget* parent )
setEditTriggers( NoEditTriggers );
setHeader( m_header );
// HACK: enable moving of first column: QTBUG-33974 / https://github.com/qtproject/qtbase/commit/e0fc088c0c8bc61dbcaf5928b24986cd61a22777
QTableView unused;
unused.setVerticalHeader( header() );
header()->setParent( this );
unused.setVerticalHeader( new QHeaderView( Qt::Horizontal, &unused ) );
setSortingEnabled( true );
sortByColumn( -1 );
setContextMenuPolicy( Qt::CustomContextMenu );
@@ -677,7 +689,6 @@ TrackView::wheelEvent( QWheelEvent* event )
QTreeView::wheelEvent( event );
m_delegate->resetHoverIndex();
repaint();
}
@@ -764,6 +775,44 @@ TrackView::onCustomContextMenu( const QPoint& pos )
m_contextMenu->setSupportedActions( m_contextMenu->supportedActions() | ContextMenu::ActionMarkListened
| ContextMenu::ActionDelete );
if ( proxyModel()->style() != PlayableProxyModel::Collection )
{
bool allDownloaded = true;
bool noneDownloadable = true;
bool downloadable = false;
foreach ( const QModelIndex& index, selectedIndexes() )
{
if ( index.column() )
continue;
PlayableItem* item = proxyModel()->itemFromIndex( proxyModel()->mapToSource( index ) );
if( item->query()->results().isEmpty() )
continue;
downloadable = !item->query()->results().first()->downloadFormats().isEmpty();
if ( downloadable )
{
noneDownloadable = false;
}
if ( downloadable && DownloadManager::instance()->localFileForDownload( item->query()->results().first()->downloadFormats().first().url.toString() ).isEmpty() )
{
allDownloaded = false;
}
if ( !allDownloaded || !noneDownloadable )
{
break;
}
}
if ( !allDownloaded || !noneDownloadable )
{
m_contextMenu->setSupportedActions( m_contextMenu->supportedActions() | ContextMenu::ActionDownload );
}
}
QList<query_ptr> queries;
foreach ( const QModelIndex& index, selectedIndexes() )
{
@@ -795,6 +844,10 @@ TrackView::onMenuTriggered( int action )
deleteSelectedItems();
break;
case ContextMenu::ActionDownload:
downloadSelectedItems();
break;
default:
break;
}
@@ -870,6 +923,30 @@ TrackView::deleteSelectedItems()
}
void
TrackView::downloadSelectedItems()
{
foreach ( const QModelIndex& index, selectedIndexes() )
{
if ( index.column() )
continue;
PlayableItem* item = proxyModel()->itemFromIndex( proxyModel()->mapToSource( index ) );
if ( !item )
continue;
if ( item->query()->results().isEmpty() || item->query()->results().first()->downloadFormats().isEmpty() )
continue;
if ( !DownloadManager::instance()->localFileForDownload( item->query()->results().first()->downloadFormats().first().url.toString() ).isEmpty() )
continue;
DownloadManager::instance()->addJob( item->result()->toDownloadJob( item->result()->downloadFormats().first() ) );
}
}
void
TrackView::verifySize()
{

View File

@@ -89,6 +89,7 @@ public:
public slots:
virtual void onItemActivated( const QModelIndex& index );
virtual void downloadSelectedItems();
virtual void deleteSelectedItems();
void playItem();

View File

@@ -247,8 +247,8 @@ TreeProxyModel::filterAcceptsRow( int sourceRow, const QModelIndex& sourceParent
if ( !item->result()->isOnline() && ti->result()->isOnline() )
return false;
if ( ( item->result()->resolvedByCollection().isNull() || !item->result()->resolvedByCollection()->isLocal() ) &&
!ti->result()->resolvedByCollection().isNull() && ti->result()->resolvedByCollection()->isLocal() )
if ( ( !item->result()->isLocal() ) &&
!ti->result()->isLocal() )
{
return false;
}

View File

@@ -156,6 +156,7 @@ XspfUpdater::setAutoUpdate( bool autoUpdate )
emit changed();
}
void
XspfUpdater::setInterval( int intervalMsecs )
{

View File

@@ -67,6 +67,14 @@ JSAccount::scriptPluginFactory( const QString& type, const scriptobject_ptr& obj
}
void
JSAccount::showDebugger()
{
tLog() << Q_FUNC_INFO << name() << "Show debugger";
m_engine->showWebInspector();
}
QString
JSAccount::serializeQVariantMap( const QVariantMap& map )
{

View File

@@ -69,6 +69,8 @@ public:
void setResolver( JSResolver* resolver );
void scriptPluginFactory( const QString& type, const scriptobject_ptr& object ) override;
void showDebugger() override;
static QString serializeQVariantMap(const QVariantMap& map);
void reportNativeScriptJobResult( int resultId, const QVariantMap& result ) override;

View File

@@ -541,7 +541,6 @@ void
JSResolver::resolve( const Tomahawk::query_ptr& query )
{
ScriptJob* job = scriptAccount()->resolve( scriptObject(), query, "resolver" );
connect( job, SIGNAL( done( QVariantMap ) ), SLOT( onResolveRequestDone( QVariantMap ) ) );
job->start();
@@ -563,15 +562,24 @@ JSResolver::onResolveRequestDone( const QVariantMap& data )
}
else
{
if ( !data.value( "artists" ).isNull() )
{
QList< artist_ptr > artists = scriptAccount()->parseArtistVariantList( data.value( "artists" ).toList() );
Tomahawk::Pipeline::instance()->reportArtists( qid, artists );
}
if ( !data.value( "albums" ).isNull() )
{
QList< album_ptr > albums = scriptAccount()->parseAlbumVariantList( data.value( "albums" ).toList() );
Tomahawk::Pipeline::instance()->reportAlbums( qid, albums );
}
QList< Tomahawk::result_ptr > results = scriptAccount()->parseResultVariantList( data.value( "tracks" ).toList() );
foreach( const result_ptr& result, results )
{
result->setResolvedByResolver( this );
result->setFriendlySource( name() );
}
Tomahawk::Pipeline::instance()->reportResults( qid, this, results );
}

View File

@@ -1,12 +1,12 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright (C) 2011 Leo Franchi <lfranchi@kde.org>
* Copyright (C) 2014 Dominik Schmidt <domme@tomahawk-player.org>
* Copyright (C) 2015, Christian Muehlhaeuser <muesli@tomahawk-player.org>
* Copyright 2011, Leo Franchi <lfranchi@kde.org>
* Copyright 2014, Dominik Schmidt <domme@tomahawk-player.org>
* Copyright 2015-2016, Christian Muehlhaeuser <muesli@tomahawk-player.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 2 of the License, or
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Tomahawk is distributed in the hope that it will be useful,
@@ -17,6 +17,7 @@
* You should have received a copy of the GNU General Public License
* along with Tomahawk. If not, see <http://www.gnu.org/licenses/>.
*/
#include "ScriptAccount.h"
#include "ScriptObject.h"
@@ -31,9 +32,11 @@
#include "ScriptLinkGeneratorPlugin.h"
#include "ScriptInfoPlugin.h"
// TODO:
#include "../Artist.h"
#include "../Album.h"
#include "../Result.h"
#include "../Track.h"
#include <QTime>
@@ -153,11 +156,17 @@ ScriptAccount::reportScriptJobResult( const QVariantMap& result )
Q_ASSERT( job );
// got a successful job result
if ( result[ "error"].isNull() )
if ( result[ "error" ].isNull() )
{
const QVariantMap data = result[ "data" ].toMap();
job->reportResults( data );
if ( result[ "data" ].type() == QVariant::Map )
{
const QVariantMap data = result[ "data" ].toMap();
job->reportResultsMap( data );
}
else
{
job->reportResults( result[ "data" ] );
}
}
else
{
@@ -201,6 +210,10 @@ ScriptAccount::unregisterScriptPlugin( const QString& type, const QString& objec
{
m_infoPluginFactory->unregisterPlugin( object );
}
else if( type == "linkParser" )
{
// TODO
}
else
{
tLog() << "This plugin type is not handled by Tomahawk or simply cannot be removed yet";
@@ -231,6 +244,10 @@ ScriptAccount::scriptPluginFactory( const QString& type, const scriptobject_ptr&
ScriptLinkGeneratorPlugin* lgp = new ScriptLinkGeneratorPlugin( object );
Utils::LinkGenerator::instance()->addPlugin( lgp );
}
else if( type == "linkParser" )
{
tLog() << "Plugin registered linkParser, which is not implemented yet. UrlLookup won't work";
}
else if ( type == "infoPlugin" )
{
m_infoPluginFactory->registerPlugin( object, this );
@@ -247,6 +264,12 @@ ScriptAccount::scriptPluginFactory( const QString& type, const scriptobject_ptr&
}
void
ScriptAccount::showDebugger()
{
}
void
ScriptAccount::onJobDeleted( const QString& jobId )
{
@@ -254,15 +277,67 @@ ScriptAccount::onJobDeleted( const QString& jobId )
}
QList< Tomahawk::artist_ptr >
ScriptAccount::parseArtistVariantList( const QVariantList& artistList )
{
QList< Tomahawk::artist_ptr > artists;
QString artist;
foreach( const QVariant& a, artistList )
{
artist = a.toString().trimmed();
if ( artist.isEmpty() )
continue;
artists << Tomahawk::Artist::get( artist );
}
return artists;
}
QList< Tomahawk::album_ptr >
ScriptAccount::parseAlbumVariantList( const QVariantList& albumList )
{
QList< Tomahawk::album_ptr > albums;
QString artistString;
QString albumString;
foreach( const QVariant& av, albumList )
{
QVariantMap m = av.toMap();
artistString = m.value( "artist" ).toString().trimmed();
albumString = m.value( "album" ).toString().trimmed();
if ( artistString.isEmpty() || albumString.isEmpty() )
continue;
albums << Tomahawk::Album::get( Tomahawk::Artist::get( artistString ), albumString );
}
return albums;
}
QList< Tomahawk::result_ptr >
ScriptAccount::parseResultVariantList( const QVariantList& reslist )
{
QList< Tomahawk::result_ptr > results;
foreach( const QVariant& rv, reslist )
{
QVariantMap m = rv.toMap();
const QString artistString = m.value("artist").toString().trimmed();
const QString trackString = m.value("track").toString().trimmed();
if ( artistString.isEmpty() || trackString.isEmpty() )
{
tLog() << Q_FUNC_INFO << "Could not parse Track" << m;
continue;
}
int duration = m.value( "duration", 0 ).toInt();
if ( duration <= 0 && m.contains( "durationString" ) )
{
@@ -270,8 +345,8 @@ ScriptAccount::parseResultVariantList( const QVariantList& reslist )
duration = time.secsTo( QTime( 0, 0 ) ) * -1;
}
Tomahawk::track_ptr track = Tomahawk::Track::get( m.value( "artist" ).toString(),
m.value( "track" ).toString(),
Tomahawk::track_ptr track = Tomahawk::Track::get( artistString,
trackString,
m.value( "album" ).toString(),
m.value( "albumArtist" ).toString(),
duration,
@@ -302,11 +377,17 @@ ScriptAccount::parseResultVariantList( const QVariantList& reslist )
// rp->track()->setAttributes( attr );
}
rp->setMimetype( m.value( "mimetype" ).toString() );
if ( rp->mimetype().isEmpty() )
QString mimetype = m.value( "mimetype" ).toString();
if ( mimetype.isEmpty() )
{
rp->setMimetype( TomahawkUtils::extensionToMimetype( m.value( "extension" ).toString() ) );
Q_ASSERT( !rp->mimetype().isEmpty() );
mimetype = TomahawkUtils::extensionToMimetype( m.value( "extension" ).toString() );
}
Q_ASSERT( !mimetype.isEmpty() );
if ( !mimetype.isEmpty() )
{
rp->setMimetype( mimetype );
}
rp->setFriendlySource( name() );

View File

@@ -1,12 +1,12 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright (C) 2011 Leo Franchi <lfranchi@kde.org>
* Copyright (C) 2014 Dominik Schmidt <domme@tomahawk-player.org>
* Copyright (C) 2015, Christian Muehlhaeuser <muesli@tomahawk-player.org>
* Copyright 2011, Leo Franchi <lfranchi@kde.org>
* Copyright 2014, Dominik Schmidt <domme@tomahawk-player.org>
* Copyright 2015-2016, Christian Muehlhaeuser <muesli@tomahawk-player.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 2 of the License, or
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Tomahawk is distributed in the hope that it will be useful,
@@ -76,7 +76,11 @@ public:
virtual void scriptPluginFactory( const QString& type, const scriptobject_ptr& object );
virtual void showDebugger();
// helpers
QList< Tomahawk::artist_ptr > parseArtistVariantList( const QVariantList& artistList );
QList< Tomahawk::album_ptr > parseAlbumVariantList( const QVariantList& albumList );
QList< Tomahawk::result_ptr > parseResultVariantList( const QVariantList& reslist );
ScriptJob* resolve( const scriptobject_ptr& scriptObject, const query_ptr& query, const QString& resolveType );

View File

@@ -28,6 +28,8 @@
#include "utils/Logger.h"
#include "../Result.h"
#include <QtConcurrentRun>
using namespace Tomahawk;
ScriptCommand_AllTracks::ScriptCommand_AllTracks( const Tomahawk::collection_ptr& collection,
@@ -117,20 +119,22 @@ ScriptCommand_AllTracks::onTracksJobDone( const QVariantMap& result )
QSharedPointer< ScriptCollection > collection = m_collection.objectCast< ScriptCollection >();
Q_ASSERT( !collection.isNull() );
QList< Tomahawk::result_ptr > t = collection->scriptAccount()->parseResultVariantList( result[ "tracks" ].toList() );
QList< Tomahawk::query_ptr > queries;
foreach ( const Tomahawk::result_ptr& result, t )
QtConcurrent::run( [] ( ScriptCommand_AllTracks* t, ScriptJob* job, const QVariantMap& result, const QSharedPointer< ScriptCollection >& collection )
{
result->setResolvedByCollection( m_collection );
queries.append( result->toQuery() );
}
QList< Tomahawk::result_ptr > results = collection->scriptAccount()->parseResultVariantList( result[ "tracks" ].toList() );
tDebug() << Q_FUNC_INFO << "about to push" << queries.count() << "tracks";
QList< Tomahawk::query_ptr > queries;
foreach ( const Tomahawk::result_ptr& result, results )
{
result->setResolvedByCollection( collection );
queries.append( result->toQuery() );
}
emit tracks( queries );
emit done();
tDebug() << Q_FUNC_INFO << "about to push" << queries.count() << "tracks";
job->deleteLater();
emit t->tracks( queries );
emit t->done();
job->deleteLater();
}, this, job, result, collection );
}

View File

@@ -21,7 +21,7 @@
#include "ScriptEngine.h"
#include "jobview/ErrorStatusMessage.h"
#include "jobview/ScriptErrorStatusMessage.h"
#include "jobview/JobStatusModel.h"
#include "jobview/JobStatusView.h"
#include "utils/Logger.h"
@@ -51,13 +51,13 @@ ScriptEngine::ScriptEngine( JSAccount* parent )
settings()->setAttribute( QWebSettings::LocalContentCanAccessRemoteUrls, true );
settings()->setOfflineStorageDefaultQuota(100 * 1024 * 1024 /* 100 Mb */);
settings()->setOfflineWebApplicationCacheQuota(100 * 1024 * 1024 /* 100 Mb */);
settings()->setAttribute( QWebSettings::DeveloperExtrasEnabled, true );
// HACK
QStringList cmdArgs = QCoreApplication::instance()->arguments();
int position = cmdArgs.indexOf( "--show-inspector" ) + 1;
if ( position > 0 && !cmdArgs.at( position ).isEmpty() && parent->name().contains( cmdArgs.at( position ), Qt::CaseInsensitive ) ) {
settings()->setAttribute( QWebSettings::DeveloperExtrasEnabled, true );
QMetaObject::invokeMethod( this, "initWebInspector", Qt::QueuedConnection );
QMetaObject::invokeMethod( this, "showWebInspector", Qt::QueuedConnection );
}
// Tomahawk is not a user agent
@@ -74,24 +74,13 @@ ScriptEngine::ScriptEngine( JSAccount* parent )
}
void
ScriptEngine::initWebInspector()
{
m_webInspector.reset( new QWebInspector() );
m_webInspector->setPage( this );
m_webInspector->setMinimumWidth( 800 );
m_webInspector->setMinimumHeight( 600 );
m_webInspector->show();
}
void
ScriptEngine::javaScriptConsoleMessage( const QString& message, int lineNumber, const QString& sourceID )
{
tLog() << "JAVASCRIPT:" << QString( "%1:%2" ).arg( m_scriptPath ).arg( lineNumber ) << message << sourceID;
#ifdef QT_DEBUG
QFileInfo scriptPath( m_scriptPath );
JobStatusView::instance()->model()->addJob( new ErrorStatusMessage( tr( "Resolver Error: %1:%2 %3" ).arg( scriptPath.fileName() ).arg( lineNumber ).arg( message ) ) );
JobStatusView::instance()->model()->addJob( new ScriptErrorStatusMessage( tr( "%1:%2 %3" ).arg( scriptPath.fileName() ).arg( lineNumber ).arg( message ), m_parent ) );
#endif
}
@@ -110,7 +99,7 @@ ScriptEngine::sslErrorHandler( QNetworkReply* qnr, const QList<QSslError>& errli
QMessageBox question( TomahawkUtils::tomahawkWindow() );
question.setWindowTitle( tr( "SSL Error" ) );
question.setText( tr( "You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br>"
question.setText( tr( "You have asked %applicationName to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br>"
"<b>%2</b><br><br>"
"Do you want to trust this connection?" )
.arg( qnr->url().host() )
@@ -148,6 +137,21 @@ ScriptEngine::setScriptPath( const QString& scriptPath )
}
void
ScriptEngine::showWebInspector()
{
if ( m_webInspector.isNull() )
{
m_webInspector.reset( new QWebInspector() );
m_webInspector->setPage( this );
m_webInspector->setMinimumWidth( 800 );
m_webInspector->setMinimumHeight( 600 );
}
m_webInspector->show();
}
bool
ScriptEngine::shouldInterruptJavaScript()
{

View File

@@ -49,13 +49,13 @@ public:
public slots:
bool shouldInterruptJavaScript();
void showWebInspector();
protected:
virtual void javaScriptConsoleMessage( const QString& message, int lineNumber, const QString& sourceID );
private slots:
void sslErrorHandler( QNetworkReply* qnr, const QList<QSslError>& errlist );
void initWebInspector();
private:
JSAccount* m_parent;

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