1
0
mirror of https://github.com/tomahawk-player/tomahawk.git synced 2025-09-07 04:30:43 +02:00

Compare commits

...

308 Commits

Author SHA1 Message Date
Dominik Schmidt
45cc0652d9 Stop crashing when changing shown collection in qml 2011-09-10 05:12:09 +02:00
Dominik Schmidt
6df1b9462f Don't load QML in an undefined state 2011-09-08 04:25:42 +02:00
Dominik Schmidt
cbb44907f4 Add possibility to start songs from QML 2011-09-08 04:12:16 +02:00
Dominik Schmidt
7185b4e778 Whoops 2011-09-08 02:54:07 +02:00
Dominik Schmidt
a0c3806da7 Expose name and isPlaying status in TreeModel 2011-09-07 09:31:53 +02:00
Dominik Schmidt
0d0f9be3f2 Expose currently selected collection to qml 2011-09-07 08:51:21 +02:00
Dominik Schmidt
048c82ac24 Add lazyload getters for models to the ViewManager. 2011-09-07 08:50:39 +02:00
Dominik Schmidt
d49ce2f242 Allow changing current view from qml 2011-09-07 07:23:46 +02:00
Dominik Schmidt
354f1f8f2e Expose DisplayRole as name in sourcesModel 2011-09-07 06:59:24 +02:00
Dominik Schmidt
2472171e8e Expose sourcesModel to QML 2011-09-07 06:13:51 +02:00
Dominik Schmidt
f5b0c35274 Share one trayIcon across all windows 2011-09-07 06:12:07 +02:00
Dominik Schmidt
5ea4b28ad4 Show artist and track in TomahawkTouchWindow title too 2011-09-07 06:02:44 +02:00
Dominik Schmidt
58f239b3a9 Rename TomahawkWindowDeclarative to TomahawkTouchWindow and make it a subclass of TomahawkWindow 2011-09-07 05:38:58 +02:00
Dominik Schmidt
769be854fa Initialize ViewManager before SourcesModel 2011-09-07 05:26:36 +02:00
Dominik Schmidt
a6013ef411 Move SourcesModel initialization to TomahawkWindow 2011-09-07 04:10:20 +02:00
Dominik Schmidt
c16528763f Fix regression where windowTitle was set empty 2011-09-07 02:58:11 +02:00
Dominik Schmidt
b8d684fed8 Split TomahawkWindow into TomahawkWindow and TomahawkDesktopWindow which now contains everything specific to the main tomahawk gui 2011-09-07 01:34:09 +02:00
Dominik Schmidt
1a2b7907d0 Fix cmake for clang 2011-09-06 23:45:09 +02:00
Dominik Schmidt
bcc5e01f8b Resolve undefined reference to play-slot in TomahawkWindowDeclarative 2011-09-06 22:53:06 +02:00
Dominik Schmidt
3e8bcabfbc Add TomahawkWindowDeclarative 2011-09-06 11:19:18 +02:00
Dominik Schmidt
c2cd281445 Expose GlobalActionManager::openLink to QML 2011-09-06 03:00:20 +02:00
Dominik Schmidt
9fdb0c9393 Expose audioEngine states to QML 2011-09-06 03:00:20 +02:00
Dominik Schmidt
fca061d9bc Add basic support for loading a qml gui 2011-09-06 03:00:19 +02:00
Jason Herskowitz
700e53eb2c Adding images for footnotes and queue headers. Open, close, expand and collapse. 2011-09-05 08:45:25 -04:00
Christian Muehlhaeuser
5968d6c903 * Installing a scene filter doesn't work. Not needed either. 2011-09-05 12:28:46 +02:00
Christian Muehlhaeuser
c7317dbe54 * Updated ChangeLog. 2011-09-05 12:26:00 +02:00
Christian Muehlhaeuser
031f52f6b7 * Only linux seems to behave wrong here... maybe it's Oxygen-style. Need to investigate. 2011-09-05 11:58:07 +02:00
Christian Muehlhaeuser
fa6579f8f0 * Static gradient for InfoBar speeds up window resizing. 2011-09-05 11:04:02 +02:00
Christian Muehlhaeuser
c9c99d58ab * Fixed search-input layout on non-OSX. 2011-09-05 10:56:29 +02:00
Christian Muehlhaeuser
b9d6548243 * Moved infobar/ out of the playlist/ dir. 2011-09-05 10:40:12 +02:00
Christian Muehlhaeuser
914c02b7ab * Moved widgets out of utils/ dir. 2011-09-05 10:37:14 +02:00
Christian Muehlhaeuser
b83a7bed00 * Properly elide artist's biography on artist-page. 2011-09-05 10:28:21 +02:00
Christian Muehlhaeuser
f3c19779cd * Fine-tuned filtering tree-models. 2011-09-05 09:26:45 +02:00
Christian Muehlhaeuser
9ef5894685 * Move TreeModel filtering to TreeProxyModel. 2011-09-05 06:32:40 +02:00
Christian Muehlhaeuser
71117d3c42 * Filter word by word, not the total term, e.g. 'rock you we will' finds 'we will rock you'. 2011-09-05 05:50:08 +02:00
Casey Link
079f758b66 Update the WhatsHot widget to fetch and correctly display charts based on the selection in the breadcrumb. For the moment I've removed the right panel, but it will be coming back (probably). 2011-09-04 22:37:09 -05:00
Casey Link
dd83f121c8 When the selected crumb doesn't have any children, emit the parent as having been selected. 2011-09-04 22:37:09 -05:00
Casey Link
8f326ee41b Add a signal to tell the outside world when the crumb changed. 2011-09-04 22:37:09 -05:00
Casey Link
f32320fd67 Fix detection of tracks/artists charts in lastfm 2011-09-04 22:37:09 -05:00
Casey Link
0a7e49f6d5 Change how chart lists and charts are passed through the info system. 2011-09-04 22:37:09 -05:00
Christian Muehlhaeuser
33d8c0314c * Allow filtering by album name. 2011-09-05 04:56:11 +02:00
Christian Muehlhaeuser
b73d5b4424 * Fixed filtering by artist name. 2011-09-05 04:52:28 +02:00
Christian Muehlhaeuser
c4a450e5de * Add filter capabilities to TreeModel / -View. 2011-09-05 04:50:30 +02:00
Jason Herskowitz
b32ac0cf04 New filter icon if we can use it with native search component 2011-09-04 22:10:47 -04:00
Christian Muehlhaeuser
52c0e9993b * Fixed filter behaviour. 2011-09-05 03:52:41 +02:00
Christian Muehlhaeuser
1c3b3e9173 * Bring back the filter, move deps into libtomahawk/thirdparty/. 2011-09-05 02:51:41 +02:00
Christian Muehlhaeuser
9690276079 * Fixed TWK-278: Store columns after reordering and introduce ViewHeader as a base-class for self-restoring QHeaderViews. 2011-09-04 12:35:52 +02:00
Christian Muehlhaeuser
58362883b3 * Some more cleanups. 2011-09-04 08:58:09 +02:00
Christian Muehlhaeuser
5b8f35294b * Don't react to hover events unless TrackView is in Detailed mode. 2011-09-04 08:49:37 +02:00
Christian Muehlhaeuser
12c5b18b05 * Resize ContextPage's columns. 2011-09-04 08:45:31 +02:00
Christian Muehlhaeuser
05315b14bd * Add forgotten ui file. 2011-09-04 07:25:21 +02:00
Christian Muehlhaeuser
dd842a1393 * Removed ugly queue button for good. 2011-09-04 07:23:54 +02:00
Christian Muehlhaeuser
a467b8f2e8 * Added initial ContextView. Yikes. 2011-09-04 04:23:10 +02:00
Christian Muehlhaeuser
5dfb3ccc5c * Fixed TWK-492: Don't crash when pressing enter in an empty playlist. 2011-09-02 01:18:57 +02:00
Casey Link
776a2deb91 Make the root icon clickable and expose a signal. 2011-09-01 00:29:05 +00:00
Casey Link
c79985ad86 When there isn't enough room to display all the crumbs, collapse from the left. 2011-09-01 00:28:38 +00:00
Casey Link
aeaf569eb1 Remove slot/signal connection leftover from testing. 2011-08-31 17:02:16 -05:00
Casey Link
e05b8b1843 If the current crumb doesn't have any children, do not show the arrow. 2011-08-31 17:00:04 -05:00
Casey Link
a6452dbeee A little hack to force the second crumb to automatically expand. 2011-08-31 16:39:09 -05:00
Casey Link
d15daf4a85 Implement the data model for the breadcrumb in the whatshot widget. 2011-08-31 21:23:26 +00:00
Casey Link
16a2f94816 Fix bug in the breadcrumb that prevent buttons from showing up. 2011-08-31 21:23:26 +00:00
Casey Link
7a7de39bf7 Implement ChartInfoCapabilities in the lastfm plugin 2011-08-31 21:23:26 +00:00
Casey Link
37530eeeed Replace the comboboxes in the whatshot widget with the breadcrumb. No data model yet, that is coming soon. 2011-08-31 21:23:26 +00:00
Casey Link
c2fe912277 A breadcrumb widget, tomahawk style. 2011-08-31 21:23:26 +00:00
Christian Muehlhaeuser
0c3a9c61b5 * Don't retrieve anything for invalid TreeModelItems. 2011-08-31 13:01:52 +02:00
Christian Muehlhaeuser
e75e204007 * Fixed crash bug caused by cover fetching and a race condition. 2011-08-31 12:59:44 +02:00
Christian Muehlhaeuser
098999ca8c * Fixed crash bug in AlbumModel. 2011-08-31 11:37:22 +02:00
Alejandro Wainzinger
14e6a7fce4 Return 404 if content is null. 2011-08-30 22:07:53 -07:00
Jeff Mitchell
2bedc3bdd0 Extra safety 2011-08-30 11:55:47 -04:00
Leo Franchi
58bff6af4c style fix 2011-08-30 07:58:51 -04:00
Casey Link
bf03b7b756 Fetch charts data on the event loop to prevent a strange deadlock on startup. 2011-08-29 23:40:52 -05:00
Christian Muehlhaeuser
7f324bc45c * Unbreak OSX compile. 2011-08-30 03:11:47 +02:00
Casey Link
626f9acfac Add missing comma, doh 2011-08-29 19:41:36 -05:00
Casey Link
302dbd0224 Some test documentation for enumns.. lets see what doxygen does. 2011-08-29 19:34:14 -05:00
Casey Link
57dea3061b Add some documentation to the new classes 2011-08-29 19:34:14 -05:00
Christian Muehlhaeuser
3e12072dd9 * TomahawkWindow cleanup. 2011-08-30 02:29:45 +02:00
Christian Muehlhaeuser
be10085958 * Fixed ChangeLog typo. 2011-08-30 02:20:49 +02:00
Casey Link
6e6ff2ae53 Merge branch 'whatshot' 2011-08-29 18:56:12 -05:00
Casey Link
320e5a3252 Fix oopsies leftover from the playlist model rename 2011-08-29 23:55:37 +00:00
Jason Herskowitz
f031d25741 Added fix for Spotify links 2011-08-30 01:30:36 +02:00
Christian Muehlhaeuser
a554ee0fa8 * Fixed TWK-451: Properly parent the friends auth / add dialog. 2011-08-30 00:53:29 +02:00
Christian Muehlhaeuser
f774302623 * Fixed TWK-482: Don't close after showing a jabber friend request. 2011-08-30 00:26:58 +02:00
Leo Franchi
0cea650181 TWK-477: Restore code that was lost during file split, and add a proper license header 2011-08-29 18:10:49 -04:00
Jeff Mitchell
47c515caa4 Do please check your debug for null pointers :-) 2011-08-29 13:13:10 -04:00
Jeff Mitchell
0319e5f2f5 Don't show 0 when there is no albumpos for a track 2011-08-29 12:06:42 -04:00
Jeff Mitchell
39bbd128c2 Change the position of the albumpos column to be next to the album 2011-08-29 11:54:09 -04:00
Casey Link
a25d174f64 Merge branch 'master' into whatshot
Conflicts:
	src/libtomahawk/CMakeLists.txt
2011-08-29 10:43:10 -05:00
Leo Franchi
65709e92ff Add file 2011-08-29 11:27:50 -04:00
Christian Muehlhaeuser
89104fc27d * Added more debug output for playlist errors. 2011-08-29 17:12:25 +02:00
Leo Franchi
28e7413bdd Make QCA2 an optional dep, it's only required for HMAC(md5) for resolvers 2011-08-29 11:09:31 -04:00
Jeff Mitchell
d5a2e4b325 Remove dependency on XComposite on X11. It wasn't actually a dependency
on the library, just the development headers (used to check for th
existence of compositing at runtime, but not necessary for us).
2011-08-29 11:03:28 -04:00
Leo Franchi
bde3d93f6a Changelog++ 2011-08-29 10:50:59 -04:00
Leo Franchi
8ed476ccf6 Add QCA2 as an optional dep to tomahawk as well for HMAC signature generation for resolvers
Also add a helper JS function
2011-08-29 10:49:52 -04:00
Leo Franchi
5c5042769b Update overlay text 2011-08-29 10:49:52 -04:00
Christian Muehlhaeuser
6cca932bf4 * Stick to 80-char max-length for ChangeLog. 2011-08-29 16:20:33 +02:00
Leo Franchi
3b832576af Add new feature to 0.3 changelog 2011-08-29 10:18:46 -04:00
Leo Franchi
fa8746fc70 Sync changelog 2011-08-29 10:17:25 -04:00
Christian Muehlhaeuser
e44a78ba8d * Be safer. 2011-08-29 16:07:57 +02:00
Christian Muehlhaeuser
cf25f3c072 * Some debug and safety meassures. 2011-08-29 16:01:53 +02:00
Jeff Mitchell
4fc7c6415b Fix seeker getting "stuck" if it goes ahead on some tracks 2011-08-28 16:47:14 -04:00
Jeff Mitchell
271e6c480d Re-disable phonon seek checking, for now 2011-08-28 16:23:25 -04:00
Jeff Mitchell
57e72e53f9 Fix seeking for local files; also, disable dragging the seek slider if phonon reports that the object cannot be seeked, to not have ugly jumping around. 2011-08-28 16:13:27 -04:00
Jeff Mitchell
a9a9d11f97 Remove debugging 2011-08-28 14:41:02 -04:00
Jeff Mitchell
4b4cecc027 Finish making the seekslider the sleekslider 2011-08-28 14:38:33 -04:00
Jeff Mitchell
e5f6f40289 Initial work on the sleeker slider 2011-08-28 04:16:18 -04:00
Jeff Mitchell
d2af8a8a39 Fix null pointer crash in treeproxymodel 2011-08-28 03:47:11 -04:00
Jeff Mitchell
7166ba161f Update changelog 2011-08-28 02:23:23 -04:00
Jeff Mitchell
ea0fb3c099 Merge branch 'master' of github.com:tomahawk-player/tomahawk 2011-08-28 02:17:49 -04:00
Jeff Mitchell
ea6d892058 Fix regression in audioengine logic when encountering the end of the playlist 2011-08-28 02:16:27 -04:00
Christian Muehlhaeuser
dffb3deebe * Updated ChangeLog. 2011-08-28 06:08:45 +02:00
Christian Muehlhaeuser
c5876c0f9e * Absolutely prevent dupe temp commands from being executed twice. 2011-08-28 05:44:45 +02:00
Christian Muehlhaeuser
89aff8bcbb * Cleaned up database syncing a bit. 2011-08-28 04:50:31 +02:00
Jeff Mitchell
71339862b6 Changelogify Twitter issue 2011-08-27 22:09:45 -04:00
Jeff Mitchell
9e4d320cff Make public/mentions timelines check less often to offset bumping up the dm timer 2011-08-27 22:04:58 -04:00
Jeff Mitchell
cca1ac1532 Some further cleanup and make dms poll a bit more often while keeping the timelines longer 2011-08-27 22:04:24 -04:00
Jeff Mitchell
d8c09ff502 Hopefully really fix the twitter issue 2011-08-27 20:28:59 -04:00
Jeff Mitchell
678eeb1232 Fix twitter saved offer key issue, maybe 2011-08-27 18:00:24 -04:00
Jeff Mitchell
644135c88a Move libqnetwm down one level and hopefully fix building for leo 2011-08-27 16:21:19 -04:00
Jeff Mitchell
dc9f250322 Fix include path 2011-08-27 16:07:53 -04:00
Christian Muehlhaeuser
33a63a45e8 * Fixed mutex deadlock in SourceList. 2011-08-27 21:46:13 +02:00
Leo Franchi
50bce627a8 Show recently created playlists and stations instead of recently looked at. Old model is still there. 2011-08-27 15:28:02 -04:00
Michael Zanetti
49373bdfd0 dont crash when dropping an item with no album to album 2011-08-27 20:37:38 +02:00
Michael Zanetti
f39a982e00 fix dropping of query_ptrs to Top10 2011-08-27 20:37:38 +02:00
Jeff Mitchell
6a12e9fe04 Changelogify++ 2011-08-27 14:02:22 -04:00
Michael Zanetti
16e77cb144 fix dropping of top 10 for albums and tracks 2011-08-27 17:35:48 +02:00
Michael Zanetti
eaca242277 changed the colors to silver 2011-08-27 17:35:48 +02:00
Michael Zanetti
851f9124ba increase font size in dropmenu. People say it was too small for distinguishing normal and bold. 2011-08-27 17:35:48 +02:00
Michael Zanetti
01c2927339 paint the hovered item w/o QStyle::State_Selected but w/ QStyle::State_Active 2011-08-27 17:35:47 +02:00
Michael Zanetti
9f6dd0a104 fix font shrinking issue on OSX with the dragndrop menu 2011-08-27 17:35:47 +02:00
Michael Zanetti
52037d8847 cache the mimedata so we can paint the items after the dropLeaveEvent and made animations a bit faster 2011-08-27 17:35:47 +02:00
Michael Zanetti
39af693de1 remove styleoption selected before painting the text to avoid two selection boxes 2011-08-27 17:35:47 +02:00
Michael Zanetti
9cc919eb08 improved the animation 2011-08-27 17:35:47 +02:00
Jason Herskowitz
2e3e0afa7c Replace all songs drop zone icon with something that is more square. 2011-08-27 17:35:47 +02:00
Michael Zanetti
f0a05acf01 fix off-by-one painting bug in dropmenu 2011-08-27 17:35:47 +02:00
Michael Zanetti
a7d1275e52 replace BounceAnimaton with ExpoAnimation 2011-08-27 17:35:47 +02:00
Michael Zanetti
232097b06b make use of new drop icons 2011-08-27 17:35:47 +02:00
Jason Herskowitz
b1ed8d2419 Add drop zone icons for song and album. 2011-08-27 17:35:47 +02:00
Michael Zanetti
2c5040c638 improved animation 2011-08-27 17:35:47 +02:00
Michael Zanetti
f1dfd84213 make the dropmenu work with categoryitems 2011-08-27 17:35:47 +02:00
Michael Zanetti
74fa941159 fix bug where non expanded items were tracked expanded ones 2011-08-27 17:35:47 +02:00
Michael Zanetti
559433162f make top 10 really top 10 instead of top 50 2011-08-27 17:35:46 +02:00
Michael Zanetti
fbefe4185f fixes in dragndrop menu 2011-08-27 17:35:46 +02:00
Michael Zanetti
71a5888cba make use of new icons 2011-08-27 17:35:46 +02:00
Michael Zanetti
e7cd5bddc4 more work on the drag and drop menu 2011-08-27 17:35:46 +02:00
Jason Herskowitz
b70810da9c Icons for artist drop areas for playlist drawer 2011-08-27 17:35:46 +02:00
Michael Zanetti
4fe15984ce TWK-434: Added drag indicators for artist and album 2011-08-27 17:35:46 +02:00
Michael Zanetti
1d6c407003 TWK-411: Dragging an album/artist to "New Station" creates a station based on that artist instead of all the tracks 2011-08-27 17:35:46 +02:00
Michael Zanetti
a227ae4bcb more work on the drag and drop menu 2011-08-27 17:35:46 +02:00
Christian Muehlhaeuser
13db62b71c * Fixed always resolving top songs in DropJob. 2011-08-27 17:35:46 +02:00
Michael Zanetti
bb72c3efb2 correctly check for isNull 2011-08-27 17:35:46 +02:00
Michael Zanetti
34c8c79ef5 fixed order of filtering. initial work on the Top10 drop action (does actually Top50 for now and tracks can not be resolved yet) 2011-08-27 17:35:46 +02:00
Michael Zanetti
6aa7531623 dropping "All items" and "Only local items" works now 2011-08-27 17:35:46 +02:00
Michael Zanetti
eea5fa2826 cleanup 2011-08-27 17:35:46 +02:00
Michael Zanetti
a14db5e7f2 readded the editorEvent which somehow got lost in the previous merge and cleaned up a bit 2011-08-27 17:35:46 +02:00
Michael Zanetti
42508b7917 initial work on a menu for drag and drop 2011-08-27 17:35:46 +02:00
Jeff Mitchell
6b865eb4f2 Add account skeleton 2011-08-27 17:09:13 -04:00
Jeff Mitchell
87c86b6349 Hells. Yes. Make TH window come to front when launched with a URL. (X11, not sure if Win works yet.) 2011-08-27 02:47:17 -04:00
Jeff Mitchell
4b5caaceef Fix compile on mingw32 2011-08-27 02:37:50 -04:00
Jeff Mitchell
dccc4e6225 Add definitions for bringToFront. Can't test on X11 as TH crashes with database errors before I get the chance. 2011-08-27 02:29:05 -04:00
Jeff Mitchell
971cc742bb Add ability to query all sources with a getInfo call. Watch out for finished() :-) 2011-08-27 00:45:16 -04:00
Casey Link
2bff9e9a27 fix charts image in resources 2011-08-27 01:45:39 +00:00
Casey Link
6cba5c990e Change display name of whatshot widget to Charts and use the charts icon 2011-08-26 20:12:14 -05:00
Casey Link
a2f2fc7867 Merge branch 'master' into whatshot
Conflicts:
	src/tomahawkapp.cpp
2011-08-26 19:54:55 -05:00
Casey Link
ec2dec4faf Handle our own Arrow Drawing.
Adds AntiAliasing and uses our own colors.
2011-08-26 19:47:11 -05:00
Dominik Schmidt
e2fb440af4 Square the .svg version of tomahawk-icon (Larifari's first contribution) 2011-08-27 02:17:53 +02:00
Casey Link
ccc8d3b26a Use the new Header and ComboBox widgets in the whatshot widget. 2011-08-26 18:44:25 -05:00
Casey Link
e43e45cd76 Introduce StyleHelper, HeaderWidget and ComboBox
These are customized widgets for tomahawk that make headers pretty.
2011-08-26 18:43:35 -05:00
Jeff Mitchell
fc2de7721a None of this works, but maybe it's a start since Qt directly isn't
working
2011-08-26 18:40:19 -04:00
Jeff Mitchell
65a50c5a85 Better structuring 2011-08-26 17:47:08 -04:00
Jeff Mitchell
cd78b77784 Draw the battle lines 2011-08-26 17:03:49 -04:00
Jeff Mitchell
1c250b334b Handle checking queue in canGoNext so that we can use next() all the
time instead of randomly skipping it and using loadNextTrack directly
sometimes
2011-08-26 16:12:51 -04:00
Jeff Mitchell
d06c43bfe6 Use next() so that the hasNextTrack checks are run 2011-08-26 16:08:16 -04:00
Leo Franchi
1994ab33fe Don't depend on sender() as it can be null in queued events 2011-08-26 13:26:54 -04:00
Jeff Mitchell
9208257c96 Base64 encode our twitter credentials like elsewhere 2011-08-26 12:01:24 -04:00
Jeff Mitchell
f479fd4e7c Add logic back to audioengine, but simplify in sourcetreeview 2011-08-26 11:27:10 -04:00
Christian Muehlhaeuser
36486d10d1 * Notify user even when there's no album for the current track. 2011-08-26 17:11:34 +02:00
Jeff Mitchell
c7e2a5c1c1 Doh, smart pointers -- fix compile 2011-08-26 10:22:15 -04:00
Jeff Mitchell
a32ff26401 Fix crash; pointer safety! 2011-08-26 10:16:55 -04:00
Jeff Mitchell
b86f6301ff More sometimes-useful debug 2011-08-26 00:40:01 -04:00
Jeff Mitchell
146eaa226f Add debug and some logic to disconnect sourceplaylistinterface connections 2011-08-25 23:49:40 -04:00
Leo Franchi
3fa9fa337e TWK-441: Try to fix crash where sourcetreeitem would hold on to a dangling parent 2011-08-25 23:35:08 -04:00
Leo Franchi
597fec4982 Remove New Playlist widget for now, until we refactor the playlist UIs.
Also update the new playlist type widget to take into account the changes. Make the dialog a sheet on osx.
2011-08-25 23:11:57 -04:00
Jeff Mitchell
01b199c4a8 Fix listen along menu not resetting when you stop listening along 2011-08-25 22:57:14 -04:00
Jeff Mitchell
6dde11c89a Simplify logic -- I think this should still work fine with source playlist interface and also fix issues where someone has stopped playing a track 2011-08-25 20:28:25 -04:00
Dominik Schmidt
3c30ebd4d6 lfranchi: muesli: jherskowitz: awainzin: REMOVED LEGACY JABBER SUPPORT :P 2011-08-26 01:29:50 +02:00
Christian Muehlhaeuser
ff10760838 * Fixed TWK-469: Added album position as a TrackModel column. 2011-08-26 01:10:45 +02:00
Christian Muehlhaeuser
c4fbb6e0a7 * Fixed TWK-461: Always pre-sort searches by score column. 2011-08-26 00:48:39 +02:00
Christian Muehlhaeuser
67acb25da0 * Remove obsolete debug output. 2011-08-25 03:51:47 +02:00
Christian Muehlhaeuser
3e0092d299 * Guard pipeline against dupe queries. 2011-08-25 03:43:11 +02:00
Leo Franchi
9801370a47 Don't resolve a query twice. EchonestGenerator no longer gives out auto-resolving queries, resolve your own. 2011-08-24 21:39:35 -04:00
Christian Muehlhaeuser
2010cb41c7 * Add debug output. 2011-08-25 03:18:35 +02:00
Christian Muehlhaeuser
5f9f9481e7 * Refactor pipeline. 2011-08-25 02:55:31 +02:00
Leo Franchi
6f639ecf10 TWK-340: smooth transform for more pixmaps 2011-08-24 20:37:31 -04:00
Leo Franchi
50d00fc1bf TWK-447: Clear albums model before appending to it 2011-08-24 19:32:34 -04:00
Leo Franchi
def10a2e2b TWK-459: More rdio unbreaking and add support in rdioURI/rdioURL tomahawk urls 2011-08-24 19:13:54 -04:00
Leo Franchi
b7db4e0e6b TWK-323: Really fix rdio links 2011-08-24 19:11:54 -04:00
Michael Zanetti
92b612f44f make trackURLs work again
(cherry picked from commit 4327d61df7)
2011-08-24 07:42:54 -04:00
Christian Muehlhaeuser
d47cafbebf * Make sure onResolvingFinished gets called. 2011-08-24 03:02:26 +02:00
Christian Muehlhaeuser
895d65a24e * Cleaned up debugging in Servent. 2011-08-24 00:33:23 +02:00
Christian Muehlhaeuser
c9bf3035f2 * Don't assert out on too late results. 2011-08-23 18:15:23 +02:00
Leo Franchi
41f3193355 Don't double-delete playlists either 2011-08-23 10:20:26 -04:00
Leo Franchi
924c1b3a72 Changelog
(cherry picked from commit 1279b1fa12)
2011-08-22 19:17:55 -04:00
Leo Franchi
ac317eb0a6 TWK-431: Don't re-use the same infosystem caller id for every artistinfowidget. 2011-08-22 18:26:09 -04:00
Leo Franchi
36768db7e4 TWK-443: Don't double-delete dynamic widgets 2011-08-22 16:28:56 -04:00
Jason Herskowitz
97ee6f7d5e Edited ChangeLog via GitHub 2011-08-22 14:10:21 -03:00
Leo Franchi
e0c5108fb4 Changelog 2011-08-22 07:36:01 -04:00
Leo Franchi
fe77940545 Changelog. 2011-08-22 07:36:01 -04:00
Jason Herskowitz
51ae8fea77 Add icon for charts. 2011-08-21 22:19:25 -04:00
Christian Muehlhaeuser
c76c557ed1 * Fixed tray-icon's play / pause states. 2011-08-22 02:36:44 +02:00
Christian Muehlhaeuser
4200975576 Merge pull request #37 from Horrendus/master
Make a Play/Pause entry in Menu of the Trayicon
2011-08-21 17:25:09 -07:00
Casey Link
415d2e8ec7 ignore my ctags file 2011-08-21 18:55:53 -05:00
Stefan Derkits
612fd47293 Menu of TrayIcon now has a Play/Pause Entry
-) When starting Tomahawk the text is play
-) When playing a Song, the text is pause
-) When pausing a Song, the text is play

Action is connected to the playPause() slot of audioengine
2011-08-22 01:48:27 +02:00
Casey Link
ebb71321f6 Add a splitter to the charts widget. 2011-08-21 18:20:00 -05:00
Casey Link
fc235ccf1b Implement fetching and displaying of the Top Tracks Chart. 2011-08-21 18:04:44 -05:00
Casey Link
a21992a99a Either the lastfm XML output has changed, or this library was buggy all along. This patch fixes the parsing of <track> elements. 2011-08-21 18:03:21 -05:00
Alejandro Wainzinger
573e36fdfd Hopefully fix infinite loop caused by requesting a cover when the cache has issues and/or can't find the cover. 2011-08-21 14:26:30 -07:00
Christian Muehlhaeuser
48aeddc0a9 * Fixed filtering out unwanted dupes when viewing a local collection. 2011-08-21 22:50:41 +02:00
Casey Link
c6a104af93 Use a proxy model that doesn't sort, so we preserve the ranking order 2011-08-21 13:48:07 -05:00
Casey Link
68d3783b26 Implement chart.TopArtists in the lastfm plugin 2011-08-21 13:00:15 -05:00
Casey Link
228699863f Add new Chart InfoTypes for retrieving charts 2011-08-21 13:00:15 -05:00
Casey Link
a4a3c4fb17 Add What's Hot page with corresponding widget. 2011-08-21 12:56:41 -05:00
Christian Muehlhaeuser
13a93cfa45 * Bumped version no to 0.2.99 for master. 2011-08-21 04:55:53 +02:00
Christian Muehlhaeuser
3feee712bd * Updated ChangeLog. 2011-08-21 04:53:30 +02:00
Christian Muehlhaeuser
99e3e06716 * Fixed TWK-442: Provide stable sort ordering in Track- / TreeProxyModel. 2011-08-21 03:51:26 +02:00
Christian Muehlhaeuser
1653e16e7d * Fixed TWK-435: Don't block pipeline if a resolver times out. 2011-08-21 03:31:35 +02:00
Dominik Schmidt
977757cc7e TWK-410: possible fix 2011-08-21 03:23:29 +02:00
Christian Muehlhaeuser
71f6a3e81a * Don't filter out some wanted tracks, e.g. when multiple tracks of an album have the same name, but different tracks numbers. 2011-08-21 02:56:29 +02:00
Christian Muehlhaeuser
43bf42df6f * Fixed TWK-387: Stop showing a loading spinner, if there are no items to load. 2011-08-21 02:38:14 +02:00
Leo Franchi
552a07eee7 Be a bit safer if model changes 2011-08-20 11:50:37 -04:00
Michael Zanetti
97c694baba don't add duplicates when dropping albums or artists and fix losing items when combining synchronous and asynchrounous drop operations 2011-08-20 14:36:48 +02:00
Leo Franchi
5d512c180f Only remove playlists from saved list if that playlist was deleted and is still in our internal db 2011-08-20 01:09:34 -04:00
Leo Franchi
7f089714a6 bail out on a deleted widget 2011-08-20 00:58:32 -04:00
Leo Franchi
9737f765e5 TWK-414: Fix selecting newly created playlists and stations.
God this was a PITA. What is going on:

Playlists are created by collection item. SourcesModel::linkSourceItemToPage is called from *constructor* of PlaylistItem, so BEFORE
the CollectionItem calls endRowsAdded(). This means when the SourcesProxyModel does the mapFromSource() during the activateIndex() call
(from linkSourceItemToPage) it won't have the new index in the mapping yet as endRowsAdded() hasn't been called. So that will fail.

We have to queue the signal *from the source model to the proxy* not from the proxy to the view.

EUGH.

Also, fix a few logic errors from teh ViewManager/WeakPointer refactor that had bad logic
2011-08-20 00:47:53 -04:00
Leo Franchi
6521198127 TWK-401: Only update source stats after addfiles dbcmd is finished, instead of playing a race condition game. Also, refresh recent albums on stats change 2011-08-19 16:54:37 -04:00
Leo Franchi
498d7052de TWK-415: Don't crash if the trackproxymodel has an invalid current track 2011-08-19 15:45:21 -04:00
Leo Franchi
f0a6d4e8bb TWK-420: Show resolver wrench immediately after enabling a resolver. 2011-08-19 14:50:17 -04:00
Leo Franchi
e7bd29d228 remove stale include 2011-08-19 14:12:43 -04:00
Leo Franchi
5fd7505f7f fixes for linux 2011-08-19 13:54:15 -04:00
Leo Franchi
bc2ea013ac delete maclineedit 2011-08-19 13:41:01 -04:00
Leo Franchi
c1ecab349a Delete viewpages when we are done with them and remove Back/Forward buttons.
Remove toolbar and move search widget into top of sourcetree
2011-08-19 13:35:10 -04:00
Alejandro Wainzinger
2f4805c119 Temporary image cover goes in /tmp.
* Fixes TWK-430
2011-08-18 15:58:15 -07:00
Christian Muehlhaeuser
622150fca2 * Cleaned up icons a bit. 2011-08-18 23:25:39 +02:00
Jason Herskowitz
237de4f9a6 Add icons for artist, album and search temporary sidebar items. We should use these for drag indicators for album and artist drags 2011-08-18 17:11:21 -04:00
Leo Franchi
97d62b06c1 enable some stuff 2011-08-17 18:16:11 -04:00
Alejandro Wainzinger
25f37d692e ChangeLog++ 2011-08-18 00:13:10 +02:00
Christian Muehlhaeuser
33e2c4f935 * Updated ChangeLog. 2011-08-18 00:06:24 +02:00
Christian Muehlhaeuser
d24f2b2bb0 * Remember last used script path and store it. 2011-08-17 23:30:45 +02:00
Christian Muehlhaeuser
76bd0670d8 * No need for clucene in Tomahawk binary. 2011-08-17 22:11:54 +02:00
Leo Franchi
4abb53ccdf Fix resolvers on qt 4.8, baseUrl is required for html5 security policy stuff for localStorage 2011-08-17 14:35:27 -04:00
Leo Franchi
31d19196e2 remove offensively verbose debug 2011-08-17 13:46:12 -04:00
Jason Herskowitz
77d60fa02d De-decapitate avatar placeholder 2011-08-17 10:59:12 -04:00
Leo Franchi
178095acc0 and add semicolon 2011-08-17 10:12:29 -04:00
Leo Franchi
2c846c4ae2 Always select source item when new playlist/stations are created 2011-08-17 10:10:26 -04:00
Alejandro Wainzinger
ab462cd7b8 Actually, don't rely on Phonon's isSeekable. It sometimes lies. 2011-08-17 06:14:56 -07:00
Alejandro Wainzinger
a88afe34c2 Return false if can't seek in MPRIS plugin, a string is actually not a boolean.
awainzin--;
2011-08-17 05:58:02 -07:00
Alejandro Wainzinger
79356b9da8 Respect Phonon's isSeekable() function before seeking. 2011-08-17 05:53:39 -07:00
Alejandro Wainzinger
b74cefd72f Try to respect seek restrictions placed on the playlist. 2011-08-17 04:05:16 -07:00
Alejandro Wainzinger
1d222064fb Add the Seeked signal in MPRIS. 2011-08-17 03:17:31 -07:00
Alejandro Wainzinger
6b1d919518 Use full string in tempfile check. 2011-08-17 02:47:26 -07:00
Alejandro Wainzinger
016bd7eb47 Fix potential crash when temporary file doesn't exist, move coverfetching to metadata. 2011-08-17 02:14:15 -07:00
Leo Franchi
ae9d6f8df4 To temporary pages it is! 2011-08-16 23:14:29 -04:00
Alejandro Wainzinger
8beb3173c6 Clean up MPRIS album cover code a bit. 2011-08-16 19:32:32 -07:00
Alejandro Wainzinger
0bddfe3f0d MPRIS: create a new temp file for each new cover, deleting previous. Prevents caching on client side. 2011-08-16 19:18:45 -07:00
Christian Muehlhaeuser
24febc1066 * More verbose debug output in DBSyncConnection. 2011-08-17 04:14:52 +02:00
Alejandro Wainzinger
97b615da41 Merge branch 'master' of github.com:tomahawk-player/tomahawk 2011-08-16 19:05:59 -07:00
Alejandro Wainzinger
3ee5016230 Push album covers through MPRIS. 2011-08-16 19:05:25 -07:00
Leo Franchi
dae9718e35 remove useless code 2011-08-16 21:38:01 -04:00
Leo Franchi
ec2164b77e allow deleting of temporary item in source tree view 2011-08-16 21:36:06 -04:00
Christian Muehlhaeuser
35a7acd606 * Fixed sorting of identical items in Tree- & TrackProxyModel. 2011-08-17 03:33:31 +02:00
Christian Muehlhaeuser
ac3d410559 * Fixed sidebar sorting for items with identical names. 2011-08-17 03:18:39 +02:00
Christian Muehlhaeuser
b1d57df4fc * Moved the AudioControl QSlider stylesheets into SeekSlider. 2011-08-17 02:35:06 +02:00
Christian Muehlhaeuser
b78e2dd8b6 * No need for mouse tracking. 2011-08-17 02:24:42 +02:00
Christian Muehlhaeuser
c3419cb082 * Fixed TWK-212: Sliders should jump to the position they are clicked on. 2011-08-17 02:20:41 +02:00
Leo Franchi
713243cc5d Allow to show the setting dialog always, and disable +/- buttons till loaded 2011-08-16 19:06:34 -04:00
Leo Franchi
ee8da33e06 Show spinner on initial load while loading sip plugins in preferences. Disable Settings action till servent is loaded 2011-08-16 19:00:29 -04:00
Christian Muehlhaeuser
08f868c2ce * Fix sorting for real. 2011-08-17 00:17:01 +02:00
Christian Muehlhaeuser
4d012b156a * Fixed sidebar sorting. 2011-08-17 00:15:08 +02:00
Michael Zanetti
4c5d34ceb7 Merge branch 'master' of github.com:tomahawk-player/tomahawk 2011-08-16 23:34:44 +02:00
Michael Zanetti
8c4b4cd5a9 added drag capabilities to albumview ins sourceinfowidget and welcomewidget 2011-08-16 23:33:41 +02:00
Alejandro Wainzinger
c1498bbae0 Merge branch 'master' of github.com:tomahawk-player/tomahawk 2011-08-16 22:36:07 +02:00
Alejandro Wainzinger
89daf243f4 When settings have changed and adiumplugin reads it, don't try to clear adium status if plugin goes inactive. 2011-08-16 22:35:15 +02:00
Michael Zanetti
5b0cd3b458 fix copyright headers in SettingsListDelegate 2011-08-16 19:28:17 +02:00
Michael Zanetti
8aa18a7e03 fixed copyright headers 2011-08-16 19:25:50 +02:00
Michael Zanetti
e3b2c547d8 Created DropJob which will start a query for the dropped items and wait for the results 2011-08-16 19:25:50 +02:00
Michael Zanetti
6cb2677c9b more work to be able to drag and drop mixed mimetypes (e.g. an artist and some tracks) 2011-08-16 19:25:50 +02:00
Michael Zanetti
09de8c8387 added drag and drop to playlists for albums and artists 2011-08-16 19:25:50 +02:00
Christian Muehlhaeuser
e6778b5631 * Un-regress listen-along. 2011-08-16 03:24:17 +02:00
Christian Muehlhaeuser
9653918a63 Merge remote-tracking branch 'origin/stable' 2011-08-16 03:14:31 +02:00
Christian Muehlhaeuser
b7d231997d * Don't try to play unavailable results. 2011-08-16 03:08:11 +02:00
Christian Muehlhaeuser
3326e375ae Merge remote-tracking branch 'origin/stable' 2011-08-16 02:45:16 +02:00
Christian Muehlhaeuser
6d26361f4e * Don't crash when encountering an unknown artist in a search. 2011-08-16 01:44:07 +02:00
Christian Muehlhaeuser
611d2015f9 Merge remote-tracking branch 'origin/stable' 2011-08-16 01:30:09 +02:00
Christian Muehlhaeuser
29f88e29ab * Fixed Javascript resolvers not working / breaking Tomahawk. 2011-08-16 01:25:53 +02:00
Christian Muehlhaeuser
58d65c8605 Merge pull request #36 from xevix/master
MPRIS Support - fixed as discussed.
2011-08-15 15:55:30 -07:00
Alejandro Wainzinger
6947a59aa2 Merge branch 'master' of https://github.com/tomahawk-player/tomahawk 2011-08-15 15:53:39 -07:00
Alejandro Wainzinger
2dbf992e56 Properly fix deletion of worker and cache objects in separate threads that are not the GUI thread. 2011-08-15 15:51:39 -07:00
Alejandro Wainzinger
767602c0dc Merge branch 'master' of https://github.com/tomahawk-player/tomahawk 2011-08-15 13:30:59 -07:00
Alejandro Wainzinger
c1cbc3576c Merge branch 'master' of https://github.com/tomahawk-player/tomahawk 2011-08-14 07:04:48 -07:00
Alejandro Wainzinger
97e12f9b95 Create QThread subclasses for InfoSystemWorker and InfoSystemCache, so we don't try to delete them from a different thread (illegal in Qt), instead delete from QThread subclass' destructor. 2011-08-14 02:32:47 -07:00
Alejandro Wainzinger
d243a5f70e Merge branch 'master' of https://github.com/tomahawk-player/tomahawk 2011-08-13 02:14:11 -07:00
Alejandro Wainzinger
7eaed053f5 Merge branch 'master' of https://github.com/tomahawk-player/tomahawk 2011-08-12 19:45:52 -07:00
Alejandro Wainzinger
7354896a86 Implement remaining MPRIS PropertiesChanged update signals, seeking, etc. 2011-08-12 19:35:57 -07:00
Alejandro Wainzinger
ce396ab060 Implement PropertiesChanged for AudioState in MPRIS. 2011-08-12 07:25:33 -07:00
Alejandro Wainzinger
5f59b30a8c Add basic metadata MPRIS info. 2011-08-11 17:49:30 -07:00
Alejandro Wainzinger
49dcd70dd2 Implement most MPRIS methods, minus metadata and related methods. 2011-08-11 12:27:15 -07:00
Alejandro Wainzinger
6996c704eb Merge branch 'master' of https://github.com/tomahawk-player/tomahawk 2011-08-10 05:26:51 -07:00
Alejandro Wainzinger
d59dcda052 Add MprisPluginPlayerAdaptor skeleton. 2011-08-09 11:07:46 -07:00
Alejandro Wainzinger
c90c0a261d Initial skeleton for MPRIS support. 2011-08-09 02:31:54 -07:00
267 changed files with 13970 additions and 3250 deletions

1
.gitignore vendored
View File

@@ -16,3 +16,4 @@ thirdparty/qtweetlib/WARNING-twitter-api-keys
clang/
win/
gcc/
tags

View File

@@ -16,7 +16,7 @@ SET( TOMAHAWK_DESCRIPTION_SUMMARY "The social media player" )
SET( TOMAHAWK_VERSION_MAJOR 0 )
SET( TOMAHAWK_VERSION_MINOR 2 )
SET( TOMAHAWK_VERSION_PATCH 1 )
SET( TOMAHAWK_VERSION_PATCH 99 )
#SET( TOMAHAWK_VERSION_RC 0 )
@@ -24,6 +24,8 @@ SET( TOMAHAWK_VERSION_PATCH 1 )
# build options
option(BUILD_GUI "Build Tomahawk with GUI" ON)
option(BUILD_RELEASE "Generate TOMAHAWK_VERSION without GIT info" OFF)
option(BUILD_GUI_QML"Build Tomahawk with QML Support" OFF)
# generate version string
@@ -71,6 +73,15 @@ ELSE()
MESSAGE( STATUS "Building Tomahawk ${TOMAHAWK_VERSION} full GUI version ***" )
LIST(APPEND NEEDED_QT4_COMPONENTS "QtGui" "QtWebkit" )
ENDIF()
IF( BUILD_GUI_QML )
MESSAGE( STATUS "Building Tomahawk QML version ***" )
LIST(APPEND NEEDED_QT4_COMPONENTS "QtDeclarative" )
ENDIF()
IF( BUILD_GUI AND UNIX AND NOT APPLE )
FIND_PACKAGE( X11 )
ENDIF()
macro_optional_find_package(Qt4 4.7.0 COMPONENTS ${NEEDED_QT4_COMPONENTS} )
macro_log_feature(QT4_FOUND "Qt" "A cross-platform application and UI framework" "http://qt.nokia.com" TRUE "" "If you see this, although libqt4-devel is installed, check whether \n the qtwebkit-devel package is installed as well")
@@ -98,7 +109,8 @@ check_taglib_filename( COMPLEX_TAGLIB_FILENAME )
macro_optional_find_package(Boost)
macro_log_feature(Boost_FOUND "Boost" "Provides free peer-reviewed portable C++ source libraries" "http://www.boost.org" TRUE "" "") #FIXME: give useful explaination
macro_optional_find_package(QCA2)
macro_log_feature(QCA2_FOUND "QCA2" "Provides encryption and signing functions required for Grooveshark resolver" "http://delta.affinix.com/qca/" FALSE "" "")
# required
#While we distribute our own liblastfm2, don't need to look for it
@@ -153,9 +165,12 @@ macro_optional_find_package(KDE4Installed)
# macro_optional_find_package(KDE4)
IF(KDE4_FOUND)
#KDE4 adds and removes some compiler flags that we don't like
STRING( REPLACE "-std=iso9899:1990" "" CLEAN_C_FLAGS ${CMAKE_C_FLAGS} )
SET( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fexceptions" )
IF( CMAKE_C_FLAGS )
# KDE4 adds and removes some compiler flags that we don't like
# (only for gcc not for clang e.g.)
STRING( REPLACE "-std=iso9899:1990" "" CLEAN_C_FLAGS ${CMAKE_C_FLAGS} )
SET( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fexceptions" )
ENDIF()
ELSE()
SET( CLEAN_C_FLAGS ${CMAKE_C_FLAGS} )
ENDIF()

View File

@@ -0,0 +1,48 @@
# - Try to find QCA2 (Qt Cryptography Architecture 2)
# Once done this will define
#
# QCA2_FOUND - system has QCA2
# QCA2_INCLUDE_DIR - the QCA2 include directory
# QCA2_LIBRARIES - the libraries needed to use QCA2
# QCA2_DEFINITIONS - Compiler switches required for using QCA2
#
# use pkg-config to get the directories and then use these values
# in the FIND_PATH() and FIND_LIBRARY() calls
# Copyright (c) 2006, Michael Larouche, <michael.larouche@kdemail.net>
#
# Redistribution and use is allowed according to the terms of the BSD license.
# For details see the accompanying COPYING-CMAKE-SCRIPTS file.
include(FindLibraryWithDebug)
if (QCA2_INCLUDE_DIR AND QCA2_LIBRARIES)
# in cache already
set(QCA2_FOUND TRUE)
else (QCA2_INCLUDE_DIR AND QCA2_LIBRARIES)
if (NOT WIN32)
find_package(PkgConfig)
pkg_check_modules(PC_QCA2 qca2)
set(QCA2_DEFINITIONS ${PC_QCA2_CFLAGS_OTHER})
endif (NOT WIN32)
find_library_with_debug(QCA2_LIBRARIES
WIN32_DEBUG_POSTFIX d
NAMES qca
HINTS ${PC_QCA2_LIBDIR} ${PC_QCA2_LIBRARY_DIRS}
)
find_path(QCA2_INCLUDE_DIR qca.h
HINTS ${PC_QCA2_INCLUDEDIR} ${PC_QCA2_INCLUDE_DIRS}
PATH_SUFFIXES QtCrypto)
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(QCA2 DEFAULT_MSG QCA2_LIBRARIES QCA2_INCLUDE_DIR)
mark_as_advanced(QCA2_INCLUDE_DIR QCA2_LIBRARIES)
endif (QCA2_INCLUDE_DIR AND QCA2_LIBRARIES)

View File

@@ -203,7 +203,7 @@ Function PageReinstall
IntCmp $R0 ${VER_MINOR} build_check new_version older_version
build_check:
ReadRegDWORD $R0 HKLM "Software\Tomahawk" "VersionBuild"
IntCmp $R0 ${VER_BUILD} new_version older_version
IntCmp $R0 ${VER_BUILD} same_version new_version older_version
new_version:
!insertmacro INSTALLOPTIONS_WRITE "NSIS.InstallOptions.ini" "Field 1" "Text" "An older version of Tomahawk is installed on your system. It is recommended that you uninstall the current version before installing. Select the operation you want to perform and click Next to continue."

View File

@@ -1,3 +1,35 @@
Version 0.3.0:
* Added Charts page, which shows various sources' top hits & artists.
* The Collection tree-views can now be filtered.
* Moved the song queue below to the left, below the sidebar.
* Added Footnotes, a contextual view that you can slide it.
* Show recently added playlists in dashboard rather than recently opened
playlists.
* Added MPRIS 2.1 support.
Version 0.2.3:
* Fixed opening Rdio and Spotify links.
* Fixed potential crash in sidebar during syncing of sources.
* When Listening Along, the last song a peer plays is no longer duplicated.
* Fixed an issue where the Twitter plugin could get out of sync if the
database was cleared, leading to eventual crashes when re-connecting.
* Fixed duplicate albums showing up on Dashboard.
* Automatically sort search results by score.
* Fixed stations being stuck not fetching more songs.
* Fixed issue where artist bio could be referring to a different artist.
* Opening a "tomahawk" URL (or other URL with Tomahawk) brings the Tomahawk
window to the foreground.
Version 0.2.2:
* Fixed crash pressing previous and next when playing a song from the Queue.
* Fixed issue where wrench for newly added resolvers would not show up.
* Fixed sidebar statistics not updating after collection scan finished.
* Fixed omitting a few tracks in the Collection tree-view.
* Fixed sidebar & track sorting issues.
* Seek- & volume sliders now directly jump to the position you clicked on.
* Added ability to drag artists and albums within Tomahawk.
* (OS X) Fixed Ogg Vorbis support.
Version 0.2.1:
* Fixed crashing trying to play an unavailable track.
* Fixed a crash caused by using Javascript resolvers.

View File

@@ -1,60 +1,60 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 14.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 43363) -->
<!-- Generator: Adobe Illustrator 15.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="841.89px" height="595.28px" viewBox="0 0 841.89 595.28" enable-background="new 0 0 841.89 595.28" xml:space="preserve">
<path fill="#010101" d="M543.773,253.359c0-49.962-19.305-86.572-54.194-104.54C477.838,71.133,405.027,13.418,316.568,13.418
c-86.917,0-159.483,56.481-172.605,132.58c-38.049,16.818-59.889,55.24-59.889,107.361c0,27.029,7.186,51.825,20.321,71.748
c-1.816,1.566-3.444,3.283-4.768,5.24c0,0,2.582,2.36,3.689,3.374c-4.262,2.123-8.22,4.322-11.555,6.669
c0,0,3.022,7.355,5.898,14.352c-11.991,9.687-21.314,18.92-27.553,27.51c0,0,1.612,1.789,2.011,2.224
c-12.355,9.158-22.133,18.206-28.528,27.021c0,0,6.925,7.351,12.408,13.167c-17.824,28.759-43.348,74.85-43.348,102.987
c0,3.281,0.346,6.318,1.093,9.054c1.764,6.442,5.752,11.437,11.229,14.058c15.255,7.311,39.648-6.364,72.589-40.434
c-0.828,8.288-1.457,16.663-1.457,24.737c0,16.804,2.266,32.07,8.77,41.249c4.886,6.895,10.898,9.07,15.084,9.683
c6.721,0.989,26.375,3.796,57.992-61.296c2.678,0.705,5.421,1.179,8.235,1.179c8.798,0,17.067-3.425,23.287-9.639
c4.286-4.288,7.133-9.584,8.562-15.313c7.516-2.129,14.555-6.702,20.779-13.191c19.563,24.861,44.78,37.643,75.38,37.643
c7.318,0,14.474-0.755,21.281-2.243c0.392-0.075,2.555-0.622,2.555-0.622l0.62-0.167c23.05-5.824,42.463-20.41,58-43.254
c7.2,9.231,15.733,15.994,24.849,19.458c1.139,6.658,4.224,12.806,9.1,17.69c6.22,6.214,14.488,9.639,23.288,9.639
c8.798,0,17.065-3.425,23.285-9.639c6.221-6.228,9.649-14.494,9.649-23.292s-3.429-17.066-9.649-23.294
c-2.988-2.988-6.476-5.296-10.248-6.923c0.277-2.947,0.613-5.848,0.613-9.007c0-10.031-1.4-20.982-4.192-31.9
c-1.196-4.672-2.757-8.745-4.511-12.567c2.838-6.467,4.805-13.268,4.805-20.426c0-7.317-1.74-14.422-4.706-21.011
C509.263,358.346,543.773,313.068,543.773,253.359z M451.232,150.998c-33.395-54.619-88.048-93.359-134.569-85.472
c-49.024-7.864-102.777,30.64-135.326,85.018c-2.648,0.208-5.278,0.497-7.875,0.88c8.694-54.414,69.429-111.108,143.107-111.108
c74.113,0,135.141,57.197,143.269,112.08C457.013,151.799,454.137,151.348,451.232,150.998z"/>
<path fill="#010101" stroke="#FFFFFF" stroke-width="4.8437" d="M110.937,367.38c-72.03,93.07-97.843,163.348-81.865,170.997
c15.97,7.654,74.912-46.056,139.701-143.288c45.751-68.648,60.797-94.429,45.68-95.922l20.614-43.357l-10.685-6.644l-33.912,33.371
width="600px" height="600px" viewBox="0 0 600 600" enable-background="new 0 0 600 600" xml:space="preserve">
<path fill="#010101" d="M543.773,253.359c0-49.962-19.305-86.572-54.194-104.54C477.84,71.133,405.027,13.418,316.568,13.418
c-86.917,0-159.483,56.481-172.605,132.58c-38.049,16.818-59.889,55.24-59.889,107.361c0,27.029,7.188,51.824,20.321,71.749
c-1.816,1.565-3.444,3.282-4.77,5.239c0,0,2.582,2.359,3.689,3.373c-4.262,2.123-8.22,4.322-11.555,6.67
c0,0,3.021,7.355,5.896,14.353c-11.991,9.688-21.312,18.92-27.553,27.51c0,0,1.611,1.789,2.011,2.225
c-12.354,9.158-22.133,18.205-28.528,27.021c0,0,6.927,7.35,12.408,13.166c-17.824,28.76-43.348,74.85-43.348,102.988
c0,3.278,0.346,6.315,1.093,9.053c1.764,6.443,5.752,11.438,11.229,14.059c15.255,7.312,39.646-6.362,72.589-40.434
c-0.828,8.287-1.457,16.662-1.457,24.736c0,16.805,2.266,32.069,8.77,41.25c4.887,6.895,10.898,9.067,15.084,9.682
c6.723,0.99,26.375,3.797,57.992-61.295c2.678,0.705,5.421,1.178,8.235,1.178c8.798,0,17.065-3.424,23.287-9.639
c4.284-4.287,7.133-9.584,8.562-15.312c7.516-2.129,14.555-6.701,20.779-13.19c19.562,24.86,44.779,37.643,75.38,37.643
c7.317,0,14.474-0.754,21.281-2.242c0.392-0.073,2.555-0.623,2.555-0.623l0.62-0.166c23.05-5.823,42.463-20.409,58-43.254
c7.2,9.23,15.731,15.994,24.849,19.457c1.141,6.658,4.226,12.808,9.102,17.691c6.22,6.213,14.487,9.639,23.288,9.639
c8.798,0,17.063-3.426,23.285-9.639c6.221-6.229,9.647-14.494,9.647-23.293c0-8.797-3.429-17.066-9.647-23.293
c-2.988-2.988-6.478-5.297-10.248-6.924c0.275-2.947,0.611-5.849,0.611-9.009c0-10.028-1.398-20.979-4.19-31.897
c-1.196-4.672-2.757-8.746-4.513-12.568c2.84-6.467,4.807-13.268,4.807-20.426c0-7.316-1.74-14.422-4.706-21.01
C509.263,358.345,543.773,313.068,543.773,253.359z M451.232,150.998c-33.396-54.619-88.048-93.359-134.569-85.472
c-49.022-7.864-102.775,30.64-135.326,85.018c-2.646,0.208-5.276,0.497-7.875,0.88c8.694-54.414,69.431-111.108,143.107-111.108
c74.113,0,135.142,57.197,143.271,112.08C457.013,151.799,454.137,151.348,451.232,150.998z"/>
<path fill="#010101" stroke="#FFFFFF" stroke-width="4.8437" d="M110.937,367.38c-72.03,93.07-97.843,163.349-81.865,170.996
c15.97,7.654,74.912-46.055,139.701-143.287c45.751-68.647,60.797-94.43,45.68-95.922l20.614-43.357l-10.687-6.644l-33.91,33.371
C179.323,272.615,166.073,296.146,110.937,367.38z"/>
<path fill="#010101" stroke="#FFFFFF" stroke-width="4.8437" d="M140.932,379.755c-39.099,113.042-39.738,189.89-20.813,192.654
c18.912,2.787,59.039-67.616,89.306-182.6c21.381-81.178,27.255-111.03,11.438-108.001l5.133-48.806l-13.207-3.354l-22.194,42.936
<path fill="#010101" stroke="#FFFFFF" stroke-width="4.8437" d="M140.932,379.755c-39.101,113.041-39.738,189.89-20.813,192.654
c18.912,2.787,59.039-67.617,89.306-182.603c21.383-81.178,27.257-111.029,11.438-108l5.133-48.806l-13.207-3.354l-22.192,42.936
C175.736,266.142,170.865,293.244,140.932,379.755z"/>
<circle fill="none" stroke="#010101" stroke-width="4.8437" cx="410.28" cy="388.831" r="37.801"/>
<circle fill="none" stroke="#010101" stroke-width="4.8437" cx="410.28" cy="388.832" r="37.801"/>
<path fill="#ED2224" stroke="#010101" stroke-width="4.8437" d="M475.237,157.792c-7.115-73.197-76.231-130.645-160.495-130.645
c-83.402,0-152.009,56.27-160.306,128.393c-39.809,13.607-58.455,52.219-58.455,97.818c0,56.947,34.681,103.109,89.925,103.109
c35.784,0,67.089-19.432,84.771-48.551c14.01,3.533,28.778,5.463,44.066,5.463c13.16,0,25.932-1.438,38.173-4.077
c17.966,28.357,49.108,47.165,84.604,47.165c55.665,0,90.696-46.162,90.696-103.109
C528.216,210.073,512.208,173.073,475.237,157.792z M314.742,274.908c-10.501,0-20.723-0.878-30.584-2.506
c-83.402,0-152.009,56.27-160.306,128.393c-39.811,13.607-58.455,52.219-58.455,97.818c0,56.946,34.681,103.108,89.925,103.108
c35.784,0,67.089-19.432,84.771-48.551c14.01,3.533,28.776,5.463,44.064,5.463c13.16,0,25.934-1.438,38.173-4.076
c17.966,28.357,49.108,47.164,84.604,47.164c55.665,0,90.696-46.162,90.696-103.108
C528.218,210.073,512.209,173.073,475.237,157.792z M314.742,274.908c-10.501,0-20.723-0.878-30.584-2.506
c1.125-6.177,1.778-12.527,1.778-19.043c0-56.941-44.786-103.104-100.03-103.104c-4.858,0-9.597,0.478-14.271,1.169
c8.694-54.414,69.43-111.108,143.107-111.108c74.113,0,135.142,57.197,143.268,112.08c-6.617-1.398-13.471-2.141-20.49-2.141
c8.692-54.414,69.43-111.108,143.105-111.108c74.113,0,135.144,57.197,143.27,112.08c-6.617-1.398-13.473-2.141-20.49-2.141
c-55.675,0-100.801,46.163-100.801,103.104c0,6.852,0.687,13.536,1.931,20.013C330.872,274.364,322.888,274.908,314.742,274.908z"/>
<linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="-55.6953" y1="95.2363" x2="39.7773" y2="95.2363" gradientTransform="matrix(-0.3782 -0.8348 1.083 -0.4907 70.5233 338.0542)">
<linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="-1087.6663" y1="191.4385" x2="-992.1917" y2="191.4385" gradientTransform="matrix(-0.3782 -0.8348 1.083 -0.4907 -423.9551 -476.2263)">
<stop offset="0" style="stop-color:#010101"/>
<stop offset="0.2606" style="stop-color:#3E221C"/>
<stop offset="0.7732" style="stop-color:#B03126"/>
<stop offset="1" style="stop-color:#ED2224"/>
</linearGradient>
<path fill="url(#SVGID_1_)" d="M153.69,260.35c31.322-14.19,65.739-18.887,77.129,6.254c11.391,25.14-4.763,57.027-36.091,71.213
c-31.323,14.194-65.948,5.312-77.338-19.829C106,292.849,122.366,274.543,153.69,260.35z"/>
<linearGradient id="SVGID_2_" gradientUnits="userSpaceOnUse" x1="-180.8516" y1="181.2642" x2="-85.3789" y2="181.2642" gradientTransform="matrix(-0.9008 -0.3607 0.555 -1.386 252.7537 479.2919)">
<path fill="url(#SVGID_1_)" d="M153.69,260.35c31.322-14.19,65.739-18.887,77.129,6.254c11.391,25.14-4.763,57.026-36.091,71.214
c-31.323,14.192-65.948,5.311-77.338-19.83C106,292.849,122.366,274.543,153.69,260.35z"/>
<linearGradient id="SVGID_2_" gradientUnits="userSpaceOnUse" x1="-1275.886" y1="215.5034" x2="-1180.4114" y2="215.5034" gradientTransform="matrix(-0.9008 -0.3607 0.555 -1.386 -752.6548 131.7681)">
<stop offset="0" style="stop-color:#B11F24"/>
<stop offset="0.1017" style="stop-color:#B62024"/>
<stop offset="1" style="stop-color:#ED2224"/>
</linearGradient>
<path fill="url(#SVGID_2_)" d="M427.738,265.165c16.043-40.085,41.342-74.985,68.473-64.122
c27.122,10.86,36.111,52.165,20.056,92.248c-16.053,40.089-51.062,63.772-78.189,52.911
<path fill="url(#SVGID_2_)" d="M427.738,265.165c16.043-40.085,41.342-74.985,68.475-64.122
c27.121,10.86,36.109,52.165,20.056,92.248c-16.053,40.089-51.062,63.773-78.189,52.912
C410.95,335.343,411.683,305.253,427.738,265.165z"/>
<path fill="#010101" stroke="#010101" stroke-width="4.8437" d="M448.219,425.226c7.776,30.397,3.982,62.358-14.521,62.358
c-16.301,0-34.904-17.333-42.675-47.733c-7.777-30.4,4.243-75.016,14.521-62.365C423.583,399.707,440.442,394.825,448.219,425.226z"
/>
<linearGradient id="SVGID_3_" gradientUnits="userSpaceOnUse" x1="217.0322" y1="557.499" x2="165.1699" y2="467.6709" gradientTransform="matrix(0.9868 0.0515 0.0515 0.7987 -5.5217 21.3475)">
<path fill="#010101" stroke="#010101" stroke-width="4.8437" d="M448.219,425.226c7.774,30.396,3.98,62.357-14.521,62.357
c-16.301,0-34.904-17.332-42.675-47.732c-7.777-30.399,4.243-75.016,14.521-62.364C423.584,399.707,440.442,394.826,448.219,425.226
z"/>
<linearGradient id="SVGID_3_" gradientUnits="userSpaceOnUse" x1="-639.5422" y1="548.502" x2="-691.4051" y2="458.673" gradientTransform="matrix(0.9868 0.0515 0.0515 0.7987 840.2104 72.6462)">
<stop offset="0" style="stop-color:#5B3E1D"/>
<stop offset="0.1121" style="stop-color:#42301B"/>
<stop offset="0.2377" style="stop-color:#2E2316"/>
@@ -64,29 +64,30 @@
<stop offset="1" style="stop-color:#000000"/>
</linearGradient>
<path fill="url(#SVGID_3_)" stroke="#010101" stroke-width="4.8437" d="M184.076,437.859c-6.111,23.875,1.745,46.508,17.54,50.549
c15.79,4.039,33.552-12.045,39.658-35.924c6.101-23.881-9.153-57.129-24.947-61.17C200.537,387.275,190.178,413.973,184.076,437.859
c15.79,4.039,33.552-12.045,39.658-35.924c6.101-23.882-9.153-57.129-24.947-61.17C200.537,387.275,190.178,413.972,184.076,437.859
z"/>
<path fill="#010101" stroke="#010101" stroke-width="4.8437" d="M314.837,65.525c72.755-12.333,165.503,89.311,165.503,187.528
c0,98.215-74.305,162.834-165.707,162.834"/>
<linearGradient id="SVGID_4_" gradientUnits="userSpaceOnUse" x1="194.4072" y1="374.3564" x2="358.2427" y2="90.5851">
<path fill="#010101" stroke="#010101" stroke-width="4.8437" d="M314.837,65.525c72.757-12.333,165.505,89.311,165.505,187.528
c0,98.213-74.307,162.833-165.707,162.833"/>
<linearGradient id="SVGID_4_" gradientUnits="userSpaceOnUse" x1="-664.3811" y1="374.0068" x2="-500.546" y2="90.2362" gradientTransform="matrix(1 0 0 1 858.79 0.3506)">
<stop offset="0" style="stop-color:#000000"/>
<stop offset="1" style="stop-color:#5B3E1D"/>
</linearGradient>
<path fill="url(#SVGID_4_)" stroke="#010101" stroke-width="4.8437" d="M314.634,415.888c-91.402,0-165.297-64.619-165.297-162.834
<path fill="url(#SVGID_4_)" stroke="#010101" stroke-width="4.8437" d="M314.635,415.888c-91.402,0-165.299-64.619-165.299-162.833
c0-98.218,88.615-199.862,165.5-187.528"/>
<linearGradient id="SVGID_5_" gradientUnits="userSpaceOnUse" x1="297.3164" y1="139.4717" x2="418.2684" y2="-70.0234" gradientTransform="matrix(-0.6092 0.7802 -0.4118 -0.3216 476.7253 -96.4624)">
<linearGradient id="SVGID_5_" gradientUnits="userSpaceOnUse" x1="-760.302" y1="-37.9087" x2="-639.3506" y2="-247.4027" gradientTransform="matrix(-0.6092 0.7802 -0.4118 -0.3216 -240.6183 671.6459)">
<stop offset="0" style="stop-color:#8B654B"/>
<stop offset="1" style="stop-color:#000000"/>
</linearGradient>
<path fill="url(#SVGID_5_)" d="M169.611,266.578c-27.578-21.539-16.863-81.354,23.935-133.604
c40.802-52.245,93.808-72.894,126.21-59.689c26.67,10.864,14.474,85.434-26.328,137.681
c40.804-52.245,93.81-72.894,126.21-59.689c26.672,10.864,14.476,85.434-26.326,137.681
C252.63,263.215,197.193,288.112,169.611,266.578z"/>
<circle fill="#010101" cx="214.367" cy="388.831" r="34.682"/>
<circle fill="#010101" cx="214.367" cy="388.832" r="34.682"/>
<g>
<path fill="#F4DFED" d="M131.831,427.709c-23.973,79.733-18.741,132.267-9.011,135.459c10.855,3.563,43.561-47.223,60.839-128.324
c12.189-57.265,11.727-83.75,0.009-81.625l3.505-31.111l-8.901-2.432l-14.271,32.445
C152.554,347.564,150.184,366.689,131.831,427.709z"/>
<linearGradient id="SVGID_6_" gradientUnits="userSpaceOnUse" x1="148.1519" y1="319.6758" x2="148.1518" y2="563.168">
<path fill="#F4DFED" d="M131.831,427.708c-23.973,79.734-18.741,132.268-9.011,135.459c10.854,3.564,43.561-47.223,60.839-128.324
c12.189-57.264,11.729-83.75,0.009-81.625l3.505-31.108l-8.899-2.435l-14.271,32.445
C152.554,347.564,150.184,366.689,131.831,427.708z"/>
<linearGradient id="SVGID_6_" gradientUnits="userSpaceOnUse" x1="-710.6404" y1="319.3252" x2="-710.6405" y2="562.8164" gradientTransform="matrix(1 0 0 1 858.79 0.3506)">
<stop offset="0" style="stop-color:#571A54"/>
<stop offset="0.2641" style="stop-color:#7C5378"/>
<stop offset="0.528" style="stop-color:#A78AA3"/>
@@ -94,11 +95,11 @@
<stop offset="0.9095" style="stop-color:#EAE1E8"/>
<stop offset="1" style="stop-color:#F9F3F8"/>
</linearGradient>
<path fill="url(#SVGID_6_)" d="M146.41,479.117l-22.914,19.186l35.325-65.182l4.683-17.371c0,0,3.098-26.758-24.228,14.713
c16.962-56.432,32.317-62.998,32.317-62.998l11.404-46.658l-4.725-1.131l-14.702,30.23c-11.429-4.588-13.859,14.521-32.392,75.485
c-24.224,79.655-20.501,124.085-8.358,137.776L146.41,479.117z"/>
<path fill="url(#SVGID_6_)" d="M146.41,479.117l-22.914,19.186l35.325-65.182l4.683-17.371c0,0,3.1-26.758-24.228,14.713
c16.962-56.432,32.317-62.998,32.317-62.998l11.402-46.658l-4.725-1.131l-14.702,30.23c-11.429-4.588-13.859,14.521-32.392,75.483
c-24.226,79.656-20.501,124.087-8.358,137.777L146.41,479.117z"/>
<linearGradient id="SVGID_7_" gradientUnits="userSpaceOnUse" x1="75.7998" y1="402.9658" x2="43.9086" y2="490.7602" gradientTransform="matrix(0.9399 -0.3414 0.3414 0.9399 -44.7892 56.207)">
<linearGradient id="SVGID_7_" gradientUnits="userSpaceOnUse" x1="-789.3508" y1="444.1797" x2="-821.2424" y2="531.975" gradientTransform="matrix(0.9399 -0.3414 0.3414 0.9399 754.2939 -277.891)">
<stop offset="0" style="stop-color:#802986"/>
<stop offset="0.2641" style="stop-color:#9D66A2"/>
<stop offset="0.528" style="stop-color:#BE9BC2"/>
@@ -106,10 +107,10 @@
<stop offset="0.9095" style="stop-color:#F0E6F1"/>
<stop offset="1" style="stop-color:#F9F3F8"/>
</linearGradient>
<path fill="url(#SVGID_7_)" d="M147.332,501.478l31.839-92.4c0,0,6.608,60.692-7.474,93.405
c1.438-44.878-2.328-36.951-9.176-25.254C159.71,482.03,147.332,501.478,147.332,501.478z"/>
<path fill="url(#SVGID_7_)" d="M147.332,501.478l31.839-92.399c0,0,6.608,60.69-7.474,93.406
c1.438-44.879-2.328-36.951-9.176-25.257C159.71,482.031,147.332,501.478,147.332,501.478z"/>
<linearGradient id="SVGID_8_" gradientUnits="userSpaceOnUse" x1="100.2559" y1="370.0205" x2="67.8228" y2="459.3065" gradientTransform="matrix(0.9399 -0.3414 0.3414 0.9399 -44.7892 56.207)">
<linearGradient id="SVGID_8_" gradientUnits="userSpaceOnUse" x1="-764.8928" y1="411.2344" x2="-797.3259" y2="500.5203" gradientTransform="matrix(0.9399 -0.3414 0.3414 0.9399 754.2939 -277.891)">
<stop offset="0" style="stop-color:#802986"/>
<stop offset="0.2641" style="stop-color:#9D66A2"/>
<stop offset="0.528" style="stop-color:#BE9BC2"/>
@@ -117,10 +118,10 @@
<stop offset="0.9095" style="stop-color:#F0E6F1"/>
<stop offset="1" style="stop-color:#F9F3F8"/>
</linearGradient>
<path fill="url(#SVGID_8_)" d="M156.286,463.716l38.825-93.952c0,0,2.251,62.278-11.831,94.991
c-0.298-44.249-5.388-28.089-8.538-23.508C165.662,454.416,156.286,463.716,156.286,463.716z"/>
<path fill="url(#SVGID_8_)" d="M156.286,463.716l38.825-93.953c0,0,2.251,62.279-11.831,94.992
c-0.298-44.25-5.388-28.09-8.538-23.508C165.662,454.416,156.286,463.716,156.286,463.716z"/>
<linearGradient id="SVGID_9_" gradientUnits="userSpaceOnUse" x1="19.1768" y1="337.1123" x2="-32.4432" y2="389.2988" gradientTransform="matrix(0.711 -0.7032 0.7032 0.711 -56.5767 131.5096)">
<linearGradient id="SVGID_9_" gradientUnits="userSpaceOnUse" x1="-872.7551" y1="422.8174" x2="-924.3751" y2="475.0038" gradientTransform="matrix(0.711 -0.7032 0.7032 0.711 517.3177 -556.6326)">
<stop offset="0" style="stop-color:#571A54"/>
<stop offset="0.2641" style="stop-color:#7C5378"/>
<stop offset="0.528" style="stop-color:#A78AA3"/>
@@ -128,21 +129,22 @@
<stop offset="0.9095" style="stop-color:#EAE1E8"/>
<stop offset="1" style="stop-color:#F9F3F8"/>
</linearGradient>
<path fill="url(#SVGID_9_)" d="M180.916,431.115l9.787-73.404c0,0,18.589,43.111,16.428,71.425
c-11.773-30.423-13.671-21.048-17.578-14.96C187.396,417.538,180.916,431.115,180.916,431.115z"/>
<path fill="#1E3D73" d="M174.914,335.556L158.76,362.31l-23.24,76.647c0,0,1.211-8.173,1.788-15.883l-18.783,50.773
c0,0,8.386-31.226,3.609-28.382c-4.777,2.84,4.225-21.91-23.102,13.729c5.316-18.514,20.495-39.508,33.324-54.9
c-2.172,0.474-7.1,4.788-18.387,18.374c4.38-15.232,14.957-32.934,25.358-47.898c-1.159-2.683-5.151-3.889-21.234,5.221
C134.366,356.719,174.914,335.556,174.914,335.556z"/>
<path fill="url(#SVGID_9_)" d="M180.916,431.115l9.787-73.404c0,0,18.589,43.111,16.428,71.426
c-11.771-30.424-13.671-21.049-17.576-14.961C187.396,417.539,180.916,431.115,180.916,431.115z"/>
<path fill="#1E3D73" d="M174.914,335.556L158.76,362.31l-23.24,76.646c0,0,1.211-8.172,1.788-15.883l-18.783,50.772
c0,0,8.386-31.227,3.607-28.383c-4.775,2.84,4.227-21.91-23.102,13.73c5.316-18.517,20.495-39.511,33.324-54.9
c-2.172,0.473-7.102,4.787-18.388,18.373c4.381-15.232,14.956-32.934,25.357-47.898c-1.159-2.682-5.151-3.889-21.234,5.224
C134.366,356.718,174.914,335.556,174.914,335.556z"/>
</g>
<g>
<path fill="#F4DFED" d="M94.453,401.163c-49.752,66.752-62.779,117.913-54.719,124.242c8.987,7.059,57.064-29.51,100.989-99.844
c31.011-49.657,39.62-74.709,27.879-76.714l13.916-28.052l-7.539-5.319L150.496,341.1
C141.291,332.911,132.531,350.072,94.453,401.163z"/>
<path fill="#656B84" d="M90.603,454.464L62.515,464.67l55.456-49.201l10.331-14.731c0,0,12.047-24.096-27.794,5.558
c35.211-47.255,51.89-48.182,51.89-48.182l26.641-39.956l-4.058-2.681l-24.133,23.399c-9.177-8.222-17.989,8.911-56.224,59.885
c-49.96,66.6-61.634,109.635-54.889,126.645L90.603,454.464z"/>
<linearGradient id="SVGID_10_" gradientUnits="userSpaceOnUse" x1="106.2383" y1="485.0312" x2="121.9901" y2="395.6984">
<path fill="#F4DFED" d="M94.453,401.164c-49.752,66.752-62.779,117.912-54.719,124.242c8.987,7.059,57.062-29.513,100.989-99.847
c31.011-49.655,39.62-74.709,27.879-76.713l13.916-28.053l-7.539-5.318l-24.483,25.623
C141.291,332.912,132.531,350.072,94.453,401.164z"/>
<path fill="#656B84" d="M90.603,454.464l-28.088,10.205l55.456-49.201l10.331-14.729c0,0,12.047-24.097-27.796,5.559
c35.213-47.256,51.892-48.182,51.892-48.182l26.641-39.957l-4.058-2.683l-24.133,23.4c-9.179-8.223-17.989,8.91-56.226,59.885
c-49.96,66.601-61.634,109.635-54.889,126.646L90.603,454.464z"/>
<linearGradient id="SVGID_10_" gradientUnits="userSpaceOnUse" x1="-752.551" y1="484.6816" x2="-736.7991" y2="395.3488" gradientTransform="matrix(1 0 0 1 858.79 0.3506)">
<stop offset="0" style="stop-color:#571A54"/>
<stop offset="0.2641" style="stop-color:#7C5378"/>
<stop offset="0.528" style="stop-color:#A78AA3"/>
@@ -150,9 +152,10 @@
<stop offset="0.9095" style="stop-color:#EAE1E8"/>
<stop offset="1" style="stop-color:#F9F3F8"/>
</linearGradient>
<path fill="url(#SVGID_10_)" d="M83.829,475.791l61.474-75.981c0,0-14.507,59.304-38.916,85.248
c16.678-41.696,12.058-33.368,0-26.873C101.491,460.821,83.829,475.791,83.829,475.791z"/>
<linearGradient id="SVGID_11_" gradientUnits="userSpaceOnUse" x1="130.9844" y1="453.7012" x2="146.8758" y2="363.5764">
<path fill="url(#SVGID_10_)" d="M83.829,475.791l61.476-75.98c0,0-14.509,59.306-38.916,85.248
c16.678-41.694,12.058-33.366,0-26.873C101.491,460.822,83.829,475.791,83.829,475.791z"/>
<linearGradient id="SVGID_11_" gradientUnits="userSpaceOnUse" x1="-727.8064" y1="453.3486" x2="-711.9149" y2="363.2233" gradientTransform="matrix(1 0 0 1 858.79 0.3506)">
<stop offset="0" style="stop-color:#571A54"/>
<stop offset="0.2641" style="stop-color:#7C5378"/>
<stop offset="0.528" style="stop-color:#A78AA3"/>
@@ -160,37 +163,36 @@
<stop offset="0.9095" style="stop-color:#EAE1E8"/>
<stop offset="1" style="stop-color:#F9F3F8"/>
</linearGradient>
<path fill="url(#SVGID_11_)" d="M105.143,443.356l68.564-75.05c0,0-19.143,59.297-43.551,85.248
c14.824-41.701,10.567-27.993,0-25.023C124.802,430.041,105.143,443.356,105.143,443.356z"/>
<path fill="#656B84" d="M139.423,421.124l34.26-65.654c0,0,2.748,46.867-8.945,72.74c-0.676-32.615-3.041-28.432-11.413-20.06
C150.5,410.974,139.423,421.124,139.423,421.124z"/>
<path fill="#656B84" d="M166.408,329.256l-24.322,19.63l-48.011,64.112c0,0,3.931-7.274,7.104-14.322l-34.994,41.313
c0,0,18.552-26.484,13.089-25.448c-5.464,1.04,11.451-19.148-26.399,5.019c11.313-15.587,32.751-30.137,50.064-40.227
c-2.205-0.298-8.316,2.077-23.552,10.989c9.313-12.818,25.302-25.842,40.183-36.357c-0.175-2.919-3.515-5.416-21.74-2.346
C121.065,335.3,166.408,329.256,166.408,329.256z"/>
<path fill="url(#SVGID_11_)" d="M105.143,443.357l68.562-75.052c0,0-19.143,59.297-43.551,85.248
c14.824-41.7,10.567-27.994,0-25.022C124.802,430.041,105.143,443.357,105.143,443.357z"/>
<path fill="#656B84" d="M139.423,421.125l34.262-65.654c0,0,2.748,46.867-8.945,72.74c-0.676-32.615-3.041-28.432-11.413-20.061
C150.5,410.974,139.423,421.125,139.423,421.125z"/>
<path fill="#656B84" d="M166.408,329.255l-24.322,19.631l-48.011,64.111c0,0,3.931-7.273,7.104-14.322L66.185,439.99
c0,0,18.552-26.484,13.089-25.449c-5.464,1.041,11.451-19.148-26.399,5.02c11.313-15.588,32.751-30.137,50.064-40.227
c-2.205-0.299-8.316,2.076-23.552,10.988c9.312-12.818,25.302-25.842,40.183-36.357c-0.175-2.918-3.515-5.416-21.74-2.346
C121.065,335.3,166.408,329.255,166.408,329.255z"/>
</g>
<g>
<path fill="#D9AF82" enable-background="new " d="M315.05,115.063c-50.514,0-83.218,15.021-109.641,64.839
c-2.502,4.713-0.232,14.337-2.796,19.152c-13.845,26.007-37.089,48.236-37.089,71.707c0,34.337,19.1,65.637,42.505,89.297
c12.185,71.644,34.7,151.593,104.339,151.593"/>
<path fill="#010101" d="M203.271,178.766c-1.519,2.855-1.59,6.774-1.656,10.562c-0.062,3.283-0.123,6.677-1.14,8.588
c-4.537,8.519-10.146,16.707-15.577,24.624c-11.21,16.353-21.797,31.798-21.797,48.221c0,30.307,15.166,62.382,42.666,90.413
c12.578,73.606,36.115,150.477,106.6,150.477v-1.546c-67.084,0-89.722-78.554-101.95-150.453l-0.127-0.748l-0.54-0.549
c-26.957-27.255-41.805-58.361-41.805-87.594c0-14.923,10.174-29.766,20.949-45.482c5.501-8.027,11.192-16.327,15.857-25.087
c1.56-2.935,1.636-6.92,1.707-10.773c0.057-3.221,0.118-6.551,1.088-8.381c24.256-45.734,54.398-65.974,107.503-65.974v-2.422
<path fill="#D9AF82" d="M315.05,115.063c-50.514,0-83.218,15.021-109.641,64.839c-2.502,4.713-0.23,14.337-2.796,19.152
c-13.845,26.007-37.089,48.236-37.089,71.707c0,34.335,19.1,65.637,42.505,89.296c12.185,71.646,34.7,151.595,104.339,151.595"/>
<path fill="#010101" d="M203.271,178.766c-1.521,2.855-1.59,6.774-1.656,10.562c-0.062,3.283-0.123,6.677-1.14,8.588
c-4.537,8.519-10.146,16.707-15.577,24.624c-11.21,16.353-21.797,31.798-21.797,48.221c0,30.307,15.166,62.38,42.666,90.412
c12.578,73.607,36.113,150.479,106.6,150.479v-1.547c-67.084,0-89.722-78.556-101.95-150.453l-0.127-0.748l-0.54-0.549
c-26.957-27.257-41.805-58.361-41.805-87.594c0-14.923,10.174-29.766,20.949-45.482c5.501-8.027,11.19-16.327,15.855-25.087
c1.562-2.935,1.638-6.92,1.707-10.773c0.059-3.221,0.118-6.551,1.088-8.381c24.258-45.734,54.398-65.974,107.503-65.974v-2.422
C259.849,112.641,228.506,131.181,203.271,178.766z"/>
</g>
<path fill="#8B654B" d="M312.368,511.65c72.045,0,92.163-79.949,104.338-151.593c23.405-23.66,42.512-54.96,42.512-89.297
c0-37.004-22.588-49.51-38.609-77.154c-5.681-9.798-1.496-24.634-8.084-33.382c-26.905-35.728-53.772-45.162-97.475-45.162"/>
<path fill="#8B654B" d="M312.368,511.65c72.045,0,92.163-79.949,104.338-151.595c23.405-23.657,42.514-54.959,42.514-89.296
c0-37.004-22.588-49.51-38.609-77.154c-5.681-9.798-1.496-24.634-8.084-33.382c-26.905-35.728-53.772-45.162-97.477-45.162"/>
<ellipse fill="#D9AF82" cx="313.792" cy="343.947" rx="21.906" ry="68.144"/>
<path fill="#8B654B" d="M342.916,373.454c0,7.813-14.98,12.535-27.275,12.535c-12.293,0-27.808-4.735-27.808-12.549
c3.43-3.43,15.246,4.886,27.544,4.886C334.789,378.326,342.916,312.495,342.916,373.454z"/>
<path fill="#D9AF82" d="M335.697,435.217c0,37.637-9.81,68.831-21.91,68.831c-12.096,0-21.901-31.194-21.901-68.831
c0-37.639,9.882-50.259,21.977-50.259C325.962,384.958,335.697,397.578,335.697,435.217z"/>
<path opacity="0.36" fill="#8A644B" d="M328.365,281.605c-1.826,24.681-14.186-7.573-34.757,20.974
c-8.672,12.026-23.23,10.877-38.845,10.877c-39.61,0-66.544-26.03-67.859-38.868c-2.517-24.557,29.024-39.586,71.464-39.586
<path fill="#8B654B" d="M342.916,373.455c0,7.812-14.979,12.535-27.273,12.535c-12.293,0-27.81-4.736-27.81-12.549
c3.432-3.433,15.246,4.885,27.544,4.885C334.789,378.326,342.916,312.496,342.916,373.455z"/>
<path fill="#D9AF82" d="M335.697,435.216c0,37.637-9.81,68.832-21.91,68.832c-12.096,0-21.899-31.195-21.899-68.832
c0-37.639,9.882-50.258,21.977-50.258C325.962,384.958,335.697,397.578,335.697,435.216z"/>
<path opacity="0.36" fill="#8A644B" enable-background="new " d="M328.365,281.605c-1.826,24.682-14.186-7.573-34.757,20.975
c-8.672,12.025-23.23,10.877-38.845,10.877c-39.61,0-66.544-26.031-67.859-38.869c-2.517-24.557,29.024-39.586,71.464-39.586
C297.028,235.002,329.255,269.622,328.365,281.605z"/>
<linearGradient id="SVGID_12_" gradientUnits="userSpaceOnUse" x1="214.7554" y1="341.0537" x2="290.4385" y2="341.0537">
<linearGradient id="SVGID_12_" gradientUnits="userSpaceOnUse" x1="-644.0354" y1="340.7031" x2="-568.3508" y2="340.7031" gradientTransform="matrix(1 0 0 1 858.79 0.3506)">
<stop offset="0" style="stop-color:#D9AF82"/>
<stop offset="0.0786" style="stop-color:#DFB791"/>
<stop offset="0.2053" style="stop-color:#E7C1A5"/>
@@ -199,40 +201,38 @@
<stop offset="0.6917" style="stop-color:#F4D3C7"/>
<stop offset="1" style="stop-color:#F5D4C9"/>
</linearGradient>
<path opacity="0.36" fill="url(#SVGID_12_)" d="M272.246,361.529c-28.722,19.46-56.346-24.299-57.491-43.314
c15.913,13.821,62.032,18.457,75.683-2.517L272.246,361.529z"/>
<path opacity="0.36" fill="#8A644B" d="M313.706,461.213c-38.167,0-61.231-13.681-61.231-31.63c0-6.048,15.651-24.078,37.32-49.365
c5.952-3.699,7.464,10.038,14.958,4.731C331.61,392.044,328.318,461.213,313.706,461.213z"/>
<path opacity="0.36" fill="url(#SVGID_12_)" enable-background="new " d="M272.246,361.529
c-28.722,19.461-56.346-24.299-57.491-43.314c15.913,13.822,62.032,18.457,75.685-2.516L272.246,361.529z"/>
<path opacity="0.36" fill="#8A644B" enable-background="new " d="M313.706,461.212c-38.167,0-61.229-13.68-61.229-31.629
c0-6.049,15.649-24.078,37.318-49.365c5.952-3.699,7.464,10.037,14.958,4.73C331.61,392.044,328.318,461.212,313.706,461.212z"/>
<g>
<path fill="#8A644B" d="M260.464,472.116c8.339,23.94,32.817,36.466,52.688,36.466v-4.845
C294.99,503.737,268.056,493.921,260.464,472.116L260.464,472.116z"/>
<path fill="#8A644B" d="M260.464,472.117c8.341,23.938,32.817,36.465,52.688,36.465v-4.844
C294.99,503.738,268.056,493.921,260.464,472.117L260.464,472.117z"/>
</g>
<g>
<path fill="#8A644B" d="M180.954,271.658c0,21.359,5.388,36.679,27.099,48.222c0.587,1.121,7.021,13.387,7.021,13.387
c8.916,16.375,19.02,34.94,19.02,54.406h4.844c0-20.699-10.416-39.839-19.612-56.725l-7.586-14.455l-0.709-0.426
C190.509,305.4,180.954,291.289,180.954,271.658L180.954,271.658z"/>
<path fill="#8A644B" d="M180.954,271.658c0,21.359,5.388,36.679,27.101,48.222c0.587,1.121,7.021,13.387,7.021,13.387
c8.916,16.375,19.021,34.939,19.021,54.406h4.844c0-20.699-10.416-39.84-19.612-56.725l-7.586-14.455l-0.709-0.426
C190.51,305.4,180.954,291.289,180.954,271.658L180.954,271.658z"/>
</g>
<g>
<path fill="#010101" d="M208.814,230.649l-0.104-0.97c-3.108-15.25-3.226-33.651-0.312-49.217l-4.764-0.889
<path fill="#010101" d="M208.814,230.649l-0.104-0.97c-3.106-15.25-3.226-33.651-0.312-49.217l-4.764-0.889
C200.613,195.722,205.583,214.815,208.814,230.649z"/>
</g>
<linearGradient id="SVGID_13_" gradientUnits="userSpaceOnUse" x1="191.0591" y1="310.5205" x2="451.5771" y2="310.5205">
<linearGradient id="SVGID_13_" gradientUnits="userSpaceOnUse" x1="-667.7327" y1="310.1709" x2="-407.218" y2="310.1709" gradientTransform="matrix(1 0 0 1 858.79 0.3506)">
<stop offset="0" style="stop-color:#5B3E1D"/>
<stop offset="1" style="stop-color:#010101"/>
</linearGradient>
<path fill="url(#SVGID_13_)" d="M423.943,199.054c-3.983-11.059-0.737-20.853-8.061-31.231
c-19.885-28.185-51.072-56.27-108.998-52.618l5.212,140.363c0,0-122.733-25.589-121.021,15.935c0.227,5.387,40.188,2.88,63.46,4.428
c-26.092,4.566-32.185,5.898-32.185,8.838c0,6.206,21.863,0.683,25.108,2.467c4.976,2.739-43.139,7.052-34.351,8.757
c10.057,1.951,44.474,3.086,57.444,3.086c28.929,0,41.077-25.412,41.077-4.485c7.346,27.893,14.739,59.24,13.972,69.882
c-1.475,20.662-19.62,20.188-19.62,20.188l5.089,24.285c-8.557,0.439-6.245-1.324-13.85-0.549
c-14.427,0-32.354,13.032-32.354,16.054l39.989,0.435c0,0-29.081,13.771-22.975,22.928c4.763,7.143,20.316,13.378,31.091,13.424
c0,0,3.273,8.55,3.273,22.352c0,13.811-4.035,22.565-4.035,22.565c46.171,0,67.49-49.036,80.324-118.446
c6.083-32.876,20.018-58.106,36.034-71.657c15.742-13.321,23.008-26.641,23.008-52.071
C451.577,249.847,443.574,230.354,423.943,199.054z"/>
<path fill="#D9AF82" d="M325.395,252.416l-13.297,4.328c0,0-65.848-96.356-46.493-121.312
c12.682-16.357,24.611-18.136,40.046-19.238c13.519-0.965,21.294,16.525,27.302,24.271
C350.375,162.927,325.395,252.416,325.395,252.416z"/>
<linearGradient id="SVGID_14_" gradientUnits="userSpaceOnUse" x1="260.7119" y1="113.4033" x2="349.5527" y2="113.4033" gradientTransform="matrix(0.676 0.6564 -0.9247 0.9523 168.3961 -132.5865)">
<path fill="url(#SVGID_13_)" d="M423.943,199.054c-3.983-11.059-0.737-20.853-8.062-31.231
c-19.885-28.185-51.07-56.271-108.998-52.618l5.214,140.363c0,0-122.733-25.589-121.021,15.935c0.229,5.387,40.188,2.88,63.46,4.428
c-26.092,4.566-32.185,5.898-32.185,8.838c0,6.206,21.861,0.683,25.106,2.467c4.978,2.739-43.139,7.052-34.351,8.757
c10.057,1.951,44.474,3.086,57.442,3.086c28.931,0,41.077-25.413,41.077-4.485c7.348,27.894,14.739,59.239,13.974,69.882
c-1.477,20.661-19.62,20.188-19.62,20.188l5.089,24.285c-8.557,0.438-6.245-1.324-13.85-0.549c-14.429,0-32.354,13.03-32.354,16.055
l39.989,0.434c0,0-29.081,13.771-22.977,22.931c4.765,7.143,20.316,13.377,31.093,13.424c0,0,3.271,8.549,3.271,22.352
c0,13.812-4.035,22.564-4.035,22.564c46.171,0,67.49-49.035,80.324-118.445c6.083-32.877,20.018-58.104,36.034-71.658
c15.742-13.319,23.008-26.64,23.008-52.07C451.577,249.847,443.574,230.354,423.943,199.054z"/>
<path fill="#D9AF82" d="M325.395,252.416l-13.297,4.328c0,0-65.85-96.356-46.493-121.312c12.682-16.357,24.609-18.136,40.046-19.238
c13.519-0.965,21.294,16.525,27.302,24.271C350.375,162.927,325.395,252.416,325.395,252.416z"/>
<linearGradient id="SVGID_14_" gradientUnits="userSpaceOnUse" x1="-629.0159" y1="50.6562" x2="-540.175" y2="50.6562" gradientTransform="matrix(0.676 0.6564 -0.9247 0.9523 711.8302 511.1852)">
<stop offset="0" style="stop-color:#FFFFFF"/>
<stop offset="0.0769" style="stop-color:#F4E9DD"/>
<stop offset="0.1814" style="stop-color:#EBD6BF"/>
@@ -242,10 +242,10 @@
<stop offset="0.7265" style="stop-color:#DAB084"/>
<stop offset="1" style="stop-color:#D9AF82"/>
</linearGradient>
<path fill="url(#SVGID_14_)" d="M305.253,199.27c-26.745,27.542-70.168,41.961-81.283,24.357
c-14.513-22.972-12.896-47.545,13.854-75.082c26.74-27.547,64.922-33.847,85.28-14.081
<path fill="url(#SVGID_14_)" d="M305.253,199.27c-26.745,27.542-70.168,41.961-81.281,24.357
c-14.515-22.972-12.896-47.545,13.854-75.082c26.74-27.547,64.922-33.847,85.28-14.081
C343.464,154.233,332.003,171.726,305.253,199.27z"/>
<linearGradient id="SVGID_15_" gradientUnits="userSpaceOnUse" x1="241.0796" y1="401.8057" x2="241.0796" y2="318.2148">
<linearGradient id="SVGID_15_" gradientUnits="userSpaceOnUse" x1="-617.7107" y1="401.4551" x2="-617.7107" y2="317.8633" gradientTransform="matrix(1 0 0 1 858.79 0.3506)">
<stop offset="0" style="stop-color:#D9AF82"/>
<stop offset="0.1127" style="stop-color:#BD936E"/>
<stop offset="0.2679" style="stop-color:#A57C5E"/>
@@ -253,27 +253,29 @@
<stop offset="0.6554" style="stop-color:#8D664D"/>
<stop offset="1" style="stop-color:#8A644B"/>
</linearGradient>
<path fill="url(#SVGID_15_)" d="M272.246,361.529c0,46.274-35.419,42.799-45.106,36.896c0-45.751-17.227-80.21-17.227-80.21
S249.882,354.221,272.246,361.529z"/>
<linearGradient id="SVGID_16_" gradientUnits="userSpaceOnUse" x1="175.4648" y1="345.0176" x2="128.5757" y2="362.0838" gradientTransform="matrix(-1 0 0 1 538.9277 0)">
<path fill="url(#SVGID_15_)" d="M272.246,361.529c0,46.272-35.419,42.799-45.104,36.896c0-45.752-17.229-80.211-17.229-80.211
S249.882,354.22,272.246,361.529z"/>
<linearGradient id="SVGID_16_" gradientUnits="userSpaceOnUse" x1="-925.8547" y1="344.666" x2="-972.7458" y2="361.7328" gradientTransform="matrix(-1 0 0 1 -562.3931 0.3506)">
<stop offset="0" style="stop-color:#382815"/>
<stop offset="0.4081" style="stop-color:#553E2B"/>
<stop offset="1" style="stop-color:#8B654B"/>
</linearGradient>
<path fill="url(#SVGID_16_)" d="M359.338,356.35c0,46.27,21.504,44.032,31.073,44.032c7.488-45.135,35.671-81.515,35.671-81.515
S381.703,349.045,359.338,356.35z"/>
<path fill="url(#SVGID_16_)" d="M359.338,356.349c0,46.271,21.506,44.033,31.073,44.033c7.488-45.135,35.671-81.516,35.671-81.516
S381.703,349.044,359.338,356.349z"/>
<path fill="none" stroke="#010101" stroke-width="4.8437" d="M173.934,294.978"/>
<g>
<path fill="#010101" d="M311.507,115.089c53.106,0,83.247,20.214,107.503,65.948c0.904,1.702,0.904,4.883,0.904,7.96
c-0.005,4.146-0.005,8.432,2.029,11.419c16.157,23.757,31.933,48.809,31.933,70.969c0,32.381-12.818,62.458-37.074,86.969
l-0.541,0.549l-0.128,0.748c-12.222,71.899-36.672,150.453-103.765,150.453v1.517c70.499,0,95.847-76.845,108.416-150.456
c24.801-25.335,37.935-56.354,37.935-89.78c0-23.537-16.19-49.311-32.774-73.694c-1.153-1.698-1.192-5.061-1.192-8.37
c0-0.109,0.004-0.537,0.004-0.646c0-3.567-0.041-7.221-1.469-9.91c-25.237-47.585-56.579-66.125-111.78-66.125V115.089z"/>
<path fill="#010101" d="M311.507,115.089c53.106,0,83.247,20.214,107.505,65.948c0.902,1.702,0.902,4.883,0.902,7.96
c-0.005,4.146-0.005,8.432,2.029,11.419c16.157,23.757,31.934,48.809,31.934,70.969c0,32.38-12.817,62.458-37.073,86.969
l-0.541,0.551l-0.13,0.748c-12.222,71.897-36.672,150.451-103.765,150.451v1.518c70.499,0,95.847-76.844,108.416-150.455
c24.803-25.336,37.937-56.355,37.937-89.781c0-23.537-16.19-49.311-32.774-73.694c-1.151-1.698-1.19-5.061-1.19-8.37
c0-0.109,0.004-0.537,0.004-0.646c0-3.567-0.041-7.221-1.471-9.91C398.053,131.18,366.71,112.64,311.51,112.64v2.449H311.507
L311.507,115.089z"/>
</g>
<polygon fill="#010101" points="339.974,84.79 334.59,115.675 300.608,113.719 312.435,66.413 "/>
<circle fill="#010101" stroke="#010101" stroke-width="4.8437" cx="452.06" cy="492.948" r="19.204"/>
<g>
<linearGradient id="SVGID_17_" gradientUnits="userSpaceOnUse" x1="109.1782" y1="336.3027" x2="170.7935" y2="336.3027">
<linearGradient id="SVGID_17_" gradientUnits="userSpaceOnUse" x1="-749.6116" y1="335.9512" x2="-687.9983" y2="335.9512" gradientTransform="matrix(1 0 0 1 858.79 0.3506)">
<stop offset="0" style="stop-color:#571A54"/>
<stop offset="0.2641" style="stop-color:#7C5378"/>
<stop offset="0.528" style="stop-color:#A78AA3"/>
@@ -281,10 +283,10 @@
<stop offset="0.9095" style="stop-color:#EAE1E8"/>
<stop offset="1" style="stop-color:#F9F3F8"/>
</linearGradient>
<path fill="url(#SVGID_17_)" d="M109.178,338.034c9.592-7.823,34.696,4.862,34.833,4.862l26.782-8.396
C170.211,334.501,120.246,321.648,109.178,338.034z"/>
<path fill="url(#SVGID_17_)" d="M109.178,338.035c9.592-7.824,34.694,4.861,34.833,4.861l26.78-8.396
C170.211,334.501,120.246,321.648,109.178,338.035z"/>
</g>
<linearGradient id="SVGID_18_" gradientUnits="userSpaceOnUse" x1="-102.0645" y1="221.0444" x2="-84.4219" y2="221.0444" gradientTransform="matrix(-0.0113 -0.7169 1.2476 -0.0198 36.6184 302.7065)">
<linearGradient id="SVGID_18_" gradientUnits="userSpaceOnUse" x1="-1081.0227" y1="320.6133" x2="-1063.3801" y2="320.6133" gradientTransform="matrix(-0.0113 -0.7169 1.2476 -0.0198 -98.6661 -397.1379)">
<stop offset="0" style="stop-color:#D9AF82"/>
<stop offset="0.0786" style="stop-color:#DFB791"/>
<stop offset="0.2053" style="stop-color:#E7C1A5"/>
@@ -293,10 +295,10 @@
<stop offset="0.6917" style="stop-color:#F4D3C7"/>
<stop offset="1" style="stop-color:#F5D4C9"/>
</linearGradient>
<path opacity="0.36" fill="url(#SVGID_18_)" d="M313.352,358.852c6.074-0.1,11.045,2.653,11.102,6.149
c0.058,3.49-4.829,6.399-10.907,6.498c-6.078,0.101-11.045-2.657-11.107-6.153C302.388,361.854,307.269,358.946,313.352,358.852z"/>
<path fill="#483218" d="M397.109,309.378c15.699-3.759,26.445-18.402,20.099-27.063c-38.74,19.354-75.295,11.369-85.531,9.147
<path opacity="0.36" fill="url(#SVGID_18_)" enable-background="new " d="M313.352,358.851c6.074-0.1,11.045,2.654,11.102,6.15
c0.06,3.49-4.829,6.398-10.907,6.498c-6.076,0.1-11.045-2.658-11.105-6.154C302.388,361.853,307.269,358.947,313.352,358.851z"/>
<path fill="#483218" d="M397.109,309.378c15.699-3.76,26.445-18.402,20.099-27.063c-38.738,19.354-75.295,11.369-85.529,9.147
C368.014,309.492,371.117,315.595,397.109,309.378z"/>
<path fill="#B4B4B4" d="M310.92,73.714c-38.372,0-93.118,22.758-117.549,74.944c-6.089,12.999-6.656,32.87,0.638,45.014
c0,0,18.604-81.43,108.231-86.494"/>
<path fill="#B4B4B4" d="M310.92,73.714c-38.372,0-93.116,22.758-117.549,74.944c-6.089,12.999-6.656,32.87,0.64,45.014
c0,0,18.604-81.43,108.229-86.494"/>
</svg>

Before

Width:  |  Height:  |  Size: 23 KiB

After

Width:  |  Height:  |  Size: 23 KiB

BIN
data/images/album-icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

BIN
data/images/artist-icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 91 KiB

BIN
data/images/charts.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

BIN
data/images/close.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

BIN
data/images/collapse.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

BIN
data/images/drop-album.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

BIN
data/images/drop-song.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 1.0 MiB

BIN
data/images/open.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

BIN
data/images/search-icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 91 KiB

View File

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 4.2 KiB

View File

Before

Width:  |  Height:  |  Size: 5.1 KiB

After

Width:  |  Height:  |  Size: 5.1 KiB

View File

Before

Width:  |  Height:  |  Size: 7.3 KiB

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 19 KiB

View File

@@ -91,6 +91,11 @@ var TomahawkResolver = {
var configJson = JSON.stringify( config );
window.localStorage[ this.scriptPath() ] = configJson;
this.newConfigSaved();
},
newConfigSaved: function()
{
},
resolve: function( qid, artist, album, title )
{

View File

@@ -85,8 +85,15 @@
<file>data/images/automatic-playlist.png</file>
<file>data/images/station.png</file>
<file>data/images/new-additions.png</file>
<file>data/images/charts.png</file>
<file>data/images/loved_playlist.png</file>
<file>data/images/dashboard.png</file>
<file>data/images/artist-icon.png</file>
<file>data/images/album-icon.png</file>
<file>data/images/search-icon.png</file>
<file>data/images/track-icon-22x22.png</file>
<file>data/images/track-icon-32x32.png</file>
<file>data/images/track-icon-16x16.png</file>
<file>data/stylesheets/topbar-radiobuttons.css</file>
<file>data/icons/tomahawk-icon-16x16.png</file>
<file>data/icons/tomahawk-icon-32x32.png</file>
@@ -94,9 +101,6 @@
<file>data/icons/tomahawk-icon-128x128.png</file>
<file>data/icons/tomahawk-icon-256x256.png</file>
<file>data/icons/tomahawk-icon-512x512.png</file>
<file>data/icons/audio-x-generic-22x22.png</file>
<file>data/icons/audio-x-generic-32x32.png</file>
<file>data/icons/audio-x-generic-16x16.png</file>
<file>data/www/auth.html</file>
<file>data/www/auth.na.html</file>
<file>data/www/tomahawk_banner_small.png</file>
@@ -106,5 +110,10 @@
<file>data/sql/dbmigrate-25_to_26.sql</file>
<file>data/js/tomahawk.js</file>
<file>data/images/avatar_frame.png</file>
<file>data/images/drop-all-songs.png</file>
<file>data/images/drop-local-songs.png</file>
<file>data/images/drop-top-songs.png</file>
<file>data/images/drop-song.png</file>
<file>data/images/drop-album.png</file>
</qresource>
</RCC>

View File

@@ -57,11 +57,14 @@ SET( tomahawkSourcesGui ${tomahawkSourcesGui}
sourcetree/sourcesmodel.cpp
sourcetree/sourcesproxymodel.cpp
sourcetree/sourcetreeview.cpp
sourcetree/sourcedelegate.cpp
sourcetree/animationhelper.cpp
sourcetree/items/sourcetreeitem.cpp
sourcetree/items/collectionitem.cpp
sourcetree/items/playlistitems.cpp
sourcetree/items/categoryitems.cpp
sourcetree/items/genericpageitems.cpp
sourcetree/items/temporarypageitem.cpp
transferview.cpp
PipelineStatusView.cpp
@@ -75,6 +78,7 @@ SET( tomahawkSourcesGui ${tomahawkSourcesGui}
settingslistdelegate.cpp
resolversmodel.cpp
tomahawkwindow.cpp
tomahawkdesktopwindow.cpp
)
SET( tomahawkHeaders ${tomahawkHeaders}
@@ -101,11 +105,14 @@ SET( tomahawkHeadersGui ${tomahawkHeadersGui}
sourcetree/sourcesmodel.h
sourcetree/sourcesproxymodel.h
sourcetree/sourcetreeview.h
sourcetree/sourcedelegate.h
sourcetree/animationhelper.h
sourcetree/items/sourcetreeitem.h
sourcetree/items/collectionitem.h
sourcetree/items/playlistitems.h
sourcetree/items/categoryitems.h
sourcetree/items/genericpageitems.h
sourcetree/items/temporarypageitem.h
transferview.h
PipelineStatusView.h
@@ -120,14 +127,14 @@ SET( tomahawkHeadersGui ${tomahawkHeadersGui}
resolversmodel.h
delegateconfigwrapper.h
tomahawkwindow.h
tomahawkdesktopwindow.h
)
SET( tomahawkUI ${tomahawkUI}
tomahawkwindow.ui
tomahawkdesktopwindow.ui
diagnosticsdialog.ui
stackedsettingsdialog.ui
proxydialog.ui
searchbox.ui
audiocontrols.ui
)
@@ -170,13 +177,12 @@ ENDIF( UNIX )
IF( APPLE )
INCLUDE_DIRECTORIES( ${CMAKE_SOURCE_DIR}/thirdparty/SPMediaKeyTap )
SET( tomahawkHeaders ${tomahawkHeaders} mac/tomahawkapp_mac.h mac/macshortcuthandler.h )
SET( tomahawkSources ${tomahawkSources} mac/tomahawkapp_mac.mm mac/macshortcuthandler.cpp )
SET( tomahawkHeaders ${tomahawkHeaders} mac/tomahawkapp_mac.h mac/macshortcuthandler.h )
SET( tomahawkSources ${tomahawkSources} mac/tomahawkapp_mac.mm mac/macshortcuthandler.cpp )
IF(HAVE_SPARKLE)
SET( tomahawkHeaders ${tomahawkHeaders} ${SPARKLE}/Headers )
ENDIF(HAVE_SPARKLE)
ENDIF( APPLE )
IF(GLOOX_FOUND)
@@ -186,6 +192,10 @@ IF(GLOOX_FOUND)
ENDIF(GLOOX_FOUND)
ADD_SUBDIRECTORY( sip )
IF(QCA2_FOUND)
INCLUDE_DIRECTORIES( ${QCA2_INCLUDE_DIR} )
ENDIF(QCA2_FOUND)
kde4_add_app_icon( tomahawkSources "${CMAKE_SOURCE_DIR}/data/icons/tomahawk-icon-*.png" )
qt4_add_resources( RC_SRCS "../resources.qrc" )
qt4_wrap_cpp( tomahawkMoc ${tomahawkHeaders} )
@@ -226,7 +236,11 @@ IF(GLOOX_FOUND)
SET(LINK_LIBRARIES ${LINK_LIBRARIES} ${GLOOX_LIBRARIES} )
ENDIF(GLOOX_FOUND)
TARGET_LINK_LIBRARIES( tomahawk
IF(QCA2_FOUND)
SET(LINK_LIBRARIES ${LINK_LIBRARIES} ${QCA2_LIBRARIES} )
ENDIF(QCA2_FOUND)
SET( tomahawkLinkLibraries
${LINK_LIBRARIES}
${TOMAHAWK_LIBRARIES}
${PHONON_LIBS}
@@ -237,9 +251,14 @@ TARGET_LINK_LIBRARIES( tomahawk
${QXTWEB_LIBRARIES}
${QJSON_LIBRARIES}
${TAGLIB_LIBRARIES}
${CLUCENE_LIBRARIES}
)
TARGET_LINK_LIBRARIES( tomahawk ${tomahawkLinkLibraries} )
IF( BUILD_GUI_QML )
# sic! add_subdirectory would make it harder to reuse ${final_src}
INCLUDE( active/CMakeLists.txt )
ENDIF()
IF( APPLE )
IF(HAVE_SPARKLE)

View File

@@ -22,7 +22,7 @@
#include <QTreeWidget>
#include "typedefs.h"
#include "utils/animatedsplitter.h"
#include "widgets/animatedsplitter.h"
class StreamConnection;

30
src/active/CMakeLists.txt Normal file
View File

@@ -0,0 +1,30 @@
SET( touchmahawkSources ${final_src} )
#LIST( REMOVE_ITEM touchmahawkSources "main.cpp" )
SET( touchmahawkHeaders
active/tomahawktouchwindow.h
)
SET( touchmahawkSources
${touchmahawkSources}
active/tomahawktouchwindow.cpp
# active/main.cpp
)
qt4_wrap_cpp( touchmahawkMoc ${touchmahawkHeaders} )
SET( touchmahawkFinalSources ${touchmahawkMoc} ${touchmahawkSources} )
SET( touchmahawkLinkLibraries
kdeclarative
${tomahawkLinkLibraries}
) #${QT_QTDECLARATIVE_LIBRARY}
ADD_EXECUTABLE( active-tomahawk ${touchmahawkFinalSources} )
TARGET_LINK_LIBRARIES( active-tomahawk ${touchmahawkLinkLibraries} )
SET_TARGET_PROPERTIES( active-tomahawk PROPERTIES COMPILE_FLAGS -DTOUCHMAHAWK )
INSTALL( TARGETS active-tomahawk DESTINATION bin )

View File

@@ -0,0 +1,134 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2011, 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 "active/tomahawktouchwindow.h"
#include "utils/logger.h"
#include "audio/audioengine.h"
#include "globalactionmanager.h"
#include "sourcesmodel.h"
#include "items/sourcetreeitem.h"
#include "items/collectionitem.h"
#include "viewmanager.h"
#include "libtomahawk/playlist/treeproxymodel.h"
#include <QFileSystemWatcher>
#include <QtDeclarative>
#define QMLGUI "/home/domme/dev/sources/tomahawk-qml"
TomahawkTouchWindow::TomahawkTouchWindow()
: m_view(0)
, m_currentPlaylistTreeModel(0)
{
QFileSystemWatcher* watcher = new QFileSystemWatcher;
watcher->addPath( QMLGUI );
connect( watcher, SIGNAL( directoryChanged( QString ) ), SLOT( loadQml() ));
loadQml();
setCentralWidget( m_view );
setWindowTitle("Touch-ma-hawk");
}
TomahawkTouchWindow::~TomahawkTouchWindow()
{
}
void TomahawkTouchWindow::play(const QModelIndex& index )
{
TreeModelItem* item = m_currentPlaylistTreeModel->sourceModel()->itemFromIndex( m_currentPlaylistTreeModel->mapToSource( index ) );
if ( item )
{
m_currentPlaylistTreeModel->sourceModel()->setCurrentItem( item->index );
AudioEngine::instance()->playItem( m_currentPlaylistTreeModel, item->result() );
}
}
void TomahawkTouchWindow::activateItem(const QModelIndex& index)
{
tLog() << Q_FUNC_INFO << index;
SourceTreeItem* item = qobject_cast< SourceTreeItem* >( s_sourcesModel->data( index, SourcesModel::SourceTreeItemRole ).value< SourceTreeItem* >() );
item->activate();
CollectionItem* collectionItem = qobject_cast< CollectionItem* >( item );
if( collectionItem )
{
tLog() << "Activate collectionItem!";
Tomahawk::collection_ptr collection = collectionItem->source()->collection();
TreeModel* model = ViewManager::instance()->treeModelForCollection( collection );
TreeProxyModel* proxyModel = m_modelProxyModels.value( model );
if( !proxyModel )
{
proxyModel = new TreeProxyModel();
//m_currentPlaylistTreeModel->setDynamicSortFilter( true );
proxyModel->setSourceTreeModel( model );
proxyModel->sort(TreeModel::Name, Qt::AscendingOrder );
//m_currentPlaylistTreeModel->setShowModes( false );
//m_currentPlaylistTreeModel->setSortRole( );
}
m_currentPlaylistTreeModel = proxyModel;
emit currentTreeModelChanged();
//m_view->rootContext()->setContextProperty( "currentPlaylistTreeModel", m_currentPlaylistTreeModel );
}
}
void
TomahawkTouchWindow::loadQml()
{
tLog() << Q_FUNC_INFO;
qmlRegisterType<AudioEngine>("org.tomahawkplayer.qmlcomponents", 1, 0, "AudioEngine");
qmlRegisterType<TreeProxyModel>("org.tomahawkplayer.qmlcomponents", 1, 0, "TreeProxyModel");
if( !m_view )
{
tLog()<< Q_FUNC_INFO << "create qml view";
m_view = new QDeclarativeView;
m_view->setResizeMode(QDeclarativeView::SizeRootObjectToView);
m_view->show();
}
tLog()<< Q_FUNC_INFO << "clear component cache";
m_view->engine()->clearComponentCache();
tLog()<< Q_FUNC_INFO << "set context property";
QDeclarativeContext* context = m_view->rootContext();
tLog()<< Q_FUNC_INFO << "make objects accessible from qml";
context->setContextProperty( "touchWindow", this );
context->setContextProperty( "audioEngine", AudioEngine::instance() );
context->setContextProperty( "globalActionManager", GlobalActionManager::instance() );
context->setContextProperty( "sourcesModel", s_sourcesModel );
// don't start in an undefined state
m_currentPlaylistTreeModel = 0;
//context->setContextProperty( "currentPlaylistTreeModel", m_currentPlaylistTreeModel );
tLog()<< Q_FUNC_INFO << "set source";
m_view->setSource( QUrl::fromLocalFile( QMLGUI "/main.qml" ) );
}

View File

@@ -0,0 +1,62 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2011, 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 TOMAHAWKTOUCHWINDOW_H
#define TOMAHAWKTOUCHWINDOW_H
#include "tomahawkwindow.h"
#include <QtDeclarative>
class TomahawkTouchWindow;
class QFileSystemWatcher;
class TreeModel;
class TreeProxyModel;
class TomahawkTouchWindow : public TomahawkWindow
{
Q_OBJECT
Q_PROPERTY( TreeProxyModel* currentTreeModel READ currentTreeModel NOTIFY currentTreeModelChanged )
public:
TomahawkTouchWindow();
~TomahawkTouchWindow();
Q_INVOKABLE void play( const QModelIndex& index );
Q_INVOKABLE void activateItem( const QModelIndex& index );
signals:
void currentTreeModelChanged();
private slots:
void loadQml();
private:
TreeProxyModel* currentTreeModel() { return m_currentPlaylistTreeModel; }
QDeclarativeView* m_view;
QFileSystemWatcher* m_watcher;
TreeProxyModel* m_currentPlaylistTreeModel;
QHash< TreeModel*, TreeProxyModel* > m_modelProxyModels;
};
#endif // TOMAHAWKTOUCHWINDOW_H

View File

@@ -24,17 +24,16 @@
#include <QMouseEvent>
#include "audio/audioengine.h"
#include "viewmanager.h"
#include "playlist/playlistview.h"
#include "database/database.h"
#include "database/databasecommand_socialaction.h"
#include "album.h"
#include "utils/imagebutton.h"
#include "widgets/imagebutton.h"
#include "utils/tomahawkutils.h"
#include "utils/logger.h"
#include <globalactionmanager.h>
#include "album.h"
#include "dropjob.h"
#include "globalactionmanager.h"
#include "viewmanager.h"
using namespace Tomahawk;
@@ -90,56 +89,14 @@ AudioControls::AudioControls( QWidget* parent )
ui->ownerLabel->setForegroundRole( QPalette::Dark );
ui->metaDataArea->setStyleSheet( "QWidget#metaDataArea {\nborder-width: 4px;\nborder-image: url(" RESPATH "images/now-playing-panel.png) 4 4 4 4 stretch stretch; }" );
ui->seekSlider->setFixedHeight( 20 );
ui->seekSlider->setEnabled( true );
ui->seekSlider->setStyleSheet( "QSlider::groove::horizontal {"
"margin: 5px; border-width: 3px;"
"border-image: url(" RESPATH "images/seek-slider-bkg.png) 3 3 3 3 stretch stretch;"
"}"
"QSlider::sub-page:horizontal {"
"margin: 5px; border-width: 3px;"
"border-image: url(" RESPATH "images/seek-slider-level.png) 3 3 3 3 stretch stretch;"
"}"
"QSlider::handle::horizontal {"
"margin-bottom: -7px; margin-top: -7px;"
"margin-left: -4px; margin-right: -4px;"
"height: 17px; width: 16px;"
"background-image: url(" RESPATH "images/seek-and-volume-knob-rest.png);"
"background-repeat: no-repeat;"
"}" );
ui->volumeSlider->setFixedHeight( 20 );
ui->volumeSlider->setRange( 0, 100 );
ui->volumeSlider->setValue( AudioEngine::instance()->volume() );
ui->volumeSlider->setStyleSheet( "QSlider::groove::horizontal {"
"margin: 5px; border-width: 3px;"
"border-image: url(" RESPATH "images/volume-slider-bkg.png) 3 3 3 3 stretch stretch;"
"}"
"QSlider::sub-page:horizontal {"
"margin: 5px; border-width: 3px;"
"border-image: url(" RESPATH "images/seek-slider-level.png) 3 3 3 3 stretch stretch;"
"}"
m_sliderTimeLine.setCurveShape( QTimeLine::LinearCurve );
ui->seekSlider->setTimeLine( &m_sliderTimeLine );
"QSlider::handle::horizontal {"
"margin-bottom: -7px; margin-top: -7px;"
"margin-left: -4px; margin-right: -4px;"
"height: 17px; width: 16px;"
"background-image: url(" RESPATH "images/seek-and-volume-knob-rest.png);"
"background-repeat: no-repeat;"
"}" );
/* m_playAction = new QAction( this );
m_pauseAction = new QAction( this );
m_prevAction = new QAction( this );
m_nextAction = new QAction( this );
connect( m_playAction, SIGNAL( triggered() ), (QObject*)APP->audioEngine(), SLOT( play() ) );
connect( m_pauseAction, SIGNAL( triggered() ), (QObject*)APP->audioEngine(), SLOT( pause() ) );
connect( m_prevAction, SIGNAL( triggered() ), (QObject*)APP->audioEngine(), SLOT( previous() ) );
connect( m_nextAction, SIGNAL( triggered() ), (QObject*)APP->audioEngine(), SLOT( next() ) ); */
connect( &m_sliderTimeLine, SIGNAL( frameChanged( int ) ), ui->seekSlider, SLOT( setValue( int ) ) );
connect( ui->seekSlider, SIGNAL( valueChanged( int ) ), AudioEngine::instance(), SLOT( seek( int ) ) );
connect( ui->volumeSlider, SIGNAL( valueChanged( int ) ), AudioEngine::instance(), SLOT( setVolume( int ) ) );
@@ -167,6 +124,7 @@ AudioControls::AudioControls( QWidget* parent )
connect( AudioEngine::instance(), SIGNAL( paused() ), SLOT( onPlaybackPaused() ) );
connect( AudioEngine::instance(), SIGNAL( resumed() ), SLOT( onPlaybackResumed() ) );
connect( AudioEngine::instance(), SIGNAL( stopped() ), SLOT( onPlaybackStopped() ) );
connect( AudioEngine::instance(), SIGNAL( seeked( qint64 ) ), SLOT( onPlaybackSeeked( qint64 ) ) );
connect( AudioEngine::instance(), SIGNAL( timerMilliSeconds( qint64 ) ), SLOT( onPlaybackTimer( qint64 ) ) );
connect( AudioEngine::instance(), SIGNAL( volumeChanged( int ) ), SLOT( onVolumeChanged( int ) ) );
@@ -179,7 +137,6 @@ AudioControls::AudioControls( QWidget* parent )
connect( Tomahawk::InfoSystem::InfoSystem::instance(), SIGNAL( finished( QString ) ), SLOT( infoSystemFinished( QString ) ) );
ui->buttonAreaLayout->setSpacing( 0 );
ui->stackedLayout->setSpacing( 0 );
ui->stackedLayout->setContentsMargins( 0, 0, 0, 0 );
@@ -228,7 +185,27 @@ AudioControls::onPlaybackStarted( const Tomahawk::result_ptr& result )
{
tDebug( LOGEXTRA ) << Q_FUNC_INFO;
onPlaybackLoading( result );
if ( result.isNull() )
return;
if ( m_currentTrack.isNull() || ( !m_currentTrack.isNull() && m_currentTrack.data()->id() != result.data()->id() ) )
onPlaybackLoading( result );
qint64 duration = AudioEngine::instance()->currentTrackTotalTime();
if ( duration == -1 )
duration = result.data()->duration() * 1000;
ui->seekSlider->setRange( 0, duration );
ui->seekSlider->setValue( 0 );
m_sliderTimeLine.stop();
m_sliderTimeLine.setDuration( duration );
m_sliderTimeLine.setFrameRange( 0, duration );
m_sliderTimeLine.setCurrentTime( 0 );
m_seekMsecs = -1;
ui->seekSlider->setVisible( true );
Tomahawk::InfoSystem::InfoCriteriaHash trackInfo;
trackInfo["artist"] = result->artist()->name();
@@ -299,14 +276,7 @@ AudioControls::onPlaybackLoading( const Tomahawk::result_ptr& result )
ui->coverImage->setPixmap( m_defaultCover );
ui->timeLabel->setText( TomahawkUtils::timeToString( 0 ) );
ui->timeLeftLabel->setText( "-" + TomahawkUtils::timeToString( result->duration() ) );
ui->seekSlider->setRange( 0, m_currentTrack->duration() * 1000 );
ui->seekSlider->setValue( 0 );
ui->seekSlider->setVisible( true );
/* m_playAction->setEnabled( false );
m_pauseAction->setEnabled( true ); */
ui->timeLeftLabel->setText( "-" + TomahawkUtils::timeToString( result.data()->duration() ) );
ui->stackedLayout->setCurrentWidget( ui->pauseButton );
@@ -343,26 +313,35 @@ AudioControls::socialActionsLoaded()
void
AudioControls::onPlaybackPaused()
{
/* m_pauseAction->setEnabled( false );
m_playAction->setEnabled( true ); */
tDebug( LOGEXTRA ) << Q_FUNC_INFO;
ui->stackedLayout->setCurrentWidget( ui->playPauseButton );
m_sliderTimeLine.setPaused( true );
}
void
AudioControls::onPlaybackResumed()
{
/* m_playAction->setEnabled( false );
m_pauseAction->setEnabled( true ); */
tDebug( LOGEXTRA ) << Q_FUNC_INFO;
ui->stackedLayout->setCurrentWidget( ui->pauseButton );
ui->loveButton->setVisible( true );
m_sliderTimeLine.resume();
}
void
AudioControls::onPlaybackSeeked( qint64 msec )
{
tDebug( LOGEXTRA ) << Q_FUNC_INFO << " setting current timer to " << msec;
m_sliderTimeLine.setPaused( true );
m_sliderTimeLine.setCurrentTime( msec );
m_seekMsecs = msec;
}
void
AudioControls::onPlaybackStopped()
{
tDebug( LOGEXTRA ) << Q_FUNC_INFO;
m_currentTrack.clear();
ui->artistTrackLabel->setText( "" );
@@ -372,19 +351,19 @@ AudioControls::onPlaybackStopped()
ui->timeLeftLabel->setText( "" );
ui->coverImage->setPixmap( QPixmap() );
ui->seekSlider->setVisible( false );
m_sliderTimeLine.stop();
m_sliderTimeLine.setCurrentTime( 0 );
ui->stackedLayout->setCurrentWidget( ui->playPauseButton );
ui->loveButton->setEnabled( false );
ui->loveButton->setVisible( false );
/* m_pauseAction->setEnabled( false );
m_playAction->setEnabled( true ); */
}
void
AudioControls::onPlaybackTimer( qint64 msElapsed )
{
//tDebug( LOGEXTRA ) << Q_FUNC_INFO << " msElapsed = " << msElapsed << " and timer current time = " << m_sliderTimeLine.currentTime() << " and m_seekMsecs = " << m_seekMsecs;
if ( m_currentTrack.isNull() )
return;
@@ -393,7 +372,25 @@ AudioControls::onPlaybackTimer( qint64 msElapsed )
const int seconds = msElapsed / 1000;
ui->timeLabel->setText( TomahawkUtils::timeToString( seconds ) );
ui->timeLeftLabel->setText( "-" + TomahawkUtils::timeToString( m_currentTrack->duration() - seconds ) );
ui->seekSlider->setValue( msElapsed );
if ( m_sliderTimeLine.currentTime() > msElapsed || m_seekMsecs != -1 )
{
m_sliderTimeLine.setPaused( true );
m_sliderTimeLine.setCurrentTime( msElapsed );
m_seekMsecs = -1;
if ( AudioEngine::instance()->state() != AudioEngine::Paused )
m_sliderTimeLine.resume();
}
else if ( m_sliderTimeLine.duration() > msElapsed && m_sliderTimeLine.state() == QTimeLine::NotRunning )
{
ui->seekSlider->setEnabled( AudioEngine::instance()->canSeek() );
m_sliderTimeLine.resume();
}
else if ( m_sliderTimeLine.state() == QTimeLine::Paused && AudioEngine::instance()->state() != AudioEngine::Paused )
{
ui->seekSlider->setEnabled( AudioEngine::instance()->canSeek() );
m_sliderTimeLine.resume();
}
ui->seekSlider->blockSignals( false );
}
@@ -521,7 +518,7 @@ AudioControls::onTrackClicked()
void
AudioControls::dragEnterEvent( QDragEnterEvent* e )
{
if ( GlobalActionManager::instance()->acceptsMimeData( e->mimeData() ) )
if ( DropJob::acceptsMimeData( e->mimeData() ) )
e->acceptProposedAction();
}
@@ -538,10 +535,11 @@ void
AudioControls::dropEvent( QDropEvent* e )
{
tDebug() << "AudioControls got drop:" << e->mimeData()->formats();
if ( GlobalActionManager::instance()->acceptsMimeData( e->mimeData() ) )
if ( DropJob::acceptsMimeData( e->mimeData() ) )
{
connect( GlobalActionManager::instance(), SIGNAL( tracks( QList<Tomahawk::query_ptr> ) ), this, SLOT( droppedTracks( QList<Tomahawk::query_ptr> ) ) );
GlobalActionManager::instance()->tracksFromMimeData( e->mimeData() );
DropJob *dj = new DropJob();
connect( dj, SIGNAL( tracks( QList<Tomahawk::query_ptr> ) ), this, SLOT( droppedTracks( QList<Tomahawk::query_ptr> ) ) );
dj->tracksFromMimeData( e->mimeData() );
e->accept();
}
@@ -551,8 +549,6 @@ AudioControls::dropEvent( QDropEvent* e )
void
AudioControls::droppedTracks( QList< query_ptr > tracks )
{
disconnect( GlobalActionManager::instance(), SIGNAL( tracks( QList<Tomahawk::query_ptr> ) ), this, SLOT( droppedTracks( QList<Tomahawk::query_ptr> ) ) );
if ( !tracks.isEmpty() )
{
// queue and play the first if nothign is playing

View File

@@ -20,6 +20,7 @@
#define AUDIOCONTROLS_H
#include <QWidget>
#include <QTimeLine>
#include "result.h"
#include "playlistinterface.h"
@@ -60,6 +61,7 @@ private slots:
void onPlaybackLoading( const Tomahawk::result_ptr& result );
void onPlaybackPaused();
void onPlaybackResumed();
void onPlaybackSeeked( qint64 msec );
void onPlaybackStopped();
void onPlaybackTimer( qint64 msElapsed );
@@ -79,19 +81,18 @@ private slots:
void droppedTracks( QList<Tomahawk::query_ptr> );
void socialActionsLoaded();
private:
Ui::AudioControls *ui;
QAction* m_playAction;
QAction* m_pauseAction;
QAction* m_prevAction;
QAction* m_nextAction;
QPixmap m_defaultCover;
Tomahawk::result_ptr m_currentTrack;
Tomahawk::PlaylistInterface::RepeatMode m_repeatMode;
bool m_shuffled;
QTimeLine m_sliderTimeLine;
qint64 m_seekMsecs;
};
#endif // AUDIOCONTROLS_H

View File

@@ -337,7 +337,7 @@
</widget>
</item>
<item>
<widget class="QSlider" name="seekSlider">
<widget class="SeekSlider" name="seekSlider">
<property name="minimumSize">
<size>
<width>0</width>
@@ -489,7 +489,7 @@
</widget>
</item>
<item>
<widget class="QSlider" name="volumeSlider">
<widget class="SeekSlider" name="volumeSlider">
<property name="minimumSize">
<size>
<width>0</width>
@@ -529,15 +529,20 @@
</layout>
</widget>
<customwidgets>
<customwidget>
<class>SeekSlider</class>
<extends>QSlider</extends>
<header location="global">widgets/SeekSlider.h</header>
</customwidget>
<customwidget>
<class>ImageButton</class>
<extends>QPushButton</extends>
<header>utils/imagebutton.h</header>
<header>widgets/imagebutton.h</header>
</customwidget>
<customwidget>
<class>QueryLabel</class>
<extends>QLabel</extends>
<header>utils/querylabel.h</header>
<header>widgets/querylabel.h</header>
</customwidget>
</customwidgets>
<resources/>

View File

@@ -17,5 +17,6 @@
#cmakedefine LIBLASTFM_FOUND
#cmakedefine GLOOX_FOUND
#cmakedefine QCA2_FOUND
#endif // CONFIG_H_IN

View File

@@ -31,6 +31,7 @@ set( libSources
viewmanager.cpp
globalactionmanager.cpp
contextmenu.cpp
dropjob.cpp
sip/SipPlugin.cpp
sip/SipHandler.cpp
@@ -39,6 +40,13 @@ set( libSources
audio/audioengine.cpp
context/ContextPage.cpp
context/ContextWidget.cpp
context/pages/TopTracksContext.cpp
context/pages/RelatedArtistsContext.cpp
context/pages/WikipediaContext.cpp
context/pages/WebContext.cpp
database/database.cpp
database/fuzzyindex.cpp
database/databasecollection.cpp
@@ -66,6 +74,7 @@ set( libSources
database/databasecommand_playbackhistory.cpp
database/databasecommand_setplaylistrevision.cpp
database/databasecommand_loadallplaylists.cpp
database/databasecommand_loadallsortedplaylists.cpp
database/databasecommand_loadallsources.cpp
database/databasecommand_createplaylist.cpp
database/databasecommand_deleteplaylist.cpp
@@ -86,6 +95,8 @@ set( libSources
database/databasecommand_genericselect.cpp
database/database.cpp
infobar/infobar.cpp
infosystem/infosystemcache.cpp
infosystem/infosystem.cpp
infosystem/infosystemworker.cpp
@@ -120,6 +131,7 @@ set( libSources
playlist/albumview.cpp
playlist/artistview.cpp
playlist/customplaylistview.cpp
playlist/ViewHeader.cpp
playlist/topbar/topbar.cpp
playlist/topbar/clearbutton.cpp
@@ -127,8 +139,6 @@ set( libSources
playlist/topbar/lineedit.cpp
playlist/topbar/searchbutton.cpp
playlist/infobar/infobar.cpp
playlist/dynamic/DynamicPlaylist.cpp
playlist/dynamic/DynamicControl.cpp
playlist/dynamic/GeneratorFactory.cpp
@@ -160,36 +170,47 @@ set( libSources
network/controlconnection.cpp
utils/tomahawkutils.cpp
utils/querylabel.cpp
utils/elidedlabel.cpp
utils/imagebutton.cpp
utils/logger.cpp
utils/proxystyle.cpp
utils/widgetdragfilter.cpp
utils/animatedsplitter.cpp
utils/xspfloader.cpp
utils/xspfgenerator.cpp
utils/jspfloader.cpp
utils/spotifyparser.cpp
utils/rdioparser.cpp
utils/shortenedlinkparser.cpp
utils/stylehelper.cpp
widgets/querylabel.cpp
widgets/imagebutton.cpp
widgets/animatedsplitter.cpp
widgets/elidedlabel.cpp
widgets/newplaylistwidget.cpp
widgets/searchwidget.cpp
widgets/SeekSlider.cpp
widgets/playlisttypeselectordlg.cpp
widgets/welcomewidget.cpp
widgets/welcomeplaylistmodel.cpp
widgets/whatshotwidget.cpp
widgets/RecentlyPlayedPlaylistsModel.cpp
widgets/RecentPlaylistsModel.cpp
widgets/overlaywidget.cpp
widgets/HeaderLabel.cpp
widgets/HeaderWidget.cpp
widgets/combobox.cpp
widgets/SocialPlaylistWidget.cpp
widgets/infowidgets/sourceinfowidget.cpp
widgets/infowidgets/ArtistInfoWidget.cpp
widgets/infowidgets/AlbumInfoWidget.cpp
widgets/kbreadcrumbselectionmodel.cpp
widgets/breadcrumbbar.cpp
widgets/breadcrumbbuttonbase.cpp
widgets/headerbreadcrumb.cpp
widgets/siblingcrumbbutton.cpp
kdsingleapplicationguard/kdsingleapplicationguard.cpp
kdsingleapplicationguard/kdsharedmemorylocker.cpp
kdsingleapplicationguard/kdtoolsglobal.cpp
kdsingleapplicationguard/kdlockedsharedmemorypointer.cpp
thirdparty/kdsingleapplicationguard/kdsingleapplicationguard.cpp
thirdparty/kdsingleapplicationguard/kdsharedmemorylocker.cpp
thirdparty/kdsingleapplicationguard/kdtoolsglobal.cpp
thirdparty/kdsingleapplicationguard/kdlockedsharedmemorypointer.cpp
)
set( libHeaders
@@ -208,6 +229,7 @@ set( libHeaders
viewmanager.h
globalactionmanager.h
contextmenu.h
dropjob.h
artist.h
album.h
@@ -220,6 +242,13 @@ set( libHeaders
audio/audioengine.h
context/ContextPage.h
context/ContextWidget.h
context/pages/TopTracksContext.h
context/pages/RelatedArtistsContext.h
context/pages/WikipediaContext.h
context/pages/WebContext.h
database/database.h
database/fuzzyindex.h
database/databaseworker.h
@@ -245,6 +274,7 @@ set( libHeaders
database/databasecommand_playbackhistory.h
database/databasecommand_setplaylistrevision.h
database/databasecommand_loadallplaylists.h
database/databasecommand_loadallsortedplaylists.h
database/databasecommand_loadallsources.h
database/databasecommand_createplaylist.h
database/databasecommand_deleteplaylist.h
@@ -266,6 +296,8 @@ set( libHeaders
database/databasecommand_loadsocialactions.h
database/databasecommand_genericselect.h
infobar/infobar.h
infosystem/infosystem.h
infosystem/infosystemworker.h
infosystem/infosystemcache.h
@@ -310,6 +342,7 @@ set( libHeaders
playlist/albumview.h
playlist/artistview.h
playlist/customplaylistview.h
playlist/ViewHeader.h
playlist/topbar/topbar.h
playlist/topbar/clearbutton.h
@@ -318,8 +351,6 @@ set( libHeaders
playlist/topbar/lineedit_p.h
playlist/topbar/searchbutton.h
playlist/infobar/infobar.h
playlist/dynamic/DynamicPlaylist.h
playlist/dynamic/DynamicControl.h
playlist/dynamic/GeneratorInterface.h
@@ -339,32 +370,45 @@ set( libHeaders
playlist/dynamic/database/DatabaseControl.h
playlist/dynamic/database/DatabaseGenerator.h
utils/querylabel.h
utils/elidedlabel.h
utils/animatedcounterlabel.h
utils/imagebutton.h
utils/widgetdragfilter.h
utils/animatedsplitter.h
utils/xspfloader.h
utils/xspfgenerator.h
utils/jspfloader.h
utils/spotifyparser.h
utils/rdioparser.h
utils/shortenedlinkparser.h
utils/stylehelper.h
widgets/querylabel.h
widgets/animatedcounterlabel.h
widgets/imagebutton.h
widgets/animatedsplitter.h
widgets/elidedlabel.h
widgets/newplaylistwidget.h
widgets/searchwidget.h
widgets/SeekSlider.h
widgets/playlisttypeselectordlg.h
widgets/welcomewidget.h
widgets/welcomeplaylistmodel.h
widgets/whatshotwidget.h
widgets/RecentlyPlayedPlaylistsModel.h
widgets/RecentPlaylistsModel.h
widgets/overlaywidget.h
widgets/HeaderLabel.h
widgets/HeaderWidget.h
widgets/combobox.h
widgets/SocialPlaylistWidget.h
widgets/infowidgets/sourceinfowidget.h
widgets/infowidgets/ArtistInfoWidget.h
widgets/infowidgets/AlbumInfoWidget.h
widgets/kbreadcrumbselectionmodel.h
widgets/kbreadcrumbselectionmodel_p.h
widgets/breadcrumbbar.h
widgets/breadcrumbbuttonbase.h
widgets/headerbreadcrumb.h
widgets/siblingcrumbbutton.h
kdsingleapplicationguard/kdsingleapplicationguard.h
thirdparty/kdsingleapplicationguard/kdsingleapplicationguard.h
thirdparty/Qocoa/qsearchfield.h
)
set( libHeaders_NoMOC
@@ -383,12 +427,15 @@ set( libUI ${libUI}
widgets/newplaylistwidget.ui
widgets/searchwidget.ui
widgets/welcomewidget.ui
widgets/whatshotwidget.ui
widgets/SocialPlaylistWidget.ui
widgets/infowidgets/sourceinfowidget.ui
widgets/infowidgets/ArtistInfoWidget.ui
widgets/infowidgets/AlbumInfoWidget.ui
playlist/topbar/topbar.ui
playlist/infobar/infobar.ui
playlist/queueview.ui
context/ContextWidget.ui
infobar/infobar.ui
)
include_directories( . ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_BINARY_DIR}/.. ..
@@ -406,16 +453,28 @@ include_directories( . ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_BINARY_DIR}/.
${LIBPORTFWD_INCLUDE_DIR}
${THIRDPARTY_DIR}/qxt/qxtweb-standalone/qxtweb
${CMAKE_BINARY_DIR}/thirdparty/liblastfm2/src
)
IF( UNIX AND NOT APPLE )
SET( libSources ${libSources}
infosystem/infoplugins/unix/mprispluginrootadaptor.cpp
infosystem/infoplugins/unix/mprispluginplayeradaptor.cpp
infosystem/infoplugins/unix/mprisplugin.cpp
infosystem/infoplugins/unix/fdonotifyplugin.cpp
infosystem/infoplugins/unix/imageconverter.cpp )
SET( libHeaders ${libHeaders}
infosystem/infoplugins/unix/mprispluginrootadaptor.h
infosystem/infoplugins/unix/mprispluginplayeradaptor.h
infosystem/infoplugins/unix/mprisplugin.h
infosystem/infoplugins/unix/fdonotifyplugin.h )
IF( BUILD_GUI AND X11_FOUND )
INCLUDE_DIRECTORIES( ${THIRDPARTY_DIR}/libqnetwm )
SET( libSources ${libSources} ${THIRDPARTY_DIR}/libqnetwm/libqnetwm/netwm.cpp )
SET( LINK_LIBRARIES ${LINK_LIBRARIES} ${X11_LIBRARIES} )
ENDIF()
ENDIF( UNIX AND NOT APPLE )
IF( WIN32 )
@@ -441,11 +500,10 @@ IF( APPLE )
SET( libSources ${libSources}
infosystem/infoplugins/mac/adium.mm
infosystem/infoplugins/mac/adiumplugin.cpp
widgets/maclineedit.mm
utils/tomahawkutils_mac.mm )
utils/tomahawkutils_mac.mm
thirdparty/Qocoa/qsearchfield_mac.mm )
SET( libHeaders ${libHeaders}
widgets/maclineedit.h
infosystem/infoplugins/mac/adium.h
infosystem/infoplugins/mac/adiumplugin.h )
@@ -459,6 +517,8 @@ IF( APPLE )
/System/Library/Frameworks/AppKit.framework
)
ELSE( APPLE )
SET( libSources ${libSources} thirdparty/Qocoa/qsearchfield.cpp )
ENDIF( APPLE )
IF(LIBLASTFM_FOUND)

View File

@@ -0,0 +1,74 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2010-2011, 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 ACCOUNT_H
#define ACCOUNT_H
#include <QtCore/QObject>
#include <QtCore/QVariantMap>
#include <QtGui/QWidget>
#include "typedefs.h"
#include "dllmacro.h"
#include "infosystem/infosystem.h"
#include "sip/SipPlugin.h"
namespace Tomahawk
{
class DLLEXPORT Account
{
typedef QMap< QString, bool > ACLMap;
public:
enum AccountTypes { InfoType, SipType };
explicit Account();
virtual ~Account();
QString accountServiceName(); // e.g. "Twitter", "Last.fm"
void setAccountServiceName( const QString &serviceName );
QString accountFriendlyName(); // e.g. screen name on the service, JID, etc.
void setAccountFriendlyName( const QString &friendlyName );
bool autoConnect();
void setAutoConnect( bool autoConnect );
QStringMap credentials();
void setCredentials( const QStringMap &credentialMap );
QVariantMap configuration();
void setConfiguration( const QVariantMap &configuration );
QWidget* configurationWidget();
ACLMap acl();
void setAcl( const ACLMap &acl );
QWidget* aclWidget();
QSet< AccountTypes > types();
void setTypes( const QSet< AccountTypes > types );
Tomahawk::InfoSystem::InfoPlugin* infoPlugin();
SipPlugin* sipPlugin();
};
};
#endif // ACCOUNT_H

View File

@@ -74,6 +74,12 @@ AudioEngine::AudioEngine()
connect( m_audioOutput, SIGNAL( volumeChanged( qreal ) ), this, SLOT( onVolumeChanged( qreal ) ) );
onVolumeChanged( m_audioOutput->volume() );
connect( this, SIGNAL(started(Tomahawk::result_ptr)), SIGNAL(currentTrackChanged()));
connect( this, SIGNAL(stopped()), SIGNAL(currentTrackChanged()));
#ifdef Q_OS_MAC
// On mac, phonon volume is independent from system volume, so the onVolumeChanged call above just sets our volume to 100%.
// Since it's indendent, we'll set it to 75% since that's nicer
@@ -137,7 +143,7 @@ AudioEngine::play()
QVariant::fromValue< Tomahawk::InfoSystem::InfoCriteriaHash >( trackInfo ) );
}
else
loadNextTrack();
next();
}
@@ -188,55 +194,93 @@ AudioEngine::previous()
{
tDebug( LOGEXTRA ) << Q_FUNC_INFO;
if ( m_playlist.isNull() )
return;
if ( m_playlist.data()->skipRestrictions() == PlaylistInterface::NoSkip ||
m_playlist.data()->skipRestrictions() == PlaylistInterface::NoSkipBackwards )
return;
loadPreviousTrack();
if( canGoPrevious() )
loadPreviousTrack();
}
void
AudioEngine::next()
{
tDebug( LOGEXTRA ) << Q_FUNC_INFO;
if( canGoNext() )
loadNextTrack();
}
bool
AudioEngine::canGoNext()
{
tDebug( LOGEXTRA ) << Q_FUNC_INFO;
if ( m_queue && m_queue->trackCount() )
return true;
if ( m_playlist.isNull() )
return;
return false;
if ( m_playlist.data()->skipRestrictions() == PlaylistInterface::NoSkip ||
m_playlist.data()->skipRestrictions() == PlaylistInterface::NoSkipForwards )
return;
return false;
if ( !m_currentTrack.isNull() && !m_playlist.data()->hasNextItem() &&
m_currentTrack->id() == m_playlist.data()->currentItem()->id() )
( m_playlist.data()->currentItem().isNull() || ( m_currentTrack->id() == m_playlist.data()->currentItem()->id() ) ) )
{
//For instance, when doing a catch-up while listening along, but the person
//you're following hasn't started a new track yet...don't do anything
tDebug( LOGEXTRA ) << Q_FUNC_INFO << "catch up";
return false;
}
return m_playlist.data()->hasNextItem();
}
bool
AudioEngine::canGoPrevious()
{
if ( m_playlist.isNull() )
return false;
if ( m_playlist.data()->skipRestrictions() == PlaylistInterface::NoSkip ||
m_playlist.data()->skipRestrictions() == PlaylistInterface::NoSkipBackwards )
return false;
return true;
}
bool
AudioEngine::canSeek()
{
bool phononCanSeek = true;
/* TODO: When phonon properly reports this, re-enable it
if ( m_mediaObject && m_mediaObject->isValid() )
phononCanSeek = m_mediaObject->isSeekable();
*/
return !m_playlist.isNull() && ( m_playlist.data()->seekRestrictions() != PlaylistInterface::NoSeek ) && phononCanSeek;
}
void
AudioEngine::seek( qint64 ms )
{
if( !canSeek() )
{
qDebug() << "Could not seek!";
return;
}
loadNextTrack();
if( isPlaying() || isPaused() )
{
tDebug( LOGVERBOSE ) << Q_FUNC_INFO << ms;
m_mediaObject->seek( ms );
emit seeked( ms );
}
}
void
AudioEngine::seek( int ms )
{
if ( !m_playlist.isNull() && m_playlist.data()->seekRestrictions() == PlaylistInterface::NoSeek )
return;
if ( isPlaying() || isPaused() )
{
tDebug( LOGVERBOSE ) << Q_FUNC_INFO << ms;
m_mediaObject->seek( ms );
}
seek( (qint64) ms );
}
void
AudioEngine::setVolume( int percentage )
{
@@ -305,11 +349,16 @@ AudioEngine::infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData requestData,
return;
}
if ( m_currentTrack.isNull() ||
m_currentTrack->track().isNull() ||
m_currentTrack->artist().isNull() )
return;
QVariantMap playInfo;
playInfo["message"] = QString( "Tomahawk is playing \"%1\" by %2 on album %3." )
.arg( m_currentTrack->track() )
.arg( m_currentTrack->artist()->name() )
.arg( m_currentTrack->album()->name() );
playInfo["message"] = tr( "Tomahawk is playing \"%1\" by %2%3." )
.arg( m_currentTrack->track() )
.arg( m_currentTrack->artist()->name() )
.arg( m_currentTrack->album().isNull() ? QString() : tr( " on album %1" ).arg( m_currentTrack->album()->name() ) );
if ( !output.isNull() && output.isValid() )
{
QVariantMap returnedData = output.value< QVariantMap >();
@@ -468,11 +517,15 @@ AudioEngine::loadNextTrack()
if ( !m_playlist.isNull() && result.isNull() )
{
tDebug( LOGEXTRA ) << Q_FUNC_INFO << " loading playlist's next item";
result = m_playlist.data()->nextItem();
}
if ( !result.isNull() )
{
tDebug( LOGEXTRA ) << Q_FUNC_INFO << " got next item, loading track";
loadTrack( result );
}
else
{
if ( !m_playlist.isNull() && m_playlist.data()->retryMode() == Tomahawk::PlaylistInterface::Retry )
@@ -487,8 +540,6 @@ AudioEngine::playItem( Tomahawk::PlaylistInterface* playlist, const Tomahawk::re
{
tDebug( LOGEXTRA ) << Q_FUNC_INFO << ( result.isNull() ? QString() : result->url() );
if ( !result->isOnline() )
return;
if ( !m_playlist.isNull() )
m_playlist.data()->reset();
@@ -512,7 +563,7 @@ AudioEngine::playlistNextTrackReady()
return;
m_waitingOnNewTrack = false;
next();
loadNextTrack();
}
@@ -564,7 +615,14 @@ AudioEngine::onStateChanged( Phonon::State newState, Phonon::State oldState )
{
m_expectStop = false;
tDebug( LOGEXTRA ) << "Finding next track.";
loadNextTrack();
if ( canGoNext() )
loadNextTrack();
else
{
if ( !m_playlist.isNull() && m_playlist.data()->retryMode() == Tomahawk::PlaylistInterface::Retry )
m_waitingOnNewTrack = true;
stop();
}
}
}
}
@@ -596,10 +654,15 @@ void
AudioEngine::setPlaylist( PlaylistInterface* playlist )
{
if ( !m_playlist.isNull() )
{
if ( m_playlist.data()->object() && m_playlist.data()->retryMode() == PlaylistInterface::Retry )
disconnect( m_playlist.data()->object(), SIGNAL( nextTrackReady() ) );
m_playlist.data()->reset();
}
if ( !playlist )
return;
m_playlist = playlist->getSharedPointer();
if ( m_playlist.data()->object() && m_playlist.data()->retryMode() == PlaylistInterface::Retry )

View File

@@ -29,6 +29,8 @@
#include "infosystem/infosystem.h"
#include "result.h"
#include "album.h"
#include "artist.h"
#include "typedefs.h"
#include "dllmacro.h"
@@ -42,7 +44,12 @@ namespace Tomahawk
class DLLEXPORT AudioEngine : public QObject
{
Q_OBJECT
Q_OBJECT
Q_ENUMS( AudioState )
Q_PROPERTY(AudioState audioState READ state NOTIFY stateChanged)
Q_PROPERTY(QString currentArtist READ currentArtist NOTIFY currentTrackChanged)
Q_PROPERTY(QString currentAlbum READ currentAlbum NOTIFY currentTrackChanged)
Q_PROPERTY(QString currentTitle READ currentTitle NOTIFY currentTrackChanged)
public:
enum AudioErrorCode { StreamReadError, AudioDeviceError, DecodeError };
@@ -69,6 +76,9 @@ public:
Tomahawk::result_ptr currentTrack() const { return m_currentTrack; }
qint64 currentTime() const { return m_mediaObject->currentTime(); }
qint64 currentTrackTotalTime() const { return m_mediaObject->totalTime(); }
public slots:
void playPause();
void play();
@@ -78,7 +88,12 @@ public slots:
void previous();
void next();
void seek( int ms );
bool canGoPrevious();
bool canGoNext();
bool canSeek();
void seek( qint64 ms );
void seek( int ms ); // for compatibility with seekbar in audiocontrols
void setVolume( int percentage );
void lowerVolume() { setVolume( volume() - AUDIO_VOLUME_STEP ); }
void raiseVolume() { setVolume( volume() + AUDIO_VOLUME_STEP ); }
@@ -94,7 +109,13 @@ public slots:
void infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData requestData, QVariant output );
void infoSystemFinished( QString caller );
const QString currentArtist() const { return !m_currentTrack.isNull() ? m_currentTrack->artist()->name() : QString(); }
const QString currentAlbum() const { return !m_currentTrack.isNull() ? currentTrack()->album()->name() : QString(); }
const QString currentTitle() const { return !m_currentTrack.isNull() ? currentTrack()->track() : QString(); }
signals:
void currentTrackChanged();
void loading( const Tomahawk::result_ptr& track );
void started( const Tomahawk::result_ptr& track );
void finished( const Tomahawk::result_ptr& track );
@@ -102,6 +123,8 @@ signals:
void paused();
void resumed();
void seeked( qint64 ms );
void stateChanged( AudioState newState, AudioState oldState );
void volumeChanged( int volume /* in percent */ );

View File

@@ -0,0 +1,95 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2010-2011, 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/>.
*/
#include "ContextPage.h"
#include <QGraphicsLinearLayout>
using namespace Tomahawk;
void
ContextProxyPage::paint( QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget )
{
painter->save();
painter->setRenderHint( QPainter::Antialiasing, true );
painter->setPen( StyleHelper::headerHighlightColor() );
painter->setBrush( StyleHelper::headerHighlightColor() );
painter->drawRoundedRect( option->rect, 4.0, 4.0 );
QFont f( font() );
f.setBold( true );
f.setPixelSize( 14 );
painter->setFont( f );
painter->setPen( Qt::white );
QRect r( 1, 1, option->rect.width(), 19 );
QTextOption to( Qt::AlignCenter );
painter->drawText( r, m_page->title(), to );
painter->restore();
QGraphicsWidget::paint( painter, option, widget );
}
void
ContextProxyPage::setPage( Tomahawk::ContextPage* page )
{
m_page = page;
#ifdef Q_OS_LINUX //FIXME: why do we need this? maybe it's only oxygen style misbehaving?
QGraphicsWebView* testWebView = qobject_cast<QGraphicsWebView*>( page->widget() );
if ( testWebView )
{
setContentsMargins( 4, 4, 4, 4 );
}
#endif
QGraphicsLinearLayout* layout = new QGraphicsLinearLayout();
layout->setContentsMargins( 4, 20, 4, 4 );
layout->addItem( page->widget() );
setLayout( layout );
page->widget()->installEventFilter( this );
}
bool
ContextProxyPage::eventFilter( QObject* watched, QEvent* event )
{
if ( event->type() == QEvent::GrabMouse )
{
emit focused();
}
return QGraphicsWidget::eventFilter( watched, event );
}
bool
ContextProxyPage::sceneEvent( QEvent* event )
{
if ( event->type() == QEvent::GrabMouse )
{
emit focused();
}
return QGraphicsWidget::sceneEvent( event );
}

View File

@@ -0,0 +1,91 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2010-2011, 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 CONTEXTPAGE_H
#define CONTEXTPAGE_H
#include <QGraphicsProxyWidget>
#include <QGraphicsWebView>
#include <QStyleOptionGraphicsItem>
#include "typedefs.h"
#include "playlistinterface.h"
#include "utils/stylehelper.h"
#include "utils/tomahawkutils.h"
#include "dllmacro.h"
#include <signal.h>
namespace Tomahawk
{
class DLLEXPORT ContextPage : public QObject
{
Q_OBJECT
public:
ContextPage() {}
virtual ~ContextPage() {}
virtual QGraphicsWidget* widget() = 0;
virtual Tomahawk::PlaylistInterface* playlistInterface() const = 0;
virtual QString title() const = 0;
virtual QString description() const = 0;
virtual QPixmap pixmap() const { return QPixmap( RESPATH "icons/tomahawk-icon-128x128.png" ); }
virtual bool jumpToCurrentTrack() = 0;
public slots:
virtual void setQuery( const Tomahawk::query_ptr& query ) = 0;
signals:
void nameChanged( const QString& );
void descriptionChanged( const QString& );
void pixmapChanged( const QPixmap& );
void destroyed( QWidget* widget );
};
class DLLEXPORT ContextProxyPage : public QGraphicsWidget
{
Q_OBJECT
public:
ContextProxyPage() : QGraphicsWidget()
{}
Tomahawk::ContextPage* page() const { return m_page; }
void setPage( Tomahawk::ContextPage* page );
virtual bool eventFilter( QObject* watched, QEvent* event );
signals:
void focused();
protected:
virtual void paint( QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget );
virtual bool sceneEvent( QEvent* event );
private:
Tomahawk::ContextPage* m_page;
};
}; // ns
#endif //CONTEXTPAGE_H

View File

@@ -0,0 +1,306 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2010-2011, 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/>.
*/
#include "ContextWidget.h"
#include "ui_ContextWidget.h"
#include <QGraphicsProxyWidget>
#include <QGraphicsScene>
#include <QPropertyAnimation>
#include <QTimeLine>
#include "context/ContextPage.h"
#include "context/pages/RelatedArtistsContext.h"
#include "context/pages/TopTracksContext.h"
#include "context/pages/WikipediaContext.h"
#include "playlist/artistview.h"
#include "playlist/treemodel.h"
#define ANIMATION_TIME 450
#define SLIDE_TIME 350
using namespace Tomahawk;
ContextWidget::ContextWidget( QWidget* parent )
: QWidget( parent )
, ui( new Ui::ContextWidget )
, m_minHeight( 22 )
, m_currentView( 0 )
, m_visible( false )
{
ui->setupUi( this );
TomahawkUtils::unmarginLayout( layout() );
setContentsMargins( 0, 0, 0, 0 );
m_scene = new QGraphicsScene( this );
TopTracksContext* ttc = new TopTracksContext();
RelatedArtistsContext* rac = new RelatedArtistsContext();
WebContext* wiki = new WikipediaContext();
WebContext* lastfm = new LastfmContext();
m_views << ttc;
m_views << rac;
m_views << wiki;
m_views << lastfm;
foreach ( ContextPage* view, m_views )
{
ContextProxyPage* page = new ContextProxyPage();
page->setPage( view );
m_scene->addItem( page );
connect( page, SIGNAL( focused() ), SLOT( onPageFocused() ) );
m_pages << page;
}
ui->contextView->setScene( m_scene );
ui->contextView->setFrameShape( QFrame::NoFrame );
ui->contextView->setStyleSheet( "background: transparent" );
ui->contextView->setAttribute( Qt::WA_MacShowFocusRect, 0 );
ui->contextView->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding );
ui->contextView->hide();
QPalette whitePal = ui->toggleButton->palette();
whitePal.setColor( QPalette::Foreground, Qt::white );
ui->toggleButton->setPalette( whitePal );
QFont boldFont = ui->toggleButton->font();
boldFont.setPixelSize( 12 );
boldFont.setBold( true );
ui->toggleButton->setFont( boldFont );
ui->toggleButton->setText( tr( "Open Dashboard" ) );
setAutoFillBackground( true );
setFixedHeight( m_minHeight );
QPalette pal = palette();
pal.setBrush( QPalette::Window, QColor( 0x70, 0x70, 0x70 ) );
setPalette( pal );
connect( ui->toggleButton, SIGNAL( clicked() ), SLOT( toggleSize() ) );
m_timeLine = new QTimeLine( ANIMATION_TIME, this );
m_timeLine->setUpdateInterval( 20 );
m_timeLine->setEasingCurve( QEasingCurve::OutCubic );
connect( m_timeLine, SIGNAL( frameChanged( int ) ), SLOT( onAnimationStep( int ) ) );
connect( m_timeLine, SIGNAL( finished() ), SLOT( onAnimationFinished() ) );
}
ContextWidget::~ContextWidget()
{
}
void
ContextWidget::layoutViews( bool animate )
{
int smallViewWidth = 120;
float smallViewOpacity = 0.6;
int margin = 6;
int maxVisible = 2;
int itemSize = ( m_scene->sceneRect().width() - smallViewWidth * 2 ) / maxVisible;
int firstPos = margin;
float opacity;
if ( m_currentView > 0 )
firstPos = smallViewWidth;
if ( m_currentView + maxVisible >= m_pages.count() )
{
int delta = m_pages.count() - m_currentView;
firstPos = m_scene->sceneRect().width() - ( delta * itemSize ) + 1;
}
for ( int i = 0; i < m_pages.count(); i++ )
{
QGraphicsWidget* view = m_pages.at( i );
int x = firstPos - ( ( m_currentView - i ) * itemSize );
if ( ( x < smallViewWidth && x < firstPos ) || i > m_currentView + maxVisible - 1 )
{
opacity = smallViewOpacity;
}
else
{
opacity = 1.0;
}
{
QPropertyAnimation* animation = new QPropertyAnimation( view, "opacity" );
animation->setDuration( SLIDE_TIME );
animation->setEndValue( opacity );
animation->start();
}
QRect rect( x, margin, itemSize - margin * 2, m_scene->sceneRect().height() - margin * 2 );
if ( animate )
{
{
QPropertyAnimation* animation = new QPropertyAnimation( view, "geometry" );
animation->setDuration( SLIDE_TIME );
animation->setEndValue( rect );
animation->start();
}
}
else
{
view->setGeometry( rect );
}
}
}
void
ContextWidget::onPageFocused()
{
ContextProxyPage* widget = qobject_cast< ContextProxyPage* >( sender() );
int i = 0;
foreach ( ContextProxyPage* view, m_pages )
{
if ( view == widget )
{
m_currentView = i;
layoutViews( true );
return;
}
i++;
}
}
void
ContextWidget::fadeOut( bool animate )
{
foreach ( QGraphicsWidget* view, m_pages )
{
if ( animate )
{
QPropertyAnimation* animation = new QPropertyAnimation( view, "opacity" );
animation->setDuration( SLIDE_TIME );
animation->setEndValue( 0.0 );
animation->start();
}
else
view->setOpacity( 0.0 );
}
}
void
ContextWidget::setQuery( const Tomahawk::query_ptr& query, bool force )
{
if ( query.isNull() )
return;
if ( !force && !m_query.isNull() && query->artist() == m_query->artist() )
return;
m_query = query;
if ( height() > m_minHeight )
{
foreach ( ContextProxyPage* proxy, m_pages )
{
proxy->page()->setQuery( query );
}
layoutViews( true );
}
}
void
ContextWidget::toggleSize()
{
m_maxHeight = TomahawkUtils::tomahawkWindow()->height() * 0.3;
if ( height() == m_minHeight )
{
ui->toggleButton->setText( tr( "Close Dashboard" ) );
m_timeLine->setFrameRange( height(), m_maxHeight );
m_timeLine->setDirection( QTimeLine::Forward );
m_timeLine->start();
}
else
{
ui->toggleButton->setText( tr( "Open Dashboard" ) );
m_visible = false;
ui->contextView->hide();
m_timeLine->setFrameRange( m_minHeight, height() );
m_timeLine->setDirection( QTimeLine::Backward );
m_timeLine->start();
}
}
void
ContextWidget::onAnimationStep( int frame )
{
setFixedHeight( frame );
}
void
ContextWidget::onAnimationFinished()
{
if ( m_timeLine->direction() == QTimeLine::Forward )
{
setFixedHeight( m_maxHeight );
m_visible = true;
ui->contextView->show();
fadeOut( false );
m_scene->setSceneRect( ui->contextView->viewport()->rect() );
layoutViews( false );
setQuery( m_query, true );
}
else
{
setFixedHeight( m_minHeight );
}
}
void
ContextWidget::paintEvent( QPaintEvent* e )
{
QWidget::paintEvent( e );
}
void
ContextWidget::resizeEvent( QResizeEvent* e )
{
QWidget::resizeEvent( e );
if ( m_visible )
{
m_scene->setSceneRect( ui->contextView->viewport()->rect() );
layoutViews( false );
}
}

View File

@@ -0,0 +1,88 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2010-2011, 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 CONTEXTWIDGET_H
#define CONTEXTWIDGET_H
#include <QGraphicsView>
#include "dllmacro.h"
#include "query.h"
class QGraphicsScene;
class QGraphicsWebView;
class QGraphicsWidget;
class QTimeLine;
namespace Tomahawk
{
class ContextPage;
class ContextProxyPage;
}
namespace Ui
{
class ContextWidget;
}
class DLLEXPORT ContextWidget : public QWidget
{
Q_OBJECT
public:
ContextWidget( QWidget* parent = 0 );
~ContextWidget();
public slots:
void setQuery( const Tomahawk::query_ptr& query, bool force = false );
void toggleSize();
private slots:
void onPageFocused();
void onAnimationStep( int frame );
void onAnimationFinished();
protected:
void paintEvent( QPaintEvent* e );
void resizeEvent( QResizeEvent* e );
private:
void fadeOut( bool animate );
void layoutViews( bool animate = true );
Ui::ContextWidget* ui;
int m_minHeight;
int m_maxHeight;
QTimeLine* m_timeLine;
QGraphicsScene* m_scene;
QList<Tomahawk::ContextPage*> m_views;
QList<Tomahawk::ContextProxyPage*> m_pages;
int m_currentView;
Tomahawk::query_ptr m_query;
bool m_visible;
};
#endif // CONTEXTWIDGET_H

View File

@@ -0,0 +1,76 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ContextWidget</class>
<widget class="QWidget" name="ContextWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>774</width>
<height>72</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>72</height>
</size>
</property>
<property name="windowTitle">
<string>InfoBar</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="HeaderLabel" name="toggleButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Dashboard</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>1</width>
<height>16</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QGraphicsView" name="contextView"/>
</item>
</layout>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>HeaderLabel</class>
<extends>QLabel</extends>
<header location="global">widgets/HeaderLabel.h</header>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>

View File

@@ -0,0 +1,123 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2010-2011, 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/>.
*/
#include "RelatedArtistsContext.h"
#include <QHeaderView>
#include "playlist/artistview.h"
#include "playlist/treemodel.h"
using namespace Tomahawk;
RelatedArtistsContext::RelatedArtistsContext()
: ContextPage()
, m_infoId( uuid() )
{
m_relatedView = new ArtistView();
m_relatedView->setGuid( "RelatedArtistsContext" );
m_relatedModel = new TreeModel( m_relatedView );
m_relatedModel->setColumnStyle( TreeModel::TrackOnly );
m_relatedView->setTreeModel( m_relatedModel );
m_relatedView->setVerticalScrollBarPolicy( Qt::ScrollBarAlwaysOff );
QPalette pal = m_relatedView->palette();
pal.setColor( QPalette::Window, QColor( 0, 0, 0, 0 ) );
m_relatedView->setPalette( pal );
m_proxy = new QGraphicsProxyWidget();
m_proxy->setWidget( m_relatedView );
connect( Tomahawk::InfoSystem::InfoSystem::instance(),
SIGNAL( info( Tomahawk::InfoSystem::InfoRequestData, QVariant ) ),
SLOT( infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData, QVariant ) ) );
connect( Tomahawk::InfoSystem::InfoSystem::instance(), SIGNAL( finished( QString ) ), SLOT( infoSystemFinished( QString ) ) );
}
RelatedArtistsContext::~RelatedArtistsContext()
{
}
void
RelatedArtistsContext::setQuery( const Tomahawk::query_ptr& query )
{
if ( !m_query.isNull() && query->artist() == m_query->artist() )
return;
m_query = query;
Tomahawk::InfoSystem::InfoCriteriaHash artistInfo;
artistInfo["artist"] = query->artist();
Tomahawk::InfoSystem::InfoRequestData requestData;
requestData.caller = m_infoId;
requestData.customData = QVariantMap();
requestData.input = QVariant::fromValue< Tomahawk::InfoSystem::InfoCriteriaHash >( artistInfo );
requestData.type = Tomahawk::InfoSystem::InfoArtistSimilars;
Tomahawk::InfoSystem::InfoSystem::instance()->getInfo( requestData );
}
void
RelatedArtistsContext::infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData requestData, QVariant output )
{
if ( requestData.caller != m_infoId )
return;
InfoSystem::InfoCriteriaHash trackInfo;
trackInfo = requestData.input.value< InfoSystem::InfoCriteriaHash >();
if ( output.canConvert< QVariantMap >() )
{
if ( trackInfo["artist"] != m_query->artist() )
{
qDebug() << "Returned info was for:" << trackInfo["artist"] << "- was looking for:" << m_query->artist();
return;
}
}
QVariantMap returnedData = output.value< QVariantMap >();
switch ( requestData.type )
{
case InfoSystem::InfoArtistSimilars:
{
m_relatedModel->clear();
const QStringList artists = returnedData["artists"].toStringList();
foreach ( const QString& artist, artists )
{
m_relatedModel->addArtists( Artist::get( artist ) );
}
break;
}
default:
return;
}
}
void
RelatedArtistsContext::infoSystemFinished( QString target )
{
Q_UNUSED( target );
}

View File

@@ -0,0 +1,67 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2010-2011, 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 RELATEDARTISTSCONTEXT_H
#define RELATEDARTISTSCONTEXT_H
#include <QGraphicsProxyWidget>
#include "dllmacro.h"
#include "query.h"
#include "context/ContextPage.h"
#include "infosystem/infosystem.h"
class TreeModel;
class ArtistView;
class DLLEXPORT RelatedArtistsContext : public Tomahawk::ContextPage
{
Q_OBJECT
public:
RelatedArtistsContext();
~RelatedArtistsContext();
virtual QGraphicsWidget* widget() { return m_proxy; }
virtual Tomahawk::PlaylistInterface* playlistInterface() const { return 0; }
virtual QString title() const { return tr( "Related Artists" ); }
virtual QString description() const { return QString(); }
virtual bool jumpToCurrentTrack() { return false; }
public slots:
virtual void setQuery( const Tomahawk::query_ptr& query );
private slots:
void infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData requestData, QVariant output );
void infoSystemFinished( QString target );
private:
ArtistView* m_relatedView;
TreeModel* m_relatedModel;
QGraphicsProxyWidget* m_proxy;
QString m_infoId;
Tomahawk::query_ptr m_query;
};
#endif // RELATEDARTISTSCONTEXT_H

View File

@@ -0,0 +1,129 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2010-2011, 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/>.
*/
#include "TopTracksContext.h"
#include "playlist/playlistmodel.h"
#include "playlist/playlistview.h"
#include "playlist/trackheader.h"
using namespace Tomahawk;
TopTracksContext::TopTracksContext()
: ContextPage()
, m_infoId( uuid() )
{
m_topHitsView = new PlaylistView();
m_topHitsView->setGuid( "TopTracksContext" );
m_topHitsView->setUpdatesContextView( false );
m_topHitsModel = new PlaylistModel( m_topHitsView );
m_topHitsModel->setStyle( TrackModel::Short );
m_topHitsView->setPlaylistModel( m_topHitsModel );
m_topHitsView->setVerticalScrollBarPolicy( Qt::ScrollBarAlwaysOff );
QPalette pal = m_topHitsView->palette();
pal.setColor( QPalette::Window, QColor( 0, 0, 0, 0 ) );
m_topHitsView->setPalette( pal );
m_proxy = new QGraphicsProxyWidget();
m_proxy->setWidget( m_topHitsView );
connect( Tomahawk::InfoSystem::InfoSystem::instance(),
SIGNAL( info( Tomahawk::InfoSystem::InfoRequestData, QVariant ) ),
SLOT( infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData, QVariant ) ) );
connect( Tomahawk::InfoSystem::InfoSystem::instance(), SIGNAL( finished( QString ) ), SLOT( infoSystemFinished( QString ) ) );
}
TopTracksContext::~TopTracksContext()
{
}
void
TopTracksContext::setQuery( const Tomahawk::query_ptr& query )
{
if ( !m_query.isNull() && query->artist() == m_query->artist() )
return;
m_query = query;
Tomahawk::InfoSystem::InfoCriteriaHash artistInfo;
artistInfo["artist"] = query->artist();
Tomahawk::InfoSystem::InfoRequestData requestData;
requestData.caller = m_infoId;
requestData.customData = QVariantMap();
requestData.input = QVariant::fromValue< Tomahawk::InfoSystem::InfoCriteriaHash >( artistInfo );
requestData.type = Tomahawk::InfoSystem::InfoArtistSongs;
Tomahawk::InfoSystem::InfoSystem::instance()->getInfo( requestData );
}
void
TopTracksContext::infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData requestData, QVariant output )
{
if ( requestData.caller != m_infoId )
return;
InfoSystem::InfoCriteriaHash trackInfo;
trackInfo = requestData.input.value< InfoSystem::InfoCriteriaHash >();
if ( output.canConvert< QVariantMap >() )
{
if ( trackInfo["artist"] != m_query->artist() )
{
qDebug() << "Returned info was for:" << trackInfo["artist"] << "- was looking for:" << m_query->artist();
return;
}
}
QVariantMap returnedData = output.value< QVariantMap >();
switch ( requestData.type )
{
case InfoSystem::InfoArtistSongs:
{
m_topHitsModel->clear();
const QStringList tracks = returnedData["tracks"].toStringList();
int i = 0;
foreach ( const QString& track, tracks )
{
query_ptr query = Query::get( m_query->artist(), track, QString(), uuid() );
m_topHitsModel->append( query );
if ( ++i == 15 )
break;
}
break;
}
default:
return;
}
}
void
TopTracksContext::infoSystemFinished( QString target )
{
Q_UNUSED( target );
}

View File

@@ -0,0 +1,67 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2010-2011, 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 TOPTRACKSCONTEXT_H
#define TOPTRACKSCONTEXT_H
#include <QGraphicsProxyWidget>
#include "dllmacro.h"
#include "query.h"
#include "context/ContextPage.h"
#include "infosystem/infosystem.h"
class PlaylistModel;
class PlaylistView;
class DLLEXPORT TopTracksContext : public Tomahawk::ContextPage
{
Q_OBJECT
public:
TopTracksContext();
~TopTracksContext();
virtual QGraphicsWidget* widget() { return m_proxy; }
virtual Tomahawk::PlaylistInterface* playlistInterface() const { return 0; }
virtual QString title() const { return tr( "Top Hits" ); }
virtual QString description() const { return QString(); }
virtual bool jumpToCurrentTrack() { return false; }
public slots:
virtual void setQuery( const Tomahawk::query_ptr& query );
private slots:
void infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData requestData, QVariant output );
void infoSystemFinished( QString target );
private:
PlaylistView* m_topHitsView;
PlaylistModel* m_topHitsModel;
QGraphicsProxyWidget* m_proxy;
QString m_infoId;
Tomahawk::query_ptr m_query;
};
#endif // TOPTRACKSCONTEXT_H

View File

@@ -0,0 +1,37 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2010-2011, 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/>.
*/
#include "WebContext.h"
using namespace Tomahawk;
WebContext::WebContext()
: ContextPage()
{
m_webView = new QGraphicsWebView();
QPalette pal = m_webView->palette();
pal.setColor( QPalette::Window, QColor( 0, 0, 0, 0 ) );
m_webView->setPalette( pal );
}
WebContext::~WebContext()
{
}

View File

@@ -0,0 +1,45 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2010-2011, 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 WEBCONTEXT_H
#define WEBCONTEXT_H
#include <QGraphicsWebView>
#include "dllmacro.h"
#include "query.h"
#include "context/ContextPage.h"
class DLLEXPORT WebContext : public Tomahawk::ContextPage
{
Q_OBJECT
public:
WebContext();
~WebContext();
QGraphicsWebView* webView() const { return m_webView; }
virtual QGraphicsWidget* widget() { return m_webView; }
private:
QGraphicsWebView* m_webView;
Tomahawk::query_ptr m_query;
};
#endif // WEBCONTEXT_H

View File

@@ -0,0 +1,43 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2010-2011, 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/>.
*/
#include "WikipediaContext.h"
using namespace Tomahawk;
void
WikipediaContext::setQuery( const Tomahawk::query_ptr& query )
{
if ( !m_query.isNull() && query->artist() == m_query->artist() )
return;
m_query = query;
webView()->load( QString( "http://en.wikipedia.org/w/index.php?printable=yes&title=%1" ).arg( query->artist() ) );
}
void
LastfmContext::setQuery( const Tomahawk::query_ptr& query )
{
if ( !m_query.isNull() && query->artist() == m_query->artist() )
return;
m_query = query;
webView()->load( QString( "http://last.fm/music/%1" ).arg( query->artist() ) );
}

View File

@@ -0,0 +1,74 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2010-2011, 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 WIKIPEDIACONTEXT_H
#define WIKIPEDIACONTEXT_H
#include <QGraphicsProxyWidget>
#include "dllmacro.h"
#include "query.h"
#include "WebContext.h"
class DLLEXPORT WikipediaContext : public WebContext
{
Q_OBJECT
public:
WikipediaContext() : WebContext() {}
~WikipediaContext() {}
virtual Tomahawk::PlaylistInterface* playlistInterface() const { return 0; }
virtual QString title() const { return tr( "Wikipedia" ); }
virtual QString description() const { return QString(); }
virtual bool jumpToCurrentTrack() { return false; }
public slots:
virtual void setQuery( const Tomahawk::query_ptr& query );
private:
Tomahawk::query_ptr m_query;
};
class DLLEXPORT LastfmContext : public WebContext
{
Q_OBJECT
public:
LastfmContext() : WebContext() {}
~LastfmContext() {}
virtual Tomahawk::PlaylistInterface* playlistInterface() const { return 0; }
virtual QString title() const { return tr( "Last.fm" ); }
virtual QString description() const { return QString(); }
virtual bool jumpToCurrentTrack() { return false; }
public slots:
virtual void setQuery( const Tomahawk::query_ptr& query );
private:
Tomahawk::query_ptr m_query;
};
#endif // WIKIPEDIACONTEXT_H

View File

@@ -27,6 +27,7 @@
#include "databasecommand_collectionstats.h"
#include "databaseimpl.h"
#include "network/controlconnection.h"
#include "sourcelist.h"
#include "utils/logger.h"
@@ -72,7 +73,15 @@ DatabaseCommand_AddFiles::postCommitHook()
emit notify( m_queries );
if( source()->isLocal() )
{
Servent::instance()->triggerDBSync();
// Re-calculate local db stats
DatabaseCommand_CollectionStats* cmd = new DatabaseCommand_CollectionStats( SourceList::instance()->getLocal() );
connect( cmd, SIGNAL( done( QVariantMap ) ),
SourceList::instance()->getLocal().data(), SLOT( setStats( QVariantMap ) ), Qt::QueuedConnection );
Database::instance()->enqueue( QSharedPointer<DatabaseCommand>( cmd ) );
}
}

View File

@@ -21,6 +21,7 @@
#include <QSqlQuery>
#include "databaseimpl.h"
#include "utils/tomahawkutils.h"
#include "utils/logger.h"
@@ -29,7 +30,7 @@ DatabaseCommand_AllAlbums::execForArtist( DatabaseImpl* dbi )
{
TomahawkSqlQuery query = dbi->newquery();
QList<Tomahawk::album_ptr> al;
QString orderToken, sourceToken;
QString orderToken, sourceToken, filterToken, tables;
switch ( m_sortOrder )
{
@@ -43,17 +44,33 @@ DatabaseCommand_AllAlbums::execForArtist( DatabaseImpl* dbi )
if ( !m_collection.isNull() )
sourceToken = QString( "AND file.source %1 " ).arg( m_collection->source()->isLocal() ? "IS NULL" : QString( "= %1" ).arg( m_collection->source()->id() ) );
if ( !m_filter.isEmpty() )
{
QString filtersql;
QStringList sl = m_filter.split( " ", QString::SkipEmptyParts );
foreach( QString s, sl )
{
filtersql += QString( " AND ( artist.name LIKE '%%1%' OR album.name LIKE '%%1%' OR track.name LIKE '%%1%' )" ).arg( TomahawkUtils::sqlEscape( s ) );
}
filterToken = QString( "AND artist.id = file_join.artist AND file_join.track = track.id %1" ).arg( filtersql );
tables = "artist, track, file, file_join";
}
else
tables = "file, file_join";
QString sql = QString(
"SELECT DISTINCT album.id, album.name "
"FROM file, file_join "
"FROM %1 "
"LEFT OUTER JOIN album "
"ON file_join.album = album.id "
"WHERE file.id = file_join.file "
"AND file_join.artist = %1 "
"%2 "
"%3 %4 %5"
).arg( m_artist->id() )
"AND file_join.artist = %2 "
"%3 %4 %5 %6 %7"
).arg( tables )
.arg( m_artist->id() )
.arg( sourceToken )
.arg( filterToken )
.arg( m_sortOrder > 0 ? QString( "ORDER BY %1" ).arg( orderToken ) : QString() )
.arg( m_sortDescending ? "DESC" : QString() )
.arg( m_amount > 0 ? QString( "LIMIT 0, %1" ).arg( m_amount ) : QString() );
@@ -74,8 +91,7 @@ DatabaseCommand_AllAlbums::execForArtist( DatabaseImpl* dbi )
al << album;
}
if ( al.count() )
emit albums( al, data() );
emit albums( al, data() );
emit done();
}
@@ -124,8 +140,7 @@ DatabaseCommand_AllAlbums::execForCollection( DatabaseImpl* dbi )
al << album;
}
if ( al.count() )
emit albums( al, data() );
emit albums( al, data() );
emit done();
}

View File

@@ -59,6 +59,7 @@ public:
void setLimit( unsigned int amount ) { m_amount = amount; }
void setSortOrder( DatabaseCommand_AllAlbums::SortOrder order ) { m_sortOrder = order; }
void setSortDescending( bool descending ) { m_sortDescending = descending; }
void setFilter( const QString& filter ) { m_filter = filter; }
signals:
void albums( const QList<Tomahawk::album_ptr>&, const QVariant& data );
@@ -71,6 +72,7 @@ private:
unsigned int m_amount;
DatabaseCommand_AllAlbums::SortOrder m_sortOrder;
bool m_sortDescending;
QString m_filter;
};
#endif // DATABASECOMMAND_ALLALBUMS_H

View File

@@ -21,6 +21,7 @@
#include <QSqlQuery>
#include "databaseimpl.h"
#include "utils/tomahawkutils.h"
#include "utils/logger.h"
@@ -29,7 +30,7 @@ DatabaseCommand_AllArtists::exec( DatabaseImpl* dbi )
{
TomahawkSqlQuery query = dbi->newquery();
QList<Tomahawk::artist_ptr> al;
QString orderToken, sourceToken;
QString orderToken, sourceToken, filterToken, tables;
switch ( m_sortOrder )
{
@@ -41,15 +42,32 @@ DatabaseCommand_AllArtists::exec( DatabaseImpl* dbi )
}
if ( !m_collection.isNull() )
sourceToken = QString( "AND file.source %1 " ).arg( m_collection->source()->isLocal() ? "IS NULL" : QString( "= %1" ).arg( m_collection->source()->id() ) );
sourceToken = QString( "AND file.source %1" ).arg( m_collection->source()->isLocal() ? "IS NULL" : QString( "= %1" ).arg( m_collection->source()->id() ) );
if ( !m_filter.isEmpty() )
{
QString filtersql;
QStringList sl = m_filter.split( " ", QString::SkipEmptyParts );
foreach( QString s, sl )
{
filtersql += QString( " AND ( artist.name LIKE '%%1%' OR album.name LIKE '%%1%' OR track.name LIKE '%%1%' )" ).arg( TomahawkUtils::sqlEscape( s ) );
}
filterToken = QString( "AND file_join.album = album.id AND file_join.track = track.id %1" ).arg( filtersql );
tables = "artist, track, album, file, file_join";
}
else
tables = "artist, file, file_join";
QString sql = QString(
"SELECT DISTINCT artist.id, artist.name "
"FROM artist, file, file_join "
"FROM %1 "
"WHERE file.id = file_join.file "
"AND file_join.artist = artist.id "
"%1 %2 %3 %4"
).arg( sourceToken )
"%2 %3 %4 %5 %6"
).arg( tables )
.arg( sourceToken )
.arg( filterToken )
.arg( m_sortOrder > 0 ? QString( "ORDER BY %1" ).arg( orderToken ) : QString() )
.arg( m_sortDescending ? "DESC" : QString() )
.arg( m_amount > 0 ? QString( "LIMIT 0, %1" ).arg( m_amount ) : QString() );
@@ -64,7 +82,6 @@ DatabaseCommand_AllArtists::exec( DatabaseImpl* dbi )
al << artist;
}
if ( al.count() )
emit artists( al );
emit artists( al );
emit done();
}

View File

@@ -54,6 +54,7 @@ public:
void setLimit( unsigned int amount ) { m_amount = amount; }
void setSortOrder( DatabaseCommand_AllArtists::SortOrder order ) { m_sortOrder = order; }
void setSortDescending( bool descending ) { m_sortDescending = descending; }
void setFilter( const QString& filter ) { m_filter = filter; }
signals:
void artists( const QList<Tomahawk::artist_ptr>& );
@@ -64,6 +65,7 @@ private:
unsigned int m_amount;
DatabaseCommand_AllArtists::SortOrder m_sortOrder;
bool m_sortDescending;
QString m_filter;
};
#endif // DATABASECOMMAND_ALLARTISTS_H

View File

@@ -57,11 +57,7 @@ DatabaseCommand_CollectionStats::exec( DatabaseImpl* dbi )
{
m.insert( "numfiles", query.value( 0 ).toInt() );
m.insert( "lastmodified", query.value( 1 ).toInt() );
if ( !source()->isLocal() && !source()->lastOpGuid().isEmpty() )
m.insert( "lastop", source()->lastOpGuid() );
else
m.insert( "lastop", query.value( 2 ).toString() );
m.insert( "lastop", query.value( 2 ).toString() );
}
emit done( m );

View File

@@ -31,11 +31,31 @@ void
DatabaseCommand_LoadAllAutoPlaylists::exec( DatabaseImpl* dbi )
{
TomahawkSqlQuery query = dbi->newquery();
QString orderToken, sourceToken;
switch ( m_sortOrder )
{
case 0:
break;
case DatabaseCommand_LoadAllPlaylists::ModificationTime:
orderToken = "playlist.createdOn";
}
if ( !source().isNull() )
sourceToken = QString( "AND source %1 " ).arg( source()->isLocal() ? "IS NULL" : QString( "= %1" ).arg( source()->id() ) );
query.exec( QString( "SELECT playlist.guid as guid, title, info, creator, createdOn, lastmodified, shared, currentrevision, dynamic_playlist.pltype, dynamic_playlist.plmode "
"FROM playlist, dynamic_playlist WHERE source %1 AND dynplaylist = 'true' AND playlist.guid = dynamic_playlist.guid AND dynamic_playlist.plmode = %2 AND dynamic_playlist.autoload = 'true'" )
.arg( source()->isLocal() ? "IS NULL" : QString( "=%1" ).arg( source()->id() ) )
.arg( Static ) );
"FROM playlist, dynamic_playlist WHERE dynplaylist = 'true' AND playlist.guid = dynamic_playlist.guid AND dynamic_playlist.plmode = %1 AND dynamic_playlist.autoload = 'true' "
"%2"
"%3 %4 %5"
)
.arg( Static )
.arg( sourceToken )
.arg( m_sortOrder > 0 ? QString( "ORDER BY %1" ).arg( orderToken ) : QString() )
.arg( m_sortDescending ? "DESC" : QString() )
.arg( m_limitAmount > 0 ? QString( "LIMIT 0, %1" ).arg( m_limitAmount ) : QString() ) );
QList<dynplaylist_ptr> plists;
while ( query.next() )

View File

@@ -24,6 +24,7 @@
#include "databasecommand.h"
#include "typedefs.h"
#include "databasecommand_loadallplaylists.h"
class DatabaseCommand_LoadAllAutoPlaylists : public DatabaseCommand
{
@@ -32,15 +33,27 @@ class DatabaseCommand_LoadAllAutoPlaylists : public DatabaseCommand
public:
explicit DatabaseCommand_LoadAllAutoPlaylists( const Tomahawk::source_ptr& s, QObject* parent = 0 )
: DatabaseCommand( s, parent )
, m_limitAmount( 0 )
, m_sortOrder( DatabaseCommand_LoadAllPlaylists::None )
, m_sortDescending( false )
{}
virtual void exec( DatabaseImpl* );
virtual bool doesMutates() const { return false; }
virtual QString commandname() const { return "loadallautoplaylists"; }
void setLimit( unsigned int limit ) { m_limitAmount = limit; }
void setSortOrder( DatabaseCommand_LoadAllPlaylists::SortOrder order ) { m_sortOrder = order; }
void setSortDescending( bool descending ) { m_sortDescending = descending; }
signals:
void autoPlaylistLoaded( const Tomahawk::source_ptr& source, const QVariantList& data );
void done();
private:
unsigned int m_limitAmount;
DatabaseCommand_LoadAllPlaylists::SortOrder m_sortOrder;
bool m_sortDescending;
};
#endif

View File

@@ -1,6 +1,7 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2010-2011, Christian Muehlhaeuser <muesli@tomahawk-player.org>
* Copyright 2011, 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
@@ -31,12 +32,31 @@ void
DatabaseCommand_LoadAllPlaylists::exec( DatabaseImpl* dbi )
{
TomahawkSqlQuery query = dbi->newquery();
QString orderToken, sourceToken;
switch ( m_sortOrder )
{
case 0:
break;
case ModificationTime:
orderToken = "playlist.createdOn";
}
if ( !source().isNull() )
sourceToken = QString( "AND source %1 " ).arg( source()->isLocal() ? "IS NULL" : QString( "= %1" ).arg( source()->id() ) );
query.exec( QString( "SELECT guid, title, info, creator, lastmodified, shared, currentrevision, createdOn "
"FROM playlist WHERE source %1 AND dynplaylist = 'false'" )
.arg( source()->isLocal() ? "IS NULL" :
QString( "= %1" ).arg( source()->id() )
) );
"FROM playlist "
"WHERE dynplaylist = 'false' "
"%1 "
"%2 %3 %4"
)
.arg( sourceToken )
.arg( m_sortOrder > 0 ? QString( "ORDER BY %1" ).arg( orderToken ) : QString() )
.arg( m_sortDescending ? "DESC" : QString() )
.arg( m_limitAmount > 0 ? QString( "LIMIT 0, %1" ).arg( m_limitAmount ) : QString() ) );
QList<playlist_ptr> plists;
while ( query.next() )

View File

@@ -1,6 +1,7 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
*
* Copyright 2010-2011, Christian Muehlhaeuser <muesli@tomahawk-player.org>
* Copyright 2011, 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
@@ -29,19 +30,41 @@
class DLLEXPORT DatabaseCommand_LoadAllPlaylists : public DatabaseCommand
{
Q_OBJECT
Q_OBJECT
public:
enum SortOrder {
None = 0,
ModificationTime = 1
};
enum SortAscDesc {
NoOrder = 0,
Ascending = 1,
Descending = 2
};
explicit DatabaseCommand_LoadAllPlaylists( const Tomahawk::source_ptr& s, QObject* parent = 0 )
: DatabaseCommand( s, parent )
, m_limitAmount( 0 )
, m_sortOrder( None )
, m_sortDescending( false )
{}
virtual void exec( DatabaseImpl* );
virtual bool doesMutates() const { return false; }
virtual QString commandname() const { return "loadallplaylists"; }
void setLimit( unsigned int limit ) { m_limitAmount = limit; }
void setSortOrder( SortOrder order ) { m_sortOrder = order; }
void setSortDescending( bool descending ) { m_sortDescending = descending; }
signals:
void done( const QList<Tomahawk::playlist_ptr>& playlists );
private:
unsigned int m_limitAmount;
SortOrder m_sortOrder;
bool m_sortDescending;
};
#endif // DATABASECOMMAND_LOADALLPLAYLIST_H

View File

@@ -0,0 +1,108 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2010-2011, Leo Franchi <lfranchi@kde.org>
*
* Tomahawk is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Tomahawk is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Tomahawk. If not, see <http://www.gnu.org/licenses/>.
*/
#include "databasecommand_loadallsortedplaylists.h"
#include "databaseimpl.h"
#include "playlist.h"
#include <libtomahawk/sourcelist.h>
using namespace Tomahawk;
void
DatabaseCommand_LoadAllSortedPlaylists::exec( DatabaseImpl* dbi )
{
TomahawkSqlQuery query = dbi->newquery();
QString orderToken, sourceToken, ascDescToken;
switch ( m_sortOrder )
{
case 0:
break;
case DatabaseCommand_LoadAllPlaylists::ModificationTime:
orderToken = "playlist.createdOn";
}
switch ( m_sortAscDesc )
{
case DatabaseCommand_LoadAllPlaylists::NoOrder:
break;
case DatabaseCommand_LoadAllPlaylists::Ascending:
ascDescToken = "ASC";
break;
case DatabaseCommand_LoadAllPlaylists::Descending:
ascDescToken = "DESC";
break;
}
if ( !source().isNull() )
sourceToken = QString( "AND source %1 " ).arg( source()->isLocal() ? "IS NULL" : QString( "= %1" ).arg( source()->id() ) );
query.exec( QString( "SELECT playlist.guid as guid, title, info, creator, lastmodified, shared, currentrevision, createdOn, dynplaylist, source, dynamic_playlist.pltype, dynamic_playlist.plmode "
"FROM playlist "
"LEFT JOIN dynamic_playlist ON playlist.guid = dynamic_playlist.guid "
"%1 "
"%2 %3 %4"
)
.arg( sourceToken )
.arg( m_sortOrder > 0 ? QString( "ORDER BY %1" ).arg( orderToken ) : QString() )
.arg( ascDescToken )
.arg( m_limitAmount > 0 ? QString( "LIMIT 0, %1" ).arg( m_limitAmount ) : QString() ) );
QList<SourcePlaylistPair> plists;
while ( query.next() )
{
plists << QPair< int, QString >( query.value(9).toInt(), query.value(0).toString() );
// playlist_ptr p;
// bool dynamic = query.value(8).toBool();
// source_ptr s = SourceList::instance()->get( query.value(9).toInt() );
//
// if ( dynamic )
// {
// p = dynplaylist_ptr( new DynamicPlaylist( s,
// query.value(6).toString(), //current rev
// query.value(1).toString(), //title
// query.value(2).toString(), //info
// query.value(3).toString(), //creator
// query.value(7).toInt(), //createdOn
// query.value(10).toString(), //type
// (GeneratorMode)query.value(11).toInt(), // mode
// query.value(5).toBool(), //shared
// query.value(4).toInt(), //lastmod
// query.value(0).toString() //GUID
// ) );
// } else
// {
// p = playlist_ptr( new Playlist( s, //src
// query.value(6).toString(), //current rev
// query.value(1).toString(), //title
// query.value(2).toString(), //info
// query.value(3).toString(), //creator
// query.value(7).toInt(), //createdOn
// query.value(5).toBool(), //shared
// query.value(4).toInt(), //lastmod
// query.value(0).toString() //GUID
// ) );
// }
// plists.append( p );
}
emit done( plists );
}

View File

@@ -0,0 +1,65 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2010-2011, Leo Franchi <lfranchi@kde.org>
*
* Tomahawk is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Tomahawk is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Tomahawk. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef DATABASECOMMAND_LOADALLSORTEDPLAYLISTS_H
#define DATABASECOMMAND_LOADALLSORTEDPLAYLISTS_H
#include "libtomahawk/database/databasecommand.h"
#include "databasecommand_loadallplaylists.h"
/**
* Loads *all* playlists, automatic playlists, and stations. Another dbcmd because otherwise loading them all
* is fragmented across 3 dbcmds with a different interface.
*
* You probably want to limit / sort the output.
*/
class DatabaseCommand_LoadAllSortedPlaylists : public DatabaseCommand
{
Q_OBJECT
public:
// don't macros rock... not
typedef QPair<int,QString> SourcePlaylistPair;
explicit DatabaseCommand_LoadAllSortedPlaylists( const Tomahawk::source_ptr& s, QObject* parent = 0 )
: DatabaseCommand( s, parent )
, m_limitAmount( 0 )
, m_sortOrder( DatabaseCommand_LoadAllPlaylists::None )
, m_sortAscDesc( DatabaseCommand_LoadAllPlaylists::NoOrder )
{
qRegisterMetaType<QList<DatabaseCommand_LoadAllSortedPlaylists::SourcePlaylistPair> >("QList<DatabaseCommand_LoadAllSortedPlaylists::SourcePlaylistPair>");
}
virtual void exec( DatabaseImpl* );
virtual bool doesMutates() const { return false; }
virtual QString commandname() const { return "loadallsortedplaylists"; }
void setLimit( unsigned int limit ) { m_limitAmount = limit; }
void setSortOrder( DatabaseCommand_LoadAllPlaylists::SortOrder order ) { m_sortOrder = order; }
void setSortAscDesc( DatabaseCommand_LoadAllPlaylists::SortAscDesc asc ) { m_sortAscDesc = asc; }
signals:
void done( const QList<DatabaseCommand_LoadAllSortedPlaylists::SourcePlaylistPair>& playlistGuids ); // QPair< sourceid, playlistguid>
private:
unsigned int m_limitAmount;
DatabaseCommand_LoadAllPlaylists::SortOrder m_sortOrder;
DatabaseCommand_LoadAllPlaylists::SortAscDesc m_sortAscDesc;
};
Q_DECLARE_METATYPE(QList<DatabaseCommand_LoadAllSortedPlaylists::SourcePlaylistPair>)
#endif // DATABASECOMMAND_LOADALLSORTEDPLAYLISTS_H

View File

@@ -31,11 +31,32 @@ void
DatabaseCommand_LoadAllStations::exec( DatabaseImpl* dbi )
{
TomahawkSqlQuery query = dbi->newquery();
QString orderToken, sourceToken;
switch ( m_sortOrder )
{
case 0:
break;
case DatabaseCommand_LoadAllPlaylists::ModificationTime:
orderToken = "playlist.createdOn";
}
if ( !source().isNull() )
sourceToken = QString( "AND source %1 " ).arg( source()->isLocal() ? "IS NULL" : QString( "= %1" ).arg( source()->id() ) );
query.exec( QString( "SELECT playlist.guid as guid, title, info, creator, createdOn, lastmodified, shared, currentrevision, dynamic_playlist.pltype, dynamic_playlist.plmode "
"FROM playlist, dynamic_playlist WHERE source %1 AND dynplaylist = 'true' AND playlist.guid = dynamic_playlist.guid AND dynamic_playlist.plmode = %2 AND dynamic_playlist.autoload = 'true'" )
.arg( source()->isLocal() ? "IS NULL" : QString( "=%1" ).arg( source()->id() ) )
.arg( OnDemand ) );
"FROM playlist, dynamic_playlist WHERE "
"dynplaylist = 'true' AND playlist.guid = dynamic_playlist.guid AND dynamic_playlist.plmode = %1 AND dynamic_playlist.autoload = 'true' "
"%2"
"%3 %4 %5"
)
.arg( OnDemand )
.arg( sourceToken )
.arg( m_sortOrder > 0 ? QString( "ORDER BY %1" ).arg( orderToken ) : QString() )
.arg( m_sortDescending ? "DESC" : QString() )
.arg( m_limitAmount > 0 ? QString( "LIMIT 0, %1" ).arg( m_limitAmount ) : QString() ) );
QList<dynplaylist_ptr> plists;
while ( query.next() )

View File

@@ -24,6 +24,7 @@
#include "databasecommand.h"
#include "typedefs.h"
#include "databasecommand_loadallplaylists.h"
class DatabaseCommand_LoadAllStations : public DatabaseCommand
{
@@ -32,15 +33,27 @@ class DatabaseCommand_LoadAllStations : public DatabaseCommand
public:
explicit DatabaseCommand_LoadAllStations( const Tomahawk::source_ptr& s, QObject* parent = 0 )
: DatabaseCommand( s, parent )
, m_limitAmount( 0 )
, m_sortOrder( DatabaseCommand_LoadAllPlaylists::None )
, m_sortDescending( false )
{}
virtual void exec( DatabaseImpl* );
virtual bool doesMutates() const { return false; }
virtual QString commandname() const { return "loadallstations"; }
void setLimit( unsigned int limit ) { m_limitAmount = limit; }
void setSortOrder( DatabaseCommand_LoadAllPlaylists::SortOrder order ) { m_sortOrder = order; }
void setSortDescending( bool descending ) { m_sortDescending = descending; }
signals:
void stationLoaded( const Tomahawk::source_ptr& source, const QVariantList& data );
void done();
private:
unsigned int m_limitAmount;
DatabaseCommand_LoadAllPlaylists::SortOrder m_sortOrder;
bool m_sortDescending;
};
#endif

View File

@@ -26,6 +26,22 @@ DatabaseCommand_loadOps::exec( DatabaseImpl* dbi )
{
QList< dbop_ptr > ops;
if ( !m_since.isEmpty() )
{
TomahawkSqlQuery query = dbi->newquery();
query.prepare( QString( "SELECT id FROM oplog WHERE guid = ?" ) );
query.addBindValue( m_since );
query.exec();
if ( !query.next() )
{
tLog() << "Unknown oplog guid, requested, not replying:" << m_since;
Q_ASSERT( false );
emit done( m_since, m_since, ops );
return;
}
}
TomahawkSqlQuery query = dbi->newquery();
query.prepare( QString(
"SELECT guid, command, json, compressed, singleton "

View File

@@ -45,11 +45,13 @@ DatabaseCommand_LogPlayback::postCommitHook()
if ( m_action == Finished )
{
tDebug( LOGEXTRA ) << Q_FUNC_INFO << " logging finished from source " << source().data()->id();
emit trackPlayed( q );
}
// if the play time is more than 10 minutes in the past, ignore
else if ( m_action == Started && QDateTime::fromTime_t( playtime() ).secsTo( QDateTime::currentDateTime() ) < 600 )
{
tDebug( LOGEXTRA ) << Q_FUNC_INFO << " logging started from source " << source().data()->id();
emit trackPlaying( q );
}

View File

@@ -110,6 +110,7 @@ DatabaseWorker::doWork()
try
{
{
tDebug() << "Executing cmd:" << cmd->guid();
cmd->_exec( m_dbimpl ); // runs actual SQL stuff
if ( cmd->loggable() )

516
src/libtomahawk/dropjob.cpp Normal file
View File

@@ -0,0 +1,516 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2011, Michael Zanetti <mzanetti@kde.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 "dropjob.h"
#include "artist.h"
#include "album.h"
#include "utils/spotifyparser.h"
#include "utils/rdioparser.h"
#include "utils/shortenedlinkparser.h"
#include "utils/logger.h"
#include "globalactionmanager.h"
#include "infosystem/infosystem.h"
using namespace Tomahawk;
DropJob::DropJob( QObject *parent )
: QObject( parent )
, m_queryCount( 0 )
{
}
DropJob::~DropJob()
{
qDebug() << "destryong DropJob";
}
/// QMIMEDATA HANDLING
QStringList
DropJob::mimeTypes()
{
QStringList mimeTypes;
mimeTypes << "application/tomahawk.query.list"
<< "application/tomahawk.plentry.list"
<< "application/tomahawk.result.list"
<< "application/tomahawk.result"
<< "application/tomahawk.metadata.artist"
<< "application/tomahawk.metadata.album"
<< "application/tomahawk.mixed"
<< "text/plain";
return mimeTypes;
}
bool
DropJob::acceptsMimeData( const QMimeData* data, bool tracksOnly )
{
if ( data->hasFormat( "application/tomahawk.query.list" )
|| data->hasFormat( "application/tomahawk.plentry.list" )
|| data->hasFormat( "application/tomahawk.result.list" )
|| data->hasFormat( "application/tomahawk.result" )
|| data->hasFormat( "application/tomahawk.mixed" )
|| data->hasFormat( "application/tomahawk.metadata.album" )
|| data->hasFormat( "application/tomahawk.metadata.artist" ) )
{
return true;
}
// crude check for spotify tracks
if ( data->hasFormat( "text/plain" ) && data->data( "text/plain" ).contains( "spotify" ) &&
( tracksOnly ? data->data( "text/plain" ).contains( "track" ) : true ) )
return true;
// crude check for rdio tracks
if ( data->hasFormat( "text/plain" ) && data->data( "text/plain" ).contains( "rdio.com" ) &&
( tracksOnly ? data->data( "text/plain" ).contains( "track" ) : true ) )
return true;
// We whitelist t.co and bit.ly (and j.mp) since they do some link checking. Often playable (e.g. spotify..) links hide behind them,
// so we do an extra level of lookup
if ( ( data->hasFormat( "text/plain" ) && data->data( "text/plain" ).contains( "bit.ly" ) ) ||
( data->hasFormat( "text/plain" ) && data->data( "text/plain" ).contains( "j.mp" ) ) ||
( data->hasFormat( "text/plain" ) && data->data( "text/plain" ).contains( "t.co" ) ) ||
( data->hasFormat( "text/plain" ) && data->data( "text/plain" ).contains( "rd.io" ) ) )
return true;
return false;
}
void
DropJob::setGetWholeArtists( bool getWholeArtists )
{
m_getWholeArtists = getWholeArtists;
}
void
DropJob::setGetWholeAlbums( bool getWholeAlbums )
{
m_getWholeAlbums = getWholeAlbums;
}
void
DropJob::tracksFromMimeData( const QMimeData* data, bool allowDuplicates, bool onlyLocal, bool top10 )
{
m_allowDuplicates = allowDuplicates;
m_onlyLocal = onlyLocal;
m_top10 = top10;
parseMimeData( data );
if ( m_queryCount == 0 )
{
if ( onlyLocal )
removeRemoteSources();
if ( !allowDuplicates )
removeDuplicates();
emit tracks( m_resultList );
deleteLater();
}
}
void
DropJob::parseMimeData( const QMimeData *data )
{
QList< query_ptr > results;
if ( data->hasFormat( "application/tomahawk.query.list" ) )
results = tracksFromQueryList( data );
else if ( data->hasFormat( "application/tomahawk.result.list" ) )
results = tracksFromResultList( data );
else if ( data->hasFormat( "application/tomahawk.metadata.album" ) )
results = tracksFromAlbumMetaData( data );
else if ( data->hasFormat( "application/tomahawk.metadata.artist" ) )
results = tracksFromArtistMetaData( data );
else if ( data->hasFormat( "application/tomahawk.mixed" ) )
tracksFromMixedData( data );
else if ( data->hasFormat( "text/plain" ) )
{
QString plainData = QString::fromUtf8( data->data( "text/plain" ).constData() );
tDebug() << "Got text/plain mime data:" << data->data( "text/plain" ) << "decoded to:" << plainData;
handleTrackUrls ( plainData );
}
m_resultList.append( results );
}
QList< query_ptr >
DropJob::tracksFromQueryList( const QMimeData* data )
{
QList< query_ptr > queries;
QByteArray itemData = data->data( "application/tomahawk.query.list" );
QDataStream stream( &itemData, QIODevice::ReadOnly );
while ( !stream.atEnd() )
{
qlonglong qptr;
stream >> qptr;
query_ptr* query = reinterpret_cast<query_ptr*>(qptr);
if ( query && !query->isNull() )
{
tDebug() << "Dropped query item:" << query->data()->artist() << "-" << query->data()->track();
if ( m_top10 )
{
getTopTen( query->data()->artist() );
}
else if ( m_getWholeArtists )
{
queries << getArtist( query->data()->artist() );
}
else if ( m_getWholeAlbums )
{
queries << getAlbum( query->data()->artist(), query->data()->album() );
}
else
{
queries << *query;
}
}
}
return queries;
}
QList< query_ptr >
DropJob::tracksFromResultList( const QMimeData* data )
{
QList< query_ptr > queries;
QByteArray itemData = data->data( "application/tomahawk.result.list" );
QDataStream stream( &itemData, QIODevice::ReadOnly );
while ( !stream.atEnd() )
{
qlonglong qptr;
stream >> qptr;
result_ptr* result = reinterpret_cast<result_ptr*>(qptr);
if ( result && !result->isNull() )
{
tDebug() << "Dropped result item:" << result->data()->artist()->name() << "-" << result->data()->track();
query_ptr q = result->data()->toQuery();
if ( m_top10 )
{
getTopTen( q->artist() );
}
else if ( m_getWholeArtists )
{
queries << getArtist( q->artist() );
}
else if ( m_getWholeAlbums )
{
queries << getAlbum( q->artist(), q->album() );
}
else
{
q->addResults( QList< result_ptr >() << *result );
queries << q;
}
}
}
return queries;
}
QList< query_ptr >
DropJob::tracksFromAlbumMetaData( const QMimeData *data )
{
QList<query_ptr> queries;
QByteArray itemData = data->data( "application/tomahawk.metadata.album" );
QDataStream stream( &itemData, QIODevice::ReadOnly );
while ( !stream.atEnd() )
{
QString artist;
stream >> artist;
QString album;
stream >> album;
if ( m_top10 )
getTopTen( artist );
else if ( m_getWholeArtists )
queries << getArtist( artist );
else
queries << getAlbum( artist, album );
}
return queries;
}
QList< query_ptr >
DropJob::tracksFromArtistMetaData( const QMimeData *data )
{
QList<query_ptr> queries;
QByteArray itemData = data->data( "application/tomahawk.metadata.artist" );
QDataStream stream( &itemData, QIODevice::ReadOnly );
while ( !stream.atEnd() )
{
QString artist;
stream >> artist;
if ( !m_top10 )
{
queries << getArtist( artist );
}
else
{
getTopTen( artist );
}
}
return queries;
}
QList< query_ptr >
DropJob::tracksFromMixedData( const QMimeData *data )
{
QList< query_ptr > queries;
QByteArray itemData = data->data( "application/tomahawk.mixed" );
QDataStream stream( &itemData, QIODevice::ReadOnly );
QString mimeType;
while ( !stream.atEnd() )
{
stream >> mimeType;
qDebug() << "mimetype is" << mimeType;
QByteArray singleData;
QDataStream singleStream( &singleData, QIODevice::WriteOnly );
QMimeData singleMimeData;
if ( mimeType == "application/tomahawk.query.list" || mimeType == "application/tomahawk.result.list" )
{
qlonglong query;
stream >> query;
singleStream << query;
}
else if ( mimeType == "application/tomahawk.metadata.album" )
{
QString artist;
stream >> artist;
singleStream << artist;
QString album;
stream >> album;
singleStream << album;
qDebug() << "got artist" << artist << "and album" << album;
}
else if ( mimeType == "application/tomahawk.metadata.artist" )
{
QString artist;
stream >> artist;
singleStream << artist;
qDebug() << "got artist" << artist;
}
singleMimeData.setData( mimeType, singleData );
parseMimeData( &singleMimeData );
}
return queries;
}
void
DropJob::handleTrackUrls( const QString& urls )
{
if ( urls.contains( "open.spotify.com/track") ||
urls.contains( "spotify:track" ) )
{
QStringList tracks = urls.split( "\n" );
tDebug() << "Got a list of spotify urls!" << tracks;
SpotifyParser* spot = new SpotifyParser( tracks, this );
connect( spot, SIGNAL( tracks( QList<Tomahawk::query_ptr> ) ), this, SLOT( onTracksAdded( QList< Tomahawk::query_ptr > ) ) );
m_queryCount++;
} else if ( urls.contains( "rdio.com" ) )
{
QStringList tracks = urls.split( "\n" );
tDebug() << "Got a list of rdio urls!" << tracks;
RdioParser* rdio = new RdioParser( this );
connect( rdio, SIGNAL( tracks( QList<Tomahawk::query_ptr> ) ), this, SLOT( onTracksAdded( QList< Tomahawk::query_ptr > ) ) );
m_queryCount++;
rdio->parse( tracks );
} else if ( urls.contains( "bit.ly" ) ||
urls.contains( "j.mp" ) ||
urls.contains( "t.co" ) ||
urls.contains( "rd.io" ) )
{
QStringList tracks = urls.split( "\n" );
tDebug() << "Got a list of shortened urls!" << tracks;
ShortenedLinkParser* parser = new ShortenedLinkParser( tracks, this );
connect( parser, SIGNAL( urls( QStringList ) ), this, SLOT( expandedUrls( QStringList ) ) );
m_queryCount++;
}
}
void
DropJob::expandedUrls( QStringList urls )
{
m_queryCount--;
handleTrackUrls( urls.join( "\n" ) );
}
void
DropJob::onTracksAdded( const QList<Tomahawk::query_ptr>& tracksList )
{
m_resultList.append( tracksList );
if ( --m_queryCount == 0 )
{
if ( m_onlyLocal )
removeRemoteSources();
if ( !m_allowDuplicates )
removeDuplicates();
emit tracks( m_resultList );
deleteLater();
}
}
void
DropJob::removeDuplicates()
{
QList< Tomahawk::query_ptr > list;
foreach ( const Tomahawk::query_ptr& item, m_resultList )
{
bool contains = false;
foreach( const Tomahawk::query_ptr &tmpItem, list )
if ( item->album() == tmpItem->album()
&& item->artist() == tmpItem->artist()
&& item->track() == tmpItem->track() )
contains = true;
if ( !contains )
list.append( item );
}
m_resultList = list;
}
void
DropJob::removeRemoteSources()
{
QList< Tomahawk::query_ptr > list;
foreach ( const Tomahawk::query_ptr& item, m_resultList )
{
bool hasLocalSource = false;
foreach ( const Tomahawk::result_ptr& result, item->results() )
{
if ( !result->collection()->source().isNull() && result->collection()->source()->isLocal() )
hasLocalSource = true;
}
if ( hasLocalSource )
list.append( item );
}
m_resultList = list;
}
void
DropJob::infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData requestData, QVariant output )
{
if ( requestData.caller == "changeme" )
{
Tomahawk::InfoSystem::InfoCriteriaHash artistInfo;
artistInfo = requestData.input.value< Tomahawk::InfoSystem::InfoCriteriaHash >();
QString artist = artistInfo["artist"];
qDebug() << "Got requestData response for artist" << artist << output;
QList< query_ptr > results;
int i = 0;
foreach ( const QVariant& title, output.toMap().value( "tracks" ).toList() )
{
qDebug() << "got title" << title;
results << Query::get( artist, title.toString(), QString(), uuid() );
if ( ++i == 10 ) // Only getting top ten for now. Would make sense to make it configurable
break;
}
onTracksAdded( results );
}
}
QList< query_ptr >
DropJob::getArtist( const QString &artist )
{
artist_ptr artistPtr = Artist::get( artist );
if ( artistPtr->tracks().isEmpty() )
{
connect( artistPtr.data(), SIGNAL( tracksAdded( QList<Tomahawk::query_ptr> ) ),
SLOT( onTracksAdded( QList<Tomahawk::query_ptr> ) ) );
m_queryCount++;
return QList< query_ptr >();
}
else
return artistPtr->tracks();
}
QList< query_ptr >
DropJob::getAlbum(const QString &artist, const QString &album)
{
artist_ptr artistPtr = Artist::get( artist );
album_ptr albumPtr = Album::get( artistPtr, album );
if ( albumPtr.isNull() )
return QList< query_ptr >();
if ( albumPtr->tracks().isEmpty() )
{
connect( albumPtr.data(), SIGNAL( tracksAdded( QList<Tomahawk::query_ptr> ) ),
SLOT( onTracksAdded( QList<Tomahawk::query_ptr> ) ) );
m_queryCount++;
return QList< query_ptr >();
}
else
return albumPtr->tracks();
}
void
DropJob::getTopTen( const QString &artist )
{
connect( Tomahawk::InfoSystem::InfoSystem::instance(),
SIGNAL( info( Tomahawk::InfoSystem::InfoRequestData, QVariant ) ),
SLOT( infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData, QVariant ) ) );
Tomahawk::InfoSystem::InfoCriteriaHash artistInfo;
artistInfo["artist"] = artist;
Tomahawk::InfoSystem::InfoRequestData requestData;
requestData.caller = "changeme";
requestData.customData = QVariantMap();
requestData.input = QVariant::fromValue< Tomahawk::InfoSystem::InfoCriteriaHash >( artistInfo );
requestData.type = Tomahawk::InfoSystem::InfoArtistSongs;
Tomahawk::InfoSystem::InfoSystem::instance()->getInfo( requestData );
m_queryCount++;
}

93
src/libtomahawk/dropjob.h Normal file
View File

@@ -0,0 +1,93 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2011, Michael Zanetti <mzanetti@kde.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 DROPJOB_H
#define DROPJOB_H
#include "query.h"
#include "infosystem/infosystem.h"
#include <QObject>
#include <QStringList>
#include <QMimeData>
class DLLEXPORT DropJob : public QObject
{
Q_OBJECT
public:
explicit DropJob( QObject *parent = 0 );
~DropJob();
/**
* QMimeData helpers
*
* Call this to parse the tracks in a QMimeData object to query_ptrs. This will parse internal tomahawk
* data as well as all other formats supported (spotify, etc).
*
* Connect to tracks( QList< query_ptr> ); for the extracted tracks.
*/
static bool acceptsMimeData( const QMimeData* data, bool tracksOnly = true );
static QStringList mimeTypes();
void setGetWholeArtists( bool getWholeArtists );
void setGetWholeAlbums( bool getWholeAlbums );
void tracksFromMimeData( const QMimeData* data, bool allowDuplicates = false, bool onlyLocal = false, bool top10 = false );
signals:
/// QMimeData parsing results
void tracks( const QList< Tomahawk::query_ptr >& tracks );
private slots:
void expandedUrls( QStringList );
void onTracksAdded( const QList<Tomahawk::query_ptr>& );
void infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData requestData, QVariant output );
private:
/// handle parsing mime data
void parseMimeData( const QMimeData* data );
void handleTrackUrls( const QString& urls );
QList< Tomahawk::query_ptr > tracksFromQueryList( const QMimeData* d );
QList< Tomahawk::query_ptr > tracksFromResultList( const QMimeData* d );
QList< Tomahawk::query_ptr > tracksFromArtistMetaData( const QMimeData* d );
QList< Tomahawk::query_ptr > tracksFromAlbumMetaData( const QMimeData* d );
QList< Tomahawk::query_ptr > tracksFromMixedData( const QMimeData* d );
QList< Tomahawk::query_ptr > getArtist( const QString& artist );
QList< Tomahawk::query_ptr > getAlbum( const QString& artist, const QString& album );
void getTopTen( const QString& artist );
void removeDuplicates();
void removeRemoteSources();
int m_queryCount;
bool m_allowDuplicates;
bool m_onlyLocal;
bool m_getWholeArtists;
bool m_getWholeAlbums;
bool m_top10;
QList< Tomahawk::query_ptr > m_resultList;
};
#endif // DROPJOB_H

View File

@@ -64,7 +64,6 @@ GlobalActionManager::instance()
GlobalActionManager::GlobalActionManager( QObject* parent )
: QObject( parent )
{
m_mimeTypes << "application/tomahawk.query.list" << "application/tomahawk.plentry.list" << "application/tomahawk.result.list" << "text/plain";
}
GlobalActionManager::~GlobalActionManager()
@@ -358,6 +357,8 @@ GlobalActionManager::doQueueAdd( const QStringList& parts, const QList< QPair< Q
if( queueSpotify( parts, queryItems ) )
return true;
else if( queueRdio( parts, queryItems ) )
return true;
QPair< QString, QString > pair;
@@ -428,6 +429,26 @@ GlobalActionManager::queueSpotify( const QStringList& , const QList< QPair< QStr
return true;
}
bool
GlobalActionManager::queueRdio( const QStringList& , const QList< QPair< QString, QString > >& queryItems )
{
QString url;
QPair< QString, QString > pair;
foreach( pair, queryItems ) {
if( pair.first == "rdioURL" )
url = pair.second;
else if( pair.first == "rdioURI" )
url = pair.second;
}
if( url.isEmpty() )
return false;
openRdioLink( url );
return true;
}
bool
GlobalActionManager::handleSearchCommand( const QUrl& url )
@@ -446,7 +467,7 @@ GlobalActionManager::handleSearchCommand( const QUrl& url )
return false;
ViewManager::instance()->showSuperCollection();
ViewManager::instance()->topbar()->setFilter( queryStr );
// ViewManager::instance()->topbar()->setFilter( queryStr );
return true;
}
@@ -618,6 +639,8 @@ GlobalActionManager::handlePlayCommand( const QUrl& url )
if( parts[ 0 ] == "track" ) {
if( playSpotify( url ) )
return true;
else if( playRdio( url ) )
return true;
QPair< QString, QString > pair;
QString title, artist, album, urlStr;
@@ -653,13 +676,13 @@ GlobalActionManager::playSpotify( const QUrl& url )
QString spotifyUrl = url.hasQueryItem( "spotifyURI" ) ? url.queryItemValue( "spotifyURI" ) : url.queryItemValue( "spotifyURL" );
SpotifyParser* p = new SpotifyParser( spotifyUrl, this );
connect( p, SIGNAL( track( Tomahawk::query_ptr ) ), this, SLOT( spotifyToPlay( Tomahawk::query_ptr ) ) );
connect( p, SIGNAL( track( Tomahawk::query_ptr ) ), this, SLOT( playNow( Tomahawk::query_ptr ) ) );
return true;
}
void
GlobalActionManager::spotifyToPlay( const query_ptr& q )
GlobalActionManager::playNow( const query_ptr& q )
{
Pipeline::instance()->resolve( q, true );
@@ -667,6 +690,21 @@ GlobalActionManager::spotifyToPlay( const query_ptr& q )
connect( q.data(), SIGNAL( resolvingFinished( bool ) ), this, SLOT( waitingForResolved( bool ) ) );
}
bool
GlobalActionManager::playRdio( const QUrl& url )
{
if( !url.hasQueryItem( "rdioURI" ) && !url.hasQueryItem( "rdioURL" ) )
return false;
QString rdioUrl = url.hasQueryItem( "rdioURI" ) ? url.queryItemValue( "spotifyURI" ) : url.queryItemValue( "rdioURL" );
RdioParser* p = new RdioParser( this );
p->parse( rdioUrl );
connect( p, SIGNAL( track( Tomahawk::query_ptr ) ), this, SLOT( playNow( Tomahawk::query_ptr ) ) );
return true;
}
bool GlobalActionManager::handleBookmarkCommand(const QUrl& url)
{
@@ -779,149 +817,6 @@ GlobalActionManager::hostname() const
return QString( "http://toma.hk" );
}
/// QMIMEDATA HANDLING
QStringList
GlobalActionManager::mimeTypes() const
{
return m_mimeTypes;
}
bool
GlobalActionManager::acceptsMimeData( const QMimeData* data, bool tracksOnly )
{
if ( data->hasFormat( "application/tomahawk.query.list" )
|| data->hasFormat( "application/tomahawk.plentry.list" )
|| data->hasFormat( "application/tomahawk.result.list" ) )
{
return true;
}
// crude check for spotify tracks
if ( data->hasFormat( "text/plain" ) && data->data( "text/plain" ).contains( "spotify" ) &&
( tracksOnly ? data->data( "text/plain" ).contains( "track" ) : true ) )
return true;
// crude check for rdio tracks
if ( data->hasFormat( "text/plain" ) && data->data( "text/plain" ).contains( "rdio.com" ) &&
( tracksOnly ? data->data( "text/plain" ).contains( "track" ) : true ) )
return true;
// We whitelist t.co and bit.ly (and j.mp) since they do some link checking. Often playable (e.g. spotify..) links hide behind them,
// so we do an extra level of lookup
if ( ( data->hasFormat( "text/plain" ) && data->data( "text/plain" ).contains( "bit.ly" ) ) ||
( data->hasFormat( "text/plain" ) && data->data( "text/plain" ).contains( "j.mp" ) ) ||
( data->hasFormat( "text/plain" ) && data->data( "text/plain" ).contains( "t.co" ) ) ||
( data->hasFormat( "text/plain" ) && data->data( "text/plain" ).contains( "rd.io" ) ) )
return true;
return false;
}
void
GlobalActionManager::tracksFromMimeData( const QMimeData* data )
{
if ( data->hasFormat( "application/tomahawk.query.list" ) )
emit tracks( tracksFromQueryList( data ) );
else if ( data->hasFormat( "application/tomahawk.result.list" ) )
emit tracks( tracksFromResultList( data ) );
else if ( data->hasFormat( "text/plain" ) )
{
QString plainData = QString::fromUtf8( data->data( "text/plain" ).constData() );
tDebug() << "Got text/plain mime data:" << data->data( "text/plain" ) << "decoded to:" << plainData;
handleTrackUrls ( plainData );
}
}
void
GlobalActionManager::handleTrackUrls( const QString& urls )
{
if ( urls.contains( "open.spotify.com/track") ||
urls.contains( "spotify:track" ) )
{
QStringList tracks = urls.split( "\n" );
tDebug() << "Got a list of spotify urls!" << tracks;
SpotifyParser* spot = new SpotifyParser( tracks, this );
connect( spot, SIGNAL( tracks( QList<Tomahawk::query_ptr> ) ), this, SIGNAL( tracks( QList<Tomahawk::query_ptr> ) ) );
} else if ( urls.contains( "rdio.com" ) )
{
QStringList tracks = urls.split( "\n" );
tDebug() << "Got a list of rdio urls!" << tracks;
RdioParser* rdio = new RdioParser( this );
connect( rdio, SIGNAL( tracks( QList<Tomahawk::query_ptr> ) ), this, SIGNAL( tracks( QList<Tomahawk::query_ptr> ) ) );
rdio->parse( tracks );
} else if ( urls.contains( "bit.ly" ) ||
urls.contains( "j.mp" ) ||
urls.contains( "t.co" ) ||
urls.contains( "rd.io" ) )
{
QStringList tracks = urls.split( "\n" );
tDebug() << "Got a list of shortened urls!" << tracks;
ShortenedLinkParser* parser = new ShortenedLinkParser( tracks, this );
connect( parser, SIGNAL( urls( QStringList ) ), this, SLOT( expandedUrls( QStringList ) ) );
}
}
void
GlobalActionManager::expandedUrls( QStringList urls )
{
handleTrackUrls( urls.join( "\n" ) );
}
QList< query_ptr >
GlobalActionManager::tracksFromQueryList( const QMimeData* data )
{
QList< query_ptr > queries;
QByteArray itemData = data->data( "application/tomahawk.query.list" );
QDataStream stream( &itemData, QIODevice::ReadOnly );
while ( !stream.atEnd() )
{
qlonglong qptr;
stream >> qptr;
query_ptr* query = reinterpret_cast<query_ptr*>(qptr);
if ( query && !query->isNull() )
{
tDebug() << "Dropped query item:" << query->data()->artist() << "-" << query->data()->track();
queries << *query;
}
}
return queries;
}
QList< query_ptr >
GlobalActionManager::tracksFromResultList( const QMimeData* data )
{
QList< query_ptr > queries;
QByteArray itemData = data->data( "application/tomahawk.result.list" );
QDataStream stream( &itemData, QIODevice::ReadOnly );
while ( !stream.atEnd() )
{
qlonglong qptr;
stream >> qptr;
result_ptr* result = reinterpret_cast<result_ptr*>(qptr);
if ( result && !result->isNull() )
{
tDebug() << "Dropped result item:" << result->data()->artist()->name() << "-" << result->data()->track();
query_ptr q = result->data()->toQuery();
q->addResults( QList< result_ptr >() << *result );
queries << q;
}
}
return queries;
}
/// SPOTIFY URL HANDLING

View File

@@ -39,7 +39,7 @@ public:
virtual ~GlobalActionManager();
QUrl openLinkFromQuery( const Tomahawk::query_ptr& query ) const;
QUrl openLink( const QString& title, const QString& artist, const QString& album ) const;
Q_INVOKABLE QUrl openLink( const QString& title, const QString& artist, const QString& album ) const;
/// Takes a spotify link and performs the default open action on it
bool openSpotifyLink( const QString& link );
@@ -51,18 +51,6 @@ public:
QString copyPlaylistToClipboard( const Tomahawk::dynplaylist_ptr& playlist );
void savePlaylistToFile( const Tomahawk::playlist_ptr& playlist, const QString& filename );
/**
* QMimeData helpers
*
* Call this to parse the tracks in a QMimeData object to query_ptrs. This will parse internal tomahawk
* data as well as all other formats supported (spotify, etc).
*
* Connect to tracks( QList< query_ptr> ); for the extracted tracks.
*/
bool acceptsMimeData( const QMimeData* data, bool tracksOnly = true );
void tracksFromMimeData( const QMimeData* data );
QStringList mimeTypes() const;
public slots:
bool parseTomahawkLink( const QString& link );
void waitingForResolved( bool );
@@ -70,18 +58,14 @@ public slots:
Tomahawk::dynplaylist_ptr loadDynamicPlaylist( const QUrl& url, bool station );
void handleOpenTrack( const Tomahawk::query_ptr& qry );
signals:
/// QMimeData parsing results
void tracks( const QList< Tomahawk::query_ptr >& tracks );
private slots:
void bookmarkPlaylistCreated( const Tomahawk::playlist_ptr& pl );
void showPlaylist();
void xspfCreated( const QByteArray& xspf );
void expandedUrls( QStringList );
void spotifyToPlay( const Tomahawk::query_ptr& );
void playNow( const Tomahawk::query_ptr& );
private:
explicit GlobalActionManager( QObject* parent = 0 );
void doBookmark( const Tomahawk::playlist_ptr& pl, const Tomahawk::query_ptr& q );
@@ -100,11 +84,8 @@ private:
bool playSpotify( const QUrl& url );
bool queueSpotify( const QStringList& parts, const QList< QPair< QString, QString > >& queryItems );
/// handle parsing mime data
void handleTrackUrls( const QString& urls );
QList< Tomahawk::query_ptr > tracksFromQueryList( const QMimeData* d );
QList< Tomahawk::query_ptr > tracksFromResultList( const QMimeData* d );
bool playRdio( const QUrl& url );
bool queueRdio( const QStringList& parts, const QList< QPair< QString, QString > >& queryItems );
QString hostname() const;
@@ -112,7 +93,6 @@ private:
Tomahawk::query_ptr m_waitingToBookmark;
Tomahawk::query_ptr m_waitingToPlay;
QStringList m_mimeTypes;
static GlobalActionManager* s_instance;
};

View File

@@ -20,22 +20,26 @@
#include "ui_infobar.h"
#include <QLabel>
#include <QPropertyAnimation>
#include <QPixmap>
#include "viewmanager.h"
#include "thirdparty/Qocoa/qsearchfield.h"
#include "utils/tomahawkutils.h"
#include "utils/logger.h"
#define ANIMATION_TIME 400
#define IMAGE_HEIGHT 64
using namespace Tomahawk;
InfoBar::InfoBar( QWidget* parent )
: QWidget( parent )
, ui( new Ui::InfoBar )
{
ui->setupUi( this );
layout()->setSpacing( 0 );
layout()->setContentsMargins( 0, 0, 0, 0 );
TomahawkUtils::unmarginLayout( layout() );
layout()->setContentsMargins( 8, 4, 8, 4 );
QFont boldFont = ui->captionLabel->font();
boldFont.setPixelSize( 18 );
@@ -66,7 +70,23 @@ InfoBar::InfoBar( QWidget* parent )
ui->longDescriptionLabel->setText( QString() );
ui->imageLabel->setText( QString() );
m_searchWidget = new QSearchField( this );
m_searchWidget->setPlaceholderText( tr( "Filter..." ) );
m_searchWidget->setMinimumWidth( 180 );
connect( m_searchWidget, SIGNAL( textChanged( QString ) ), this, SLOT( onFilterEdited() ) );
ui->horizontalLayout->addWidget( m_searchWidget );
QLinearGradient gradient = QLinearGradient( QPoint( 0, 0 ), QPoint( 500, 200 ) ); //HACK
gradient.setColorAt( 0.0, QColor( 100, 100, 100 ) );
gradient.setColorAt( 0.8, QColor( 63, 63, 63 ) );
QPalette p = palette();
p.setBrush( QPalette::Window, QBrush( gradient ) );
setPalette( p );
setAutoFillBackground( true );
connect( ViewManager::instance(), SIGNAL( filterAvailable( bool ) ), SLOT( setFilterAvailable( bool ) ) );
}
@@ -114,6 +134,27 @@ InfoBar::setPixmap( const QPixmap& p )
}
void
InfoBar::setFilter( const QString& filter )
{
m_searchWidget->setText( filter );
}
void
InfoBar::setFilterAvailable( bool b )
{
m_searchWidget->setVisible( b );
}
void
InfoBar::onFilterEdited()
{
emit filterTextChanged( m_searchWidget->text() );
}
void
InfoBar::changeEvent( QEvent* e )
{
@@ -128,18 +169,3 @@ InfoBar::changeEvent( QEvent* e )
break;
}
}
void
InfoBar::resizeEvent( QResizeEvent* e )
{
QWidget::resizeEvent( e );
QLinearGradient gradient = QLinearGradient( contentsRect().topLeft(), contentsRect().bottomRight() );
gradient.setColorAt( 0.0, QColor( 100, 100, 100 ) );
gradient.setColorAt( 1.0, QColor( 63, 63, 63 ) );
QPalette p = palette();
p.setBrush( QPalette::Window, QBrush( gradient ) );
setPalette( p );
}

View File

@@ -22,6 +22,11 @@
#include <QWidget>
#include "dllmacro.h"
#include "query.h"
class QTimeLine;
class QSearchField;
class ContextWidget;
namespace Ui
{
@@ -42,12 +47,22 @@ public slots:
void setLongDescription( const QString& s );
void setPixmap( const QPixmap& p );
void setFilter( const QString& filter );
void setFilterAvailable( bool b );
signals:
void filterTextChanged( const QString& filter );
protected:
void changeEvent( QEvent* e );
void resizeEvent( QResizeEvent* e );
private slots:
void onFilterEdited();
private:
Ui::InfoBar* ui;
QSearchField* m_searchWidget;
};
#endif // INFOBAR_H

View File

@@ -0,0 +1,182 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>InfoBar</class>
<widget class="QWidget" name="InfoBar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>774</width>
<height>80</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>72</height>
</size>
</property>
<property name="windowTitle">
<string>InfoBar</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="sizeConstraint">
<enum>QLayout::SetDefaultConstraint</enum>
</property>
<item>
<widget class="QLabel" name="imageLabel">
<property name="minimumSize">
<size>
<width>64</width>
<height>64</height>
</size>
</property>
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>16</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="sizeConstraint">
<enum>QLayout::SetDefaultConstraint</enum>
</property>
<item>
<widget class="ElidedLabel" name="captionLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>TextLabel</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
</property>
</widget>
</item>
<item>
<widget class="ElidedLabel" name="descriptionLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>TextLabel</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
</property>
</widget>
</item>
</layout>
</item>
<item>
<spacer name="horizontalSpacer_4">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>16</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout_2">
<property name="sizeConstraint">
<enum>QLayout::SetMaximumSize</enum>
</property>
<item>
<widget class="ElidedLabel" name="longDescriptionLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>62</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>62</height>
</size>
</property>
<property name="text">
<string>TextLabel</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_5">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>1</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>ElidedLabel</class>
<extends>QLabel</extends>
<header>widgets/elidedlabel.h</header>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>

View File

@@ -22,6 +22,7 @@
#include <QSettings>
#include <QCryptographicHash>
#include <QNetworkConfiguration>
#include <QDomElement>
#include "album.h"
#include "typedefs.h"
@@ -33,6 +34,8 @@
#include <lastfm/ws.h>
#include <lastfm/XmlQuery>
#include <qjson/parser.h>
using namespace Tomahawk::InfoSystem;
static QString
@@ -47,7 +50,7 @@ LastFmPlugin::LastFmPlugin()
: InfoPlugin()
, m_scrobbler( 0 )
{
m_supportedGetTypes << InfoAlbumCoverArt << InfoArtistImages << InfoArtistSimilars << InfoArtistSongs;
m_supportedGetTypes << InfoAlbumCoverArt << InfoArtistImages << InfoArtistSimilars << InfoArtistSongs << InfoChart << InfoChartCapabilities;
m_supportedPushTypes << InfoSubmitScrobble << InfoSubmitNowPlaying << InfoLove << InfoUnLove;
/*
@@ -87,6 +90,7 @@ LastFmPlugin::~LastFmPlugin()
{
qDebug() << Q_FUNC_INFO;
delete m_scrobbler;
m_scrobbler = 0;
}
@@ -147,6 +151,13 @@ LastFmPlugin::getInfo( uint requestId, Tomahawk::InfoSystem::InfoRequestData req
fetchTopTracks( requestId, requestData );
break;
case InfoChart:
fetchChart( requestId, requestData );
break;
case InfoChartCapabilities:
fetchChartCapabilities( requestId, requestData );
break;
default:
dataError( requestId, requestData );
}
@@ -298,6 +309,40 @@ LastFmPlugin::fetchTopTracks( uint requestId, Tomahawk::InfoSystem::InfoRequestD
emit getCachedInfo( requestId, criteria, 2419200000, requestData );
}
void
LastFmPlugin::fetchChart( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData )
{
if ( !requestData.input.canConvert< Tomahawk::InfoSystem::InfoCriteriaHash >() )
{
dataError( requestId, requestData );
return;
}
InfoCriteriaHash hash = requestData.input.value< Tomahawk::InfoSystem::InfoCriteriaHash >();
Tomahawk::InfoSystem::InfoCriteriaHash criteria;
if ( !hash.contains( "chart_id" ) )
{
dataError( requestId, requestData );
return;
} else {
criteria["chart_id"] = hash["chart_id"];
}
emit getCachedInfo( requestId, criteria, 0, requestData );
}
void
LastFmPlugin::fetchChartCapabilities( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData )
{
if ( !requestData.input.canConvert< Tomahawk::InfoSystem::InfoCriteriaHash >() )
{
dataError( requestId, requestData );
return;
}
InfoCriteriaHash hash = requestData.input.value< Tomahawk::InfoSystem::InfoCriteriaHash >();
Tomahawk::InfoSystem::InfoCriteriaHash criteria;
emit getCachedInfo( requestId, criteria, 0, requestData );
}
void
LastFmPlugin::fetchCoverArt( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData )
@@ -356,6 +401,47 @@ LastFmPlugin::notInCacheSlot( uint requestId, QHash<QString, QString> criteria,
switch ( requestData.type )
{
case InfoChart:
{
tDebug() << "LastFmPlugin: InfoChart not in cache, fetching";
QMap<QString, QString> args;
tDebug() << "LastFmPlugin: " << "args chart_id" << criteria["chart_id"];
args["method"] = criteria["chart_id"];
args["limit"] = "100";
QNetworkReply* reply = lastfm::ws::get(args);
reply->setProperty( "requestId", requestId );
reply->setProperty( "requestData", QVariant::fromValue< Tomahawk::InfoSystem::InfoRequestData >( requestData ) );
connect( reply, SIGNAL( finished() ), SLOT( chartReturned() ) );
return;
}
case InfoChartCapabilities:
{
QList<Chart> track_charts;
track_charts.append(Chart("chart.getTopTracks", "Top Tracks", "tracks"));
track_charts.append(Chart("chart.getLovedTracks", "Loved Tracks", "tracks"));
track_charts.append(Chart("chart.getHypedTracks", "Hyped Tracks", "tracks"));
QList<Chart> artist_charts;
artist_charts.append(Chart("chart.getTopArtists", "Top Artists", "artists"));
artist_charts.append(Chart("chart.getHypedArtists", "Hyped Artists", "artists"));
QVariantMap charts;
charts.insert("Tracks", QVariant::fromValue<QList<Chart> >(track_charts));
charts.insert("Artists", QVariant::fromValue<QList<Chart> >(artist_charts));
QVariantMap result;
result.insert("Last.fm", QVariant::fromValue<QVariantMap>(charts));
emit info(
requestId,
requestData,
result
);
return;
}
case InfoArtistSimilars:
{
lastfm::Artist a( criteria["artist"] );
@@ -447,6 +533,51 @@ LastFmPlugin::similarArtistsReturned()
emit updateCache( criteria, 2419200000, requestData.type, returnedData );
}
void
LastFmPlugin::chartReturned()
{
tDebug() << "LastfmPlugin: InfoChart data returned!";
QNetworkReply* reply = qobject_cast<QNetworkReply*>( sender() );
QVariantMap returnedData;
const QRegExp tracks_rx( "chart\\.\\S+tracks\\S*", Qt::CaseInsensitive );
const QRegExp artists_rx( "chart\\.\\S+artists\\S*", Qt::CaseInsensitive );
const QString url = reply->url().toString();
if( url.contains( tracks_rx ) ) {
QList<lastfm::Track> tracks = parseTrackList( reply );
QList<ArtistTrackPair> top_tracks;
foreach( const lastfm::Track &t, tracks ) {
ArtistTrackPair pair;
pair.artist = t.artist().toString();
pair.track = t.title();
top_tracks << pair;
}
tDebug() << "LastFmPlugin:" << "\tgot " << top_tracks.size() << " tracks";
returnedData["tracks"] = QVariant::fromValue( top_tracks );
returnedData["type"] = "tracks";
} else if( url.contains( artists_rx ) ) {
QList<lastfm::Artist> list = lastfm::Artist::list( reply );
QStringList al;
tDebug() << "LastFmPlugin:"<< "\tgot " << list.size() << " artists";
foreach ( const lastfm::Artist& a, list )
al << a.toString();
returnedData["artists"] = al;
returnedData["type"] = "artists";
} else {
tDebug() << "LastfmPlugin:: got non tracks and non artists";
}
Tomahawk::InfoSystem::InfoRequestData requestData = reply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >();
emit info(
reply->property( "requestId" ).toUInt(),
requestData,
returnedData
);
// TODO update cache
}
void
LastFmPlugin::topTracksReturned()
@@ -468,7 +599,7 @@ LastFmPlugin::topTracksReturned()
Tomahawk::InfoSystem::InfoCriteriaHash origData = requestData.input.value< Tomahawk::InfoSystem::InfoCriteriaHash>();
Tomahawk::InfoSystem::InfoCriteriaHash criteria;
criteria["artist"] = origData["artist"];
emit updateCache( criteria, 2419200000, requestData.type, returnedData );
emit updateCache( criteria, 0, requestData.type, returnedData );
}
@@ -604,7 +735,10 @@ LastFmPlugin::settingsChanged()
m_pw = TomahawkSettings::instance()->lastFmPassword();
// credentials have changed, have to re-create scrobbler for them to take effect
if( m_scrobbler )
{
delete m_scrobbler;
m_scrobbler = 0;
}
createScrobbler();
}
@@ -675,3 +809,21 @@ LastFmPlugin::createScrobbler()
}
}
QList<lastfm::Track>
LastFmPlugin::parseTrackList( QNetworkReply * reply )
{
QList<lastfm::Track> tracks;
try {
lastfm::XmlQuery lfm = lastfm::ws::parse(reply);
foreach (lastfm::XmlQuery xq, lfm.children( "track" )) {
tracks.append( lastfm::Track( xq ) );
}
}
catch (lastfm::ws::ParseError& e)
{
qWarning() << e.what();
}
return tracks;
}

View File

@@ -52,6 +52,7 @@ public slots:
void artistImagesReturned();
void similarArtistsReturned();
void topTracksReturned();
void chartReturned();
void namChangedSlot( QNetworkAccessManager *nam );
@@ -66,6 +67,8 @@ private:
void fetchArtistImages( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData );
void fetchSimilarArtists( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData );
void fetchTopTracks( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData );
void fetchChart( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData );
void fetchChartCapabilities( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData );
void createScrobbler();
void nowPlaying( const QVariant &input );
@@ -74,6 +77,8 @@ private:
void dataError( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData );
QList<lastfm::Track> parseTrackList( QNetworkReply * reply );
lastfm::MutableTrack m_track;
lastfm::Audioscrobbler* m_scrobbler;
QString m_pw;

View File

@@ -96,8 +96,6 @@ void
AdiumPlugin::settingsChanged()
{
m_active = TomahawkSettings::instance()->nowPlayingEnabled();
if( !m_active )
setStatus( "" );
}

View File

@@ -0,0 +1,672 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2010-2011, 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/>.
*/
#include <QApplication>
#include <QImage>
#include <QtDBus/QtDBus>
#include "audio/audioengine.h"
#include "infosystem/infosystemworker.h"
#include "album.h"
#include "artist.h"
#include "result.h"
#include "tomahawksettings.h"
#include "globalactionmanager.h"
#include "utils/logger.h"
#include "utils/tomahawkutils.h"
#include "mprisplugin.h"
#include "mprispluginrootadaptor.h"
#include "mprispluginplayeradaptor.h"
using namespace Tomahawk::InfoSystem;
static QString s_mpInfoIdentifier = QString( "MPRISPLUGIN" );
MprisPlugin::MprisPlugin()
: InfoPlugin()
, m_coverTempFile( 0 )
{
qDebug() << Q_FUNC_INFO;
// init
m_playbackStatus = "Stopped";
// Types of pushInfo we care about
m_supportedPushTypes << InfoNowPlaying << InfoNowPaused << InfoNowResumed << InfoNowStopped;
// DBus connection
new MprisPluginRootAdaptor( this );
new MprisPluginPlayerAdaptor( this );
QDBusConnection dbus = QDBusConnection::sessionBus();
dbus.registerObject("/org/mpris/MediaPlayer2", this);
dbus.registerService("org.mpris.MediaPlayer2.tomahawk");
// Listen to volume changes
connect( AudioEngine::instance(), SIGNAL( volumeChanged( int ) ),
SLOT( onVolumeChanged( int ) ) );
// When the playlist changes, signals for several properties are sent
connect( AudioEngine::instance(), SIGNAL( playlistChanged( Tomahawk::PlaylistInterface* ) ),
SLOT( onPlaylistChanged( Tomahawk::PlaylistInterface* ) ) );
// When a track is added or removed, CanGoNext updated signal is sent
PlaylistInterface *playlist = AudioEngine::instance()->playlist();
if( playlist )
connect( playlist->object(), SIGNAL( trackCountChanged( unsigned int ) ),
SLOT( onTrackCountChanged( unsigned int ) ) );
// Connect to AudioEngine's seeked signal
connect( AudioEngine::instance(), SIGNAL( seeked( qint64 ) ),
SLOT( onSeeked( qint64 ) ) );
// Connect to the InfoSystem (we need to get album covers via getInfo)
connect( Tomahawk::InfoSystem::InfoSystem::instance(),
SIGNAL( info( Tomahawk::InfoSystem::InfoRequestData, QVariant ) ),
SLOT( infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData, QVariant ) ) );
connect( Tomahawk::InfoSystem::InfoSystem::instance(), SIGNAL( finished( QString ) ), SLOT( infoSystemFinished( QString ) ) );
}
MprisPlugin::~MprisPlugin()
{
qDebug() << Q_FUNC_INFO;
delete m_coverTempFile;
}
// org.mpris.MediaPlayer2
bool
MprisPlugin::canQuit() const
{
qDebug() << Q_FUNC_INFO;
return true;
}
bool
MprisPlugin::canRaise() const
{
qDebug() << Q_FUNC_INFO;
return false;
}
bool
MprisPlugin::hasTrackList() const
{
qDebug() << Q_FUNC_INFO;
return false;
}
QString
MprisPlugin::identity() const
{
return QString("Tomahawk");
}
QString
MprisPlugin::desktopEntry() const
{
return QString("tomahawk");
}
QStringList
MprisPlugin::supportedUriSchemes() const
{
QStringList uriSchemes;
uriSchemes << "tomahawk" << "spotify";
return uriSchemes;
}
QStringList
MprisPlugin::supportedMimeTypes() const
{
return QStringList();
}
void
MprisPlugin::Raise()
{
}
void
MprisPlugin::Quit()
{
QApplication::quit();
}
// org.mpris.MediaPlayer2.Player
bool
MprisPlugin::canControl() const
{
return true;
}
bool
MprisPlugin::canGoNext() const
{
return AudioEngine::instance()->canGoNext();
}
bool
MprisPlugin::canGoPrevious() const
{
return AudioEngine::instance()->canGoPrevious();
}
bool
MprisPlugin::canPause() const
{
return AudioEngine::instance()->currentTrack();
}
bool
MprisPlugin::canPlay() const
{
// If there is a currently playing track, or if there is a playlist with at least 1 track, you can hit play
PlaylistInterface *p = AudioEngine::instance()->playlist();
return AudioEngine::instance()->currentTrack() || ( p && p->trackCount() );
}
bool
MprisPlugin::canSeek() const
{
PlaylistInterface *p = AudioEngine::instance()->playlist();
if (!p)
return false;
return p->seekRestrictions() != PlaylistInterface::NoSeek;
}
QString
MprisPlugin::loopStatus() const
{
PlaylistInterface *p = AudioEngine::instance()->playlist();
if (!p)
return "None";
PlaylistInterface::RepeatMode mode = p->repeatMode();
switch( mode )
{
case PlaylistInterface::RepeatOne:
return "Track";
break;
case PlaylistInterface::RepeatAll:
return "Playlist";
break;
case PlaylistInterface::NoRepeat:
return "None";
break;
default:
return QString("None");
break;
}
return QString("None");
}
void
MprisPlugin::setLoopStatus( const QString &value )
{
PlaylistInterface *p = AudioEngine::instance()->playlist();
if (!p)
return;
if( value == "Track")
p->setRepeatMode( PlaylistInterface::RepeatOne );
else if( value == "Playlist" )
p->setRepeatMode( PlaylistInterface::RepeatAll );
else if( value == "None" )
p->setRepeatMode( PlaylistInterface::NoRepeat );
}
double
MprisPlugin::maximumRate() const
{
return 1.0;
}
QVariantMap
MprisPlugin::metadata() const
{
QVariantMap metadataMap;
Tomahawk::result_ptr track = AudioEngine::instance()->currentTrack();
if( track )
{
metadataMap.insert( "mpris:trackid", QString( "/track/" ) + track->id().replace( "-", "" ) );
metadataMap.insert( "mpris:length", track->duration() );
metadataMap.insert( "xesam:album", track->album()->name() );
metadataMap.insert( "xesam:artist", track->artist()->name() );
metadataMap.insert( "xesam:title", track->track() );
// Only return art if tempfile exists, and if its name contains the same "artist_album_tomahawk_cover.png"
if( m_coverTempFile && m_coverTempFile->exists() &&
m_coverTempFile->fileName().contains( track->artist()->name() + "_" + track->album()->name() + "_tomahawk_cover.png" ) )
metadataMap.insert( "mpris:artUrl", QFileInfo( *m_coverTempFile ).absoluteFilePath() );
else
{
// Need to fetch the album cover
Tomahawk::InfoSystem::InfoCriteriaHash trackInfo;
trackInfo["artist"] = track->artist()->name();
trackInfo["album"] = track->album()->name();
Tomahawk::InfoSystem::InfoRequestData requestData;
requestData.caller = s_mpInfoIdentifier;
requestData.type = Tomahawk::InfoSystem::InfoAlbumCoverArt;
requestData.input = QVariant::fromValue< Tomahawk::InfoSystem::InfoCriteriaHash >( trackInfo );
requestData.customData = QVariantMap();
Tomahawk::InfoSystem::InfoSystem::instance()->getInfo( requestData );
}
}
return metadataMap;
}
double
MprisPlugin::minimumRate() const
{
return 1.0;
}
QString
MprisPlugin::playbackStatus() const
{
return m_playbackStatus;
}
qlonglong
MprisPlugin::position() const
{
// Convert Tomahawk's milliseconds to microseconds
return (qlonglong) ( AudioEngine::instance()->currentTime() * 1000 );
}
double
MprisPlugin::rate() const
{
return 1.0;
}
void
MprisPlugin::setRate( double value )
{
Q_UNUSED( value );
}
bool
MprisPlugin::shuffle() const
{
PlaylistInterface *p = AudioEngine::instance()->playlist();
if (!p)
return false;
return p->shuffled();
}
void
MprisPlugin::setShuffle( bool value )
{
PlaylistInterface *p = AudioEngine::instance()->playlist();
if (!p)
return;
return p->setShuffled( value );
}
double
MprisPlugin::volume() const
{
return AudioEngine::instance()->volume();
}
void
MprisPlugin::setVolume( double value )
{
AudioEngine::instance()->setVolume( value );
}
void
MprisPlugin::Next()
{
AudioEngine::instance()->next();
}
void
MprisPlugin::OpenUri( const QString &Uri )
{
if( Uri.contains( "tomahawk://" ) )
GlobalActionManager::instance()->parseTomahawkLink( Uri );
else if( Uri.contains( "spotify:" ) )
GlobalActionManager::instance()->openSpotifyLink( Uri );
}
void
MprisPlugin::Pause()
{
AudioEngine::instance()->pause();
}
void
MprisPlugin::Play()
{
AudioEngine::instance()->play();
}
void
MprisPlugin::PlayPause()
{
AudioEngine::instance()->playPause();
}
void
MprisPlugin::Previous()
{
AudioEngine::instance()->previous();
}
void
MprisPlugin::Seek( qlonglong Offset )
{
qDebug() << Q_FUNC_INFO;
if( !canSeek() )
return;
qlonglong seekTime = position() + Offset;
qDebug() << "seekTime: " << seekTime;
if( seekTime < 0 )
AudioEngine::instance()->seek( 0 );
else if( seekTime > AudioEngine::instance()->currentTrackTotalTime()*1000 )
Next();
// seekTime is in microseconds, but we work internally in milliseconds
else
AudioEngine::instance()->seek( (qint64) ( seekTime / 1000 ) );
}
void
MprisPlugin::SetPosition( const QDBusObjectPath &TrackId, qlonglong Position )
{
qDebug() << Q_FUNC_INFO;
if( !canSeek() )
return;
qDebug() << "path: " << TrackId.path();
qDebug() << "position: " << Position;
if( TrackId.path() != QString("/track/") + AudioEngine::instance()->currentTrack()->id().replace( "-", "" ) )
return;
if( ( Position < 0) || ( Position > AudioEngine::instance()->currentTrackTotalTime()*1000 ) )
return;
qDebug() << "seeking to: " << Position/1000 << "ms";
AudioEngine::instance()->seek( (qint64) (Position / 1000 ) );
}
void
MprisPlugin::Stop()
{
AudioEngine::instance()->stop();
}
// InfoPlugin Methods
void
MprisPlugin::getInfo( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData )
{
qDebug() << Q_FUNC_INFO;
return;
}
void
MprisPlugin::pushInfo( QString caller, Tomahawk::InfoSystem::InfoType type, QVariant input )
{
qDebug() << Q_FUNC_INFO;
bool isPlayingInfo = false;
switch ( type )
{
case InfoNowPlaying:
isPlayingInfo = true;
audioStarted( input );
break;
case InfoNowPaused:
isPlayingInfo = true;
audioPaused();
break;
case InfoNowResumed:
isPlayingInfo = true;
audioResumed( input );
break;
case InfoNowStopped:
isPlayingInfo = true;
audioStopped();
break;
default:
break;
}
if( isPlayingInfo )
notifyPropertyChanged( "org.mpris.MediaPlayer2.Player", "PlaybackStatus");
}
void
MprisPlugin::stateChanged( AudioState newState, AudioState oldState )
{
}
/** Audio state slots */
void
MprisPlugin::audioStarted( const QVariant &input )
{
qDebug() << Q_FUNC_INFO;
if ( !input.canConvert< Tomahawk::InfoSystem::InfoCriteriaHash >() )
return;
InfoCriteriaHash hash = input.value< Tomahawk::InfoSystem::InfoCriteriaHash >();
if ( !hash.contains( "title" ) || !hash.contains( "artist" ) || !hash.contains( "album" ) )
return;
m_playbackStatus = "Playing";
notifyPropertyChanged( "org.mpris.MediaPlayer2.Player", "Metadata");
//hash["artist"];
//hash["title"];
//QString nowPlaying = "";
//qDebug() << "nowPlaying: " << nowPlaying;
}
void
MprisPlugin::audioFinished( const QVariant &input )
{
//qDebug() << Q_FUNC_INFO;
}
void
MprisPlugin::audioStopped()
{
qDebug() << Q_FUNC_INFO;
m_playbackStatus = "Stopped";
}
void
MprisPlugin::audioPaused()
{
qDebug() << Q_FUNC_INFO;
m_playbackStatus = "Paused";
}
void
MprisPlugin::audioResumed( const QVariant &input )
{
qDebug() << Q_FUNC_INFO;
audioStarted( input );
}
void
MprisPlugin::onVolumeChanged( int volume )
{
Q_UNUSED( volume );
notifyPropertyChanged( "org.mpris.MediaPlayer2.Player", "Volume");
}
void
MprisPlugin::onPlaylistChanged( Tomahawk::PlaylistInterface* playlist )
{
qDebug() << Q_FUNC_INFO;
disconnect( this, SLOT( onTrackCountChanged( unsigned int ) ) );
qDebug() << "disconnected";
if( playlist )
qDebug() << "playlist not null";
if( playlist )
connect( playlist->object(), SIGNAL( trackCountChanged( unsigned int ) ),
SLOT( onTrackCountChanged( unsigned int ) ) );
qDebug() << "connected new playlist";
// Notify relevant changes
notifyPropertyChanged( "org.mpris.MediaPlayer2.Player", "LoopStatus" );
notifyPropertyChanged( "org.mpris.MediaPlayer2.Player", "Shuffle" );
notifyPropertyChanged( "org.mpris.MediaPlayer2.Player", "CanSeek" );
onTrackCountChanged( 0 );
}
void
MprisPlugin::onTrackCountChanged( unsigned int tracks )
{
Q_UNUSED( tracks );
notifyPropertyChanged( "org.mpris.MediaPlayer2.Player", "CanGoNext" );
notifyPropertyChanged( "org.mpris.MediaPlayer2.Player", "CanGoPrevious" );
}
void
MprisPlugin::onSeeked( qint64 ms )
{
qlonglong us = (qlonglong) ( ms*1000 );
emit Seeked( us );
}
void
MprisPlugin::infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData requestData, QVariant output )
{
// If the caller for the request was not us, or not the type of info we are seeking, ignore it
if ( requestData.caller != s_mpInfoIdentifier || requestData.type != Tomahawk::InfoSystem::InfoAlbumCoverArt )
{
//notifyPropertyChanged( "org.mpris.MediaPlayer2.Player", "Metadata" );
return;
}
if ( !output.canConvert< QVariantMap >() )
{
//notifyPropertyChanged( "org.mpris.MediaPlayer2.Player", "Metadata" );
tDebug( LOGINFO ) << "Cannot convert fetched art from a QByteArray";
return;
}
// Pull image data into byte array
QVariantMap returnedData = output.value< QVariantMap >();
const QByteArray ba = returnedData["imgbytes"].toByteArray();
if ( ba.length() )
{
// Load from byte array to image
QImage image;
image.loadFromData( ba );
// Pull out request data for album+artist
if( !requestData.input.canConvert< Tomahawk::InfoSystem::InfoCriteriaHash >() )
{
qDebug() << "Cannot convert metadata input to album cover retrieval";
return;
}
Tomahawk::InfoSystem::InfoCriteriaHash hash = requestData.input.value< Tomahawk::InfoSystem::InfoCriteriaHash>();
// delete the old tempfile and make new one, to avoid caching of filename by mpris clients
if( m_coverTempFile )
delete m_coverTempFile;
m_coverTempFile = new QTemporaryFile( QDir::toNativeSeparators(
QDir::tempPath() + "/" + hash["artist"] + "_" + hash["album"] + "_tomahawk_cover.png" ) );
if( !m_coverTempFile->open() )
{
qDebug() << "WARNING: could not write temporary file for cover art!";
}
// Finally, save the image to the new temp file
//if( image.save( QFileInfo( *m_coverTempFile ).absoluteFilePath(), "PNG" ) )
if( image.save( m_coverTempFile, "PNG") )
{
qDebug() << Q_FUNC_INFO << "Image saving successful, notifying";
qDebug() << "Saving to: " << QFileInfo( *m_coverTempFile ).absoluteFilePath();
m_coverTempFile->close();
notifyPropertyChanged( "org.mpris.MediaPlayer2.Player", "Metadata" );
}
else
{
qDebug() << Q_FUNC_INFO << " failed to save image!";
m_coverTempFile->close();
}
/*
if( m_coverTempFile->open() )
{
QTextStream out( m_coverTempFile );
out << ba;
m_coverTempFile->close();
notifyPropertyChanged( "org.mpris.MediaPlayer2.Player", "Metadata" );
}
*/
}
}
void
MprisPlugin::infoSystemFinished( QString target )
{
Q_UNUSED( target );
}
void
MprisPlugin::notifyPropertyChanged( const QString& interface,
const QString& propertyName )
{
QDBusMessage signal = QDBusMessage::createSignal(
"/org/mpris/MediaPlayer2",
"org.freedesktop.DBus.Properties",
"PropertiesChanged");
signal << interface;
QVariantMap changedProps;
changedProps.insert(propertyName, property(propertyName.toAscii()));
signal << changedProps;
signal << QStringList();
QDBusConnection::sessionBus().send(signal);
}

View File

@@ -0,0 +1,182 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2010-2011, 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 MPRISPLUGIN_H
#define MPRISPLUGIN_H
#include "audio/audioengine.h"
#include "infosystem/infosystem.h"
#include <QObject>
#include <QVariant>
#include <QtDBus/QtDBus>
namespace Tomahawk {
namespace InfoSystem {
class MprisPlugin : public InfoPlugin
{
Q_OBJECT
public:
MprisPlugin();
virtual ~MprisPlugin();
// MPRIS DBus Methods
// org.mpris.MediaPlayer2
Q_PROPERTY(bool CanQuit READ canQuit)
bool canQuit() const;
Q_PROPERTY(bool CanRaise READ canRaise)
bool canRaise() const;
Q_PROPERTY(QString DesktopEntry READ desktopEntry)
QString desktopEntry() const;
Q_PROPERTY(bool HasTrackList READ hasTrackList)
bool hasTrackList() const;
Q_PROPERTY(QString Identity READ identity)
QString identity() const;
Q_PROPERTY(QStringList SupportedMimeTypes READ supportedMimeTypes)
QStringList supportedMimeTypes() const;
Q_PROPERTY(QStringList SupportedUriSchemes READ supportedUriSchemes)
QStringList supportedUriSchemes() const;
// org.mpris.MediaPlayer2.Player
Q_PROPERTY(bool CanControl READ canControl)
bool canControl() const;
Q_PROPERTY(bool CanGoNext READ canGoNext)
bool canGoNext() const;
Q_PROPERTY(bool CanGoPrevious READ canGoPrevious)
bool canGoPrevious() const;
Q_PROPERTY(bool CanPause READ canPause)
bool canPause() const;
Q_PROPERTY(bool CanPlay READ canPlay)
bool canPlay() const;
Q_PROPERTY(bool CanSeek READ canSeek)
bool canSeek() const;
Q_PROPERTY(QString LoopStatus READ loopStatus WRITE setLoopStatus)
QString loopStatus() const;
void setLoopStatus(const QString &value);
Q_PROPERTY(double MaximumRate READ maximumRate)
double maximumRate() const;
Q_PROPERTY(QVariantMap Metadata READ metadata)
QVariantMap metadata() const;
Q_PROPERTY(double MinimumRate READ minimumRate)
double minimumRate() const;
Q_PROPERTY(QString PlaybackStatus READ playbackStatus)
QString playbackStatus() const;
Q_PROPERTY(qlonglong Position READ position)
qlonglong position() const;
Q_PROPERTY(double Rate READ rate WRITE setRate)
double rate() const;
void setRate(double value);
Q_PROPERTY(bool Shuffle READ shuffle WRITE setShuffle)
bool shuffle() const;
void setShuffle(bool value);
Q_PROPERTY(double Volume READ volume WRITE setVolume)
double volume() const;
void setVolume(double value);
public slots:
void namChangedSlot( QNetworkAccessManager* /*nam*/ ) {} // unused
virtual void notInCacheSlot( uint requestId, const Tomahawk::InfoSystem::InfoCriteriaHash criteria, Tomahawk::InfoSystem::InfoRequestData requestData )
{
Q_UNUSED( requestId );
Q_UNUSED( criteria );
Q_UNUSED( requestData );
}
// org.mpris.MediaPlayer2
void Raise();
void Quit();
// org.mpris.MediaPlayer2.Player
void Next();
void OpenUri(const QString &Uri);
void Pause();
void Play();
void PlayPause();
void Previous();
void Seek(qlonglong Offset);
void SetPosition(const QDBusObjectPath &TrackId, qlonglong Position);
void Stop();
protected slots:
void getInfo( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData );
void pushInfo( QString caller, Tomahawk::InfoSystem::InfoType type, QVariant input );
private slots:
void stateChanged( AudioState newState, AudioState oldState );
void onVolumeChanged( int volume );
void onPlaylistChanged( Tomahawk::PlaylistInterface* playlist);
void onTrackCountChanged( unsigned int tracks );
void onSeeked( qint64 ms );
void infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData requestData, QVariant output );
void infoSystemFinished( QString target );
signals:
void Seeked( qlonglong Position );
private:
// Get Info
// Push Info
void audioStarted( const QVariant &input );
void audioFinished( const QVariant &input );
void audioStopped();
void audioPaused();
void audioResumed( const QVariant &input );
// DBus
void notifyPropertyChanged( const QString& interface, const QString& propertyName );
QString m_playbackStatus;
QTemporaryFile *m_coverTempFile;
};
};
}
#endif // MPRISPLUGIN_H

View File

@@ -0,0 +1,205 @@
/*
* This file was generated by qdbusxml2cpp version 0.7
* Command line was: qdbusxml2cpp -a mprispluginplayeradaptor -c MprisPluginPlayerAdaptor mprispluginplayeradaptor.xml
*
* qdbusxml2cpp is Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
*
* This is an auto-generated file.
* Do not edit! All changes made to it will be lost.
*/
#include "mprispluginplayeradaptor.h"
#include <QtCore/QMetaObject>
#include <QtCore/QByteArray>
#include <QtCore/QList>
#include <QtCore/QMap>
#include <QtCore/QString>
#include <QtCore/QStringList>
#include <QtCore/QVariant>
/*
* Implementation of adaptor class MprisPluginPlayerAdaptor
*/
MprisPluginPlayerAdaptor::MprisPluginPlayerAdaptor(QObject *parent)
: QDBusAbstractAdaptor(parent)
{
// constructor
setAutoRelaySignals(true);
}
MprisPluginPlayerAdaptor::~MprisPluginPlayerAdaptor()
{
// destructor
}
bool MprisPluginPlayerAdaptor::canControl() const
{
// get the value of property CanControl
return qvariant_cast< bool >(parent()->property("CanControl"));
}
bool MprisPluginPlayerAdaptor::canGoNext() const
{
// get the value of property CanGoNext
return qvariant_cast< bool >(parent()->property("CanGoNext"));
}
bool MprisPluginPlayerAdaptor::canGoPrevious() const
{
// get the value of property CanGoPrevious
return qvariant_cast< bool >(parent()->property("CanGoPrevious"));
}
bool MprisPluginPlayerAdaptor::canPause() const
{
// get the value of property CanPause
return qvariant_cast< bool >(parent()->property("CanPause"));
}
bool MprisPluginPlayerAdaptor::canPlay() const
{
// get the value of property CanPlay
return qvariant_cast< bool >(parent()->property("CanPlay"));
}
bool MprisPluginPlayerAdaptor::canSeek() const
{
// get the value of property CanSeek
return qvariant_cast< bool >(parent()->property("CanSeek"));
}
QString MprisPluginPlayerAdaptor::loopStatus() const
{
// get the value of property LoopStatus
return qvariant_cast< QString >(parent()->property("LoopStatus"));
}
void MprisPluginPlayerAdaptor::setLoopStatus(const QString &value)
{
// set the value of property LoopStatus
parent()->setProperty("LoopStatus", qVariantFromValue(value));
}
double MprisPluginPlayerAdaptor::maximumRate() const
{
// get the value of property MaximumRate
return qvariant_cast< double >(parent()->property("MaximumRate"));
}
QVariantMap MprisPluginPlayerAdaptor::metadata() const
{
// get the value of property Metadata
return qvariant_cast< QVariantMap >(parent()->property("Metadata"));
}
double MprisPluginPlayerAdaptor::minimumRate() const
{
// get the value of property MinimumRate
return qvariant_cast< double >(parent()->property("MinimumRate"));
}
QString MprisPluginPlayerAdaptor::playbackStatus() const
{
// get the value of property PlaybackStatus
return qvariant_cast< QString >(parent()->property("PlaybackStatus"));
}
qlonglong MprisPluginPlayerAdaptor::position() const
{
// get the value of property Position
return qvariant_cast< qlonglong >(parent()->property("Position"));
}
double MprisPluginPlayerAdaptor::rate() const
{
// get the value of property Rate
return qvariant_cast< double >(parent()->property("Rate"));
}
void MprisPluginPlayerAdaptor::setRate(double value)
{
// set the value of property Rate
parent()->setProperty("Rate", qVariantFromValue(value));
}
bool MprisPluginPlayerAdaptor::shuffle() const
{
// get the value of property Shuffle
return qvariant_cast< bool >(parent()->property("Shuffle"));
}
void MprisPluginPlayerAdaptor::setShuffle(bool value)
{
// set the value of property Shuffle
parent()->setProperty("Shuffle", qVariantFromValue(value));
}
double MprisPluginPlayerAdaptor::volume() const
{
// get the value of property Volume
return qvariant_cast< double >(parent()->property("Volume"));
}
void MprisPluginPlayerAdaptor::setVolume(double value)
{
// set the value of property Volume
parent()->setProperty("Volume", qVariantFromValue(value));
}
void MprisPluginPlayerAdaptor::Next()
{
// handle method call org.mpris.MediaPlayer2.Player.Next
QMetaObject::invokeMethod(parent(), "Next");
}
void MprisPluginPlayerAdaptor::OpenUri(const QString &Uri)
{
// handle method call org.mpris.MediaPlayer2.Player.OpenUri
QMetaObject::invokeMethod(parent(), "OpenUri", Q_ARG(QString, Uri));
}
void MprisPluginPlayerAdaptor::Pause()
{
// handle method call org.mpris.MediaPlayer2.Player.Pause
QMetaObject::invokeMethod(parent(), "Pause");
}
void MprisPluginPlayerAdaptor::Play()
{
// handle method call org.mpris.MediaPlayer2.Player.Play
QMetaObject::invokeMethod(parent(), "Play");
}
void MprisPluginPlayerAdaptor::PlayPause()
{
// handle method call org.mpris.MediaPlayer2.Player.PlayPause
QMetaObject::invokeMethod(parent(), "PlayPause");
}
void MprisPluginPlayerAdaptor::Previous()
{
// handle method call org.mpris.MediaPlayer2.Player.Previous
QMetaObject::invokeMethod(parent(), "Previous");
}
void MprisPluginPlayerAdaptor::Seek(qlonglong Offset)
{
qDebug() << Q_FUNC_INFO;
// handle method call org.mpris.MediaPlayer2.Player.Seek
QMetaObject::invokeMethod(parent(), "Seek", Q_ARG(qlonglong, Offset));
}
void MprisPluginPlayerAdaptor::SetPosition(const QDBusObjectPath &TrackId, qlonglong Position)
{
qDebug() << Q_FUNC_INFO;
// handle method call org.mpris.MediaPlayer2.Player.SetPosition
QMetaObject::invokeMethod(parent(), "SetPosition", Q_ARG(QDBusObjectPath, TrackId), Q_ARG(qlonglong, Position));
}
void MprisPluginPlayerAdaptor::Stop()
{
// handle method call org.mpris.MediaPlayer2.Player.Stop
QMetaObject::invokeMethod(parent(), "Stop");
}

View File

@@ -0,0 +1,139 @@
/*
* This file was generated by qdbusxml2cpp version 0.7
* Command line was: qdbusxml2cpp -a mprispluginplayeradaptor -c MprisPluginPlayerAdaptor mprispluginplayeradaptor.xml
*
* qdbusxml2cpp is Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
*
* This is an auto-generated file.
* This file may have been hand-edited. Look for HAND-EDIT comments
* before re-generating it.
*/
#ifndef MPRISPLUGINPLAYERADAPTOR_H_1313089554
#define MPRISPLUGINPLAYERADAPTOR_H_1313089554
#include <QtCore/QObject>
#include <QtDBus/QtDBus>
class QByteArray;
template<class T> class QList;
template<class Key, class Value> class QMap;
class QString;
class QStringList;
class QVariant;
/*
* Adaptor class for interface org.mpris.MediaPlayer2.Player
*/
class MprisPluginPlayerAdaptor: public QDBusAbstractAdaptor
{
Q_OBJECT
Q_CLASSINFO("D-Bus Interface", "org.mpris.MediaPlayer2.Player")
Q_CLASSINFO("D-Bus Introspection", ""
" <interface name=\"org.mpris.MediaPlayer2.Player\">\n"
" <method name=\"Next\"/>\n"
" <method name=\"Previous\"/>\n"
" <method name=\"Pause\"/>\n"
" <method name=\"PlayPause\"/>\n"
" <method name=\"Stop\"/>\n"
" <method name=\"Play\"/>\n"
" <method name=\"Seek\">\n"
" <arg direction=\"in\" type=\"x\" name=\"Offset\"/>\n"
" </method>\n"
" <method name=\"SetPosition\">\n"
" <arg direction=\"in\" type=\"o\" name=\"TrackId\"/>\n"
" <arg direction=\"in\" type=\"x\" name=\"Position\"/>\n"
" </method>\n"
" <method name=\"OpenUri\">\n"
" <arg direction=\"in\" type=\"s\" name=\"Uri\"/>\n"
" </method>\n"
" <signal name=\"Seeked\">\n"
" <arg direction=\"out\" type=\"x\" name=\"Position\"/>\n"
" </signal>\n"
" <property access=\"read\" type=\"s\" name=\"PlaybackStatus\"/>\n"
" <property access=\"readwrite\" type=\"s\" name=\"LoopStatus\"/>\n"
" <property access=\"readwrite\" type=\"d\" name=\"Rate\"/>\n"
" <property access=\"readwrite\" type=\"b\" name=\"Shuffle\"/>\n"
" <property access=\"read\" type=\"a{sv}\" name=\"Metadata\">\n"
" <annotation value=\"QVariantMap\" name=\"com.trolltech.QtDBus.QtTypeName\"/>\n"
" </property>\n"
" <property access=\"readwrite\" type=\"d\" name=\"Volume\"/>\n"
" <property access=\"read\" type=\"x\" name=\"Position\"/>\n"
" <property access=\"read\" type=\"d\" name=\"MinimumRate\"/>\n"
" <property access=\"read\" type=\"d\" name=\"MaximumRate\"/>\n"
" <property access=\"read\" type=\"b\" name=\"CanGoNext\"/>\n"
" <property access=\"read\" type=\"b\" name=\"CanGoPrevious\"/>\n"
" <property access=\"read\" type=\"b\" name=\"CanPlay\"/>\n"
" <property access=\"read\" type=\"b\" name=\"CanPause\"/>\n"
" <property access=\"read\" type=\"b\" name=\"CanSeek\"/>\n"
" <property access=\"read\" type=\"b\" name=\"CanControl\"/>\n"
" </interface>\n"
"")
public:
MprisPluginPlayerAdaptor(QObject *parent);
virtual ~MprisPluginPlayerAdaptor();
public: // PROPERTIES
Q_PROPERTY(bool CanControl READ canControl)
bool canControl() const;
Q_PROPERTY(bool CanGoNext READ canGoNext)
bool canGoNext() const;
Q_PROPERTY(bool CanGoPrevious READ canGoPrevious)
bool canGoPrevious() const;
Q_PROPERTY(bool CanPause READ canPause)
bool canPause() const;
Q_PROPERTY(bool CanPlay READ canPlay)
bool canPlay() const;
Q_PROPERTY(bool CanSeek READ canSeek)
bool canSeek() const;
Q_PROPERTY(QString LoopStatus READ loopStatus WRITE setLoopStatus)
QString loopStatus() const;
void setLoopStatus(const QString &value);
Q_PROPERTY(double MaximumRate READ maximumRate)
double maximumRate() const;
Q_PROPERTY(QVariantMap Metadata READ metadata)
QVariantMap metadata() const;
Q_PROPERTY(double MinimumRate READ minimumRate)
double minimumRate() const;
Q_PROPERTY(QString PlaybackStatus READ playbackStatus)
QString playbackStatus() const;
Q_PROPERTY(qlonglong Position READ position)
qlonglong position() const;
Q_PROPERTY(double Rate READ rate WRITE setRate)
double rate() const;
void setRate(double value);
Q_PROPERTY(bool Shuffle READ shuffle WRITE setShuffle)
bool shuffle() const;
void setShuffle(bool value);
Q_PROPERTY(double Volume READ volume WRITE setVolume)
double volume() const;
void setVolume(double value);
public Q_SLOTS: // METHODS
void Next();
void OpenUri(const QString &Uri);
void Pause();
void Play();
void PlayPause();
void Previous();
void Seek(qlonglong Offset);
void SetPosition(const QDBusObjectPath &TrackId, qlonglong Position);
void Stop();
Q_SIGNALS: // SIGNALS
void Seeked(qlonglong Position);
};
#endif

View File

@@ -0,0 +1,41 @@
<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
<node>
<interface name="org.mpris.MediaPlayer2.Player">
<method name="Next"/>
<method name="Previous"/>
<method name="Pause"/>
<method name="PlayPause"/>
<method name="Stop"/>
<method name="Play"/>
<method name="Seek">
<arg type="x" name="Offset" direction="in" />
</method>
<method name="SetPosition">
<arg type="o" name="TrackId" direction="in" />
<arg type="x" name="Position" direction="in" />
</method>
<method name="OpenUri">
<arg type="s" name="Uri" direction="in" />
</method>
<signal name="Seeked">
<arg type="x" name="Position" direction="out" />
</signal>
<property name="PlaybackStatus" type="s" access="read"/>
<property name="LoopStatus" type="s" access="readwrite"/>
<property name="Rate" type="d" access="readwrite"/>
<property name="Shuffle" type="b" access="readwrite"/>
<property name="Metadata" type="a{sv}" access="read">
<annotation name="com.trolltech.QtDBus.QtTypeName" value="QVariantMap"/>
</property>
<property name="Volume" type="d" access="readwrite"/>
<property name="Position" type="x" access="read"/>
<property name="MinimumRate" type="d" access="read"/>
<property name="MaximumRate" type="d" access="read"/>
<property name="CanGoNext" type="b" access="read"/>
<property name="CanGoPrevious" type="b" access="read"/>
<property name="CanPlay" type="b" access="read"/>
<property name="CanPause" type="b" access="read"/>
<property name="CanSeek" type="b" access="read"/>
<property name="CanControl" type="b" access="read"/>
</interface>
</node>

View File

@@ -0,0 +1,92 @@
/*
* This file was generated by qdbusxml2cpp version 0.7
* Command line was: qdbusxml2cpp -a mprispluginrootadaptor -c MprisPluginRootAdaptor mprispluginrootadaptor.xml
*
* qdbusxml2cpp is Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
*
* This is an auto-generated file.
* Do not edit! All changes made to it will be lost.
*/
#include "mprispluginrootadaptor.h"
#include <QtCore/QMetaObject>
#include <QtCore/QByteArray>
#include <QtCore/QList>
#include <QtCore/QMap>
#include <QtCore/QString>
#include <QtCore/QStringList>
#include <QtCore/QVariant>
/*
* Implementation of adaptor class MprisPluginRootAdaptor
*/
MprisPluginRootAdaptor::MprisPluginRootAdaptor(QObject *parent)
: QDBusAbstractAdaptor(parent)
{
// constructor
setAutoRelaySignals(true);
}
MprisPluginRootAdaptor::~MprisPluginRootAdaptor()
{
// destructor
}
bool MprisPluginRootAdaptor::canQuit() const
{
// get the value of property CanQuit
return qvariant_cast< bool >(parent()->property("CanQuit"));
}
bool MprisPluginRootAdaptor::canRaise() const
{
qDebug() << Q_FUNC_INFO;
// get the value of property CanRaise
bool ret = qvariant_cast< bool >(parent()->property("CanRaise"));
qDebug() << "ret: " << ret;
return ret;
}
QString MprisPluginRootAdaptor::desktopEntry() const
{
// get the value of property DesktopEntry
return qvariant_cast< QString >(parent()->property("DesktopEntry"));
}
bool MprisPluginRootAdaptor::hasTrackList() const
{
// get the value of property HasTrackList
return qvariant_cast< bool >(parent()->property("HasTrackList"));
}
QString MprisPluginRootAdaptor::identity() const
{
// get the value of property Identity
return qvariant_cast< QString >(parent()->property("Identity"));
}
QStringList MprisPluginRootAdaptor::supportedMimeTypes() const
{
// get the value of property SupportedMimeTypes
return qvariant_cast< QStringList >(parent()->property("SupportedMimeTypes"));
}
QStringList MprisPluginRootAdaptor::supportedUriSchemes() const
{
// get the value of property SupportedUriSchemes
return qvariant_cast< QStringList >(parent()->property("SupportedUriSchemes"));
}
void MprisPluginRootAdaptor::Quit()
{
// handle method call org.mpris.MediaPlayer2.Quit
QMetaObject::invokeMethod(parent(), "Quit");
}
void MprisPluginRootAdaptor::Raise()
{
// handle method call org.mpris.MediaPlayer2.Raise
QMetaObject::invokeMethod(parent(), "Raise");
}

View File

@@ -0,0 +1,76 @@
/*
* This file was generated by qdbusxml2cpp version 0.7
* Command line was: qdbusxml2cpp -a mprispluginrootadaptor -c MprisPluginRootAdaptor mprispluginrootadaptor.xml
*
* qdbusxml2cpp is Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
*
* This is an auto-generated file.
* This file may have been hand-edited. Look for HAND-EDIT comments
* before re-generating it.
*/
#ifndef MPRISPLUGINROOTADAPTOR_H_1312900930
#define MPRISPLUGINROOTADAPTOR_H_1312900930
#include <QtCore/QObject>
#include <QtDBus/QtDBus>
class QByteArray;
template<class T> class QList;
template<class Key, class Value> class QMap;
class QString;
class QStringList;
class QVariant;
/*
* Adaptor class for interface org.mpris.MediaPlayer2
*/
class MprisPluginRootAdaptor: public QDBusAbstractAdaptor
{
Q_OBJECT
Q_CLASSINFO("D-Bus Interface", "org.mpris.MediaPlayer2")
Q_CLASSINFO("D-Bus Introspection", ""
" <interface name=\"org.mpris.MediaPlayer2\">\n"
" <method name=\"Raise\"/>\n"
" <method name=\"Quit\"/>\n"
" <property access=\"read\" type=\"b\" name=\"CanQuit\"/>\n"
" <property access=\"read\" type=\"b\" name=\"CanRaise\"/>\n"
" <property access=\"read\" type=\"b\" name=\"HasTrackList\"/>\n"
" <property access=\"read\" type=\"s\" name=\"Identity\"/>\n"
" <property access=\"read\" type=\"s\" name=\"DesktopEntry\"/>\n"
" <property access=\"read\" type=\"as\" name=\"SupportedUriSchemes\"/>\n"
" <property access=\"read\" type=\"as\" name=\"SupportedMimeTypes\"/>\n"
" </interface>\n"
"")
public:
MprisPluginRootAdaptor(QObject *parent);
virtual ~MprisPluginRootAdaptor();
public: // PROPERTIES
Q_PROPERTY(bool CanQuit READ canQuit)
bool canQuit() const;
Q_PROPERTY(bool CanRaise READ canRaise)
bool canRaise() const;
Q_PROPERTY(QString DesktopEntry READ desktopEntry)
QString desktopEntry() const;
Q_PROPERTY(bool HasTrackList READ hasTrackList)
bool hasTrackList() const;
Q_PROPERTY(QString Identity READ identity)
QString identity() const;
Q_PROPERTY(QStringList SupportedMimeTypes READ supportedMimeTypes)
QStringList supportedMimeTypes() const;
Q_PROPERTY(QStringList SupportedUriSchemes READ supportedUriSchemes)
QStringList supportedUriSchemes() const;
public Q_SLOTS: // METHODS
void Quit();
void Raise();
Q_SIGNALS: // SIGNALS
};
#endif

View File

@@ -0,0 +1,14 @@
<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
<node>
<interface name="org.mpris.MediaPlayer2">
<method name="Raise"/>
<method name="Quit"/>
<property name="CanQuit" type="b" access="read"/>
<property name="CanRaise" type="b" access="read"/>
<property name="HasTrackList" type="b" access="read"/>
<property name="Identity" type="s" access="read"/>
<property name="DesktopEntry" type="s" access="read"/>
<property name="SupportedUriSchemes" type="as" access="read"/>
<property name="SupportedMimeTypes" type="as" access="read"/>
</interface>
</node>

View File

@@ -58,14 +58,16 @@ InfoSystem::InfoSystem( QObject *parent )
qDebug() << Q_FUNC_INFO;
m_infoSystemCacheThreadController = new QThread( this );
m_infoSystemCacheThreadController = new InfoSystemCacheThread( this );
m_cache = QWeakPointer< InfoSystemCache >( new InfoSystemCache() );
m_cache.data()->moveToThread( m_infoSystemCacheThreadController );
m_infoSystemCacheThreadController->setCache( m_cache );
m_infoSystemCacheThreadController->start( QThread::IdlePriority );
m_infoSystemWorkerThreadController = new QThread( this );
m_worker = QWeakPointer< InfoSystemWorker>( new InfoSystemWorker() );
m_infoSystemWorkerThreadController = new InfoSystemWorkerThread( this );
m_worker = QWeakPointer< InfoSystemWorker >( new InfoSystemWorker() );
m_worker.data()->moveToThread( m_infoSystemWorkerThreadController );
m_infoSystemWorkerThreadController->setWorker( m_worker );
m_infoSystemWorkerThreadController->start();
QMetaObject::invokeMethod( m_worker.data(), "init", Qt::QueuedConnection, Q_ARG( QWeakPointer< Tomahawk::InfoSystem::InfoSystemCache >, m_cache ) );
@@ -89,7 +91,7 @@ InfoSystem::~InfoSystem()
m_infoSystemWorkerThreadController->quit();
m_infoSystemWorkerThreadController->wait( 60000 );
delete m_worker.data();
//delete m_worker.data();
delete m_infoSystemWorkerThreadController;
m_infoSystemWorkerThreadController = 0;
}
@@ -100,7 +102,7 @@ InfoSystem::~InfoSystem()
m_infoSystemCacheThreadController->quit();
m_infoSystemCacheThreadController->wait( 60000 );
delete m_cache.data();
//delete m_cache.data();
delete m_infoSystemCacheThreadController;
m_infoSystemCacheThreadController = 0;
}
@@ -118,15 +120,15 @@ InfoSystem::newNam() const
void
InfoSystem::getInfo( const InfoRequestData &requestData, uint timeoutMillis )
InfoSystem::getInfo( const InfoRequestData &requestData, uint timeoutMillis, bool allSources )
{
qDebug() << Q_FUNC_INFO;
QMetaObject::invokeMethod( m_worker.data(), "getInfo", Qt::QueuedConnection, Q_ARG( Tomahawk::InfoSystem::InfoRequestData, requestData ), Q_ARG( uint, timeoutMillis ) );
QMetaObject::invokeMethod( m_worker.data(), "getInfo", Qt::QueuedConnection, Q_ARG( Tomahawk::InfoSystem::InfoRequestData, requestData ), Q_ARG( uint, timeoutMillis ), Q_ARG( bool, allSources ) );
}
void
InfoSystem::getInfo( const QString &caller, const InfoTypeMap &inputMap, const QVariantMap &customData, const InfoTimeoutMap &timeoutMap )
InfoSystem::getInfo( const QString &caller, const InfoTypeMap &inputMap, const QVariantMap &customData, const InfoTimeoutMap &timeoutMap, bool allSources )
{
InfoRequestData requestData;
requestData.caller = caller;
@@ -135,7 +137,7 @@ InfoSystem::getInfo( const QString &caller, const InfoTypeMap &inputMap, const Q
{
requestData.type = type;
requestData.input = inputMap[ type ];
QMetaObject::invokeMethod( m_worker.data(), "getInfo", Qt::QueuedConnection, Q_ARG( Tomahawk::InfoSystem::InfoRequestData, requestData ), Q_ARG( uint, ( timeoutMap.contains( type ) ? timeoutMap[ type ] : 0 ) ) );
QMetaObject::invokeMethod( m_worker.data(), "getInfo", Qt::QueuedConnection, Q_ARG( Tomahawk::InfoSystem::InfoRequestData, requestData ), Q_ARG( uint, ( timeoutMap.contains( type ) ? timeoutMap[ type ] : 0 ) ), Q_ARG( bool, allSources ) );
}
}
@@ -155,6 +157,65 @@ InfoSystem::pushInfo( const QString &caller, const InfoTypeMap &input )
QMetaObject::invokeMethod( m_worker.data(), "pushInfo", Qt::QueuedConnection, Q_ARG( QString, caller ), Q_ARG( Tomahawk::InfoSystem::InfoType, type ), Q_ARG( QVariant, input[ type ] ) );
}
InfoSystemCacheThread::InfoSystemCacheThread( QObject *parent )
: QThread( parent )
{
}
InfoSystemCacheThread::~InfoSystemCacheThread()
{
delete m_cache.data();
}
void
InfoSystemCacheThread::InfoSystemCacheThread::run()
{
exec();
}
QWeakPointer< InfoSystemCache >
InfoSystemCacheThread::cache() const
{
return m_cache;
}
void
InfoSystemCacheThread::setCache( QWeakPointer< InfoSystemCache > cache )
{
m_cache = cache;
}
InfoSystemWorkerThread::InfoSystemWorkerThread( QObject *parent )
: QThread( parent )
{
}
InfoSystemWorkerThread::~InfoSystemWorkerThread()
{
}
void
InfoSystemWorkerThread::InfoSystemWorkerThread::run()
{
exec();
if( m_worker )
delete m_worker.data();
}
QWeakPointer< InfoSystemWorker >
InfoSystemWorkerThread::worker() const
{
return m_worker;
}
void
InfoSystemWorkerThread::setWorker( QWeakPointer< InfoSystemWorker > worker )
{
m_worker = worker;
}
} //namespace InfoSystem
} //namespace Tomahawk

View File

@@ -92,6 +92,18 @@ enum InfoType { // as items are saved in cache, mark them here to not change the
InfoAlbumComposer = 48,
InfoAlbumSongs = 49,
/** \var Tomahawk::InfoSystem::InfoType Tomahawk::InfoSystem::InfoType::InfoChartCapabilities
* Documentation for InfoChartCapabilities
*
* Clients of this InfoType expect a QVariant
*
*/
InfoChartCapabilities = 50,
/**
* Documentation for InfoChartArtists
*/
InfoChart = 51,
InfoMiscTopHotttness = 60,
InfoMiscTopTerms = 61,
@@ -103,7 +115,6 @@ enum InfoType { // as items are saved in cache, mark them here to not change the
InfoNowResumed = 82,
InfoNowStopped = 83,
InfoLove = 90,
InfoUnLove = 91,
@@ -119,6 +130,23 @@ struct InfoRequestData {
QVariantMap customData;
};
struct ArtistTrackPair {
QString artist;
QString track;
};
struct Chart {
Chart(){}
Chart(const QString _id, const QString _label, const QString _type) {
id = _id;
label = _label;
type = _type;
}
QString id;
QString label;
QString type;
};
typedef QMap< InfoType, QVariant > InfoTypeMap;
typedef QMap< InfoType, uint > InfoTimeoutMap;
typedef QMap< QString, QMap< QString, QString > > InfoGenericMap;
@@ -160,6 +188,38 @@ private:
typedef QWeakPointer< InfoPlugin > InfoPluginPtr;
class InfoSystemCacheThread : public QThread
{
Q_OBJECT
public:
InfoSystemCacheThread( QObject *parent );
virtual ~InfoSystemCacheThread();
void run();
QWeakPointer< InfoSystemCache > cache() const;
void setCache( QWeakPointer< InfoSystemCache > cache );
private:
QWeakPointer< InfoSystemCache > m_cache;
};
class InfoSystemWorkerThread : public QThread
{
Q_OBJECT
public:
InfoSystemWorkerThread( QObject *parent );
virtual ~InfoSystemWorkerThread();
void run();
QWeakPointer< InfoSystemWorker > worker() const;
void setWorker( QWeakPointer< InfoSystemWorker > worker );
private:
QWeakPointer< InfoSystemWorker > m_worker;
};
class DLLEXPORT InfoSystem : public QObject
{
Q_OBJECT
@@ -170,9 +230,9 @@ public:
InfoSystem( QObject *parent );
~InfoSystem();
void getInfo( const InfoRequestData &requestData, uint timeoutMillis = 0 );
void getInfo( const InfoRequestData &requestData, uint timeoutMillis = 0, bool allSources = false );
//WARNING: if changing timeoutMillis above, also change in below function in .cpp file
void getInfo( const QString &caller, const InfoTypeMap &inputMap, const QVariantMap &customData, const InfoTimeoutMap &timeoutMap = InfoTimeoutMap() );
void getInfo( const QString &caller, const InfoTypeMap &inputMap, const QVariantMap &customData, const InfoTimeoutMap &timeoutMap = InfoTimeoutMap(), bool allSources = false );
void pushInfo( const QString &caller, const InfoType type, const QVariant &input );
void pushInfo( const QString &caller, const InfoTypeMap &input );
@@ -186,8 +246,8 @@ public slots:
private:
QWeakPointer< InfoSystemCache > m_cache;
QWeakPointer< InfoSystemWorker > m_worker;
QThread* m_infoSystemCacheThreadController;
QThread* m_infoSystemWorkerThreadController;
InfoSystemCacheThread* m_infoSystemCacheThreadController;
InfoSystemWorkerThread* m_infoSystemWorkerThreadController;
static InfoSystem* s_instance;
};
@@ -196,6 +256,8 @@ private:
}
inline uint qHash( Tomahawk::InfoSystem::InfoCriteriaHash hash )
{
QCryptographicHash md5( QCryptographicHash::Md5 );
@@ -221,5 +283,9 @@ Q_DECLARE_METATYPE( Tomahawk::InfoSystem::InfoRequestData );
Q_DECLARE_METATYPE( Tomahawk::InfoSystem::InfoGenericMap );
Q_DECLARE_METATYPE( Tomahawk::InfoSystem::InfoCriteriaHash );
Q_DECLARE_METATYPE( QWeakPointer< Tomahawk::InfoSystem::InfoSystemCache > );
Q_DECLARE_METATYPE( Tomahawk::InfoSystem::ArtistTrackPair );
Q_DECLARE_METATYPE( Tomahawk::InfoSystem::Chart );
Q_DECLARE_METATYPE( QList<Tomahawk::InfoSystem::ArtistTrackPair> );
Q_DECLARE_METATYPE( QList<Tomahawk::InfoSystem::Chart> );
#endif // TOMAHAWK_INFOSYSTEM_H

View File

@@ -35,6 +35,7 @@
#endif
#ifdef Q_WS_X11
#include "infoplugins/unix/fdonotifyplugin.h"
#include "infoplugins/unix/mprisplugin.h"
#endif
#include "lastfm/NetworkAccessManager"
@@ -97,6 +98,9 @@ InfoSystemWorker::init( QWeakPointer< Tomahawk::InfoSystem::InfoSystemCache> cac
InfoPluginPtr fdonotifyptr( new FdoNotifyPlugin() );
m_plugins.append( fdonotifyptr );
registerInfoTypes( fdonotifyptr, fdonotifyptr.data()->supportedGetTypes(), fdonotifyptr.data()->supportedPushTypes() );
InfoPluginPtr mprisptr( new MprisPlugin() );
m_plugins.append( mprisptr );
registerInfoTypes( mprisptr, mprisptr.data()->supportedGetTypes(), mprisptr.data()->supportedPushTypes() );
#endif
Q_FOREACH( InfoPluginPtr plugin, m_plugins )
@@ -149,12 +153,12 @@ InfoSystemWorker::registerInfoTypes( const InfoPluginPtr &plugin, const QSet< In
}
QLinkedList< InfoPluginPtr >
QList< InfoPluginPtr >
InfoSystemWorker::determineOrderedMatches( const InfoType type ) const
{
//Dummy function for now that returns the various items in the QSet; at some point this will
//probably need to support ordering based on the data source
QLinkedList< InfoPluginPtr > providers;
QList< InfoPluginPtr > providers;
Q_FOREACH( InfoPluginPtr ptr, m_infoGetMap[type] )
providers << ptr;
return providers;
@@ -162,11 +166,11 @@ InfoSystemWorker::determineOrderedMatches( const InfoType type ) const
void
InfoSystemWorker::getInfo( Tomahawk::InfoSystem::InfoRequestData requestData, uint timeoutMillis )
InfoSystemWorker::getInfo( Tomahawk::InfoSystem::InfoRequestData requestData, uint timeoutMillis, bool allSources )
{
// qDebug() << Q_FUNC_INFO;
QLinkedList< InfoPluginPtr > providers = determineOrderedMatches( requestData.type );
QList< InfoPluginPtr > providers = determineOrderedMatches( requestData.type );
if ( providers.isEmpty() )
{
emit info( requestData, QVariant() );
@@ -174,33 +178,42 @@ InfoSystemWorker::getInfo( Tomahawk::InfoSystem::InfoRequestData requestData, ui
return;
}
InfoPluginPtr ptr = providers.first();
if ( !ptr )
if ( !allSources )
providers = QList< InfoPluginPtr >( providers.mid( 0, 1 ) );
bool foundOne = false;
foreach ( InfoPluginPtr ptr, providers )
{
if ( !ptr )
continue;
foundOne = true;
uint requestId = ++m_nextRequest;
m_requestSatisfiedMap[ requestId ] = false;
if ( timeoutMillis != 0 )
{
qint64 currMs = QDateTime::currentMSecsSinceEpoch();
m_timeRequestMapper.insert( currMs + timeoutMillis, requestId );
}
// qDebug() << "Assigning request with requestId" << requestId << "and type" << requestData.type;
m_dataTracker[ requestData.caller ][ requestData.type ] = m_dataTracker[ requestData.caller ][ requestData.type ] + 1;
// qDebug() << "Current count in dataTracker for target" << requestData.caller << "and type" << requestData.type << "is" << m_dataTracker[ requestData.caller ][ requestData.type ];
InfoRequestData* data = new InfoRequestData;
data->caller = requestData.caller;
data->type = requestData.type;
data->input = requestData.input;
data->customData = requestData.customData;
m_savedRequestMap[ requestId ] = data;
QMetaObject::invokeMethod( ptr.data(), "getInfo", Qt::QueuedConnection, Q_ARG( uint, requestId ), Q_ARG( Tomahawk::InfoSystem::InfoRequestData, requestData ) );
}
if ( !foundOne )
{
emit info( requestData, QVariant() );
checkFinished( requestData.caller );
return;
}
uint requestId = ++m_nextRequest;
m_requestSatisfiedMap[ requestId ] = false;
if ( timeoutMillis != 0 )
{
qint64 currMs = QDateTime::currentMSecsSinceEpoch();
m_timeRequestMapper.insert( currMs + timeoutMillis, requestId );
}
// qDebug() << "Assigning request with requestId" << requestId << "and type" << requestData.type;
m_dataTracker[ requestData.caller ][ requestData.type ] = m_dataTracker[ requestData.caller ][ requestData.type ] + 1;
// qDebug() << "Current count in dataTracker for target" << requestData.caller << "and type" << requestData.type << "is" << m_dataTracker[ requestData.caller ][ requestData.type ];
InfoRequestData* data = new InfoRequestData;
data->caller = requestData.caller;
data->type = requestData.type;
data->input = requestData.input;
data->customData = requestData.customData;
m_savedRequestMap[ requestId ] = data;
QMetaObject::invokeMethod( ptr.data(), "getInfo", Qt::QueuedConnection, Q_ARG( uint, requestId ), Q_ARG( Tomahawk::InfoSystem::InfoRequestData, requestData ) );
}

View File

@@ -21,13 +21,13 @@
#include "infosystem/infosystem.h"
#include <QNetworkAccessManager>
#include <QtNetwork/QNetworkAccessManager>
#include <QtCore/QObject>
#include <QtCore/QtDebug>
#include <QtCore/QMap>
#include <QtCore/QWeakPointer>
#include <QtCore/QSet>
#include <QtCore/QLinkedList>
#include <QtCore/QList>
#include <QtCore/QVariant>
#include <QtCore/QTimer>
@@ -57,7 +57,7 @@ signals:
public slots:
void init( QWeakPointer< Tomahawk::InfoSystem::InfoSystemCache > cache );
void getInfo( Tomahawk::InfoSystem::InfoRequestData requestData, uint timeoutMillis );
void getInfo( Tomahawk::InfoSystem::InfoRequestData requestData, uint timeoutMillis, bool allSources );
void pushInfo( QString caller, Tomahawk::InfoSystem::InfoType type, QVariant input );
void infoSlot( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData, QVariant output );
@@ -76,13 +76,13 @@ private:
QHash< uint, bool > m_requestSatisfiedMap;
QHash< uint, InfoRequestData* > m_savedRequestMap;
QLinkedList< InfoPluginPtr > determineOrderedMatches( const InfoType type ) const;
QList< InfoPluginPtr > determineOrderedMatches( const InfoType type ) const;
// For now, statically instantiate plugins; this is just somewhere to keep them
QLinkedList< InfoPluginPtr > m_plugins;
QList< InfoPluginPtr > m_plugins;
QMap< InfoType, QLinkedList< InfoPluginPtr > > m_infoGetMap;
QMap< InfoType, QLinkedList< InfoPluginPtr > > m_infoPushMap;
QMap< InfoType, QList< InfoPluginPtr > > m_infoGetMap;
QMap< InfoType, QList< InfoPluginPtr > > m_infoPushMap;
QWeakPointer< QNetworkAccessManager> m_nam;

View File

@@ -134,27 +134,23 @@ DBSyncConnection::check()
}
m_uscache.clear();
m_themcache.clear();
m_us.clear();
changeState( CHECKING );
// load last-modified etc data for our collection and theirs from our DB:
DatabaseCommand_CollectionStats* cmd_us =
new DatabaseCommand_CollectionStats( SourceList::instance()->getLocal() );
DatabaseCommand_CollectionStats* cmd_them =
new DatabaseCommand_CollectionStats( m_source );
connect( cmd_us, SIGNAL( done( QVariantMap ) ),
SLOT( gotUs( QVariantMap ) ) );
connect( cmd_them, SIGNAL( done( QVariantMap ) ),
SLOT( gotThemCache( QVariantMap ) ) );
DatabaseCommand_CollectionStats* cmd_us = new DatabaseCommand_CollectionStats( SourceList::instance()->getLocal() );
connect( cmd_us, SIGNAL( done( QVariantMap ) ), SLOT( gotUs( QVariantMap ) ) );
Database::instance()->enqueue( QSharedPointer<DatabaseCommand>(cmd_us) );
Database::instance()->enqueue( QSharedPointer<DatabaseCommand>(cmd_them) );
if ( m_lastop.isEmpty() )
{
DatabaseCommand_CollectionStats* cmd_them = new DatabaseCommand_CollectionStats( m_source );
connect( cmd_them, SIGNAL( done( QVariantMap ) ), SLOT( gotThem( QVariantMap ) ) );
Database::instance()->enqueue( QSharedPointer<DatabaseCommand>(cmd_them) );
}
else
fetchOpsData( m_lastop );
// restarts idle countdown
m_timer.start();
@@ -174,16 +170,24 @@ DBSyncConnection::gotUs( const QVariantMap& m )
/// Called once we've loaded our cached data about their collection
void
DBSyncConnection::gotThemCache( const QVariantMap& m )
DBSyncConnection::gotThem( const QVariantMap& m )
{
m_lastop = m.value( "lastop" ).toString();
fetchOpsData( m_lastop );
}
void
DBSyncConnection::fetchOpsData( const QString& sinceguid )
{
m_themcache = m;
changeState( FETCHING );
tLog() << "Sending a FETCHOPS cmd since:" << m_themcache.value( "lastop" ).toString();
tLog() << "Sending a FETCHOPS cmd since:" << sinceguid;
QVariantMap msg;
msg.insert( "method", "fetchops" );
msg.insert( "lastop", m_themcache.value( "lastop" ).toString() );
msg.insert( "lastop", sinceguid );
sendMsg( msg );
}
@@ -234,8 +238,7 @@ DBSyncConnection::handleMsg( msg_ptr msg )
lastOpApplied();
return;
}
// qDebug() << "APPLYING CMD" << cmd->commandname() << cmd->guid();
QSharedPointer<DatabaseCommand> cmdsp = QSharedPointer<DatabaseCommand>(cmd);
if ( !msg->is( Msg::FRAGMENT ) ) // last msg in this batch
{
@@ -243,10 +246,21 @@ DBSyncConnection::handleMsg( msg_ptr msg )
connect( cmd, SIGNAL( finished() ), SLOT( lastOpApplied() ) );
}
if ( !cmd->singletonCmd() )
m_source->setLastOpGuid( cmd->guid() );
if ( m_recentTempOps.contains( cmd->guid() ) )
{
qDebug() << "Ignoring dupe temporary command:" << cmd->guid();
return;
}
Database::instance()->enqueue( QSharedPointer<DatabaseCommand>( cmd ) );
if ( !cmd->singletonCmd() )
{
m_lastop = cmd->guid();
m_recentTempOps.clear();
}
else
m_recentTempOps << cmd->guid();
Database::instance()->enqueue( cmdsp );
return;
}
@@ -283,7 +297,7 @@ DBSyncConnection::lastOpApplied()
void
DBSyncConnection::sendOps()
{
tLog() << "Will send peer all ops since" << m_uscache.value( "lastop" ).toString();
tLog() << "Will send peer" << m_source->id() << "all ops since" << m_uscache.value( "lastop" ).toString();
source_ptr src = SourceList::instance()->getLocal();

View File

@@ -64,9 +64,13 @@ public slots:
private slots:
void gotUs( const QVariantMap& m );
void gotThemCache( const QVariantMap& m );
void lastOpApplied();
void gotThem( const QVariantMap& m );
void fetchOpsData( const QString& sinceguid );
void sendOpsData( QString sinceguid, QString lastguid, QList< dbop_ptr > ops );
void lastOpApplied();
void check();
void idleTimeout();
@@ -76,11 +80,13 @@ private:
void changeState( State newstate );
Tomahawk::source_ptr m_source;
QVariantMap m_us, m_uscache, m_themcache;
State m_state;
QVariantMap m_us, m_uscache;
QString m_lastop;
QString m_lastSentOp;
QStringList m_recentTempOps;
State m_state;
QTimer m_timer;
};

View File

@@ -60,6 +60,7 @@ Servent::Servent( QObject* parent )
: QTcpServer( parent )
, m_port( 0 )
, m_externalPort( 0 )
, m_ready( false )
, m_portfwd( 0 )
{
s_instance = this;
@@ -133,6 +134,7 @@ Servent::startListening( QHostAddress ha, bool upnp, int port )
tLog() << "Forcing static preferred host and port";
m_externalHostname = TomahawkSettings::instance()->externalHostname();
m_externalPort = TomahawkSettings::instance()->externalPort();
m_ready = true;
emit ready();
return true;
}
@@ -151,11 +153,14 @@ Servent::startListening( QHostAddress ha, bool upnp, int port )
if ( qApp->arguments().contains( "--lanhack" ) )
{
qDebug() << "LANHACK: set external address to lan address" << ha.toString();
tLog() << "LANHACK: set external address to lan address" << ha.toString();
QMetaObject::invokeMethod( this, "setExternalAddress", Qt::QueuedConnection, Q_ARG( QHostAddress, ha ), Q_ARG( unsigned int, m_port ) );
}
else
{
m_ready = true;
emit ready();
}
break;
}
break;
@@ -186,7 +191,7 @@ Servent::createConnectionKey( const QString& name, const QString &nodeid, const
cc->setId( nodeid );
cc->setOnceOnly( onceOnly );
qDebug() << "Creating connection key with name of " << cc->name() << " and id of " << cc->id() << " and key of " << _key << "; key is once only? : " << (onceOnly ? "true" : "false");
qDebug() << "Creating connection key with name of" << cc->name() << "and id of" << cc->id() << "and key of" << _key << "; key is once only? :" << (onceOnly ? "true" : "false");
registerOffer( _key, cc );
return _key;
}
@@ -224,9 +229,10 @@ Servent::setExternalAddress( QHostAddress ha, unsigned int port )
qDebug() << m_externalHostname << m_externalPort;
}
else
qDebug() << "No external access, LAN and outbound connections only!";
tLog() << "No external access, LAN and outbound connections only!";
}
m_ready = true;
emit ready();
}
@@ -348,7 +354,7 @@ Servent::readyRead()
// qDebug() << con->socket() << sock;
if( con->id() == nodeid )
{
qDebug() << "Duplicate control connection detected, dropping:" << nodeid << conntype;
tLog() << "Duplicate control connection detected, dropping:" << nodeid << conntype;
goto closeconnection;
}
}
@@ -372,7 +378,7 @@ Servent::readyRead()
Connection* conn = claimOffer( cc, nodeid, key, sock->peerAddress() );
if( !conn )
{
qDebug() << "claimOffer FAILED, key:" << key;
tLog() << "claimOffer FAILED, key:" << key;
goto closeconnection;
}
qDebug() << "claimOffer OK:" << key;
@@ -390,7 +396,7 @@ Servent::readyRead()
// fallthru to cleanup:
closeconnection:
qDebug() << "Closing incoming connection, something was wrong.";
tLog() << "Closing incoming connection, something was wrong.";
sock->_msg.clear();
sock->disconnectFromHost();
}
@@ -405,7 +411,7 @@ Servent::createParallelConnection( Connection* orig_conn, Connection* new_conn,
// if we can connect to them directly:
if( orig_conn && orig_conn->outbound() )
{
qDebug() << "Connecting directly";
tLog() << "Connecting directly";
connectToPeer( orig_conn->socket()->peerAddress().toString(),
orig_conn->peerPort(),
key,
@@ -414,7 +420,7 @@ Servent::createParallelConnection( Connection* orig_conn, Connection* new_conn,
else // ask them to connect to us:
{
QString tmpkey = uuid();
qDebug() << "Asking them to connect to us using" << tmpkey ;
tLog() << "Asking them to connect to us using" << tmpkey ;
registerOffer( tmpkey, new_conn );
QVariantMap m;
@@ -470,12 +476,18 @@ Servent::socketError( QAbstractSocket::SocketError e )
QTcpSocketExtra* sock = (QTcpSocketExtra*)sender();
if( !sock )
{
qDebug() << "SocketError, sock is null";
tLog() << "SocketError, sock is null";
return;
}
Connection* conn = sock->_conn;
qDebug() << "Servent::SocketError:" << e << conn->id() << conn->name();
if ( !conn )
{
tLog() << "SocketError, connection is null";
return;
}
tLog() << "Servent::SocketError:" << e << conn->id() << conn->name();
if( !sock->_disowned )
{
// connection will delete if we already transferred ownership, otherwise:
@@ -601,7 +613,7 @@ Servent::claimOffer( ControlConnection* cc, const QString &nodeid, const QString
}
if( !authed )
{
qDebug() << "File transfer request rejected, invalid source IP";
tLog() << "File transfer request rejected, invalid source IP";
return NULL;
}
}
@@ -635,7 +647,7 @@ Servent::claimOffer( ControlConnection* cc, const QString &nodeid, const QString
// This can happen if it's a streamconnection, but the audioengine has
// already closed the iodevice, causing the connection to be deleted before
// the peer connects and provides the first byte
qDebug() << Q_FUNC_INFO << "invalid/expired offer:" << key;
tLog() << Q_FUNC_INFO << "invalid/expired offer:" << key;
return NULL;
}
@@ -644,12 +656,12 @@ Servent::claimOffer( ControlConnection* cc, const QString &nodeid, const QString
// If there isn't a nodeid it's not the first connection and will already have been stopped
if( !checkACL( conn, nodeid, true ) )
{
qDebug() << "Connection not allowed due to ACL";
tLog() << "Connection not allowed due to ACL";
return NULL;
}
}
qDebug() << "ACL has allowed the connection";
tLog() << "ACL has allowed the connection";
if( conn->onceOnly() )
{
@@ -670,7 +682,7 @@ Servent::claimOffer( ControlConnection* cc, const QString &nodeid, const QString
}
else
{
qDebug() << "Invalid offer key:" << key;
tLog() << "Invalid offer key:" << key;
return NULL;
}
}
@@ -823,7 +835,6 @@ Servent::isIPWhitelisted( QHostAddress ip )
bool
Servent::connectedToSession( const QString& session )
{
qDebug() << Q_FUNC_INFO;
// qDebug() << "Checking against" << session;
foreach( ControlConnection* cc, m_controlconnections )
{
@@ -839,8 +850,6 @@ Servent::connectedToSession( const QString& session )
void
Servent::triggerDBSync()
{
qDebug() << Q_FUNC_INFO;
// tell peers we have new stuff they should sync
QList<source_ptr> sources = SourceList::instance()->sources();
foreach( const source_ptr& src, sources )
@@ -866,7 +875,6 @@ Servent::registerIODeviceFactory( const QString &proto, boost::function<QSharedP
QSharedPointer<QIODevice>
Servent::getIODeviceForUrl( const Tomahawk::result_ptr& result )
{
qDebug() << Q_FUNC_INFO << thread();
QSharedPointer<QIODevice> sp;
QRegExp rx( "^([a-zA-Z0-9]+)://(.+)$" );
@@ -897,7 +905,6 @@ Servent::localFileIODeviceFactory( const Tomahawk::result_ptr& result )
QSharedPointer<QIODevice>
Servent::httpIODeviceFactory( const Tomahawk::result_ptr& result )
{
qDebug() << Q_FUNC_INFO << result->url();
QNetworkRequest req( result->url() );
QNetworkReply* reply = TomahawkUtils::nam()->get( req );
return QSharedPointer<QIODevice>( reply, &QObject::deleteLater );

View File

@@ -1,5 +1,5 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
*
* Copyright 2010-2011, Christian Muehlhaeuser <muesli@tomahawk-player.org>
*
* Tomahawk is free software: you can redistribute it and/or modify
@@ -123,6 +123,8 @@ public:
QSharedPointer<QIODevice> localFileIODeviceFactory( const Tomahawk::result_ptr& result );
QSharedPointer<QIODevice> httpIODeviceFactory( const Tomahawk::result_ptr& result );
bool isReady() const { return m_ready; };
signals:
void streamStarted( StreamConnection* );
void streamFinished( StreamConnection* );
@@ -159,6 +161,7 @@ private:
int m_port, m_externalPort;
QHostAddress m_externalAddress;
QString m_externalHostname;
bool m_ready;
// currently active file transfers:
QList< StreamConnection* > m_scsessions;

View File

@@ -116,11 +116,13 @@ Pipeline::resolve( const QList<query_ptr>& qlist, bool prioritized, bool tempora
int i = 0;
foreach( const query_ptr& q, qlist )
{
if ( !m_qids.contains( q->id() ) )
m_qids.insert( q->id(), q );
if ( m_queries_pending.contains( q ) )
continue;
if ( m_qidsState.contains( q->id() ) )
continue;
if ( !m_qids.contains( q->id() ) )
m_qids.insert( q->id(), q );
if ( prioritized )
m_queries_pending.insert( i++, q );
@@ -169,8 +171,7 @@ Pipeline::reportResults( QID qid, const QList< result_ptr >& results )
if ( !m_qids.contains( qid ) )
{
qDebug() << "reportResults called for unknown QID" << qid;
Q_ASSERT( false );
tDebug() << "Result arrived too late for:" << qid;
return;
}
@@ -183,35 +184,14 @@ Pipeline::reportResults( QID qid, const QList< result_ptr >& results )
m_rids.insert( r->id(), r );
}
if ( q->solved() && !q->isFullTextQuery() )
if ( q->playable() && !q->isFullTextQuery() )
{
q->onResolvingFinished();
setQIDState( q, 0 );
if ( m_qidsTimeout.contains( q->id() ) )
m_qidsTimeout.remove( q->id() );
shuntNext();
return;
}
}
if ( decQIDState( q ) == 0 )
{
if ( !q->solved() || q->isFullTextQuery() )
q->onResolvingFinished();
if ( !m_queries_temporary.contains( q ) )
m_qids.remove( q->id() );
if ( m_qidsTimeout.contains( q->id() ) )
m_qidsTimeout.remove( q->id() );
shuntNext();
}
else
{
new FuncTimeout( 0, boost::bind( &Pipeline::timeoutShunt, this, q ), this );
}
decQIDState( q );
}
@@ -247,7 +227,6 @@ Pipeline::shuntNext()
}
setQIDState( q, rc );
new FuncTimeout( 0, boost::bind( &Pipeline::shunt, this, q ), this );
}
@@ -260,8 +239,7 @@ Pipeline::timeoutShunt( const query_ptr& q )
// are we still waiting for a timeout?
if ( m_qidsTimeout.contains( q->id() ) )
{
m_qidsTimeout.remove( q->id() );
shunt( q );
decQIDState( q );
}
}
@@ -278,7 +256,7 @@ Pipeline::shunt( const query_ptr& q )
if ( r )
{
qDebug() << "Dispatching to resolver" << r->name() << q->toString() << q->solved() << q->id();
tDebug() << "Dispatching to resolver" << r->name() << q->toString() << q->solved() << q->id();
q->setCurrentResolver( r );
r->resolve( q );
@@ -291,7 +269,9 @@ Pipeline::shunt( const query_ptr& q )
}
else
{
// we get here if we disable a resolver while a query is resolving
setQIDState( q, 0 );
return;
}
shuntNext();
@@ -327,14 +307,24 @@ Pipeline::setQIDState( const Tomahawk::query_ptr& query, int state )
{
QMutexLocker lock( &m_mut );
if ( m_qidsTimeout.contains( query->id() ) )
m_qidsTimeout.remove( query->id() );
if ( state > 0 )
{
m_qidsState.insert( query->id(), state );
new FuncTimeout( 0, boost::bind( &Pipeline::shunt, this, query ), this );
}
else
{
m_qidsState.remove( query->id() );
// qDebug() << "Queries running:" << m_qidsState.count();
query->onResolvingFinished();
if ( !m_queries_temporary.contains( query ) )
m_qids.remove( query->id() );
new FuncTimeout( 0, boost::bind( &Pipeline::shuntNext, this ), this );
}
}
@@ -358,22 +348,17 @@ Pipeline::incQIDState( const Tomahawk::query_ptr& query )
int
Pipeline::decQIDState( const Tomahawk::query_ptr& query )
{
QMutexLocker lock( &m_mut );
if ( !m_qidsState.contains( query->id() ) )
return 0;
int state = m_qidsState.value( query->id() ) - 1;
if ( state )
int state = 0;
{
m_qidsState.insert( query->id(), state );
}
else
{
m_qidsState.remove( query->id() );
// qDebug() << "Queries running:" << m_qidsState.count();
QMutexLocker lock( &m_mut );
if ( !m_qidsState.contains( query->id() ) )
return 0;
state = m_qidsState.value( query->id() ) - 1;
}
setQIDState( query, state );
return state;
}

View File

@@ -119,7 +119,7 @@ Playlist::Playlist( const source_ptr& src,
, m_currentItem( 0 )
, m_busy( false )
{
// qDebug() << Q_FUNC_INFO << "1";
qDebug() << Q_FUNC_INFO << "1" << title;
init();
}
@@ -144,7 +144,7 @@ Playlist::Playlist( const source_ptr& author,
, m_initEntries( entries )
, m_busy( false )
{
// qDebug() << Q_FUNC_INFO << "2";
qDebug() << Q_FUNC_INFO << "2" << title;
init();
}
@@ -398,12 +398,6 @@ Playlist::setNewRevision( const QString& rev,
QList<plentry_ptr> entries;
foreach( const QString& id, neworderedguids )
{
/* qDebug() << "id:" << id;
qDebug() << "newordered:" << neworderedguids.count() << neworderedguids;
qDebug() << "entriesmap:" << entriesmap.count() << entriesmap;
qDebug() << "addedmap:" << addedmap.count() << addedmap;
qDebug() << "m_entries" << m_entries; */
if( entriesmap.contains( id ) )
{
entries.append( entriesmap.value( id ) );
@@ -416,6 +410,13 @@ Playlist::setNewRevision( const QString& rev,
}
else
{
/* qDebug() << "id:" << id;
* qDebug() << "newordered:" << neworderedguids.count() << neworderedguids;
* qDebug() << "entriesmap:" << entriesmap.count() << entriesmap;
* qDebug() << "addedmap:" << addedmap.count() << addedmap;
* qDebug() << "m_entries" << m_entries; */
tLog() << "Playlist error for playlist with guid" << guid() << "from source" << author()->friendlyName();
Q_ASSERT( false ); // XXX
}
}

View File

@@ -33,6 +33,7 @@
#include <QQueue>
class DatabaseCommand_LoadAllPlaylists;
class DatabaseCommand_LoadAllSortedPlaylists;
class DatabaseCommand_SetPlaylistRevision;
class DatabaseCommand_CreatePlaylist;
@@ -124,6 +125,7 @@ Q_PROPERTY( unsigned int createdon READ createdOn WRITE setCreatedOn )
Q_PROPERTY( bool shared READ shared WRITE setShared )
friend class ::DatabaseCommand_LoadAllPlaylists;
friend class ::DatabaseCommand_LoadAllSortedPlaylists;
friend class ::DatabaseCommand_SetPlaylistRevision;
friend class ::DatabaseCommand_CreatePlaylist;
friend class DynamicPlaylist;

View File

@@ -0,0 +1,143 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2010-2011, 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/>.
*/
#include "ViewHeader.h"
#include <QContextMenuEvent>
#include <QMenu>
#include "tomahawksettings.h"
#include "utils/logger.h"
ViewHeader::ViewHeader( QAbstractItemView* parent )
: QHeaderView( Qt::Horizontal, parent )
, m_parent( parent )
, m_menu( new QMenu( this ) )
, m_sigmap( new QSignalMapper( this ) )
, m_init( false )
{
setResizeMode( QHeaderView::Interactive );
setMinimumSectionSize( 60 );
setDefaultAlignment( Qt::AlignLeft );
setMovable( true );
setStretchLastSection( true );
// m_menu->addAction( tr( "Resize columns to fit window" ), this, SLOT( onToggleResizeColumns() ) );
// m_menu->addSeparator();
connect( m_sigmap, SIGNAL( mapped( int ) ), SLOT( toggleVisibility( int ) ) );
}
ViewHeader::~ViewHeader()
{
}
int
ViewHeader::visibleSectionCount() const
{
return count() - hiddenSectionCount();
}
void
ViewHeader::onSectionsChanged()
{
TomahawkSettings::instance()->setPlaylistColumnSizes( m_guid, saveState() );
}
bool
ViewHeader::checkState()
{
if ( !count() || m_init )
return false;
QByteArray state = TomahawkSettings::instance()->playlistColumnSizes( m_guid );
if ( !state.isEmpty() )
{
restoreState( state );
if ( m_guid.startsWith( "playlistview" ) ) // HACK
setSortIndicator( -1, Qt::AscendingOrder );
}
else
{
for ( int i = 0; i < count() - 1; i++ )
{
if ( isSectionHidden( i ) )
continue;
double nw = (double)m_parent->width() * m_columnWeights.at( i );
resizeSection( i, qMax( minimumSectionSize(), int( nw - 0.5 ) ) );
}
}
m_init = true;
connect( this, SIGNAL( sectionMoved( int, int, int ) ), SLOT( onSectionsChanged() ) );
connect( this, SIGNAL( sectionResized( int, int, int ) ), SLOT( onSectionsChanged() ) );
return true;
}
void
ViewHeader::addColumnToMenu( int index )
{
QString title = m_parent->model()->headerData( index, Qt::Horizontal, Qt::DisplayRole ).toString();
QAction* action = m_menu->addAction( title, m_sigmap, SLOT( map() ) );
action->setCheckable( true );
action->setChecked( !isSectionHidden( index ) );
m_visActions << action;
m_sigmap->setMapping( action, index );
}
void
ViewHeader::contextMenuEvent( QContextMenuEvent* e )
{
qDeleteAll( m_visActions );
m_visActions.clear();
for ( int i = 0; i < count(); i++ )
addColumnToMenu( i );
m_menu->popup( e->globalPos() );
}
void
ViewHeader::onToggleResizeColumns()
{
}
void
ViewHeader::toggleVisibility( int index )
{
qDebug() << Q_FUNC_INFO << index;
if ( isSectionHidden( index ) )
showSection( index );
else
hideSection( index );
}

View File

@@ -0,0 +1,65 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2010-2011, 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 VIEWHEADER_H
#define VIEWHEADER_H
#include <QHeaderView>
#include <QSignalMapper>
#include "dllmacro.h"
class DLLEXPORT ViewHeader : public QHeaderView
{
Q_OBJECT
public:
explicit ViewHeader( QAbstractItemView* parent = 0 );
~ViewHeader();
int visibleSectionCount() const;
void setDefaultColumnWeights( QList<double> weights ) { m_columnWeights = weights; }
QString guid() const { return m_guid; }
void setGuid( const QString& guid ) { m_guid = guid; }
public slots:
void toggleVisibility( int index );
bool checkState();
protected:
void contextMenuEvent( QContextMenuEvent* e );
private slots:
virtual void onSectionsChanged();
void onToggleResizeColumns();
private:
void addColumnToMenu( int index );
QAbstractItemView* m_parent;
QString m_guid;
QList<double> m_columnWeights;
QMenu* m_menu;
QSignalMapper* m_sigmap;
QList<QAction*> m_visActions;
bool m_init;
};
#endif

View File

@@ -35,6 +35,7 @@ using namespace Tomahawk;
AlbumModel::AlbumModel( QObject* parent )
: QAbstractItemModel( parent )
, m_rootItem( new AlbumItem( 0, this ) )
, m_overwriteOnAdd( false )
{
qDebug() << Q_FUNC_INFO;
@@ -195,12 +196,13 @@ AlbumModel::mimeData( const QModelIndexList &indexes ) const
if ( item )
{
const album_ptr& album = item->album();
queryStream << qlonglong( &album );
queryStream << album->artist()->name();
queryStream << album->name();
}
}
QMimeData* mimeData = new QMimeData();
mimeData->setData( "application/tomahawk.query.list", queryData );
mimeData->setData( "application/tomahawk.metadata.album", queryData );
return mimeData;
}
@@ -235,13 +237,14 @@ AlbumModel::removeIndexes( const QList<QModelIndex>& indexes )
void
AlbumModel::addCollection( const collection_ptr& collection )
AlbumModel::addCollection( const collection_ptr& collection, bool overwrite )
{
qDebug() << Q_FUNC_INFO << collection->name()
<< collection->source()->id()
<< collection->source()->userName();
DatabaseCommand_AllAlbums* cmd = new DatabaseCommand_AllAlbums( collection );
m_overwriteOnAdd = overwrite;
connect( cmd, SIGNAL( albums( QList<Tomahawk::album_ptr>, QVariant ) ),
SLOT( addAlbums( QList<Tomahawk::album_ptr> ) ) );
@@ -253,7 +256,7 @@ AlbumModel::addCollection( const collection_ptr& collection )
void
AlbumModel::addFilteredCollection( const collection_ptr& collection, unsigned int amount, DatabaseCommand_AllAlbums::SortOrder order )
AlbumModel::addFilteredCollection( const collection_ptr& collection, unsigned int amount, DatabaseCommand_AllAlbums::SortOrder order, bool overwrite )
{
/* qDebug() << Q_FUNC_INFO << collection->name()
<< collection->source()->id()
@@ -264,6 +267,7 @@ AlbumModel::addFilteredCollection( const collection_ptr& collection, unsigned in
cmd->setLimit( amount );
cmd->setSortOrder( order );
cmd->setSortDescending( true );
m_overwriteOnAdd = overwrite;
connect( cmd, SIGNAL( albums( QList<Tomahawk::album_ptr>, QVariant ) ),
SLOT( addAlbums( QList<Tomahawk::album_ptr> ) ) );
@@ -283,6 +287,9 @@ AlbumModel::addAlbums( const QList<Tomahawk::album_ptr>& albums )
if ( !albums.count() )
return;
if ( m_overwriteOnAdd )
clear();
int c = rowCount( QModelIndex() );
QPair< int, int > crows;
crows.first = c;
@@ -313,6 +320,30 @@ AlbumModel::clear()
}
bool
AlbumModel::getCover( const QModelIndex& index )
{
AlbumItem* item = itemFromIndex( index );
if ( !item || !item->cover.isNull() )
return false;
Tomahawk::InfoSystem::InfoCriteriaHash trackInfo;
trackInfo["artist"] = item->album()->artist()->name();
trackInfo["album"] = item->album()->name();
trackInfo["pptr"] = QString::number( (qlonglong)item );
m_coverHash.insert( (qlonglong)item, index );
Tomahawk::InfoSystem::InfoRequestData requestData;
requestData.caller = s_tmInfoIdentifier;
requestData.type = Tomahawk::InfoSystem::InfoAlbumCoverArt;
requestData.input = QVariant::fromValue< Tomahawk::InfoSystem::InfoCriteriaHash >( trackInfo );
requestData.customData = QVariantMap();
Tomahawk::InfoSystem::InfoSystem::instance()->getInfo( requestData );
return true;
}
void
AlbumModel::infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData requestData, QVariant output )
{
@@ -341,12 +372,15 @@ AlbumModel::infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData requestData, Q
bool ok;
qlonglong p = pptr["pptr"].toLongLong( &ok );
AlbumItem* ai = reinterpret_cast<AlbumItem*>(p);
AlbumItem* ai = itemFromIndex( m_coverHash.take( p ) );
if ( !ai )
return;
if ( !pm.isNull() )
ai->cover = pm;
emit dataChanged( ai->index, ai->index.sibling( ai->index.row(), columnCount( QModelIndex() ) - 1 ) );
if ( ai->index.isValid() )
emit dataChanged( ai->index, ai->index.sibling( ai->index.row(), columnCount( QModelIndex() ) - 1 ) );
}
}

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