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

Compare commits

...

152 Commits

Author SHA1 Message Date
Michael Zanetti
11ac159112 workaround weird graphics problem in OSX
SVG images must not have a size of 0 or it will smash the painting engine
2012-12-29 23:17:08 +01:00
Christian Muehlhaeuser
ea3417f2be * Compile fixes. 2012-12-29 19:08:42 +01:00
Christian Muehlhaeuser
98bf0bb23e * Merged master into qmlstation. 2012-12-29 18:57:13 +01:00
Christian Muehlhaeuser
c25370c168 * Disable 'Create new station' item in sidebar. 2012-12-29 18:48:10 +01:00
Christian Muehlhaeuser
d002448fd6 * Add radio item to SourcesModel. 2012-12-29 18:47:49 +01:00
Christian Muehlhaeuser
dc4035dd97 * Now we can have temporary dynamic playlists. 2012-12-29 18:46:46 +01:00
Christian Muehlhaeuser
edac0daa52 * Silly style cleanups. 2012-12-29 18:46:09 +01:00
Christian Muehlhaeuser
7fcbe72abb * Return CoverIDRole for artists and albums in a PlayableModel. 2012-12-29 18:44:41 +01:00
Christian Muehlhaeuser
a1148ece05 * Add Radio item to sidebar. 2012-12-29 18:43:55 +01:00
Michael Zanetti
2dbe43fe89 fixed the flickering white border issue when scaling CoverImages 2012-12-27 01:21:39 +01:00
Michael Zanetti
bdfca0f100 added a quick comparison between spinners 2012-12-20 13:01:28 +01:00
Michael Zanetti
db82e18469 some more improvements in the stationview and the busyindicator 2012-12-20 00:34:56 +01:00
Michael Zanetti
dde7501162 some look and feel improvements 2012-12-20 00:03:05 +01:00
Michael Zanetti
bf2ac533e0 disable scaling of tag cloud 2012-12-16 20:05:26 +01:00
Michael Zanetti
a92d7c0f9f warnings-- 2012-12-16 19:05:42 +01:00
Jason Herskowitz
d2191ae4ec New genre icon 2012-12-16 12:56:01 -05:00
Jason Herskowitz
43b1d1129d Tweak station type icons... bigger image, white to match station icon 2012-12-16 12:40:59 -05:00
Michael Zanetti
89edcfad29 use new station images 2012-12-16 18:23:55 +01:00
Jason Herskowitz
6f7c64a83c Create icons for new Station UI. WIP. 2012-12-16 12:21:02 -05:00
Michael Zanetti
b3ea73c996 fix busy indicator and use correct graphics 2012-12-16 18:08:00 +01:00
Michael Zanetti
078965f2bc use pushbuttons also in createpages 2012-12-16 17:35:28 +01:00
Michael Zanetti
76b6fb49a9 fix layout without buttons 2012-12-16 17:31:33 +01:00
Michael Zanetti
1486a6a200 reworked buttons 2012-12-16 17:25:19 +01:00
Michael Zanetti
37996178b7 only scale image smoothly when the view is not scrolling to speed up
scolling performance
2012-12-16 16:30:07 +01:00
Michael Zanetti
e2d664704b replace GridView with QmlGridView in FlexibleView 2012-12-16 16:27:53 +01:00
Michael Zanetti
9374b3b50b make GridView smooth
fix toggleViewButtons
2012-12-16 16:16:34 +01:00
Michael Zanetti
9fa95fabd2 scrollbar fixes 2012-12-16 16:02:22 +01:00
Michael Zanetti
a54a273f12 pimped inputfield 2012-12-16 15:29:29 +01:00
Michael Zanetti
cc809c6391 multiply instead of add 2012-12-16 13:04:05 +01:00
Christian Muehlhaeuser
c8a3ef0732 * Overwrite minimumSizeHint in DeclarativeHeader. 2012-12-16 13:03:26 +01:00
Christian Muehlhaeuser
4260e3664d * Fixed trying to access root-item's name(). 2012-12-16 12:38:50 +01:00
Michael Zanetti
8894d16c87 Lots of cleanups
Add button capabilities to header
make use of header in stationview
2012-12-16 04:56:14 +01:00
Michael Zanetti
74d375bc50 Say hello to DeclarativeHeader 2012-12-16 00:19:51 +01:00
Michael Zanetti
21c494ba91 Revert "started integrating the breadcrumb"
This reverts commit ac576046c9.
2012-12-15 18:52:36 +01:00
Michael Zanetti
1bd6ebef9c wrapped QSearchField for use in QML 2012-12-15 18:52:24 +01:00
Michael Zanetti
d90a10b51b let QML initialize before showing it 2012-12-15 18:52:24 +01:00
Michael Zanetti
ac576046c9 started integrating the breadcrumb 2012-12-14 01:37:41 +01:00
Michael Zanetti
b8c2d1b0af more svg images 2012-12-13 22:01:07 +01:00
Michael Zanetti
b6e573915c make the station view smoooooth by using svgs and smooth scaling on
the pixel graphics.
2012-12-13 21:34:25 +01:00
Christian Muehlhaeuser
42171d5ddd * Fixed master-merge. 2012-12-13 20:51:28 +01:00
Jason Herskowitz
88ef7a9f08 SVG for No Source found icon 2012-12-04 09:36:15 -05:00
Christian Muehlhaeuser
747d644228 Merge branch 'master' into qmlstation 2012-12-03 22:39:16 +01:00
Christian Muehlhaeuser
f55caea634 Merge branch 'master' into qmlstation 2012-12-03 15:58:07 +01:00
Michael Zanetti
9de5ee9e5a added a scrollbar 2012-12-03 00:27:03 +01:00
Michael Zanetti
ec23f2df06 reworked buttons 2012-12-02 23:45:26 +01:00
Michael Zanetti
3eedd65c79 fix startFromGenre 2012-12-02 23:01:03 +01:00
Michael Zanetti
af05ce243d hooked up TagCloud again
made the input fields works
2012-12-02 22:51:47 +01:00
Michael Zanetti
3f7f1df61f increase delegate size and add hover effect 2012-12-02 21:32:12 +01:00
Michael Zanetti
73b5e977c8 rework StationCreator page2
make use of artistchartsmodel
2012-12-02 21:11:57 +01:00
Michael Zanetti
25a9d21e8f Merge branch 'qmlstation' of github.com:tomahawk-player/tomahawk into qmlstation 2012-12-02 21:07:47 +01:00
Christian Muehlhaeuser
bda65ac4cf * Return albumName() & artistName() for albums & artists in PlayableItem. 2012-12-02 21:06:04 +01:00
Christian Muehlhaeuser
533f717d35 * Return artist & album cover-ids from PlayableModel. 2012-12-02 21:04:17 +01:00
Michael Zanetti
7741f32536 Merge branch 'qmlstation' of github.com:tomahawk-player/tomahawk into qmlstation
Conflicts:
	src/libtomahawk/playlist/dynamic/widgets/DynamicQmlWidget.cpp
2012-12-02 20:36:32 +01:00
Christian Muehlhaeuser
8971c2dfe8 * Load artist charts into a PlayableModel. 2012-12-02 20:16:43 +01:00
Christian Muehlhaeuser
4e8ed35558 * Added example for retrieving top 15 artists in DynamicQmlWidget. 2012-12-02 18:57:58 +01:00
Christian Muehlhaeuser
9ebaa918d5 Merge branch 'master' into qmlstation 2012-12-02 18:48:04 +01:00
Michael Zanetti
feede63a13 added a common DeclarativeView class that sets commonly used stuff 2012-12-02 17:15:30 +01:00
Michael Zanetti
1938fdc727 replaced GridView in WelcomeWidget with QML Gridview
* Items don't hold correct CoverIDRoled yet
* Items don't hold correct IsPlayingRole yet
2012-12-02 12:10:22 +01:00
Christian Muehlhaeuser
4fdc6610e5 * Merged master. 2012-12-01 00:44:48 +01:00
Michael Zanetti
2c941f918d update rc file 2012-11-29 00:26:56 +01:00
Michael Zanetti
61fd5cb008 split the station creation into two steps 2012-11-29 00:20:04 +01:00
Michael Zanetti
6c8ee25c6a Merge branch 'qmlstation' of github.com:tomahawk-player/tomahawk into qmlstation 2012-11-28 21:14:05 +01:00
Michael Zanetti
9db0a64b9b Merge branch 'qmlstation' of github.com:tomahawk-player/tomahawk into qmlstation 2012-11-28 19:21:14 +01:00
Christian Muehlhaeuser
96a90af296 * Fixed compiling. 2012-11-28 15:34:10 +01:00
Christian Muehlhaeuser
369555f227 Merge branch 'master' into qmlstation 2012-11-28 13:29:56 +01:00
Christian Muehlhaeuser
9100624108 Merge branch 'master' into qmlstation 2012-11-28 13:01:46 +01:00
Christian Muehlhaeuser
6d654121a3 * Fixed ArtistInfoWidget. 2012-11-28 12:59:50 +01:00
Christian Muehlhaeuser
3f889df387 Merge branch 'master' into qmlstation 2012-11-28 11:35:51 +01:00
Christian Muehlhaeuser
848d1c860f Merge branch 'master' into qmlstation 2012-11-28 10:51:54 +01:00
Christian Muehlhaeuser
3b4b91935e Merge branch 'master' into qmlstation 2012-11-28 09:48:04 +01:00
Christian Muehlhaeuser
8fc62925ed * Merged master. 2012-11-28 08:07:13 +01:00
Christian Muehlhaeuser
4eafb24f4f * Merged master. 2012-11-28 05:58:54 +01:00
Christian Muehlhaeuser
ae83d0c903 * Fixed TreeProxyModel's PlaylistInterface. 2012-11-28 04:41:05 +01:00
Christian Muehlhaeuser
b279da0db4 * Style fixes. 2012-11-28 04:23:49 +01:00
Christian Muehlhaeuser
d1b913e3cf * Removed debug output in PlayableProxyModelPlaylistInterface. 2012-11-28 04:23:09 +01:00
Christian Muehlhaeuser
69bf320d05 * Fixed invalid conversion in PlaylistInterface::siblingResult. 2012-11-28 04:20:13 +01:00
Christian Muehlhaeuser
0166a6f652 * Manually detect whether GDB is running and disable BreakPad on OSX. 2012-11-28 00:21:41 +01:00
Christian Muehlhaeuser
a62f551135 * Retain interface compatibility. QVariantMap => QVariantList. 2012-11-27 23:36:23 +01:00
Christian Muehlhaeuser
8130b60f21 * Style fix for DbCmd_CreateDynamicPlaylist. 2012-11-27 23:22:10 +01:00
Christian Muehlhaeuser
af100c48dc Merge branch 'master' into qmlstation 2012-11-27 16:46:31 +01:00
Christian Muehlhaeuser
a58a0408a1 Merge branch 'master' into qmlstation 2012-11-27 16:45:03 +01:00
Christian Muehlhaeuser
f15e222095 * Comment out Artist & Album DTORs for now. 2012-11-27 16:31:22 +01:00
Christian Muehlhaeuser
7c0d41651f * Return default covers when we couldn't retrieve a real cover in DeclarativeCoverArtProvider. 2012-11-27 16:30:31 +01:00
Christian Muehlhaeuser
305bde97bf * Fixed artist generation. 2012-11-27 15:54:23 +01:00
Michael Zanetti
ae015ca3d9 binding loop warnings-- 2012-11-27 00:39:18 +01:00
Michael Zanetti
24358a1218 brainstorming++ 2012-11-26 21:43:14 +01:00
Michael Zanetti
5f6bccc44b loading? 2012-11-26 01:38:34 +01:00
Michael Zanetti
1046b495b6 more brainstorming 2012-11-26 00:26:57 +01:00
Michael Zanetti
c5f1d38d96 more experimenting 2012-11-25 23:50:38 +01:00
Michael Zanetti
967631b30c pimped cover gradient 2012-11-25 23:17:48 +01:00
Michael Zanetti
8899a38053 minor fixes/workarounds 2012-11-25 20:18:45 +01:00
Michael Zanetti
b20c81a071 hooked up the setuppage and smaller cleanups 2012-11-25 20:02:19 +01:00
Michael Zanetti
014f598dd2 setupscreen layouted properly 2012-11-25 19:43:47 +01:00
Christian Muehlhaeuser
897984f2a1 * Properly add radio type to genre requests. 2012-11-25 19:21:24 +01:00
Christian Muehlhaeuser
7f82cf6ea2 * Added interface for startFromGenre( QString ). 2012-11-25 19:02:12 +01:00
Michael Zanetti
166db57ec3 PathView should be quite good now 2012-11-25 18:14:48 +01:00
Michael Zanetti
2e2a566061 experiment 2012-11-25 04:50:09 +01:00
Michael Zanetti
ffa6f4ebaa fix pathview area and currentIndex 2012-11-25 04:08:21 +01:00
Michael Zanetti
e7ffb77cad more qml work 2012-11-25 02:03:02 +01:00
Christian Muehlhaeuser
d099bc0392 * Merged PlaylistInterface refactor. 2012-11-25 00:56:28 +01:00
Christian Muehlhaeuser
773c06aab7 * Added startFromTrack & startFromArtist to GeneratorInterface. 2012-11-24 23:39:41 +01:00
Michael Zanetti
8462a47985 some more work on stations 2012-11-24 23:39:29 +01:00
Christian Muehlhaeuser
4b23fe2fe0 * Make sure mode and type are set when loading a dynamic playlist. 2012-11-24 22:57:54 +01:00
Michael Zanetti
34d2215ef8 fix setting of first revision of playlist 2012-11-24 21:28:07 +01:00
Michael Zanetti
5d86bd2549 removed dynamic controls from whole generator stuff 2012-11-24 21:06:17 +01:00
Michael Zanetti
8c470c149c merged master 2012-11-24 15:43:55 +01:00
Michael Zanetti
18fbeffe02 add artist mode for stations 2012-11-24 15:29:12 +01:00
Michael Zanetti
39b9efd581 remove useless assert 2012-11-24 15:29:12 +01:00
Michael Zanetti
166d83539a some more testing work for stations 2012-11-24 15:29:12 +01:00
Michael Zanetti
7efd98e7a6 some more work on the stations 2012-11-24 15:29:12 +01:00
Michael Zanetti
f81630099a some more work on the station config 2012-11-24 15:29:12 +01:00
Christian Muehlhaeuser
667feee3cc * Mutex protect hash access. 2012-11-24 15:29:12 +01:00
Christian Muehlhaeuser
aaa0affad4 * Use largest album cover size last.fm offers. 2012-11-24 15:29:12 +01:00
Christian Muehlhaeuser
f1f577053b * Assert when trying to create an empty PlayableItem. 2012-11-24 15:29:12 +01:00
Christian Muehlhaeuser
5672debdce * Fixed getting artist covers from DeclarativeCoverArtProvider. 2012-11-24 15:29:12 +01:00
Christian Muehlhaeuser
fafa3eab15 * Add an assert for Jason. 2012-11-24 15:26:18 +01:00
Christian Muehlhaeuser
2de9880365 * Fixed cover retrieval via DeclarativeCoverArtProvider. 2012-11-24 15:26:18 +01:00
Michael Zanetti
56606f44bd dunno why, but QML seems to dislike jumps in enums... remove the Qt::UserRole + 100 2012-11-24 15:26:18 +01:00
Michael Zanetti
a95c765bb3 fix warning 2012-11-24 15:26:18 +01:00
Michael Zanetti
7d4c0f861f fix compilation without -fpermissive 2012-11-24 15:26:18 +01:00
Michael Zanetti
883bcc2670 - make the station stuff adapt to high DPIs
- more work on the config dialog
2012-11-24 15:26:18 +01:00
Christian Muehlhaeuser
fd75900e98 * Work on image provider & unique cover IDs. 2012-11-24 15:26:17 +01:00
Michael Zanetti
ca35446dfd make stations behave correctly (preload some tracks and switch to dynamic mode on play) 2012-11-24 15:24:38 +01:00
Michael Zanetti
47e3a81058 only fetch more track if they are finished resolving and there are not enough tracks available yet 2012-11-24 15:24:38 +01:00
Michael Zanetti
1ceeeec2a0 restructured QML 2012-11-24 15:24:38 +01:00
Christian Muehlhaeuser
6726c7fcf0 * Lock mutex when adding to the static hashes in Artist and Album. 2012-11-24 15:23:38 +01:00
Christian Muehlhaeuser
bc94658e56 * Added getByUniqueId( uuid ) methods to Artist, Album and Query. 2012-11-24 15:22:05 +01:00
Christian Muehlhaeuser
9104d3b92d * Disable infobar for stations. 2012-11-24 15:17:57 +01:00
Christian Muehlhaeuser
ad3bbf9b29 * Properly store / restore DynamicQmlWidgets. 2012-11-24 15:17:57 +01:00
Michael Zanetti
249c488ad9 add missing files 2012-11-24 15:17:57 +01:00
Michael Zanetti
ebda89f5a1 some more work on the station qml 2012-11-24 15:17:57 +01:00
Michael Zanetti
d626352e25 more work on the new station view 2012-11-24 15:17:11 +01:00
Michael Zanetti
bb451a89b3 a try to prefill the station with more songs 2012-11-24 15:17:11 +01:00
Michael Zanetti
b27deb851f connect also to resultsChanged 2012-11-24 15:17:11 +01:00
Christian Muehlhaeuser
3c0f11a12f * No need to hook up to query's coverChanged() signal anymore. 2012-11-24 15:17:11 +01:00
Michael Zanetti
54faabf09b played around with the QML path a bit... 2012-11-24 15:17:11 +01:00
Michael Zanetti
5cf1939a2e yay! we have album covers! 2012-11-24 15:17:11 +01:00
Christian Muehlhaeuser
ed2b7b8954 * Fixed clash between model & proxymodel user-roles. 2012-11-24 15:17:11 +01:00
Michael Zanetti
95c961cef0 fix covers again after using the proxymodel 2012-11-24 15:17:11 +01:00
Michael Zanetti
726c9248eb use the proxymodel instead of the model directly 2012-11-24 15:17:11 +01:00
Michael Zanetti
1dfe4c2acb removed unneeded slot previously used for debugging 2012-11-24 15:16:45 +01:00
Michael Zanetti
b068d544c1 added artwork to the stations view 2012-11-24 15:16:45 +01:00
Michael Zanetti
e51e74c340 more work on the station qml stuff 2012-11-24 15:16:23 +01:00
Christian Muehlhaeuser
582a52b2d5 * Call startOnDemand() when initializing DynamicQmlWidget. Not sure if we need to wait for a revision to be loaded first, though. 2012-11-24 15:15:09 +01:00
Christian Muehlhaeuser
cbfc4d2405 * Added PlayableRoles and mapped them to the according column. 2012-11-24 15:15:09 +01:00
Michael Zanetti
984e4ceb76 a very very basic pathview for covers 2012-11-24 15:15:09 +01:00
Christian Muehlhaeuser
b6415d2235 * Setup a DynamicQmlWidget when showing a station. 2012-11-24 15:15:09 +01:00
Christian Muehlhaeuser
367931f6e2 * Setup (proxy-)model and load dynamic playlist in DynamicQmlWidget. 2012-11-24 15:13:09 +01:00
Michael Zanetti
71f6676c4b added qml file template for stations 2012-11-24 15:13:09 +01:00
Christian Muehlhaeuser
c6b29481b1 * Make DynamicQmlWidget a proper ViewPage. 2012-11-24 15:11:59 +01:00
Michael Zanetti
11b19be373 added DynamicQmlWidget (not doing anything yet) 2012-11-24 15:11:59 +01:00
Michael Zanetti
9625bc801b testcomit for qtdeclarative 2012-11-24 15:08:52 +01:00
120 changed files with 4406 additions and 3481 deletions

View File

@@ -93,7 +93,7 @@ IF( NOT BUILD_GUI )
MESSAGE( STATUS "Building Tomahawk ${TOMAHAWK_VERSION} in HEADLESS mode ***" )
ELSE()
MESSAGE( STATUS "Building Tomahawk ${TOMAHAWK_VERSION} full GUI version ***" )
LIST(APPEND NEEDED_QT4_COMPONENTS "QtGui" "QtWebkit" "QtUiTools" )
LIST(APPEND NEEDED_QT4_COMPONENTS "QtGui" "QtWebkit" "QtUiTools" "QtDeclarative" )
ENDIF()
IF( BUILD_GUI AND UNIX AND NOT APPLE )

View File

@@ -0,0 +1,145 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
height="32"
version="1.1"
viewBox="0 0 32 32"
width="32"
x="0px"
y="0px"
id="svg2"
inkscape:version="0.48.3.1 r9886"
sodipodi:docname="inputfield-border.svg">
<metadata
id="metadata18">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs16">
<inkscape:perspective
sodipodi:type="inkscape:persp3d"
inkscape:vp_x="0 : 22 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_z="44 : 22 : 1"
inkscape:persp3d-origin="22 : 14.666667 : 1"
id="perspective2836" />
</defs>
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="2880"
inkscape:window-height="1720"
id="namedview14"
showgrid="true"
inkscape:zoom="22.627417"
inkscape:cx="27.047196"
inkscape:cy="19.278199"
inkscape:window-x="-12"
inkscape:window-y="45"
inkscape:window-maximized="1"
inkscape:current-layer="svg2">
<inkscape:grid
type="xygrid"
id="grid2990"
empspacing="5"
visible="true"
enabled="true"
snapvisiblegridlinesonly="true" />
</sodipodi:namedview>
<g
id="g4599">
<path
style="opacity:0.06466699;fill:none;stroke:#000000;stroke-width:4;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:8;stroke-opacity:1"
d="M 4.65625,2.53125 C 0.01699841,4.4354339 3.6476695,11.37715 2.5,15.15625 c 0.9104726,4.415191 -1.74364624,10.215434 1.125,13.9375 7.922985,0.877493 16.321668,0.399451 24.3125,0.1875 C 31.683761,26.34472 28.403491,19.966164 29.5,15.84375 28.337107,11.830292 31.896487,5.0854716 27.625,2.59375 19.995242,2.3871025 12.300962,2.5260218 4.65625,2.53125 z"
id="path4601"
inkscape:connector-curvature="0" />
<path
style="opacity:0.06466699;fill:none;stroke:#000000;stroke-width:4;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:8;stroke-opacity:1"
d="M 4.78125,2.15625 C -0.50384498,3.6307191 3.3089847,11.154694 2.15625,15 c 0.9229661,4.546013 -1.76090684,10.511754 1.125,14.3125 7.901377,1.296184 16.629903,0.28974 24.6875,0.375 C 32.275546,27.028776 28.653465,20.168522 29.84375,16 28.645472,11.822061 32.315037,4.9433197 27.90625,2.3125 20.331149,1.9143664 12.389796,2.2847244 4.78125,2.15625 z"
id="path4605"
inkscape:connector-curvature="0" />
<path
style="opacity:0.06466699;fill:none;stroke:#000000;stroke-width:4;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:8;stroke-opacity:1"
d="m 4.71875,1.8125 c -5.60622152,1.4178135 -1.8407007,9.335986 -2.9375,13.3125 1.0422893,4.584739 -2.09676889,11.02483 1.5625,14.625 8.081978,0.980787 16.725347,0.518622 24.875,0.1875 4.402595,-2.83693 0.856871,-9.769091 2,-14.0625 -1.179633,-4.234007 2.610959,-11.6624143 -2.4375,-14 C 20.11261,1.665057 12.395997,1.8099213 4.71875,1.8125 z"
id="path4609"
inkscape:connector-curvature="0" />
<path
style="opacity:0.06466699;fill:none;stroke:#000000;stroke-width:4;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:8;stroke-opacity:1"
d="M 4.34375,1.46875 C -1.4013281,3.6570978 2.6368879,11.554337 1.4375,16.09375 2.5716902,20.523565 -1.4013805,28.864524 4.59375,30.53125 12.624379,30.017378 21.200972,31.58868 28.96875,30 32.860269,25.890157 29.400879,19.01996 30.5625,13.90625 29.907854,10.034046 32.893088,1.9946986 26.90625,1.4375 19.397406,1.4612481 11.795029,1.3918794 4.34375,1.46875 z"
id="path4613"
inkscape:connector-curvature="0" />
<path
style="opacity:0.06466699;fill:none;stroke:#000000;stroke-width:4;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:8;stroke-opacity:1"
d="m 4.625,1.09375 c -6.2068547,1.3257477 -2.6318919,9.686262 -3.5625,13.96875 1.1448342,4.811525 -2.4458629,11.943183 2.09375,15.40625 8.111337,1.130173 16.686474,0.284334 24.90625,0.3125 5.588519,-2.412228 1.736328,-10.285499 2.875,-14.84375 C 29.823429,11.396143 33.723745,3.1822192 27.75,1.125 20.050017,0.99409221 12.327794,1.0658584 4.625,1.09375 z"
id="path4617"
inkscape:connector-curvature="0" />
<path
style="opacity:0.06466699;fill:none;stroke:#000000;stroke-width:4;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:8;stroke-opacity:1"
d="M 4.59375,0.75 C -0.61385497,1.3040672 0.78718888,7.8712117 0.71875,11.459821 1.1629644,17.279444 -0.11932148,23.455713 1.25,29.09375 c 2.821567,4.196632 8.916358,1.349939 13.066963,2.1875 C 19.332991,30.61559 25.044407,32.586609 29.6875,30.375 33.051694,26.90515 30.469139,21.267325 31.28125,16.968751 30.597411,12.179526 32.636881,6.6229625 30.3125,2.25 26.821643,-1.0138505 21.248342,1.5206042 16.968751,0.71875 12.844428,0.74314509 8.7159665,0.67222023 4.59375,0.75 z"
id="path4621"
inkscape:connector-curvature="0" />
<path
style="opacity:0.06466699;fill:none;stroke:#000000;stroke-width:4;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:8;stroke-opacity:1"
d="M 4.53125,0.375 C 1.3894018,0.60024126 -0.18415805,3.9813327 0.34375,6.8169646 0.40966407,13.865499 0.21403433,20.929441 0.4375,27.96875 c 0.5865041,3.066072 3.9812099,4.147942 6.7366076,3.6875 6.9294814,-0.06599 13.8743884,0.129817 20.7946424,-0.09375 3.066072,-0.586504 4.147942,-3.98121 3.6875,-6.736608 C 31.590265,17.896411 31.786067,10.951504 31.5625,4.03125 30.975996,0.96517848 27.58129,-0.11669206 24.825892,0.34375 18.061449,0.36541598 11.293922,0.30095384 4.53125,0.375 z"
id="path4625"
inkscape:connector-curvature="0" />
<path
id="path4629"
d="m 5,0 22,0 c 2.77,0 5,2.23 5,5 l 0,22 c 0,2.77 -2.23,5 -5,5 L 5,32 C 2.23,32 0,29.77 0,27 L 0,5 C 0,2.23 2.23,0 5,0 z"
style="opacity:0.064667;fill:none;stroke:#000000;stroke-width:4;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:8;stroke-opacity:1" />
<path
style="opacity:0.06466699;fill:none;stroke:#000000;stroke-width:4;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:8;stroke-opacity:1"
d="M 4.46875,-0.3125 C 0.98344595,-0.12851319 -0.95137919,3.548349 -0.34375,6.7857146 -0.21552288,13.997692 -0.59536534,21.254525 -0.16338012,28.44023 0.58057677,31.627772 4.1300096,32.842509 7.1428576,32.34375 14.235767,32.215407 21.373601,32.595537 28.44023,32.16338 31.627772,31.419423 32.842509,27.86999 32.34375,24.857142 32.215407,17.764233 32.595537,10.626399 32.16338,3.5597705 31.419423,0.37222754 27.86999,-0.84250881 24.857142,-0.34375 18.061422,-0.32218456 11.26281,-0.38641136 4.46875,-0.3125 z"
id="path4631"
inkscape:connector-curvature="0" />
<path
style="opacity:0.06466699;fill:none;stroke:#000000;stroke-width:4;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:8;stroke-opacity:1"
d="m 4.46875,-0.6875 c -4.88903887,0.31100674 -5.938444,6.0477797 -5.1875,9.9732151 0.41575444,6.5986889 -0.7714644,13.4654919 0.46875,19.9330349 2.5002101,5.615111 9.8337653,2.870216 14.535716,3.5 5.227227,-0.673134 11.102211,1.356807 15.964284,-1 4.618144,-3.665229 1.605796,-10.45511 2.46875,-15.433038 C 31.834965,11.207777 34.77447,4.6031 30.625,0.5625 26.002847,-2.3037853 19.99663,0.03815215 14.857141,-0.71875 11.394973,-0.69362162 7.9291701,-0.76635345 4.46875,-0.6875 z"
id="path4635"
inkscape:connector-curvature="0" />
<path
style="opacity:0.06466699;fill:none;stroke:#000000;stroke-width:4;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:8;stroke-opacity:1"
d="M 4.4375,-1.03125 C -2.4288841,-0.40966905 -1.3119005,8.2085409 -1.0625,13 0.16788472,19.074593 -3.8317535,27.473817 1.59375,32.0625 8.5628736,34.728281 16.662641,32.170927 24,33.0625 29.022589,34.430248 34.293748,30.315663 33.0625,25 32.365371,17.457535 34.334575,9.3983493 32.34375,2.09375 28.430894,-3.9134302 19.817453,0.11168301 14,-1.0625 c -3.186531,0.031844 -6.3778449,-0.056838 -9.5625,0.03125 z"
id="path4639"
inkscape:connector-curvature="0" />
<path
style="opacity:0.06466699;fill:none;stroke:#000000;stroke-width:4;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:8;stroke-opacity:1"
d="M 4.40625,-1.40625 C -2.6356932,-0.73578486 -1.8066321,7.9924057 -1.4375,13 -0.23753983,19.199171 -4.2465159,27.811194 1.46875,32.40625 8.5086357,35.093269 16.607199,32.562499 24,33.4375 29.206688,34.80078 34.643458,30.487912 33.4375,25 32.720722,17.386853 34.764131,9.2829251 32.65625,1.90625 28.669603,-4.2590766 19.946075,-0.32351915 14,-1.4375 c -3.196957,0.031744 -6.3986661,-0.056723 -9.59375,0.03125 z"
id="path4643"
inkscape:connector-curvature="0" />
<path
style="opacity:0.06466699;fill:none;stroke:#000000;stroke-width:4;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:8;stroke-opacity:1"
d="M 4.375,-1.75 C -2.3899272,-1.2431511 -2.4446337,7.0084439 -1.78125,12 -0.57839019,18.49983 -4.4259935,26.910214 0.625,32.21875 6.6149095,35.870482 14.375511,32.750815 21,33.78125 26.00645,34.811355 33.908351,33.579761 33.78125,27 33.025646,18.677041 35.173733,9.8203103 32.96875,1.75 28.969659,-4.6437363 20.059663,-0.68188005 14,-1.78125 10.792617,-1.7496477 7.5805122,-1.8377839 4.375,-1.75 z"
id="path4647"
inkscape:connector-curvature="0" />
<path
style="opacity:0.06466699;fill:none;stroke:#000000;stroke-width:4;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:8;stroke-opacity:1"
d="M 4.875,-2.125 C -1.8309464,-2.1825575 -3.2026722,5.8349656 -2.15625,11 -1.080826,17.712074 -4.3881621,25.748049 -0.375,31.6875 4.9763374,36.782756 13.437274,32.97527 20,34.15625 25.186853,34.907535 33.649994,34.582135 34.09375,27.625 33.532181,19.108316 35.431584,10.074869 33.375,1.78125 29.243722,-5.2757831 19.506448,-0.97339883 13,-2.15625 10.294719,-2.1339361 7.5677947,-2.1998142 4.875,-2.125 z"
id="path4651"
inkscape:connector-curvature="0" />
<path
style="opacity:0.06466699;fill:none;stroke:#000000;stroke-width:4;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:8;stroke-opacity:1"
d="M 4.34375,-2.46875 C -2.8099687,-2.0064215 -3.3295148,6.5980666 -2.5,12 -1.2963625,18.720406 -5.2816016,27.573393 0.3125,32.875 6.7337709,36.640288 14.932728,33.450664 22,34.5 27.257415,35.699243 34.692301,33.44159 34.5,27 33.751825,18.586732 35.895885,9.6625663 33.65625,1.5 29.388001,-5.6000779 19.620414,-1.344641 13,-2.5 c -2.884405,0.032599 -5.7737456,-0.058009 -8.65625,0.03125 z"
id="path4655"
inkscape:connector-curvature="0" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 11 KiB

View File

@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg width="500px" height="500px" viewBox="0 0 500 500" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns">
<title>station-artist</title>
<description>Created with Sketch (http://www.bohemiancoding.com/sketch)</description>
<defs>
<linearGradient x1="50%" y1="50%" x2="50%" y2="170.589844%" id="linearGradient-1">
<stop stop-color="rgb(254,255,254)" offset="0%"></stop>
<stop stop-color="rgb(0,0,0)" offset="100%"></stop>
</linearGradient>
</defs>
<g id="Page 1" fill="rgb(215,215,215)" fill-rule="evenodd">
<path d="M245.48466,91 C161.807243,91 94,158.187218 94,241.021267 C94,323.885705 161.807243,391 245.48466,391 C329.137526,391 397,323.885699 397,241.021267 C397.006157,158.188001 329.137415,91 245.48466,91 L245.48466,91 L245.48466,91 M328.597284,221.910531 L283.447689,255.452489 L299.857913,308.566669 C300.496157,310.61443 299.741311,312.856636 297.986141,314.126613 C297.090145,314.776792 296.034589,315.10492 294.966758,315.10492 C293.960297,315.10492 292.947699,314.807174 292.076251,314.211683 L245.496742,282.462275 L198.929509,314.211683 C197.155929,315.433048 194.774789,315.390513 193.019619,314.126613 C191.264449,312.856637 190.515741,310.620506 191.147847,308.566669 L207.551935,255.452489 L162.408476,221.910531 C160.671717,220.616248 159.947556,218.374041 160.610347,216.320204 C161.279276,214.254214 163.187869,212.850556 165.378763,212.801944 L222.071977,211.671726 L240.673094,159.213801 C241.397255,157.190346 243.32426,155.8353 245.490606,155.8353 C247.656952,155.8353 249.602368,157.190346 250.320392,159.213801 L268.92151,211.671726 L325.614723,212.801944 C327.793343,212.850556 329.708074,214.254214 330.377002,216.320204 C331.052217,218.375335 330.334166,220.616913 328.597284,221.910531 C328.597284,221.910531 330.334166,220.616913 328.597284,221.910531 L328.597284,221.910531" id="Shape" stroke="rgb(150,150,150)" stroke-width="1" fill="url(#linearGradient-1)"></path>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg width="500px" height="500px" viewBox="0 0 500 500" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns">
<title>station-genre</title>
<description>Created with Sketch (http://www.bohemiancoding.com/sketch)</description>
<defs>
<linearGradient x1="50%" y1="50%" x2="50%" y2="170.589844%" id="linearGradient-1">
<stop stop-color="rgb(254,255,254)" offset="0%"></stop>
<stop stop-color="rgb(0,0,0)" offset="100%"></stop>
</linearGradient>
</defs>
<g id="Page 1" fill="rgb(215,215,215)" fill-rule="evenodd">
<g id="Group" fill="rgb(0,0,0)">
<path d="M389.045442,230.429716 C383.570272,224.959128 241.528204,83.1885389 241.528204,83.1885389 L179.981162,84.9885389 L160.691354,104.235598 C166.349226,110.976775 171.677056,117.782657 175.749546,123.859127 C179.185524,122.035598 183.098886,121.000304 187.259779,121.000304 C200.856353,121.000304 211.871525,132.006186 211.871525,145.576774 C211.871525,159.14148 200.850461,170.141481 187.259779,170.141481 C173.669099,170.141481 162.648034,159.141481 162.648034,145.576774 C162.648034,142.612068 163.19614,139.788539 164.1568,137.159127 C158.104056,132.623833 151.208523,126.559127 144.560523,120.34148 L129.631991,135.24148 L129.631991,198.565012 L276.789714,345.447361 C276.789714,345.447361 281.192246,351.47089 287.22731,345.447361 C293.262374,339.423831 389.045432,243.817946 389.045432,243.817946 C389.045432,243.817946 394.514592,235.894158 389.045442,230.429716 C389.045442,230.429716 394.514592,235.894158 389.045442,230.429716 L389.045442,230.429716" id="Shape" stroke="rgb(150,150,150)" stroke-width="1" fill="url(#linearGradient-1)"></path>
<path d="M178.83767,140.964384 C179.008585,140.964384 179.309159,140.952619 179.427032,140.82909 C182.916053,137.346737 161.374883,108.893797 132.325244,78.5584992 C131.523712,78.4114404 129.867605,78.1349698 127.975755,78.1349698 C125.512223,78.1349698 122.20001,78.5996757 120.437818,80.8290875 C118.940839,82.7173229 118.622584,85.7232053 119.506627,89.7761465 C138.165813,109.723719 170.456964,140.964384 178.83767,140.964384 C178.83767,140.964384 170.456964,140.964384 178.83767,140.964384 L178.83767,140.964384" id="Shape" stroke="rgb(150,150,150)" stroke-width="1" fill="url(#linearGradient-1)"></path>
<path d="M141.000749,228.394014 L196.30056,367.023423 C196.30056,367.023423 197.726815,373.270482 204.551624,370.576364 C207.793113,369.288128 233.895943,358.911658 260.440793,348.358717 L260.423112,348.364599 L141.000749,228.394014 L141.000749,228.394014 L141.000749,228.394014" id="Shape" stroke="rgb(150,150,150)" stroke-width="1" fill="url(#linearGradient-1)"></path>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.8 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 11 KiB

View File

@@ -1,6 +1,11 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<<<<<<< HEAD
<svg width="38px" height="38px" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns">
<title>view-toggle-pressed-right</title>
=======
<svg width="38px" height="38px" viewBox="0 0 38 38" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns">
<title>view-toggle-pressed-left</title>
>>>>>>> master
<description>Created with Sketch (http://www.bohemiancoding.com/sketch)</description>
<defs>
<linearGradient x1="50%" y1="50%" x2="50%" y2="0%" id="linearGradient-1">
@@ -10,7 +15,11 @@
<path d="M11.9973518,0 C5.37139735,0 0,5.36387601 0,11.9973518 L0,26.0026482 C0,32.6286026 5.36387601,38 11.9973518,38 L38,38 L38,0 L11.9973518,0 Z M11.9973518,0" id="path-2"></path>
</defs>
<g id="Page 1" fill="rgb(215,215,215)" fill-rule="evenodd">
<<<<<<< HEAD
<path d="M8.00079513,0 C3.582078,0 0,3.57494774 0,8.00079513 L0,29.9992049 C0,34.417922 3.57494774,38 8.00079513,38 L38,38 L38,0 L8.00079513,0 Z M8.00079513,0" id="Rectangle 1" stroke="rgb(150,150,150)" stroke-width="1" fill="url(#radialGradient-1)"></path>
=======
<use id="Rectangle 2" stroke="rgb(150,150,150)" stroke-width="1" fill="url(#linearGradient-1)" xlink:href="#path-2"></use>
<use id="Rectangle 2-1" stroke="none" fill="none" xlink:href="#path-2"></use>
>>>>>>> master
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@@ -1,6 +1,11 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<<<<<<< HEAD
<svg width="38px" height="38px" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns">
<title>view-toggle-pressed-left</title>
=======
<svg width="38px" height="38px" viewBox="0 0 38 38" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns">
<title>view-toggle-pressed-right</title>
>>>>>>> master
<description>Created with Sketch (http://www.bohemiancoding.com/sketch)</description>
<defs>
<linearGradient x1="50%" y1="50%" x2="50%" y2="0%" id="linearGradient-1">
@@ -10,7 +15,11 @@
<path d="M0,0 L0,38 L26.0026482,38 C32.6286026,38 38,32.636124 38,26.0026482 L38,11.9973518 C38,5.37139735 32.636124,0 26.0026482,0 L0,0 Z M0,0" id="path-2"></path>
</defs>
<g id="Page 1" fill="rgb(215,215,215)" fill-rule="evenodd">
<<<<<<< HEAD
<path d="M0,0 L0,38 L29.9992049,38 C34.417922,38 38,34.4250523 38,29.9992049 L38,8.00079513 C38,3.582078 34.4250523,0 29.9992049,0 L0,0 Z M0,0" id="Rectangle 1" stroke="rgb(150,150,150)" stroke-width="1" fill="url(#radialGradient-1)"></path>
=======
<use id="Rectangle 2" stroke="rgb(150,150,150)" stroke-width="1" fill="url(#linearGradient-1)" xlink:href="#path-2"></use>
<use id="Rectangle 2-1" stroke="none" fill="none" xlink:href="#path-2"></use>
>>>>>>> master
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@@ -0,0 +1,21 @@
import QtQuick 1.1
import tomahawk 1.0
import "tomahawkimports"
// Only to be used together with DeclarativeHeader C++ class
// If you want to use the header in QML, use FlexibleHeader
Item {
anchors.fill: parent
FlexibleHeader {
anchors.fill: parent
icon: iconSource
title: caption
subtitle: description
buttonModel: buttonList
onSearchTextChanged: mainView.setFilterText(searchText)
onCurrentButtonIndexChanged: mainView.viewModeSelected(currentButtonIndex)
}
}

65
data/qml/QmlGridView.qml Normal file
View File

@@ -0,0 +1,65 @@
import QtQuick 1.1
//import tomahawk 1.0
import "tomahawkimports"
Rectangle {
anchors.fill: parent
color: "black"
Text {
id: fontMetrics
text: "Here's some sample text"
opacity: 0
}
GridView {
id: gridView
anchors.fill: parent
//anchors.rightMargin: scrollBar.width
cellHeight: cellWidth
cellWidth: calculateCoverSize(gridView.width - 3)
cacheBuffer: cellHeight * 5
function calculateCoverSize(rectWidth) {
var itemWidth = fontMetrics.width;
var itemsPerRow = Math.max( 1, Math.floor( rectWidth / itemWidth ) );
var remSpace = rectWidth - ( itemsPerRow * itemWidth );
var extraSpace = remSpace / itemsPerRow;
return itemWidth + extraSpace;
}
model: mainModel
delegate: CoverImage {
height: gridView.cellHeight// * 0.95
width: gridView.cellWidth// * 0.95
showLabels: true
showMirror: false
artistName: model.artistName
trackName: model.trackName
artworkId: model.coverID
showPlayButton: true
currentlyPlaying: isPlaying
smooth: !gridView.moving
onClicked: {
rootView.onItemClicked(index)
}
onPlayClicked: {
rootView.onItemPlayClicked(index)
}
}
}
ScrollBar {
id: scrollBar
listView: gridView
orientation: Qt.Vertical
margin: -width
}
}

34
data/qml/SpinnerTest.qml Normal file
View File

@@ -0,0 +1,34 @@
import QtQuick 1.1
import "tomahawkimports"
Rectangle {
width: 1400
height: 900
color: "black"
BusyIndicator {
anchors.centerIn: parent
anchors.horizontalCenterOffset: 200
height: 200
width: 200
}
Image {
id: svgSpinner
source: "../images/loading-animation.svg"
smooth: true
height: 200
width: 200
anchors.centerIn: parent
anchors.horizontalCenterOffset: -200
Timer {
running: true
repeat: true
interval: 200
onTriggered: svgSpinner.rotation += 360 / 12
}
}
}

162
data/qml/StationView.qml Normal file
View File

@@ -0,0 +1,162 @@
import QtQuick 1.1
import tomahawk 1.0
import "tomahawkimports"
import "stations"
Rectangle {
id: scene
color: "black"
anchors.fill: parent
state: "list"
FlexibleHeader {
id: header
anchors {
left: parent.left
top: parent.top
right: parent.right
}
height: defaultFontHeight * 4
width: parent.width
icon: "../images/station.svg"
title: mainView.title
subtitle: generator.summary
showSearchField: false
showBackButton: stationListView.currentIndex > 0
showNextButton: stationListView.currentIndex == 2
nextButtonText: "Save"
z: 1 //cover albumcovers that may leave their area
onBackPressed: stationListView.decrementCurrentIndex()
onNextPressed: stationListView.incrementCurrentIndex()
}
ListModel {
id: modeModel
ListElement { label: "By Artist"; image: "../../images/station-artist.svg"; creatorContent: "stations/CreateByArtist.qml" }
ListElement { label: "By Genre"; image: "../../images/station-genre.svg"; creatorContent: "stations/CreateByGenre.qml" }
ListElement { label: "By Year"; image: "../../images/station-year.svg"; creatorContent: "year" }
}
VisualItemModel {
id: stationVisualModel
StationCreatorPage1 {
height: scene.height - header.height
width: scene.width
model: modeModel
onItemClicked: {
stationCreator.content = modeModel.get(index).creatorContent
stationListView.incrementCurrentIndex()
}
}
StationCreatorPage2 {
id: stationCreator
height: stationListView.height
width: stationListView.width
onNext: stationListView.incrementCurrentIndex()
}
Item {
id: stationItem
height: stationListView.height
width: stationListView.width
CoverFlip {
id: coverView
anchors.right: parent.right
anchors.top: parent.top
height: parent.height
width: parent.width
interactive: false
backgroundColor: scene.color
model: dynamicModel
currentIndex: currentlyPlayedIndex
onItemPlayPauseClicked: {
mainView.playItem(index)
}
onItemClicked: {
mainView.playItem(index)
}
states: [
State {
name: "empty"; when: mainView.loading
PropertyChanges {
target: coverView
anchors.rightMargin: -coverView.width
anchors.topMargin: - coverView.height
scale: 0
}
}
]
transitions: [
Transition {
from: "empty"
to: "*"
NumberAnimation {
properties: "anchors.topMargin,anchors.rightMargin,scale"
duration: 1000
easing.type: Easing.OutQuad
}
}
]
// Behavior on anchors.topMargin {
// NumberAnimation { duration: 500 }
// }
// Behavior on anchors.rightMargin {
// NumberAnimation { duration: 500 }
// }
// Behavior on scale {
// NumberAnimation { duration: 500 }
// }
}
BusyIndicator {
id: busyIndicator
anchors.centerIn: parent
height: defaultFontHeight * 4
width: height
opacity: mainView.loading ? 1 : 0
running: mainView.loading
}
}
}
ListView {
id: stationListView
anchors {
left: parent.left
top: header.bottom
right: parent.right
bottom: parent.bottom
}
contentHeight: height
contentWidth: width
orientation: ListView.Horizontal
model: stationVisualModel
interactive: false
highlightMoveDuration: 300
onHeightChanged: {
contentHeight = scene.height
}
onWidthChanged: {
contentWidth = scene.width
}
}
}

View File

@@ -0,0 +1,69 @@
import QtQuick 1.1
import tomahawk 1.0
import "../tomahawkimports"
Item {
id: root
anchors.fill: parent
signal done()
function createStation(artist) {
mainView.startStationFromArtist(artist)
root.done()
}
Column {
id: upperColumn
anchors.horizontalCenter: parent.horizontalCenter
height: parent.height
width: defaultFontHeight * 30
anchors.bottomMargin: defaultFontHeight
spacing: defaultFontHeight
HeaderLabel {
id: headerText
text: "Create station by artist..."
}
Row {
height: artistInputField.height
width: parent.width
spacing: defaultFontHeight * 0.5
InputField {
id: artistInputField
width: parent.width - createFromInputButton.width - parent.spacing
onAccepted: createStation(text)
}
PushButton {
id: createFromInputButton
text: "Go!"
enabled: artistInputField.text.length > 2
onClicked: createStation(artistInputField.text)
}
}
Item {
height: parent.height - headerText.height - artistInputField.height - parent.spacing * 3
width: parent.width
ArtistView {
id: artistView
height: parent.height
width: parent.width
model: artistChartsModel
clip: true
delegateHeight: defaultFontHeight * 6
onItemClicked: {
createStation(artistChartsModel.itemFromIndex(index).artistName);
}
}
ScrollBar {
listView: artistView
}
}
}
}

View File

@@ -0,0 +1,88 @@
import QtQuick 1.1
import tomahawk 1.0
import "../tomahawkimports"
Item {
id: root
anchors.fill: parent
signal done()
function createStation(genre) {
mainView.startStationFromGenre(genre)
root.done()
}
ListModel {
id: styleModel
ListElement { modelData: "acoustic" }
ListElement { modelData: "alternative" }
ListElement { modelData: "alternative rock" }
ListElement { modelData: "classic" }
ListElement { modelData: "folk" }
ListElement { modelData: "indie" }
ListElement { modelData: "pop" }
ListElement { modelData: "rock" }
ListElement { modelData: "hip-hop" }
ListElement { modelData: "punk" }
ListElement { modelData: "grunge" }
ListElement { modelData: "indie" }
ListElement { modelData: "electronic" }
ListElement { modelData: "country" }
ListElement { modelData: "jazz" }
ListElement { modelData: "psychodelic" }
ListElement { modelData: "soundtrack" }
ListElement { modelData: "reggae" }
ListElement { modelData: "house" }
ListElement { modelData: "drum and base" }
}
Column {
id: upperColumn
anchors.fill: parent
anchors.bottomMargin: defaultFontHeight
spacing: defaultFontHeight
HeaderLabel {
id: headerText
anchors.horizontalCenter: parent.horizontalCenter
text: "Create station by genre..."
}
Row {
width: defaultFontHeight * 30
height: genreInputField.height
spacing: defaultFontHeight * 0.5
anchors.horizontalCenter: parent.horizontalCenter
InputField {
id: genreInputField
width: parent.width - createFromInputButton.width - parent.spacing
onAccepted: createStation(text);
}
PushButton {
id: createFromInputButton
text: "Go!"
height: genreInputField.height
enabled: genreInputField.text.length > 2
onClicked: createStation(genreInputField.text)
}
}
Item {
height: parent.height - headerText.height - genreInputField.height
width: parent.width
TagCloud {
anchors.fill: parent
anchors.margins: parent.width / 6
model: styleModel
onTagClicked: {
root.createStation(tag);
}
}
}
}
}

View File

@@ -0,0 +1,79 @@
import QtQuick 1.1
import tomahawk 1.0
import "tomahawkimports"
Item {
id: fineTuneView
property color textColor: "white"
signal done();
Grid {
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.margins: 50
anchors.horizontalCenter: parent.horizontalCenter
width: scene.width / 2
spacing: 50
columns: 2
Text {
color: fineTuneView.textColor
text: "Name:"
}
InputField {
text: echonestStation.name
onAccepted: {
print("text changed!!!")
echonestStation.name = text;
}
}
Text {
id: tempoText
text: "Tempo:"
color: "white"
}
DoubleSlider {
width: 500
height: tempoText.height
min: 0
max: 500
lowerSliderPos: echonestStation.minTempo
upperSliderPos: echonestStation.maxTempo
onValueChanged: echonestStation.setTempo( lowerSliderPos, upperSliderPos )
}
Text {
id: hotnessText
text: "Hotness:"
color: "white"
}
DoubleSlider {
width: 500
height: hotnessText.height
min: 0
max: 100
minLabel: "Less"
maxLabel: "More"
showFloatingLabel: false
lowerSliderPos: echonestStation.minHotttness * 100
upperSliderPos: echonestStation.maxHotttness * 100
onValueChanged: echonestStation.setHotttness( 1.0 * lowerSliderPos / 100, 1.0 * upperSliderPos / 100 )
}
}
Button {
id: configureButton
onClicked: fineTuneView.done();
text: "configure"
anchors.bottom: parent.bottom
anchors.bottomMargin: 20
anchors.horizontalCenter: parent.horizontalCenter
}
}

View File

@@ -0,0 +1,64 @@
import QtQuick 1.1
import tomahawk 1.0
import "../tomahawkimports"
Item {
id: root
property alias model: gridView.model
property int spacing: 10
signal itemClicked(int index)
GridView {
id: gridView
anchors.centerIn: parent
width: root.width * 9 / 10
height: cellHeight
cellWidth: (width - 1) / 3
cellHeight: cellWidth //* 10 / 16
delegate: Image {
width: gridView.cellWidth - root.spacing
height: gridView.cellHeight - root.spacing
source: image
smooth: true
Rectangle {
id: textBackground
anchors {
left: parent.left
bottom: parent.bottom
right: parent.right
}
height: parent.height / 5
color: "black"
opacity: .5
}
Text {
anchors.centerIn: textBackground
text: label
color: "white"
font.bold: true
}
Rectangle {
id: hoverShade
anchors.fill: parent
color: "white"
opacity: mouseArea.containsMouse ? .2 : 0
Behavior on opacity {
NumberAnimation { easing.type: Easing.Linear; duration: 300 }
}
}
MouseArea {
id: mouseArea
anchors.fill: parent
hoverEnabled: true
onClicked: root.itemClicked(index)
}
}
}
}

View File

@@ -0,0 +1,25 @@
import QtQuick 1.1
import tomahawk 1.0
import "../tomahawkimports"
Item {
id: root
property int margins: defaultFontHeight * 2
property alias content: contentLoader.source
signal next()
Loader {
id: contentLoader
anchors.fill: parent
anchors.margins: root.margins
}
Connections {
target: contentLoader.item
onDone: root.next()
}
}

View File

@@ -0,0 +1,71 @@
import QtQuick 1.1
import tomahawk 1.0
ListView {
id: root
property int delegateHeight: defaultFontHeight * 3
signal itemClicked(int index)
delegate: Item {
width: parent.width
height: root.delegateHeight
Rectangle {
id: background
anchors.fill: parent
radius: defaultFontHeight / 2
opacity: 0.5
gradient: Gradient {
GradientStop { position: 0.0; color: "#00FFFFFF" }
GradientStop { position: 1.0; color: "#AAFFFFFF" }
}
states: [
State {
name: "hovered"; when: mouseArea.containsMouse
PropertyChanges { target: background; opacity: 1 }
}
]
transitions: [
Transition {
from: "*"; to: "hovered"
NumberAnimation { properties: "opacity"; duration: 100 }
},
Transition {
from: "hovered"; to: "*"
NumberAnimation { properties: "opacity"; duration: 600 }
}
]
}
Row {
anchors.fill: parent
spacing: defaultFontHeight
CoverImage {
id: coverImage
height: parent.height
width: height
showLabels: false
artworkId: model.coverID
}
Text {
text: model.artistName
color: "white"
anchors.verticalCenter: parent.verticalCenter
width: parent.width - coverImage.width - parent.spacing
elide: Text.ElideRight
}
}
MouseArea {
id: mouseArea
anchors.fill: parent
onClicked: root.itemClicked(index)
hoverEnabled: true
}
}
}

View File

@@ -0,0 +1,52 @@
import QtQuick 1.1
Item {
id: busyIndicator
width: 100
height: width
property int barWidth: width / 10
property int barHeight: height / 4
property int count: 12
property color color: "white"
property int currentHighlight: 0
property bool running: true
property int interval: 200
Behavior on opacity {
NumberAnimation { duration: 500 }
}
Repeater {
model: busyIndicator.count
Item {
height: parent.height
width: busyIndicator.barWidth
anchors.centerIn: parent
Rectangle {
anchors {
top: parent.top
left: parent.left
right: parent.right
}
height: busyIndicator.barHeight
radius: width / 2
color: busyIndicator.color
}
rotation: 360 / busyIndicator.count * index
opacity: 1 - ((index > busyIndicator.currentHighlight ? busyIndicator.currentHighlight + busyIndicator.count : busyIndicator.currentHighlight) - index) / busyIndicator.count
Behavior on opacity {
NumberAnimation { duration: busyIndicator.interval }
}
}
}
Timer {
interval: busyIndicator.interval
running: busyIndicator.running
repeat: true
onTriggered: parent.currentHighlight = (parent.currentHighlight + 1) % busyIndicator.count
}
}

View File

@@ -0,0 +1,29 @@
import QtQuick 1.1
Rectangle {
id: root
color: buttonMouseArea.containsMouse ? "blue" : "gray"
border.width: 2
border.color: "white"
radius: height/2
height: buttonText.height * 1.2
width: buttonText.width * 1.5
property alias text: buttonText.text
property color textColor: "white"
signal clicked()
Text {
id: buttonText
anchors.centerIn: parent
color: root.textColor
}
MouseArea {
id: buttonMouseArea
anchors.fill: parent
hoverEnabled: true
onClicked: root.clicked();
}
}

View File

@@ -0,0 +1,137 @@
import QtQuick 1.1
import tomahawk 1.0
PathView {
id: coverView
// The start coordinates for the covers
// Default is left, centered in height
property int pathStartX: 0
property int pathStartY: height
// The size of the covers in the path
property int coverSize: height
property color backgroundColor: "black"
// emitted when a cover is clicked
signal itemClicked(int index)
// emitted when a cover is clicked
signal itemPlayPauseClicked(int index)
preferredHighlightBegin: 0.2 // scene.width / 11000
preferredHighlightEnd: preferredHighlightBegin
pathItemCount: 5
//highlightMoveDuration: 500
property bool itemHovered: false
delegate: Item {
id: delegateItem
height: coverView.coverSize
width: coverView.coverSize
scale: PathView.itemScale
// itemBrightness: PathView.itemBrightness - ((coverView.itemHovered && !coverDelegate.containsMouse) ? .4 : 0)
property double itemBrightness: PathView.itemBrightness
property double itemOpacity: PathView.itemOpacity
property int _origZ
z: coverView.width - x
CoverImage {
id: coverDelegate
height: coverView.coverSize
width: coverView.coverSize
anchors {
top: parent.top
right: parent.right
}
backgroundColor: coverView.backgroundColor
showLabels: true
showMirror: true
artistName: model.artistName
trackName: model.trackName
artworkId: model.coverID
showPlayButton: true
currentlyPlaying: isPlaying
smooth: true
// itemBrightness: PathView.itemBrightness - ((coverView.itemHovered && !coverDelegate.containsMouse) ? .4 : 0)
itemBrightness: coverDelegate.containsMouse ? 1 : parent.itemBrightness * (coverView.itemHovered ? .5 : 1)
opacity: parent.itemOpacity
z: coverView.width - x
onPlayClicked: {
console.log("***************")
coverView.itemPlayPauseClicked(index)
}
onClicked: {
coverView.itemClicked(index)
}
onContainsMouseChanged: {
if (containsMouse) {
delegateItem._origZ = delegateItem.z;
coverView.itemHovered = true
} else {
coverView.itemHovered = false
}
}
}
states: [
State {
name: "hovered"; when: coverDelegate.containsMouse && !coverView.moving && index !== currentIndex
PropertyChanges {
target: delegateItem
width: coverView.coverSize * 2
z: delegateItem._origZ
}
}
]
transitions: [
Transition {
NumberAnimation {
properties: "width"
duration: 300
easing.type: Easing.InOutSine
}
}
]
}
path: Path {
startX: coverView.pathStartX
startY: coverView.pathStartY
PathAttribute { name: "itemOpacity"; value: 0 }
PathAttribute { name: "itemBrightness"; value: 0 }
PathAttribute { name: "itemScale"; value: 1.3 }
PathLine { x: coverView.width / 4; y: coverView.height / 4 * 3}
PathPercent { value: 0.1 }
PathAttribute { name: "itemOpacity"; value: 0 }
PathAttribute { name: "itemBrightness"; value: 1 }
PathAttribute { name: "itemScale"; value: 1.0 }
PathLine { x: coverView.width / 2; y: coverView.height / 2}
PathPercent { value: 0.2 }
PathAttribute { name: "itemOpacity"; value: 1 }
PathAttribute { name: "itemBrightness"; value: 1 }
PathAttribute { name: "itemScale"; value: 0.5 }
PathLine { x: coverView.width; y: 0 }
PathPercent { value: 1 }
PathAttribute { name: "itemOpacity"; value: 1 }
PathAttribute { name: "itemBrightness"; value: 0 }
PathAttribute { name: "itemScale"; value: 0.1 }
}
}

View File

@@ -0,0 +1,197 @@
import QtQuick 1.1
Item {
id: root
// Should the artist + track labels be painted
property bool showLabels: true
// Should the play button be painted on mouse hover?
property bool showPlayButton: false
// if this is true, the play button will be swapped by a pause button
property bool currentlyPlaying: false
// Should the mirror be painted?
property bool showMirror: false
// Labels & Cover
property string artistName
property string trackName
property string artworkId
// The border color for the cover image
property color borderColor: "black"
// The border width for the cover image
property int borderWidth: 2
// needed to adjust the shadow
property color backgroundColor: "black"
// sets the brightness for the item and its mirror (1: brightest, 0: darkest)
property double itemBrightness: 1
property double mirrorBrightness: .5
// set this to true if you want to smoothly scale the cover (be aware of performance impacts)
property bool smooth: false
// will be emitted when the on hower play button is clicked
signal playClicked()
// will be emitted when the cover is clicked
signal clicked()
// will be emitted when the cover is hovered by the mouse
property alias containsMouse: mouseArea.containsMouse
MouseArea {
id: mouseArea
anchors.fill: parent
hoverEnabled: true
onClicked: {
print("Cover clicked");
root.clicked();
}
}
Rectangle {
id: itemShadow
color: backgroundColor
anchors.fill: parent
//opacity: 1 - itemBrightness
Behavior on opacity {
NumberAnimation { easing.type: Easing.Linear; duration: 300 }
}
}
Component {
id: coverImage
Item {
property bool isMirror: false
Image {
anchors.fill: parent
source: "image://albumart/" + artworkId + (isMirror ? "-mirror" : "") + (showLabels ? "-labels" : "")
smooth: root.smooth
opacity: itemBrightness
Behavior on opacity {
NumberAnimation { duration: 300 }
}
}
Rectangle {
id: itemGlow
anchors.fill: parent
anchors.topMargin: isMirror ? parent.height / 2 : 0
opacity: (mouseArea.containsMouse ? .2 : 0)
Gradient {
id: glowGradient
GradientStop { position: 0.0; color: "white" }
GradientStop { position: 0.7; color: "white" }
GradientStop { position: 0.8; color: "#00000000" }
GradientStop { position: 1.0; color: "#00000000" }
}
Gradient {
id: mirrorGlowGradient
GradientStop { position: 0.0; color: "#00000000" }
GradientStop { position: 0.5; color: "#00000000" }
GradientStop { position: 1.0; color: "#44FFFFFF" }
}
states: [
State {
name: "mirrored"; when: isMirror
PropertyChanges {
target: itemGlow
gradient: mirrorGlowGradient
}
},
State {
name: "normal"; when: !isMirror
PropertyChanges {
target: itemGlow
gradient: glowGradient
}
}
]
Behavior on opacity {
NumberAnimation { easing.type: Easing.Linear; duration: 300 }
}
}
Text {
id: trackText
color: "white"
font.bold: true
text: trackName
anchors { left: parent.left; right: parent.right; bottom: artistText.top }
anchors.margins: 2
horizontalAlignment: Text.AlignHCenter
elide: Text.ElideRight
opacity: showLabels ? itemBrightness * (isMirror ? 0.5 : 1): 0
font.pixelSize: root.height / 15
Behavior on opacity {
NumberAnimation { duration: 300 }
}
}
Text {
id: artistText
color: "white"
font.bold: trackText.text.length == 0
text: artistName
anchors { left: parent.left; right: parent.right; bottom: parent.bottom }
anchors.margins: root.height / 20
horizontalAlignment: Text.AlignHCenter
elide: Text.ElideRight
opacity: showLabels ? itemBrightness * (isMirror ? 0.5 : 1) : 0
font.pixelSize: trackText.text.length == 0 ? root.height / 10 : root.height / 15
Behavior on opacity {
NumberAnimation { duration: 300 }
}
}
}
}
Loader {
sourceComponent: coverImage
anchors.fill: parent
}
Loader {
id: mirroredCover
sourceComponent: parent.showMirror ? coverImage : undefined
anchors.fill: parent
onLoaded: {
item.isMirror = true
}
transform : [
Rotation {
angle: 180; origin.y: root.height
axis.x: 1; axis.y: 0; axis.z: 0
}
]
}
Image {
id: playButton
visible: showPlayButton ? (mouseArea.containsMouse || currentlyPlaying) : false
source: currentlyPlaying ? "../../images/pause-rest.svg" : "../../images/play-rest.svg"
anchors.centerIn: parent
height: mirroredCover.height / 5
width: height
smooth: root.smooth
MouseArea {
anchors.fill: parent
onClicked: {
print("Play button clicked");
root.playClicked();
}
}
}
}

View File

@@ -0,0 +1,151 @@
import QtQuick 1.1
Item {
id: root
width: 500
height: 10
property int min: 0
property int max: 100
/** The labels next to the slider
* if empty, min and max values are used
*/
property string minLabel: ""
property string maxLabel: ""
/** Should the floating label indicating the current position be shown? */
property bool showFloatingLabel: true
property int lowerSliderPos: 25
property int upperSliderPos: 75
signal valueChanged()
Row {
anchors.fill: parent
spacing: 10
Text {
id: minText
text: root.minLabel.length > 0 ? root.minLabel : min
color: "white"
}
Item {
id: sliderRect
height: root.height
width: parent.width - minText.width - maxText.width - parent.spacing * 2
function sliderPosToValue( sliderPos ) {
var percent = sliderPos * 100 / (sliderRect.width - lowerSlider.width);
return Math.floor(percent * (root.max - root.min) / 100) + root.min
}
function valueToSloderPos( value ) {
var percent = (value - root.min) * 100 / (root.max - root.min)
return percent * (sliderRect.width - lowerSlider.width) / 100
}
Rectangle {
id: sliderBase
height: root.height / 5
width: parent.width
color: "white"
radius: height / 2
anchors.centerIn: parent
}
Rectangle {
id: lowerSlider
height: root.height
width: height
anchors.top: root.top
radius: height/2
border.color: "black"
border.width: 2
x: sliderRect.valueToSloderPos(root.lowerSliderPos)
Rectangle {
id: lowerFloatingRect
color: "white"
anchors.bottom: lowerSlider.top
anchors.bottomMargin: 10
visible: root.showFloatingLabel && lowerSliderMouseArea.pressed
width: lowerFloatingText.width * 1.2
height: lowerFloatingText.height + height * 1.2
x: -(width - lowerSlider.width) / 2
radius: height / 4
Text {
id: lowerFloatingText
anchors.centerIn: parent
text: sliderRect.sliderPosToValue(lowerSlider.x)
}
}
}
MouseArea {
id: lowerSliderMouseArea
anchors.fill: lowerSlider
drag.target: lowerSlider
drag.axis: "XAxis"
drag.minimumX: 0
drag.maximumX: upperSlider.x - lowerSlider.width
onReleased: {
root.lowerSliderPos = sliderRect.sliderPosToValue( lowerSlider.x );
root.valueChanged();
}
}
Rectangle {
id: upperSlider
height: root.height
width: height
anchors.top: root.top
radius: height/2
border.color: "black"
border.width: 2
x: sliderRect.valueToSloderPos(root.upperSliderPos)
Rectangle {
id: upperFloatingRect
color: "white"
anchors.bottom: upperSlider.top
anchors.bottomMargin: 10
visible: root.showFloatingLabel && upperSliderMouseArea.pressed
width: upperFloatingText.width * 1.2
height: upperFloatingText.height + height * 1.2
radius: height / 4
x: -(width - upperSlider.width) / 2
Text {
id: upperFloatingText
anchors.centerIn: parent
text: sliderRect.sliderPosToValue(upperSlider.x)
}
}
}
MouseArea {
id: upperSliderMouseArea
anchors.fill: upperSlider
onClicked: print("button pressed")
drag.target: upperSlider
drag.axis: "XAxis"
drag.minimumX: lowerSlider.x + lowerSlider.width
drag.maximumX: parent.width - upperSlider.width
onReleased: {
root.upperSliderPos = sliderRect.sliderPosToValue( upperSlider.x );
root.valueChanged();
}
}
}
Text {
id: maxText
text: root.maxLabel.length > 0 ? root.maxLabel : max
color: "white"
}
}
}

View File

@@ -0,0 +1,199 @@
import QtQuick 1.1
import tomahawk 1.0
Rectangle {
id: root
// The icon
property alias icon: iconImage.source
// The title
property alias title: titleItem.titleText
// The subtitle/description
property alias subtitle: subtitleText.text
// The model for the ToggleViewButtons.
// "modelData" role name holds the iconSource
// => You can use a QStringList or StandardListModel here
property alias buttonModel: toggleViewButtons.model
// The index of the currently selected item
property alias currentButtonIndex: toggleViewButtons.currentIndex
// Should we show the searchfield?
property bool showSearchField: true
// The SearchFields text
property alias searchText: searchField.text
property bool showBackButton: false
property bool showNextButton: false
property string backButtonText: "Back"
property string nextButtonText: "Next"
// Layout spacing
property int spacing: defaultFontHeight * 0.5
signal backPressed()
signal nextPressed()
signal savePressed()
gradient: Gradient {
GradientStop { position: 0.0; color: "#615858" }
GradientStop { position: 1.0; color: "#231F1F" }
}
Row {
id: leftRow
anchors {
left: parent.left
top: parent.top
bottom: parent.bottom
right: rightRow.left
}
anchors.margins: root.spacing
spacing: root.spacing
Image {
id: iconImage
height: parent.height * 0.8
width: height
anchors.verticalCenter: parent.verticalCenter
smooth: true
}
Column {
height: parent.height
width: parent.width - iconImage.width - parent.spacing
Item {
id: titleItem
height: captionText1.height
width: parent.width
clip: true
property string titleText
onTitleTextChanged: {
if(captionText1.text.length > 0) {
captionText2.text = titleText;
renewTitleAnimation.start();
} else {
captionText1.text = titleText;
}
}
ParallelAnimation {
id: renewTitleAnimation
property int duration: 500
property variant easingType: Easing.OutBounce;
NumberAnimation { target: captionText2; property: "anchors.topMargin"; to: 0; duration: renewTitleAnimation.duration; easing.type: renewTitleAnimation.easingType }
NumberAnimation { target: captionText1; property: "anchors.topMargin"; to: captionText1.height * 2; duration: renewTitleAnimation.duration; easing.type: renewTitleAnimation.easingType }
onCompleted: {
captionText1.text = titleItem.titleText
captionText2.anchors.topMargin = -captionText2.height * 2
captionText1.anchors.topMargin = 0
}
}
Text {
id: captionText1
color: "white"
anchors.left: parent.left
anchors.top: parent.top
font.pointSize: defaultFontSize * 1.5
font.bold: true
width: parent.width
elide: Text.ElideRight
}
Text {
id: captionText2
color: "white"
anchors.left: parent.left
anchors.top: parent.top
anchors.topMargin: -height * 2
font.pointSize: defaultFontSize * 1.5
font.bold: true
width: parent.width
elide: Text.ElideRight
}
}
Text {
id: subtitleText
color: "white"
font.pointSize: defaultFontSize * 1.2
width: parent.width
elide: Text.ElideRight
}
}
}
Row {
id: rightRow
anchors {
top: parent.top
right: parent.right
rightMargin: -backButton.width - root.spacing - nextButton.width
bottom: parent.bottom
margins: root.spacing
}
width: childrenRect.width
spacing: root.spacing
layoutDirection: Qt.RightToLeft
states: [
State {
name: "oneVisible"; when: root.showBackButton && !root.showNextButton
PropertyChanges {
target: rightRow
anchors.rightMargin: -nextButton.width
}
},
State {
name: "bothVisible"; when: root.showBackButton && root.showNextButton
PropertyChanges {
target: rightRow
anchors.rightMargin: root.spacing
}
}
]
Behavior on anchors.rightMargin {
NumberAnimation { duration: 200 }
}
PushButton {
id: nextButton
anchors.verticalCenter: parent.verticalCenter
text: root.nextButtonText
onClicked: root.nextPressed();
}
PushButton {
id: backButton
anchors.verticalCenter: parent.verticalCenter
text: root.backButtonText
onClicked: root.backPressed();
}
InputField {
id: searchField
visible: root.showSearchField
anchors.verticalCenter: parent.verticalCenter
placeholderText: "Search..."
showSearchIcon: true
}
ToggleViewButtons {
id: toggleViewButtons
anchors.verticalCenter: parent.verticalCenter
height: defaultFontHeight * 1.5
}
}
}

View File

@@ -0,0 +1,7 @@
import QtQuick 1.1
Text {
color: "white"
font.pointSize: defaultFontSize + 5
font.bold: true
}

View File

@@ -0,0 +1,90 @@
import QtQuick 1.1
Rectangle {
id: root
color: "white"
border.color: "black"
border.width: defaultFontHeight * 0.1
radius: defaultFontHeight * 0.25
height: textInput.height * 1.4
width: 300
property bool showSearchIcon: false
property string text: ""
property string placeholderText: ""
property int spacing: defaultFontHeight * 0.2
signal accepted( string text )
Image {
id: searchIcon
anchors {
left: parent.left
leftMargin: root.spacing
verticalCenter: parent.verticalCenter
}
height: parent.height * 0.6
width: root.showSearchIcon ? height : 1
opacity: root.showSearchIcon ? 1 : 0
smooth: true
source: "../../images/search-icon.svg"
}
Item {
id: textItem
anchors.left: searchIcon.right
anchors.leftMargin: root.spacing
anchors.right: clearIcon.right
anchors.rightMargin: root.spacing
height: textInput.height
anchors.verticalCenter: parent.verticalCenter
TextInput {
id: textInput
width: parent.width
anchors.centerIn: parent
text: root.text
font.pointSize: defaultFontSize
onAccepted: root.accepted( text );
onTextChanged: root.text = text;
}
Text {
width: parent.width
anchors.centerIn: parent
text: root.text.length === 0 ? root.placeholderText : ""
color: "lightgray"
font.pointSize: defaultFontSize
}
}
Image {
id: clearIcon
anchors {
right: parent.right
rightMargin: root.spacing
verticalCenter: parent.verticalCenter
}
height: parent.height * 0.8
width: (root.showSearchIcon && root.text.length > 0) ? height : 1
opacity: (root.showSearchIcon && root.text.length > 0) ? 1 : 0
smooth: true
source: "../../images/search-box-dismiss-x.svg"
MouseArea {
anchors.fill: parent
onClicked: textInput.text = ""
}
}
BorderImage {
source: "../../images/inputfield-border.svg"
anchors.fill: parent
anchors.margins: root.radius * 0.1
clip: true
border.left: defaultFontHeight/4; border.top: defaultFontHeight/4
border.right: defaultFontHeight/4; border.bottom: defaultFontHeight/4
}
}

View File

@@ -0,0 +1,34 @@
import QtQuick 1.1
//import tomahawk 1.0
Rectangle {
id: root
height: buttonText.height * 1.4
width: buttonText.width + (spacing * 2)
radius: defaultFontHeight * 0.25
border.width: defaultFontHeight * 0.05
border.color: "#a7a7a7"
gradient: Gradient {
GradientStop { position: 0.0; color: mouseArea.pressed ? "#040404" : "#fbfbfb" }
GradientStop { position: 1.0; color: mouseArea.pressed ? "#8e8f8e" : "#787878" }
}
property int spacing: defaultFontHeight * 0.5
property alias text: buttonText.text
signal clicked()
Text {
id: buttonText
anchors.centerIn: root
font.pointSize: defaultFontSize
color: mouseArea.pressed ? "white" : "black"
}
MouseArea {
id: mouseArea
anchors.fill: parent
onClicked: root.clicked()
}
}

View File

@@ -0,0 +1,43 @@
import QtQuick 1.1
import tomahawk 1.0
Rectangle {
id: root
border.width: 4
border.color: enabled ? "white" : "grey"
radius: height / 2
color: (buttonMouseArea.containsMouse && enabled) ? "#22ffffff" : "black"
opacity: hidden ? 0 : 1
height: defaultFontHeight * 2
width: height
property string text
property bool enabled: true
property bool hidden: false
signal clicked()
Behavior on opacity {
NumberAnimation { duration: 200 }
}
Behavior on color {
ColorAnimation { duration: 200 }
}
Text {
anchors.centerIn: parent
text: parent.text
color: root.border.color
font.pixelSize: parent.height * .75
font.bold: true
}
MouseArea {
id: buttonMouseArea
anchors.fill: parent
hoverEnabled: true
enabled: root.enabled
onClicked: parent.clicked()
}
}

View File

@@ -0,0 +1,69 @@
import QtQuick 1.1
Item {
id: scrollBar
width: defaultFontHeight / 2
// the ListView where to attach this scrollbar
property variant listView
// the orientation of the scrollbar
property variant orientation : Qt.Vertical
property int margin: defaultFontHeight * 0.25
states: [
State {
name: "hidden"; when: !listView.moving
PropertyChanges { target: scrollBar; opacity: 0 }
},
State {
name: "visible"; when: listView.moving
PropertyChanges { target: scrollBar; opacity: 1 }
}
]
transitions: [
Transition {
from: "hidden"
to: "visible"
NumberAnimation { properties: "opacity"; duration: 200 }
},
Transition {
from: "visible"
to: "hidden"
NumberAnimation { properties: "opacity"; duration: 2000 }
}
]
anchors {
left: orientation == Qt.Vertical ? listView.right : listView.left
leftMargin: orientation == Qt.Vertical ? scrollBar.margin : 0
top: orientation == Qt.Vertical ? listView.top : listView.bottom
topMargin: orientation == Qt.Vertical ? 0 : scrollBar.margin
bottom: orientation == Qt.Vertical ? listView.bottom : undefined
right: orientation == Qt.Vertical ? undefined : listView.right
}
// A light, semi-transparent background
Rectangle {
id: background
anchors.fill: parent
radius: orientation == Qt.Vertical ? (width/2 - 1) : (height/2 - 1)
color: "white"
opacity: 0.2
clip: true
// Size the bar to the required size, depending upon the orientation.
Rectangle {
property real position: orientation == Qt.Vertical ? (listView.contentY / listView.contentHeight) : (listView.contentX / listView.contentWidth)
property real pageSize: orientation == Qt.Vertical ? (listView.height / listView.contentHeight) : (listView.width / listView.contentWidth)
x: orientation == Qt.Vertical ? 1 : (position * (scrollBar.width-2) + 1)
y: orientation == Qt.Vertical ? (position * (scrollBar.height-2) + 1) : 1
width: orientation == Qt.Vertical ? (parent.width-2) : (pageSize * (scrollBar.width-2))
height: orientation == Qt.Vertical ? (pageSize * (scrollBar.height-2)) : (parent.height-2)
radius: orientation == Qt.Vertical ? (width/2 - 1) : (height/2 - 1)
color: "white"
opacity: 1
}
}
}

View File

@@ -0,0 +1,80 @@
import QtQuick 1.1
import tomahawk 1.0
Item {
id: tagCloud
property variant model: 10
signal tagClicked( string tag )
function randomNumber(min, max) {
var date = new Date();
return (max - min) * Math.random(date.getSeconds()) + min
}
Flow {
anchors.centerIn: parent
width: parent.width
spacing: 3
Repeater {
id: cloudRepeater
model: tagCloud.model
delegate: Item {
id: cloudItem
width: delegateText.width * 1.1
height: delegateText.height
property double itemScale: Math.random() + .3
scale: itemScale
Text {
id: delegateText
color: "gray"
//text: controlModel.controlAt( index ).summary
text: modelData
font.pointSize: 16
anchors.verticalCenter: parent.verticalCenter
anchors.verticalCenterOffset: tagCloud.randomNumber(0, 15)
states: [
State {
name: "hovered"; when: cloudItemMouseArea.containsMouse
PropertyChanges {
target: delegateText
color: "white"
}
}
]
transitions: [
Transition {
from: "*"
to: "hovered"
ColorAnimation {
duration: 200
}
},
Transition {
from: "hovered"
to: "*"
ColorAnimation {
duration: 1000
}
}
]
}
MouseArea {
id: cloudItemMouseArea
hoverEnabled: true
anchors.fill: parent
onClicked: tagCloud.tagClicked( modelData )
}
Behavior on scale {
NumberAnimation { easing: Easing.Linear; duration: 1000 }
}
}
}
}
}

View File

@@ -0,0 +1,34 @@
import QtQuick 1.1
import tomahawk 1.0
Row {
id: root
width: repeater.width
property alias model: repeater.model
property int currentIndex: 0
Repeater {
id: repeater
height: root.height
width: count * height
delegate: Image {
height: repeater.height
width: height
source: "../../images/view-toggle-" + (index === root.currentIndex ? "active-" : "inactive-" ) + (index === 0 ? "left" : ( index === repeater.count - 1 ? "right" : "centre" )) + ".svg"
smooth: true
Image {
anchors.fill: parent
source: "../../images/" + modelData + (index === root.currentIndex ? "-active.svg" : "-inactive.svg")
}
MouseArea {
id: mouseArea
anchors.fill: parent
onClicked: root.currentIndex = index
}
}
}
}

View File

@@ -35,21 +35,21 @@
<file>data/images/skip-pressed.svg</file>
<file>data/images/skip-rest.svg</file>
<file>data/images/user-avatar.svg</file>
<file>data/images/view-toggle-active-centre.png</file>
<file>data/images/view-toggle-active-left.png</file>
<file>data/images/view-toggle-active-right.png</file>
<file>data/images/view-toggle-active-centre.svg</file>
<file>data/images/view-toggle-active-left.svg</file>
<file>data/images/view-toggle-active-right.svg</file>
<file>data/images/view-toggle-icon-artist-active.svg</file>
<file>data/images/view-toggle-icon-artist-inactive.svg</file>
<file>data/images/view-toggle-icon-grid-active.svg</file>
<file>data/images/view-toggle-icon-grid-inactive.svg</file>
<file>data/images/view-toggle-icon-list-active.svg</file>
<file>data/images/view-toggle-icon-list-inactive.svg</file>
<file>data/images/view-toggle-inactive-centre.png</file>
<file>data/images/view-toggle-inactive-left.png</file>
<file>data/images/view-toggle-inactive-right.png</file>
<file>data/images/view-toggle-pressed-centre.png</file>
<file>data/images/view-toggle-pressed-left.png</file>
<file>data/images/view-toggle-pressed-right.png</file>
<file>data/images/view-toggle-inactive-centre.svg</file>
<file>data/images/view-toggle-inactive-left.svg</file>
<file>data/images/view-toggle-inactive-right.svg</file>
<file>data/images/view-toggle-pressed-centre.svg</file>
<file>data/images/view-toggle-pressed-left.svg</file>
<file>data/images/view-toggle-pressed-right.svg</file>
<file>data/images/list-add.svg</file>
<file>data/images/list-remove.svg</file>
<file>data/images/arrow-up-double.svg</file>
@@ -139,6 +139,25 @@
<file>data/images/account-none.svg</file>
<file>data/images/green-dot.svg</file>
<file>data/images/exfm.png</file>
<file>data/qml/tomahawkimports/CoverImage.qml</file>
<file>data/qml/tomahawkimports/ArtistView.qml</file>
<file>data/qml/tomahawkimports/HeaderLabel.qml</file>
<file>data/qml/tomahawkimports/TagCloud.qml</file>
<file>data/qml/tomahawkimports/ScrollBar.qml</file>
<file>data/qml/tomahawkimports/InputField.qml</file>
<file>data/qml/tomahawkimports/Button.qml</file>
<file>data/qml/tomahawkimports/DoubleSlider.qml</file>
<file>data/qml/tomahawkimports/RoundedButton.qml</file>
<file>data/qml/tomahawkimports/PushButton.qml</file>
<file>data/qml/tomahawkimports/CoverFlip.qml</file>
<file>data/qml/tomahawkimports/BusyIndicator.qml</file>
<file>data/qml/StationView.qml</file>
<file>data/qml/stations/StationCreatorPage1.qml</file>
<file>data/qml/stations/StationCreatorPage2.qml</file>
<file>data/qml/stations/CreateByArtist.qml</file>
<file>data/qml/stations/StationConfig.qml</file>
<file>data/qml/QmlGridView.qml</file>
<file>data/qml/stations/CreateByGenre.qml</file>
<file>data/images/soundcloud.svg</file>
<file>data/images/resolver-default.svg</file>
<file>data/images/spotify-sourceicon.svg</file>
@@ -147,6 +166,15 @@
<file>data/images/cancel.svg</file>
<file>data/images/delete.svg</file>
<file>data/images/ok.svg</file>
<file>data/qml/tomahawkimports/FlexibleHeader.qml</file>
<file>data/qml/tomahawkimports/ToggleViewButtons.qml</file>
<file>data/qml/DeclarativeHeader.qml</file>
<file>data/images/inputfield-border.svg</file>
<file>data/images/search-box-dismiss-x.svg</file>
<file>data/images/loading-animation.svg</file>
<file>data/images/station-artist.svg</file>
<file>data/images/station-genre.svg</file>
<file>data/images/station-year.svg</file>
<file>data/images/tweet.svg</file>
<file>data/images/widget-border.png</file>
</qresource>

View File

@@ -12,6 +12,7 @@ SET( QT_USE_QTSQL TRUE )
SET( QT_USE_QTNETWORK TRUE )
SET( QT_USE_QTXML TRUE )
SET( QT_USE_QTWEBKIT TRUE )
SET( QT_USE_QTDECLARATIVE TRUE )
INCLUDE( ${QT_USE_FILE} )
INCLUDE( AddAppIconMacro )
@@ -210,6 +211,7 @@ TARGET_LINK_LIBRARIES( tomahawk
${QXTWEB_LIBRARIES}
${QJSON_LIBRARIES}
${TAGLIB_LIBRARIES}
${QT_QTDECLARATIVE_LIBRARY}
)
IF( APPLE )

View File

@@ -37,7 +37,6 @@
#include "database/DatabaseResolver.h"
#include "playlist/dynamic/GeneratorFactory.h"
#include "playlist/dynamic/echonest/EchonestGenerator.h"
#include "playlist/dynamic/database/DatabaseGenerator.h"
#include "playlist/XspfUpdater.h"
#include "network/Servent.h"
#include "web/Api_v1.h"
@@ -216,8 +215,6 @@ TomahawkApp::init()
tDebug() << "Init Echonest Factory.";
GeneratorFactory::registerFactory( "echonest", new EchonestFactory );
#endif
tDebug() << "Init Database Factory.";
GeneratorFactory::registerFactory( "database", new DatabaseFactory );
// Register shortcut handler for this platform
#ifdef Q_WS_MAC

View File

@@ -36,19 +36,11 @@ using namespace Tomahawk;
QHash< QString, album_ptr > Album::s_albumsByName = QHash< QString, album_ptr >();
QHash< unsigned int, album_ptr > Album::s_albumsById = QHash< unsigned int, album_ptr >();
QHash< QString, album_ptr > Album::s_albumsByCoverId = QHash< QString, album_ptr >();
static QMutex s_nameCacheMutex;
static QMutex s_idCacheMutex;
static QMutex s_mutex;
static QReadWriteLock s_idMutex;
Album::~Album()
{
m_ownRef.clear();
#ifndef ENABLE_HEADLESS
delete m_cover;
#endif
}
inline QString
albumCacheKey( const Tomahawk::artist_ptr& artist, const QString& albumName )
@@ -57,13 +49,27 @@ albumCacheKey( const Tomahawk::artist_ptr& artist, const QString& albumName )
}
Album::~Album()
{
/* QMutexLocker lock( &s_mutex );
s_albumsByName.remove( albumCacheKey( artist(), name() ) );
s_albumsByCoverId.remove( coverId() );*/
/* if ( id() > 0 )
s_albumsById.remove( id() );*/
#ifndef ENABLE_HEADLESS
delete m_cover;
#endif
}
album_ptr
Album::get( const Tomahawk::artist_ptr& artist, const QString& name, bool autoCreate )
{
if ( !Database::instance() || !Database::instance()->impl() )
return album_ptr();
QMutexLocker l( &s_nameCacheMutex );
QMutexLocker l( &s_mutex );
const QString key = albumCacheKey( artist, name );
if ( s_albumsByName.contains( key ) )
@@ -76,6 +82,9 @@ Album::get( const Tomahawk::artist_ptr& artist, const QString& name, bool autoCr
album->loadId( autoCreate );
s_albumsByName.insert( key, album );
s_albumsByCoverId[ album->coverId() ] = album;
s_albumsByName[ key ] = album;
return album;
}
@@ -84,9 +93,8 @@ album_ptr
Album::get( unsigned int id, const QString& name, const Tomahawk::artist_ptr& artist )
{
static QHash< unsigned int, album_ptr > s_albums;
static QMutex s_mutex;
QMutexLocker lock( &s_idCacheMutex );
QMutexLocker lock( &s_mutex );
const QString key = albumCacheKey( artist, name );
if ( s_albumsByName.contains( key ) )
@@ -102,6 +110,8 @@ Album::get( unsigned int id, const QString& name, const Tomahawk::artist_ptr& ar
a->setWeakRef( a.toWeakRef() );
s_albumsByName.insert( key, a );
s_albumsByCoverId[ a->coverId() ] = a;
s_albumsByName[ albumCacheKey( artist, name ) ] = a;
if ( id > 0 )
{
s_albumsById.insert( id, a );
@@ -111,6 +121,18 @@ Album::get( unsigned int id, const QString& name, const Tomahawk::artist_ptr& ar
}
album_ptr
Album::getByCoverId( const QString& uuid )
{
QMutexLocker lock( &s_mutex );
if ( s_albumsByCoverId.contains( uuid ) )
return s_albumsByCoverId.value( uuid );
return album_ptr();
}
Album::Album( unsigned int id, const QString& name, const Tomahawk::artist_ptr& artist )
: QObject()
, m_waitingForId( false )
@@ -141,6 +163,7 @@ Album::Album( const QString& name, const Tomahawk::artist_ptr& artist )
m_sortname = DatabaseImpl::sortname( name );
}
void
Album::onTracksLoaded( Tomahawk::ModelMode mode, const Tomahawk::collection_ptr& collection )
{
@@ -187,7 +210,10 @@ Album::id() const
m_waitingForId = false;
if ( m_id > 0 )
{
QMutexLocker lock( &s_mutex );
s_albumsById[ m_id ] = m_ownRef.toStrongRef();
}
s_idMutex.unlock();
}
@@ -216,7 +242,7 @@ Album::cover( const QSize& size, bool forceLoad ) const
trackInfo["album"] = name();
Tomahawk::InfoSystem::InfoRequestData requestData;
requestData.caller = infoid();
requestData.caller = uniqueId();
requestData.type = Tomahawk::InfoSystem::InfoAlbumCoverArt;
requestData.input = QVariant::fromValue< Tomahawk::InfoSystem::InfoStringHash >( trackInfo );
requestData.customData = QVariantMap();
@@ -267,7 +293,7 @@ Album::cover( const QSize& size, bool forceLoad ) const
void
Album::infoSystemInfo( const Tomahawk::InfoSystem::InfoRequestData& requestData, const QVariant& output )
{
if ( requestData.caller != infoid() ||
if ( requestData.caller != uniqueId() ||
requestData.type != Tomahawk::InfoSystem::InfoAlbumCoverArt )
{
return;
@@ -286,7 +312,13 @@ Album::infoSystemInfo( const Tomahawk::InfoSystem::InfoRequestData& requestData,
m_coverBuffer = ba;
}
m_coverLoaded = true;
{
QMutexLocker lock( &s_mutex );
m_coverLoaded = true;
s_albumsByCoverId.remove( coverId() );
m_coverId = uuid();
s_albumsByCoverId[ m_coverId ] = m_ownRef.toStrongRef();
}
emit coverChanged();
}
}
@@ -295,7 +327,7 @@ Album::infoSystemInfo( const Tomahawk::InfoSystem::InfoRequestData& requestData,
void
Album::infoSystemFinished( const QString& target )
{
if ( target != infoid() )
if ( target != uniqueId() )
return;
disconnect( Tomahawk::InfoSystem::InfoSystem::instance(), SIGNAL( info( Tomahawk::InfoSystem::InfoRequestData, QVariant ) ),
@@ -335,10 +367,20 @@ Album::tracks( ModelMode mode, const Tomahawk::collection_ptr& collection )
QString
Album::infoid() const
Album::uniqueId() const
{
if ( m_uuid.isEmpty() )
m_uuid = uuid();
return m_uuid;
}
QString
Album::coverId() const
{
if ( m_coverId.isEmpty() )
m_coverId = uuid();
return m_coverId;
}

View File

@@ -47,6 +47,7 @@ Q_OBJECT
public:
static album_ptr get( const Tomahawk::artist_ptr& artist, const QString& name, bool autoCreate = false );
static album_ptr get( unsigned int id, const QString& name, const Tomahawk::artist_ptr& artist );
static album_ptr getByCoverId( const QString& uuid );
Album( unsigned int id, const QString& name, const Tomahawk::artist_ptr& artist );
Album( const QString& name, const Tomahawk::artist_ptr& artist );
@@ -55,6 +56,8 @@ public:
unsigned int id() const;
QString name() const { return m_name; }
QString sortname() const { return m_sortname; }
QString uniqueId() const;
QString coverId() const;
artist_ptr artist() const;
#ifndef ENABLE_HEADLESS
@@ -83,7 +86,6 @@ private slots:
private:
Q_DISABLE_COPY( Album )
QString infoid() const;
void setIdFuture( QFuture<unsigned int> future );
mutable bool m_waitingForId;
@@ -97,6 +99,7 @@ private:
mutable bool m_coverLoaded;
mutable bool m_coverLoading;
mutable QString m_uuid;
mutable QString m_coverId;
mutable QByteArray m_coverBuffer;
#ifndef ENABLE_HEADLESS
@@ -110,6 +113,7 @@ private:
static QHash< QString, album_ptr > s_albumsByName;
static QHash< unsigned int, album_ptr > s_albumsById;
static QHash< QString, album_ptr > s_albumsByCoverId;
friend class ::IdThreadWorker;
};

View File

@@ -39,20 +39,11 @@ using namespace Tomahawk;
QHash< QString, artist_ptr > Artist::s_artistsByName = QHash< QString, artist_ptr >();
QHash< unsigned int, artist_ptr > Artist::s_artistsById = QHash< unsigned int, artist_ptr >();
QHash< QString, artist_ptr > Artist::s_artistsByCoverId = QHash< QString, artist_ptr >();
static QMutex s_nameCacheMutex;
static QMutex s_idCacheMutex;
static QMutex s_mutex;
static QReadWriteLock s_idMutex;
Artist::~Artist()
{
m_ownRef.clear();
#ifndef ENABLE_HEADLESS
delete m_cover;
#endif
}
artist_ptr
Artist::get( const QString& name, bool autoCreate )
@@ -62,7 +53,7 @@ Artist::get( const QString& name, bool autoCreate )
const QString sortname = name.toLower();
QMutexLocker lock( &s_nameCacheMutex );
QMutexLocker lock( &s_mutex );
if ( s_artistsByName.contains( sortname ) )
return s_artistsByName.value( sortname );
@@ -77,6 +68,7 @@ Artist::get( const QString& name, bool autoCreate )
artist->setWeakRef( artist.toWeakRef() );
artist->loadId( autoCreate );
s_artistsByName.insert( sortname, artist );
s_artistsByCoverId[ artist->coverId() ] = artist;
return artist;
}
@@ -85,7 +77,7 @@ Artist::get( const QString& name, bool autoCreate )
artist_ptr
Artist::get( unsigned int id, const QString& name )
{
QMutexLocker lock( &s_idCacheMutex );
QMutexLocker lock( &s_mutex );
const QString sortname = name.toLower();
if ( s_artistsByName.contains( sortname ) )
@@ -101,6 +93,7 @@ Artist::get( unsigned int id, const QString& name )
a->setWeakRef( a.toWeakRef() );
s_artistsByName.insert( sortname, a );
s_artistsByCoverId[ a->coverId() ] = a;
if ( id > 0 )
{
s_artistsById.insert( id, a );
@@ -110,6 +103,18 @@ Artist::get( unsigned int id, const QString& name )
}
artist_ptr
Artist::getByCoverId( const QString& uuid )
{
QMutexLocker lock( &s_mutex );
if ( s_artistsByCoverId.contains( uuid ) )
return s_artistsByCoverId.value( uuid );
return artist_ptr();
}
Artist::Artist( unsigned int id, const QString& name )
: QObject()
, m_waitingForFuture( false )
@@ -146,6 +151,20 @@ Artist::Artist( const QString& name )
}
Artist::~Artist()
{
/* QMutexLocker lock( &s_mutex );
s_artistsByName.remove( name() );
s_artistsByCoverId.remove( coverId() );
/* if ( id() > 0 )
s_artistsById.remove( id() );*/
#ifndef ENABLE_HEADLESS
delete m_cover;
#endif
}
void
Artist::onTracksLoaded( Tomahawk::ModelMode mode, const Tomahawk::collection_ptr& collection )
{
@@ -180,7 +199,7 @@ Artist::albums( ModelMode mode, const Tomahawk::collection_ptr& collection ) con
artistInfo["artist"] = name();
Tomahawk::InfoSystem::InfoRequestData requestData;
requestData.caller = infoid();
requestData.caller = uniqueId();
requestData.input = QVariant::fromValue< Tomahawk::InfoSystem::InfoStringHash >( artistInfo );
requestData.type = Tomahawk::InfoSystem::InfoArtistReleases;
@@ -220,7 +239,7 @@ Artist::similarArtists() const
artistInfo["artist"] = name();
Tomahawk::InfoSystem::InfoRequestData requestData;
requestData.caller = infoid();
requestData.caller = uniqueId();
requestData.customData = QVariantMap();
requestData.input = QVariant::fromValue< Tomahawk::InfoSystem::InfoStringHash >( artistInfo );
@@ -287,7 +306,10 @@ Artist::id() const
m_waitingForFuture = false;
if ( m_id > 0 )
{
QMutexLocker lock( &s_mutex );
s_artistsById[ m_id ] = m_ownRef.toStrongRef();
}
s_idMutex.unlock();
}
@@ -305,7 +327,7 @@ Artist::biography() const
trackInfo["artist"] = name();
Tomahawk::InfoSystem::InfoRequestData requestData;
requestData.caller = infoid();
requestData.caller = uniqueId();
requestData.type = Tomahawk::InfoSystem::InfoArtistBiography;
requestData.input = QVariant::fromValue< Tomahawk::InfoSystem::InfoStringHash >( trackInfo );
requestData.customData = QVariantMap();
@@ -391,7 +413,7 @@ Artist::onAlbumsFound( const QList< album_ptr >& albums, const QVariant& data )
void
Artist::infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData requestData, QVariant output )
{
if ( requestData.caller != infoid() )
if ( requestData.caller != uniqueId() )
return;
QVariantMap returnedData = output.value< QVariantMap >();
@@ -432,7 +454,13 @@ Artist::infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData requestData, QVari
m_coverBuffer = ba;
}
m_coverLoaded = true;
{
QMutexLocker lock( &s_mutex );
m_coverLoaded = true;
s_artistsByCoverId.remove( coverId() );
m_coverId = uuid();
s_artistsByCoverId[ m_coverId ] = m_ownRef.toStrongRef();
}
emit coverChanged();
}
@@ -479,7 +507,7 @@ Artist::infoSystemFinished( QString target )
{
Q_UNUSED( target );
if ( target != infoid() )
if ( target != uniqueId() )
return;
if ( --m_infoJobs == 0 )
@@ -510,7 +538,7 @@ Artist::cover( const QSize& size, bool forceLoad ) const
trackInfo["artist"] = name();
Tomahawk::InfoSystem::InfoRequestData requestData;
requestData.caller = infoid();
requestData.caller = uniqueId();
requestData.type = Tomahawk::InfoSystem::InfoArtistImages;
requestData.input = QVariant::fromValue< Tomahawk::InfoSystem::InfoStringHash >( trackInfo );
requestData.customData = QVariantMap();
@@ -585,10 +613,20 @@ Artist::tracks( ModelMode mode, const Tomahawk::collection_ptr& collection )
QString
Artist::infoid() const
Artist::uniqueId() const
{
if ( m_uuid.isEmpty() )
m_uuid = uuid();
return m_uuid;
}
QString
Artist::coverId() const
{
if ( m_coverId.isEmpty() )
m_coverId = uuid();
return m_coverId;
}

View File

@@ -46,6 +46,7 @@ Q_OBJECT
public:
static artist_ptr get( const QString& name, bool autoCreate = false );
static artist_ptr get( unsigned int id, const QString& name );
static artist_ptr getByCoverId( const QString& uuid );
Artist( unsigned int id, const QString& name );
explicit Artist( const QString& name );
@@ -54,6 +55,8 @@ public:
unsigned int id() const;
QString name() const { return m_name; }
QString sortname() const { return m_sortname; }
QString uniqueId() const;
QString coverId() const;
QList<Tomahawk::album_ptr> albums( ModelMode mode = Mixed, const Tomahawk::collection_ptr& collection = Tomahawk::collection_ptr() ) const;
QList<Tomahawk::artist_ptr> similarArtists() const;
@@ -98,7 +101,6 @@ private slots:
private:
Artist();
QString infoid() const;
void setIdFuture( QFuture<unsigned int> idFuture );
@@ -116,6 +118,7 @@ private:
bool m_biographyLoaded;
mutable QString m_uuid;
mutable QString m_coverId;
mutable int m_infoJobs;
QList<Tomahawk::album_ptr> m_databaseAlbums;
@@ -138,6 +141,7 @@ private:
static QHash< QString, artist_ptr > s_artistsByName;
static QHash< unsigned int, artist_ptr > s_artistsById;
static QHash< QString, artist_ptr > s_artistsByCoverId;
friend class ::IdThreadWorker;
};

View File

@@ -6,6 +6,7 @@ SET( QT_USE_QTNETWORK TRUE )
SET( QT_USE_QTXML TRUE )
SET( QT_USE_QTSVG TRUE )
SET( QT_USE_QTUITOOLS TRUE )
SET( QT_USE_QTDECLARATIVE TRUE )
IF( UNIX AND NOT APPLE )
SET( QT_USE_QTDBUS TRUE )
@@ -54,7 +55,6 @@ set( libGuiSources
infobar/InfoBar.cpp
playlist/FlexibleHeader.cpp
playlist/FlexibleView.cpp
playlist/TreeModel.cpp
playlist/TreeProxyModel.cpp
@@ -72,6 +72,7 @@ set( libGuiSources
playlist/AlbumModel.cpp
playlist/GridItemDelegate.cpp
playlist/GridView.cpp
playlist/QmlGridView.cpp
playlist/TreeView.cpp
playlist/ViewHeader.cpp
playlist/LovedTracksModel.cpp
@@ -88,14 +89,9 @@ set( libGuiSources
playlist/dynamic/DynamicModel.cpp
playlist/dynamic/echonest/EchonestGenerator.cpp
playlist/dynamic/echonest/EchonestControl.cpp
playlist/dynamic/echonest/EchonestSteerer.cpp
playlist/dynamic/widgets/DynamicWidget.cpp
playlist/dynamic/widgets/DynamicControlWrapper.cpp
playlist/dynamic/widgets/DynamicControlList.cpp
playlist/dynamic/widgets/DynamicQmlWidget.cpp
playlist/dynamic/widgets/ReadOrWriteWidget.cpp
playlist/dynamic/widgets/MiscControlWidgets.cpp
playlist/dynamic/widgets/CollapsibleControls.cpp
playlist/dynamic/widgets/DynamicSetupWidget.cpp
ExternalResolverGui.cpp
resolvers/ScriptResolver.cpp
@@ -128,6 +124,7 @@ set( libGuiSources
widgets/AnimatedCounterLabel.cpp
widgets/BasicHeader.cpp
widgets/DeclarativeHeader.cpp
widgets/Breadcrumb.cpp
widgets/BreadcrumbButton.cpp
widgets/CheckDirTree.cpp
@@ -155,6 +152,8 @@ set( libGuiSources
widgets/PlayableCover.cpp
widgets/SocialPlaylistWidget.cpp
widgets/SourceTreePopupDialog.cpp
widgets/DeclarativeCoverArtProvider.cpp
widgets/DeclarativeView.cpp
widgets/infowidgets/SourceInfoWidget.cpp
widgets/infowidgets/ArtistInfoWidget.cpp
widgets/infowidgets/AlbumInfoWidget.cpp
@@ -163,6 +162,8 @@ set( libGuiSources
widgets/searchlineedit/LineEdit.cpp
widgets/searchlineedit/SearchButton.cpp
widgets/searchlineedit/SearchLineEdit.cpp
widgets/SearchFieldQmlProxy.cpp
)
IF(QCA2_FOUND)
@@ -308,7 +309,7 @@ set( libSources
playlist/dynamic/GeneratorInterface.cpp
playlist/dynamic/DynamicPlaylistRevision.cpp
playlist/XspfUpdater.cpp
playlist/dynamic/database/DatabaseGenerator.cpp
# playlist/dynamic/database/DatabaseGenerator.cpp
playlist/dynamic/database/DatabaseControl.cpp
playlist/dynamic/DynamicControl.cpp
@@ -474,6 +475,7 @@ TARGET_LINK_LIBRARIES( tomahawklib
${QT_QTXML_LIBRARY}
${QT_QTSVG_LIBRARY}
${QT_QTCORE_LIBRARY}
${QT_QTDECLARATIVE_LIBRARY}
${OS_SPECIFIC_LINK_LIBRARIES}
${CMAKE_THREAD_LIBS_INIT}
${LINK_LIBRARIES}

View File

@@ -224,34 +224,36 @@ GlobalActionManager::copyPlaylistToClipboard( const dynplaylist_ptr& playlist )
link.addEncodedQueryItem( "type", "echonest" );
link.addQueryItem( "title", playlist->title() );
QList< dyncontrol_ptr > controls = playlist->generator()->controls();
foreach ( const dyncontrol_ptr& c, controls )
{
if ( c->selectedType() == "Artist" )
{
if ( c->match().toInt() == Echonest::DynamicPlaylist::ArtistType )
link.addQueryItem( "artist_limitto", c->input() );
else
link.addQueryItem( "artist", c->input() );
}
else if ( c->selectedType() == "Artist Description" )
{
link.addQueryItem( "description", c->input() );
}
else
{
QString name = c->selectedType().toLower().replace( " ", "_" );
Echonest::DynamicPlaylist::PlaylistParam p = static_cast< Echonest::DynamicPlaylist::PlaylistParam >( c->match().toInt() );
// if it is a max, set that too
if ( p == Echonest::DynamicPlaylist::MaxTempo || p == Echonest::DynamicPlaylist::MaxDuration || p == Echonest::DynamicPlaylist::MaxLoudness
|| p == Echonest::DynamicPlaylist::MaxDanceability || p == Echonest::DynamicPlaylist::MaxEnergy || p == Echonest::DynamicPlaylist::ArtistMaxFamiliarity
|| p == Echonest::DynamicPlaylist::ArtistMaxHotttnesss || p == Echonest::DynamicPlaylist::SongMaxHotttnesss || p == Echonest::DynamicPlaylist::ArtistMaxLatitude
|| p == Echonest::DynamicPlaylist::ArtistMaxLongitude )
name += "_max";
Q_ASSERT(false);
// FIXME
// QList< dyncontrol_ptr > controls = playlist->generator()->controls();
// foreach ( const dyncontrol_ptr& c, controls )
// {
// if ( c->selectedType() == "Artist" )
// {
// if ( c->match().toInt() == Echonest::DynamicPlaylist::ArtistType )
// link.addQueryItem( "artist_limitto", c->input() );
// else
// link.addQueryItem( "artist", c->input() );
// }
// else if ( c->selectedType() == "Artist Description" )
// {
// link.addQueryItem( "description", c->input() );
// }
// else
// {
// QString name = c->selectedType().toLower().replace( " ", "_" );
// Echonest::DynamicPlaylist::PlaylistParam p = static_cast< Echonest::DynamicPlaylist::PlaylistParam >( c->match().toInt() );
// // if it is a max, set that too
// if ( p == Echonest::DynamicPlaylist::MaxTempo || p == Echonest::DynamicPlaylist::MaxDuration || p == Echonest::DynamicPlaylist::MaxLoudness
// || p == Echonest::DynamicPlaylist::MaxDanceability || p == Echonest::DynamicPlaylist::MaxEnergy || p == Echonest::DynamicPlaylist::ArtistMaxFamiliarity
// || p == Echonest::DynamicPlaylist::ArtistMaxHotttnesss || p == Echonest::DynamicPlaylist::SongMaxHotttnesss || p == Echonest::DynamicPlaylist::ArtistMaxLatitude
// || p == Echonest::DynamicPlaylist::ArtistMaxLongitude )
// name += "_max";
link.addQueryItem( name, c->input() );
}
}
// link.addQueryItem( name, c->input() );
// }
// }
QClipboard* cb = QApplication::clipboard();
QByteArray data = percentEncode( link );
@@ -885,178 +887,179 @@ GlobalActionManager::loadDynamicPlaylist( const QUrl& url, bool station )
return Tomahawk::dynplaylist_ptr();
}
if ( parts[ 0 ] == "create" )
{
if ( !url.hasQueryItem( "title" ) || !url.hasQueryItem( "type" ) )
{
tLog() << "Station create command needs title and type..." << url.toString();
return Tomahawk::dynplaylist_ptr();
}
QString title = url.queryItemValue( "title" );
QString type = url.queryItemValue( "type" );
GeneratorMode m = Static;
if ( station )
m = OnDemand;
Q_ASSERT(false);
// if ( parts[ 0 ] == "create" )
// {
// if ( !url.hasQueryItem( "title" ) || !url.hasQueryItem( "type" ) )
// {
// tLog() << "Station create command needs title and type..." << url.toString();
// return Tomahawk::dynplaylist_ptr();
// }
// QString title = url.queryItemValue( "title" );
// QString type = url.queryItemValue( "type" );
// GeneratorMode m = Static;
// if ( station )
// m = OnDemand;
dynplaylist_ptr pl = DynamicPlaylist::create( SourceList::instance()->getLocal(), uuid(), title, QString(), QString(), m, false, type );
pl->setMode( m );
QList< dyncontrol_ptr > controls;
QPair< QString, QString > param;
foreach ( param, url.queryItems() )
{
if ( param.first == "artist" )
{
dyncontrol_ptr c = pl->generator()->createControl( "Artist" );
c->setInput( param.second );
c->setMatch( QString::number( (int)Echonest::DynamicPlaylist::ArtistRadioType ) );
controls << c;
}
else if ( param.first == "artist_limitto" )
{
dyncontrol_ptr c = pl->generator()->createControl( "Artist" );
c->setInput( param.second );
c->setMatch( QString::number( (int)Echonest::DynamicPlaylist::ArtistType ) );
controls << c;
}
else if ( param.first == "description" )
{
dyncontrol_ptr c = pl->generator()->createControl( "Artist Description" );
c->setInput( param.second );
c->setMatch( QString::number( (int)Echonest::DynamicPlaylist::ArtistDescriptionType ) );
controls << c;
}
else if ( param.first == "variety" )
{
dyncontrol_ptr c = pl->generator()->createControl( "Variety" );
c->setInput( param.second );
c->setMatch( QString::number( (int)Echonest::DynamicPlaylist::Variety ) );
controls << c;
}
else if ( param.first.startsWith( "tempo" ) )
{
dyncontrol_ptr c = pl->generator()->createControl( "Tempo" );
int extra = param.first.endsWith( "_max" ) ? -1 : 0;
c->setInput( param.second );
c->setMatch( QString::number( (int)Echonest::DynamicPlaylist::MinTempo + extra ) );
controls << c;
}
else if ( param.first.startsWith( "duration" ) )
{
dyncontrol_ptr c = pl->generator()->createControl( "Duration" );
int extra = param.first.endsWith( "_max" ) ? -1 : 0;
c->setInput( param.second );
c->setMatch( QString::number( (int)Echonest::DynamicPlaylist::MinDuration + extra ) );
controls << c;
}
else if ( param.first.startsWith( "loudness" ) )
{
dyncontrol_ptr c = pl->generator()->createControl( "Loudness" );
int extra = param.first.endsWith( "_max" ) ? -1 : 0;
c->setInput( param.second );
c->setMatch( QString::number( (int)Echonest::DynamicPlaylist::MinLoudness + extra ) );
controls << c;
}
else if ( param.first.startsWith( "danceability" ) )
{
dyncontrol_ptr c = pl->generator()->createControl( "Danceability" );
int extra = param.first.endsWith( "_max" ) ? 1 : 0;
c->setInput( param.second );
c->setMatch( QString::number( (int)Echonest::DynamicPlaylist::MinDanceability + extra ) );
controls << c;
}
else if ( param.first.startsWith( "energy" ) )
{
dyncontrol_ptr c = pl->generator()->createControl( "Energy" );
int extra = param.first.endsWith( "_max" ) ? 1 : 0;
c->setInput( param.second );
c->setMatch( QString::number( (int)Echonest::DynamicPlaylist::MinEnergy + extra ) );
controls << c;
}
else if ( param.first.startsWith( "artist_familiarity" ) )
{
dyncontrol_ptr c = pl->generator()->createControl( "Artist Familiarity" );
int extra = param.first.endsWith( "_max" ) ? -1 : 0;
c->setInput( param.second );
c->setMatch( QString::number( (int)Echonest::DynamicPlaylist::ArtistMinFamiliarity + extra ) );
controls << c;
}
else if ( param.first.startsWith( "artist_hotttnesss" ) )
{
dyncontrol_ptr c = pl->generator()->createControl( "Artist Hotttnesss" );
int extra = param.first.endsWith( "_max" ) ? -1 : 0;
c->setInput( param.second );
c->setMatch( QString::number( (int)Echonest::DynamicPlaylist::ArtistMinHotttnesss + extra ) );
controls << c;
}
else if ( param.first.startsWith( "song_hotttnesss" ) )
{
dyncontrol_ptr c = pl->generator()->createControl( "Song Hotttnesss" );
int extra = param.first.endsWith( "_max" ) ? -1 : 0;
c->setInput( param.second );
c->setMatch( QString::number( (int)Echonest::DynamicPlaylist::SongMinHotttnesss + extra ) );
controls << c;
}
else if ( param.first.startsWith( "longitude" ) )
{
dyncontrol_ptr c = pl->generator()->createControl( "Longitude" );
int extra = param.first.endsWith( "_max" ) ? 1 : 0;
c->setInput( param.second );
c->setMatch( QString::number( (int)Echonest::DynamicPlaylist::ArtistMinLongitude + extra ) );
controls << c;
}
else if ( param.first.startsWith( "latitude" ) )
{
dyncontrol_ptr c = pl->generator()->createControl( "Latitude" );
int extra = param.first.endsWith( "_max" ) ? 1 : 0;
c->setInput( param.second );
c->setMatch( QString::number( (int)Echonest::DynamicPlaylist::ArtistMinLatitude + extra ) );
controls << c;
}
else if ( param.first == "key" )
{
dyncontrol_ptr c = pl->generator()->createControl( "Key" );
c->setInput( param.second );
c->setMatch( QString::number( (int)Echonest::DynamicPlaylist::Key ) );
controls << c;
}
else if ( param.first == "mode" )
{
dyncontrol_ptr c = pl->generator()->createControl( "Mode" );
c->setInput( param.second );
c->setMatch( QString::number( (int)Echonest::DynamicPlaylist::Mode ) );
controls << c;
}
else if ( param.first == "mood" )
{
dyncontrol_ptr c = pl->generator()->createControl( "Mood" );
c->setInput( param.second );
c->setMatch( QString::number( (int)Echonest::DynamicPlaylist::Mood ) );
controls << c;
}
else if ( param.first == "style" )
{
dyncontrol_ptr c = pl->generator()->createControl( "Style" );
c->setInput( param.second );
c->setMatch( QString::number( (int)Echonest::DynamicPlaylist::Style ) );
controls << c;
}
else if ( param.first == "song" )
{
dyncontrol_ptr c = pl->generator()->createControl( "Song" );
c->setInput( param.second );
c->setMatch( QString::number( (int)Echonest::DynamicPlaylist::SongRadioType ) );
controls << c;
}
}
// dynplaylist_ptr pl = DynamicPlaylist::create( SourceList::instance()->getLocal(), uuid(), title, QString(), QString(), m, false, type );
// pl->setMode( m );
// QList< dyncontrol_ptr > controls;
// QPair< QString, QString > param;
// foreach ( param, url.queryItems() )
// {
// if ( param.first == "artist" )
// {
// dyncontrol_ptr c = pl->generator()->createControl( "Artist" );
// c->setInput( param.second );
// c->setMatch( QString::number( (int)Echonest::DynamicPlaylist::ArtistRadioType ) );
// controls << c;
// }
// else if ( param.first == "artist_limitto" )
// {
// dyncontrol_ptr c = pl->generator()->createControl( "Artist" );
// c->setInput( param.second );
// c->setMatch( QString::number( (int)Echonest::DynamicPlaylist::ArtistType ) );
// controls << c;
// }
// else if ( param.first == "description" )
// {
// dyncontrol_ptr c = pl->generator()->createControl( "Artist Description" );
// c->setInput( param.second );
// c->setMatch( QString::number( (int)Echonest::DynamicPlaylist::ArtistDescriptionType ) );
// controls << c;
// }
// else if ( param.first == "variety" )
// {
// dyncontrol_ptr c = pl->generator()->createControl( "Variety" );
// c->setInput( param.second );
// c->setMatch( QString::number( (int)Echonest::DynamicPlaylist::Variety ) );
// controls << c;
// }
// else if ( param.first.startsWith( "tempo" ) )
// {
// dyncontrol_ptr c = pl->generator()->createControl( "Tempo" );
// int extra = param.first.endsWith( "_max" ) ? -1 : 0;
// c->setInput( param.second );
// c->setMatch( QString::number( (int)Echonest::DynamicPlaylist::MinTempo + extra ) );
// controls << c;
// }
// else if ( param.first.startsWith( "duration" ) )
// {
// dyncontrol_ptr c = pl->generator()->createControl( "Duration" );
// int extra = param.first.endsWith( "_max" ) ? -1 : 0;
// c->setInput( param.second );
// c->setMatch( QString::number( (int)Echonest::DynamicPlaylist::MinDuration + extra ) );
// controls << c;
// }
// else if ( param.first.startsWith( "loudness" ) )
// {
// dyncontrol_ptr c = pl->generator()->createControl( "Loudness" );
// int extra = param.first.endsWith( "_max" ) ? -1 : 0;
// c->setInput( param.second );
// c->setMatch( QString::number( (int)Echonest::DynamicPlaylist::MinLoudness + extra ) );
// controls << c;
// }
// else if ( param.first.startsWith( "danceability" ) )
// {
// dyncontrol_ptr c = pl->generator()->createControl( "Danceability" );
// int extra = param.first.endsWith( "_max" ) ? 1 : 0;
// c->setInput( param.second );
// c->setMatch( QString::number( (int)Echonest::DynamicPlaylist::MinDanceability + extra ) );
// controls << c;
// }
// else if ( param.first.startsWith( "energy" ) )
// {
// dyncontrol_ptr c = pl->generator()->createControl( "Energy" );
// int extra = param.first.endsWith( "_max" ) ? 1 : 0;
// c->setInput( param.second );
// c->setMatch( QString::number( (int)Echonest::DynamicPlaylist::MinEnergy + extra ) );
// controls << c;
// }
// else if ( param.first.startsWith( "artist_familiarity" ) )
// {
// dyncontrol_ptr c = pl->generator()->createControl( "Artist Familiarity" );
// int extra = param.first.endsWith( "_max" ) ? -1 : 0;
// c->setInput( param.second );
// c->setMatch( QString::number( (int)Echonest::DynamicPlaylist::ArtistMinFamiliarity + extra ) );
// controls << c;
// }
// else if ( param.first.startsWith( "artist_hotttnesss" ) )
// {
// dyncontrol_ptr c = pl->generator()->createControl( "Artist Hotttnesss" );
// int extra = param.first.endsWith( "_max" ) ? -1 : 0;
// c->setInput( param.second );
// c->setMatch( QString::number( (int)Echonest::DynamicPlaylist::ArtistMinHotttnesss + extra ) );
// controls << c;
// }
// else if ( param.first.startsWith( "song_hotttnesss" ) )
// {
// dyncontrol_ptr c = pl->generator()->createControl( "Song Hotttnesss" );
// int extra = param.first.endsWith( "_max" ) ? -1 : 0;
// c->setInput( param.second );
// c->setMatch( QString::number( (int)Echonest::DynamicPlaylist::SongMinHotttnesss + extra ) );
// controls << c;
// }
// else if ( param.first.startsWith( "longitude" ) )
// {
// dyncontrol_ptr c = pl->generator()->createControl( "Longitude" );
// int extra = param.first.endsWith( "_max" ) ? 1 : 0;
// c->setInput( param.second );
// c->setMatch( QString::number( (int)Echonest::DynamicPlaylist::ArtistMinLongitude + extra ) );
// controls << c;
// }
// else if ( param.first.startsWith( "latitude" ) )
// {
// dyncontrol_ptr c = pl->generator()->createControl( "Latitude" );
// int extra = param.first.endsWith( "_max" ) ? 1 : 0;
// c->setInput( param.second );
// c->setMatch( QString::number( (int)Echonest::DynamicPlaylist::ArtistMinLatitude + extra ) );
// controls << c;
// }
// else if ( param.first == "key" )
// {
// dyncontrol_ptr c = pl->generator()->createControl( "Key" );
// c->setInput( param.second );
// c->setMatch( QString::number( (int)Echonest::DynamicPlaylist::Key ) );
// controls << c;
// }
// else if ( param.first == "mode" )
// {
// dyncontrol_ptr c = pl->generator()->createControl( "Mode" );
// c->setInput( param.second );
// c->setMatch( QString::number( (int)Echonest::DynamicPlaylist::Mode ) );
// controls << c;
// }
// else if ( param.first == "mood" )
// {
// dyncontrol_ptr c = pl->generator()->createControl( "Mood" );
// c->setInput( param.second );
// c->setMatch( QString::number( (int)Echonest::DynamicPlaylist::Mood ) );
// controls << c;
// }
// else if ( param.first == "style" )
// {
// dyncontrol_ptr c = pl->generator()->createControl( "Style" );
// c->setInput( param.second );
// c->setMatch( QString::number( (int)Echonest::DynamicPlaylist::Style ) );
// controls << c;
// }
// else if ( param.first == "song" )
// {
// dyncontrol_ptr c = pl->generator()->createControl( "Song" );
// c->setInput( param.second );
// c->setMatch( QString::number( (int)Echonest::DynamicPlaylist::SongRadioType ) );
// controls << c;
// }
// }
if ( m == OnDemand )
pl->createNewRevision( uuid(), pl->currentrevision(), type, controls );
else
pl->createNewRevision( uuid(), pl->currentrevision(), type, controls, pl->entries() );
// if ( m == OnDemand )
// pl->createNewRevision( uuid(), pl->currentrevision(), type, controls );
// else
// pl->createNewRevision( uuid(), pl->currentrevision(), type, controls, pl->entries() );
ViewManager::instance()->show( pl );
return pl;
}
// ViewManager::instance()->show( pl );
// return pl;
// }
return Tomahawk::dynplaylist_ptr();
}

View File

@@ -83,7 +83,10 @@ query_ptr
Query::get( const QString& artist, const QString& track, const QString& album, const QID& qid, bool autoResolve )
{
if ( artist.trimmed().isEmpty() || track.trimmed().isEmpty() )
{
Q_ASSERT( false );
return query_ptr();
}
if ( qid.isEmpty() )
autoResolve = false;
@@ -150,6 +153,7 @@ Query::Query( const QString& query, const QID& qid )
Query::~Query()
{
QMutexLocker lock( &m_mutex );
m_ownRef.clear();
m_results.clear();
}
@@ -350,6 +354,22 @@ Query::id() const
}
QString
Query::coverId() const
{
if ( m_albumPtr && m_albumPtr->coverLoaded() && !m_albumPtr->cover( QSize( 0, 0 ) ).isNull() )
{
return m_albumPtr->coverId();
}
else if ( m_artistPtr )
{
return m_artistPtr->coverId();
}
return QString();
}
void
Query::setPlayedBy( const Tomahawk::source_ptr& source, unsigned int playtime )
{

View File

@@ -95,6 +95,7 @@ public:
unsigned int numResults() const;
QID id() const;
QString coverId() const;
/// sorter for list of results
static bool resultSorter( const result_ptr& left, const result_ptr& right );

View File

@@ -31,14 +31,13 @@
#include "playlist/PlayableProxyModel.h"
#include "playlist/PlayableModel.h"
#include "playlist/TreeView.h"
#include "playlist/GridView.h"
#include "playlist/AlbumModel.h"
#include "SourceList.h"
#include "TomahawkSettings.h"
#include "playlist/PlaylistLargeItemDelegate.h"
#include "playlist/RecentlyPlayedModel.h"
#include "playlist/dynamic/widgets/DynamicWidget.h"
#include "playlist/dynamic/widgets/DynamicQmlWidget.h"
#include "widgets/NewReleasesWidget.h"
#include "widgets/WelcomeWidget.h"
@@ -54,7 +53,7 @@
#include <QVBoxLayout>
#include <QMetaMethod>
#include <QApplication>
#define FILTER_TIMEOUT 280
@@ -77,6 +76,7 @@ ViewManager::ViewManager( QObject* parent )
, m_whatsHotWidget( new WhatsHotWidget() )
, m_newReleasesWidget( new NewReleasesWidget() )
, m_recentPlaysWidget( 0 )
, m_radioView( 0 )
, m_currentPage( 0 )
, m_loaded( false )
{
@@ -138,7 +138,7 @@ ViewManager::createPageForPlaylist( const playlist_ptr& playlist )
PlaylistView* pv = new PlaylistView();
view->setDetailedView( pv );
view->setPixmap( pv->pixmap() );
view->setHeaderIconSource( pv->iconSource() );
view->setEmptyTip( tr( "This playlist is empty!" ) );
// We need to set the model on the view before loading the playlist, so spinners & co are connected
@@ -161,8 +161,8 @@ ViewManager::playlistForPage( ViewPage* page ) const
{
p = dynamic_cast< PlaylistView* >( page )->playlistModel()->playlist();
}
else if ( dynamic_cast< DynamicWidget* >( page ) )
p = dynamic_cast< DynamicWidget* >( page )->playlist();
else if ( dynamic_cast< DynamicQmlWidget* >( page ) )
p = dynamic_cast< DynamicQmlWidget* >( page )->playlist();
return p;
}
@@ -193,7 +193,7 @@ ViewManager::show( const Tomahawk::dynplaylist_ptr& playlist )
{
if ( !m_dynamicWidgets.contains( playlist ) || m_dynamicWidgets.value( playlist ).isNull() )
{
m_dynamicWidgets[ playlist ] = new Tomahawk::DynamicWidget( playlist, m_stack );
m_dynamicWidgets[ playlist ] = new Tomahawk::DynamicQmlWidget( playlist );
playlist->resolve();
}
@@ -349,6 +349,22 @@ ViewManager::showSuperCollection()
}
Tomahawk::ViewPage*
ViewManager::showRadioPage()
{
if ( !m_radioView )
{
dynplaylist_ptr playlist = DynamicPlaylist::create( SourceList::instance()->getLocal(), uuid(), QString(), "", SourceList::instance()->getLocal()->friendlyName(), OnDemand, false, QString(), false, false );
playlist->setMode( OnDemand );
m_radioView = new Tomahawk::DynamicQmlWidget( playlist, m_stack );
}
setPage( m_radioView );
return m_radioView;
}
void
ViewManager::playlistInterfaceChanged( Tomahawk::playlistinterface_ptr interface )
{
@@ -393,7 +409,7 @@ ViewManager::showRecentPlaysPage()
if ( !m_recentPlaysWidget )
{
FlexibleView* pv = new FlexibleView( m_widget );
pv->setPixmap( TomahawkUtils::defaultPixmap( TomahawkUtils::RecentlyPlayed ) );
pv->setHeaderIconSource( TomahawkUtils::defaultImagePath( TomahawkUtils::RecentlyPlayed ) );
RecentlyPlayedModel* raModel = new RecentlyPlayedModel( pv );
raModel->setTitle( tr( "Recently Played Tracks" ) );
@@ -541,7 +557,7 @@ ViewManager::setPage( ViewPage* page, bool trackHistory )
emit historyBackAvailable( m_pageHistoryBack.count() );
emit historyForwardAvailable( m_pageHistoryFwd.count() );
qDebug() << "View page shown:" << page->title();
tDebug() << "View page shown:" << page->title();
emit viewPageActivated( page );
if ( page->isTemporaryPage() )
@@ -569,13 +585,15 @@ ViewManager::setPage( ViewPage* page, bool trackHistory )
if( obj->metaObject()->indexOfSignal( "nameChanged(QString)" ) > -1 )
connect( obj, SIGNAL( nameChanged( QString ) ), m_infobar, SLOT( setCaption( QString ) ), Qt::UniqueConnection );
if( obj->metaObject()->indexOfSignal( "pixmapChanged(QPixmap)" ) > -1 )
connect( obj, SIGNAL( pixmapChanged( QPixmap ) ), m_infobar, SLOT( setPixmap( QPixmap ) ), Qt::UniqueConnection );
if( obj->metaObject()->indexOfSignal( "iconSourceChanged(QString)" ) > -1 )
connect( obj, SIGNAL( iconSourceChanged( QString ) ), m_infobar, SLOT( setPixmap( QPixmap ) ), Qt::UniqueConnection );
if( obj->metaObject()->indexOfSignal( "destroyed(QWidget*)" ) > -1 )
connect( obj, SIGNAL( destroyed( QWidget* ) ), SLOT( onWidgetDestroyed( QWidget* ) ), Qt::UniqueConnection );
}
// Let QML initialize before we call setVisible() on it (happens inside QPageStack)
qApp->processEvents();
m_stack->setCurrentWidget( page->widget() );
updateView();
@@ -645,7 +663,7 @@ ViewManager::updateView()
}
m_infobar->setLongDescription( currentPage()->longDescription() );
m_infobar->setPixmap( currentPage()->pixmap() );
m_infobar->setIconSource( currentPage()->iconSource() );
}
@@ -797,7 +815,7 @@ ViewManager::playlistForInterface( Tomahawk::playlistinterface_ptr interface ) c
Tomahawk::dynplaylist_ptr
ViewManager::dynamicPlaylistForInterface( Tomahawk::playlistinterface_ptr interface ) const
{
foreach ( QWeakPointer<DynamicWidget> view, m_dynamicWidgets.values() )
foreach ( QWeakPointer<DynamicQmlWidget> view, m_dynamicWidgets.values() )
{
if ( !view.isNull() && view.data()->playlistInterface() == interface )
{

View File

@@ -59,6 +59,7 @@ class QPushButton;
namespace Tomahawk
{
class DynamicWidget;
class DynamicQmlWidget;
}
class DLLEXPORT ViewManager : public QObject
@@ -128,6 +129,7 @@ signals:
void historyForwardAvailable( bool avail );
public slots:
Tomahawk::ViewPage* showRadioPage();
Tomahawk::ViewPage* showSuperCollection();
Tomahawk::ViewPage* showWelcomePage();
Tomahawk::ViewPage* showWhatsHotPage();
@@ -192,10 +194,11 @@ private:
WhatsHotWidget* m_whatsHotWidget;
NewReleasesWidget* m_newReleasesWidget;
Tomahawk::ViewPage* m_recentPlaysWidget;
Tomahawk::DynamicQmlWidget* m_radioView;
QList< Tomahawk::collection_ptr > m_superCollections;
QHash< Tomahawk::dynplaylist_ptr, QWeakPointer<Tomahawk::DynamicWidget> > m_dynamicWidgets;
QHash< Tomahawk::dynplaylist_ptr, QWeakPointer<Tomahawk::DynamicQmlWidget> > m_dynamicWidgets;
QHash< Tomahawk::collection_ptr, QWeakPointer<TreeView> > m_treeViews;
QHash< Tomahawk::artist_ptr, QWeakPointer<ArtistInfoWidget> > m_artistViews;
QHash< Tomahawk::album_ptr, QWeakPointer<AlbumInfoWidget> > m_albumViews;

View File

@@ -56,7 +56,7 @@ public:
virtual Tomahawk::album_ptr descriptionAlbum() const { return Tomahawk::album_ptr(); }
virtual QString longDescription() const { return QString(); }
virtual QPixmap pixmap() const { return QPixmap( RESPATH "icons/tomahawk-icon-128x128.png" ); }
virtual QString iconSource() const { return QLatin1String(RESPATH "icons/tomahawk-icon.svg"); }
virtual bool showInfoBar() const { return true; }
virtual bool showFilter() const { return false; }

View File

@@ -42,53 +42,59 @@ DatabaseCommand_CreateDynamicPlaylist::DatabaseCommand_CreateDynamicPlaylist( QO
: DatabaseCommand_CreatePlaylist( parent )
, m_autoLoad( true )
{
tDebug() << Q_FUNC_INFO << "creating dynamiccreatecommand 1";
tDebug( LOGVERBOSE ) << Q_FUNC_INFO << "Creating dynamiccreatecommand 1";
}
DatabaseCommand_CreateDynamicPlaylist::DatabaseCommand_CreateDynamicPlaylist( const source_ptr& author,
const dynplaylist_ptr& playlist, bool autoLoad )
DatabaseCommand_CreateDynamicPlaylist::DatabaseCommand_CreateDynamicPlaylist( const source_ptr& author, const dynplaylist_ptr& playlist, bool autoLoad )
: DatabaseCommand_CreatePlaylist( author, playlist.staticCast<Playlist>() )
, m_playlist( playlist )
, m_autoLoad( autoLoad )
{
tDebug() << Q_FUNC_INFO << "creating dynamiccreatecommand 2";
tDebug( LOGVERBOSE ) << Q_FUNC_INFO << "Creating dynamiccreatecommand 2";
}
DatabaseCommand_CreateDynamicPlaylist::~DatabaseCommand_CreateDynamicPlaylist()
{}
{
}
QVariant
DatabaseCommand_CreateDynamicPlaylist::playlistV() const
{
if( m_v.isNull() )
return QJson::QObjectHelper::qobject2qvariant( (QObject*)m_playlist.data() );
else
return m_v;
if ( m_v.isNull() )
return QJson::QObjectHelper::qobject2qvariant( (QObject*)m_playlist.data() );
else
return m_v;
}
void
DatabaseCommand_CreateDynamicPlaylist::exec( DatabaseImpl* lib )
{
qDebug() << Q_FUNC_INFO;
tDebug( LOGVERBOSE ) << Q_FUNC_INFO;
Q_ASSERT( !( m_playlist.isNull() && m_v.isNull() ) );
Q_ASSERT( !source().isNull() );
DatabaseCommand_CreatePlaylist::createPlaylist( lib, true );
qDebug() << "Created normal playlist, now creating additional dynamic info!";
tDebug( LOGVERBOSE ) << "Created normal playlist, now creating additional dynamic info!";
qDebug() << "Create dynamic execing!" << m_playlist << m_v;
tDebug( LOGVERBOSE ) << "Create dynamic execing!" << m_playlist << m_v;
TomahawkSqlQuery cre = lib->newquery();
cre.prepare( "INSERT INTO dynamic_playlist( guid, pltype, plmode, autoload ) "
"VALUES( ?, ?, ?, ? )" );
if( m_playlist.isNull() ) {
if ( m_playlist.isNull() )
{
QVariantMap m = m_v.toMap();
cre.addBindValue( m.value( "guid" ) );
cre.addBindValue( m.value( "type" ) );
cre.addBindValue( m.value( "mode" ) );
} else {
}
else
{
cre.addBindValue( m_playlist->guid() );
cre.addBindValue( m_playlist->type() );
cre.addBindValue( m_playlist->mode() );
@@ -101,18 +107,18 @@ DatabaseCommand_CreateDynamicPlaylist::exec( DatabaseImpl* lib )
void
DatabaseCommand_CreateDynamicPlaylist::postCommitHook()
{
qDebug() << Q_FUNC_INFO;
tDebug( LOGVERBOSE ) << Q_FUNC_INFO;
if ( source().isNull() || source()->collection().isNull() )
{
qDebug() << "Source has gone offline, not emitting to GUI.";
Q_ASSERT( false );
return;
}
if( !DatabaseCommand_CreatePlaylist::report() || report() == false )
if ( !DatabaseCommand_CreatePlaylist::report() || report() == false )
return;
qDebug() << Q_FUNC_INFO << "..reporting..";
if( m_playlist.isNull() ) {
if ( m_playlist.isNull() )
{
source_ptr src = source();
#ifndef ENABLE_HEADLESS
QMetaObject::invokeMethod( ViewManager::instance(),
@@ -121,9 +127,12 @@ DatabaseCommand_CreateDynamicPlaylist::postCommitHook()
QGenericArgument( "Tomahawk::source_ptr", (const void*)&src ),
Q_ARG( QVariant, m_v ) );
#endif
} else {
}
else
{
m_playlist->reportCreated( m_playlist );
}
if( source()->isLocal() )
if ( source()->isLocal() )
Servent::instance()->triggerDBSync();
}

View File

@@ -36,7 +36,6 @@ using namespace Tomahawk;
void
DatabaseCommand_LoadDynamicPlaylistEntries::exec( DatabaseImpl* dbi )
{
// qDebug() << "Loading dynamic playlist guid" << guid();
// load the entries first
generateEntries( dbi );
@@ -52,12 +51,8 @@ DatabaseCommand_LoadDynamicPlaylistEntries::exec( DatabaseImpl* dbi )
QString type;
GeneratorMode mode;
QList< QVariantMap > controls;
QVariantList controls;
QString playlist_guid;
// qDebug() << "Loading controls..." << revisionGuid();
// qDebug() << "SELECT playlist_revision.playlist, controls, plmode, pltype "
// "FROM dynamic_playlist_revision, playlist_revision "
// "WHERE dynamic_playlist_revision.guid = "<< revisionGuid() << " AND playlist_revision.guid = dynamic_playlist_revision.guid";
if( controlsQuery.first() )
{
@@ -67,42 +62,40 @@ DatabaseCommand_LoadDynamicPlaylistEntries::exec( DatabaseImpl* dbi )
QVariant v = parser.parse( controlsQuery.value(1).toByteArray(), &ok );
Q_ASSERT( ok && v.type() == QVariant::List ); //TODO
type = controlsQuery.value( 3 ).toString();
mode = static_cast<GeneratorMode>( controlsQuery.value( 2 ).toInt() );
QStringList controlIds = v.toStringList();
// qDebug() << "Got controls in dynamic playlist, loading:" << controlIds << controlsQuery.value(1);
foreach( const QString& controlId, controlIds )
{
TomahawkSqlQuery controlQuery = dbi->newquery();
controlQuery.prepare( "SELECT selectedType, match, input "
"FROM dynamic_playlist_controls "
"WHERE id = :id" );
controlQuery.bindValue( ":id", controlId );
controlQuery.exec();
if( controlQuery.next() )
{
QVariantMap c;
c[ "type" ] = type;
c[ "id" ] = controlId;
c[ "selectedType" ] = controlQuery.value( 0 ).toString();
c[ "match" ] = controlQuery.value( 1 ).toString();
c[ "input" ] = controlQuery.value( 2 ).toString();
controls << c;
}
}
}
else
{
// No controls or plguid is null, but that's okay. We'll get a setdynrevision command with a proper revision some point later
return;
// QStringList controlIds = v.toStringList();
//// qDebug() << "Got controls in dynamic playlist, loading:" << controlIds << controlsQuery.value(1);
// foreach( const QString& controlId, controlIds )
// {
// TomahawkSqlQuery controlQuery = dbi->newquery();
// controlQuery.prepare( "SELECT selectedType, match, input "
// "FROM dynamic_playlist_controls "
// "WHERE id = :id" );
// controlQuery.bindValue( ":id", controlId );
// controlQuery.exec();
// if( controlQuery.next() )
// {
// QVariantMap c;
// c[ "type" ] = type;
// c[ "id" ] = controlId;
// c[ "selectedType" ] = controlQuery.value( 0 ).toString();
// c[ "match" ] = controlQuery.value( 1 ).toString();
// c[ "input" ] = controlQuery.value( 2 ).toString();
// controls << c;
// }
// }
}
// else
// {
// // No controls or plguid is null, but that's okay. We'll get a setdynrevision command with a proper revision some point later
// return;
// }
if( mode == OnDemand )
{
// Q_ASSERT( m_entrymap.isEmpty() ); // ondemand should have no entry
emit done( revisionGuid(), m_islatest, type, controls, true );
}
else

View File

@@ -20,7 +20,6 @@
#define DATABASECOMMAND_LOADDYNAMICPLAYLISTENTRIES_H
#include <QObject>
#include <QVariantMap>
#include "Typedefs.h"
#include "DatabaseCommand.h"
@@ -47,14 +46,14 @@ signals:
void done( QString,
bool,
QString,
QList< QVariantMap>,
QVariantList,
bool );
// used when loading a static playlist
void done( QString,
QList< QString >,
QList< QString >,
QString,
QList< QVariantMap>,
QVariantList,
bool,
QMap< QString, Tomahawk::plentry_ptr >,
bool );

View File

@@ -37,7 +37,7 @@ DatabaseCommand_SetDynamicPlaylistRevision::DatabaseCommand_SetDynamicPlaylistRe
const QList<plentry_ptr>& entries,
const QString& type,
GeneratorMode mode,
const QList< dyncontrol_ptr >& controls )
const QVariantList& controls )
: DatabaseCommand_SetPlaylistRevision( s, playlistguid, newrev, oldrev, orderedguids, addedentries, entries )
, m_type( type )
, m_mode( mode )
@@ -54,7 +54,7 @@ DatabaseCommand_SetDynamicPlaylistRevision::DatabaseCommand_SetDynamicPlaylistRe
const QString& oldrev,
const QString& type,
GeneratorMode mode,
const QList< dyncontrol_ptr >& controls )
const QVariantList& controls )
: DatabaseCommand_SetPlaylistRevision( s, playlistguid, newrev, oldrev, QStringList(), QList< plentry_ptr >(), QList< plentry_ptr >() )
, m_type( type )
, m_mode( mode )
@@ -68,18 +68,7 @@ DatabaseCommand_SetDynamicPlaylistRevision::DatabaseCommand_SetDynamicPlaylistRe
QVariantList
DatabaseCommand_SetDynamicPlaylistRevision::controlsV()
{
if ( m_controls.isEmpty() )
return m_controlsV;
if ( !m_controls.isEmpty() && m_controlsV.isEmpty() )
{
foreach ( const dyncontrol_ptr& control, m_controls )
{
m_controlsV << QJson::QObjectHelper::qobject2qvariant( control.data() );
}
}
return m_controlsV;
return m_controls;
}
@@ -117,46 +106,21 @@ DatabaseCommand_SetDynamicPlaylistRevision::postCommitHook()
return;
}
if ( !m_controlsV.isEmpty() && m_controls.isEmpty() )
{
QList<QVariantMap> controlMap;
foreach( const QVariant& v, m_controlsV )
controlMap << v.toMap();
if ( m_mode == OnDemand )
rawPl->setRevision( newrev(),
true, // this *is* the newest revision so far
m_type,
controlMap,
m_applied );
else
rawPl->setRevision( newrev(),
orderedentriesguids,
m_previous_rev_orderedguids,
m_type,
controlMap,
true, // this *is* the newest revision so far
m_addedmap,
m_applied );
}
if ( m_mode == OnDemand )
rawPl->setRevision( newrev(),
true, // this *is* the newest revision so far
m_type,
m_controls,
m_applied );
else
{
if ( m_mode == OnDemand )
rawPl->setRevision( newrev(),
true, // this *is* the newest revision so far
m_type,
m_controls,
m_applied );
else
rawPl->setRevision( newrev(),
orderedentriesguids,
m_previous_rev_orderedguids,
m_type,
m_controls,
true, // this *is* the newest revision so far
m_addedmap,
m_applied );
}
rawPl->setRevision( newrev(),
orderedentriesguids,
m_previous_rev_orderedguids,
m_type,
m_controls,
true, // this *is* the newest revision so far
m_addedmap,
m_applied );
if ( source()->isLocal() )
Servent::instance()->triggerDBSync();
@@ -169,19 +133,9 @@ DatabaseCommand_SetDynamicPlaylistRevision::exec( DatabaseImpl* lib )
DatabaseCommand_SetPlaylistRevision::exec( lib );
QVariantList newcontrols;
if ( m_controlsV.isEmpty() && !m_controls.isEmpty() )
foreach( const QVariant& v, m_controls )
{
foreach( const dyncontrol_ptr& control, m_controls )
{
newcontrols << control->id();
}
}
else if( !m_controlsV.isEmpty() )
{
foreach( const QVariant& v, m_controlsV )
{
newcontrols << v.toMap().value( "id" );
}
newcontrols << v.toMap().value( "id" );
}
QJson::Serializer ser;
@@ -209,34 +163,17 @@ DatabaseCommand_SetDynamicPlaylistRevision::exec( DatabaseImpl* lib )
TomahawkSqlQuery controlsQuery = lib->newquery();
controlsQuery.prepare( "INSERT INTO dynamic_playlist_controls( id, playlist, selectedType, match, input ) "
"VALUES( ?, ?, ?, ?, ? )" );
if ( m_controlsV.isEmpty() && !m_controls.isEmpty() )
foreach ( const QVariant& v, m_controls )
{
foreach ( const dyncontrol_ptr& control, m_controls )
{
qDebug() << "inserting dynamic control:" << control->id() << m_playlistguid << control->selectedType() << control->match() << control->input();
controlsQuery.addBindValue( control->id() );
controlsQuery.addBindValue( m_playlistguid );
controlsQuery.addBindValue( control->selectedType() );
controlsQuery.addBindValue( control->match() );
controlsQuery.addBindValue( control->input() );
QVariantMap control = v.toMap();
qDebug() << "inserting dynamic control from JSON:" << control.value( "id" ) << m_playlistguid << control.value( "selectedType" ) << control.value( "match" ) << control.value( "input" );
controlsQuery.addBindValue( control.value( "id" ) );
controlsQuery.addBindValue( m_playlistguid );
controlsQuery.addBindValue( control.value( "selectedType" ) );
controlsQuery.addBindValue( control.value( "match" ) );
controlsQuery.addBindValue( control.value( "input" ) );
controlsQuery.exec();
}
}
else
{
foreach ( const QVariant& v, m_controlsV )
{
QVariantMap control = v.toMap();
qDebug() << "inserting dynamic control from JSON:" << control.value( "id" ) << m_playlistguid << control.value( "selectedType" ) << control.value( "match" ) << control.value( "input" );
controlsQuery.addBindValue( control.value( "id" ) );
controlsQuery.addBindValue( m_playlistguid );
controlsQuery.addBindValue( control.value( "selectedType" ) );
controlsQuery.addBindValue( control.value( "match" ) );
controlsQuery.addBindValue( control.value( "input" ) );
controlsQuery.exec();
}
controlsQuery.exec();
}
if ( m_applied )

View File

@@ -45,7 +45,7 @@ public:
const QList<plentry_ptr>& entries,
const QString& type,
GeneratorMode mode,
const QList< dyncontrol_ptr >& controls );
const QVariantList& controls );
explicit DatabaseCommand_SetDynamicPlaylistRevision( const source_ptr& s,
const QString& playlistguid,
@@ -53,7 +53,7 @@ public:
const QString& oldrev,
const QString& type,
GeneratorMode mode,
const QList< dyncontrol_ptr >& controls );
const QVariantList& controls );
QString commandname() const { return "setdynamicplaylistrevision"; }
@@ -64,7 +64,7 @@ public:
void setControlsV( const QVariantList& vlist )
{
m_controlsV = vlist;
m_controls = vlist;
}
QVariantList controlsV();
@@ -80,8 +80,7 @@ public:
private:
QString m_type;
GeneratorMode m_mode;
QList< dyncontrol_ptr > m_controls;
QList< QVariant > m_controlsV;
QVariantList m_controls;
// ARG i hate sharedpointers sometimes
DynamicPlaylist* m_playlist; // Only used if setting revision of a non-autoloaded playlist, as those aren't able to be looked up by guid

View File

@@ -184,9 +184,9 @@ InfoBar::setLongDescription( const QString& s )
void
InfoBar::setPixmap( const QPixmap& p )
InfoBar::setIconSource( const QString& iconSource )
{
ui->imageLabel->setPixmap( p.scaledToHeight( IMAGE_HEIGHT, Qt::SmoothTransformation ) );
ui->imageLabel->setPixmap( QPixmap( iconSource ).scaledToHeight( IMAGE_HEIGHT, Qt::SmoothTransformation ) );
}

View File

@@ -58,7 +58,7 @@ public slots:
void setDescription( const Tomahawk::album_ptr& album_ptr );
void setLongDescription( const QString& s );
void setPixmap( const QPixmap& p );
void setIconSource( const QString& iconSource );
void setFilter( const QString& filter );
void setFilterAvailable( bool b );

View File

@@ -1,145 +0,0 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2010-2011, Christian Muehlhaeuser <muesli@tomahawk-player.org>
* Copyright 2012, Teo Mrnjavac <teo@kde.org>
*
* Tomahawk is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Tomahawk is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU 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 "FlexibleHeader.h"
#include <QBoxLayout>
#include <QLabel>
#include <QPixmap>
#include <QCheckBox>
#include <QPaintEvent>
#include <QPainter>
#include <QRadioButton>
#include "playlist/FlexibleView.h"
#include "ViewManager.h"
#include "thirdparty/Qocoa/qsearchfield.h"
#include "utils/Closure.h"
#include "utils/TomahawkUtilsGui.h"
#include "utils/Logger.h"
#include "widgets/QueryLabel.h"
#include "Source.h"
using namespace Tomahawk;
FlexibleHeader::FlexibleHeader( FlexibleView* parent )
: BasicHeader( parent )
, m_parent( parent )
{
QFile f( RESPATH "stylesheets/topbar-radiobuttons.css" );
f.open( QFile::ReadOnly );
QString css = QString::fromAscii( f.readAll() );
f.close();
QHBoxLayout* outerModeLayout = new QHBoxLayout;
m_verticalLayout->addLayout( outerModeLayout );
outerModeLayout->addSpacing( 156 );
outerModeLayout->addStretch();
QWidget* modeWidget = new QWidget( this );
QHBoxLayout* modeLayout = new QHBoxLayout;
modeWidget->setLayout( modeLayout );
modeWidget->setStyleSheet( css );
m_radioNormal = new QRadioButton( modeWidget );
m_radioDetailed = new QRadioButton( modeWidget );
m_radioCloud = new QRadioButton( modeWidget );
//for the CSS:
m_radioNormal->setObjectName( "radioNormal" );
m_radioCloud->setObjectName( "radioCloud" );
m_radioNormal->setFocusPolicy( Qt::NoFocus );
m_radioDetailed->setFocusPolicy( Qt::NoFocus );
m_radioCloud->setFocusPolicy( Qt::NoFocus );
modeLayout->addWidget( m_radioNormal );
modeLayout->addWidget( m_radioDetailed );
modeLayout->addWidget( m_radioCloud );
modeWidget->setFixedSize( 87, 30 );
m_radioNormal->setChecked( true );
outerModeLayout->addWidget( modeWidget );
outerModeLayout->addStretch();
m_filterField = new QSearchField( this );
m_filterField->setPlaceholderText( tr( "Filter..." ) );
m_filterField->setFixedWidth( 220 );
m_mainLayout->addWidget( m_filterField );
TomahawkUtils::unmarginLayout( outerModeLayout );
TomahawkUtils::unmarginLayout( modeLayout );
connect( &m_filterTimer, SIGNAL( timeout() ), SLOT( applyFilter() ) );
connect( m_filterField, SIGNAL( textChanged( QString ) ), SLOT( onFilterEdited() ) );
NewClosure( m_radioNormal, SIGNAL( clicked() ), const_cast< FlexibleView* >( parent ), SLOT( setCurrentMode( FlexibleViewMode ) ), FlexibleView::Flat )->setAutoDelete( false );
NewClosure( m_radioDetailed, SIGNAL( clicked() ), const_cast< FlexibleView* >( parent ), SLOT( setCurrentMode( FlexibleViewMode ) ), FlexibleView::Detailed )->setAutoDelete( false );
NewClosure( m_radioCloud, SIGNAL( clicked() ), const_cast< FlexibleView* >( parent ), SLOT( setCurrentMode( FlexibleViewMode ) ), FlexibleView::Grid )->setAutoDelete( false );
}
FlexibleHeader::~FlexibleHeader()
{
}
void
FlexibleHeader::setFilter( const QString& filter )
{
m_filterField->setText( filter );
}
void
FlexibleHeader::onFilterEdited()
{
m_filter = m_filterField->text();
m_filterTimer.stop();
m_filterTimer.setInterval( 280 );
m_filterTimer.setSingleShot( true );
m_filterTimer.start();
}
void
FlexibleHeader::applyFilter()
{
emit filterTextChanged( m_filterField->text() );
}
void
FlexibleHeader::changeEvent( QEvent* e )
{
QWidget::changeEvent( e );
switch ( e->type() )
{
case QEvent::LanguageChange:
// ui->retranslateUi( this );
break;
default:
break;
}
}

View File

@@ -1,68 +0,0 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2010-2011, Christian Muehlhaeuser <muesli@tomahawk-player.org>
* Copyright 2012, Teo Mrnjavac <teo@kde.org>
*
* Tomahawk is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Tomahawk is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU 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 FLEXIBLEHEADER_H
#define FLEXIBLEHEADER_H
#include <QTimer>
#include "widgets/BasicHeader.h"
#include "DllMacro.h"
#include "Artist.h"
class QPaintEvent;
class FlexibleView;
class QRadioButton;
class QSearchField;
class DLLEXPORT FlexibleHeader : public BasicHeader
{
Q_OBJECT
public:
FlexibleHeader( FlexibleView* parent );
~FlexibleHeader();
public slots:
void setFilter( const QString& filter );
signals:
void filterTextChanged( const QString& filter );
protected:
void changeEvent( QEvent* e );
private slots:
void onFilterEdited();
void applyFilter();
private:
FlexibleView* m_parent;
QString m_filter;
QTimer m_filterTimer;
QRadioButton* m_radioCloud;
QRadioButton* m_radioDetailed;
QRadioButton* m_radioNormal;
QSearchField* m_filterField;
};
#endif

View File

@@ -22,11 +22,11 @@
#include <QStackedWidget>
#include <QVBoxLayout>
#include "playlist/FlexibleHeader.h"
#include "widgets/DeclarativeHeader.h"
#include "playlist/PlayableModel.h"
#include "playlist/PlaylistModel.h"
#include "playlist/TrackView.h"
#include "playlist/GridView.h"
#include "playlist/QmlGridView.h"
#include "playlist/PlaylistLargeItemDelegate.h"
#include "PlayableProxyModelPlaylistInterface.h"
#include "utils/TomahawkUtilsGui.h"
@@ -37,13 +37,13 @@ using namespace Tomahawk;
FlexibleView::FlexibleView( QWidget* parent )
: QWidget( parent )
, m_header( new FlexibleHeader( this ) )
, m_header( new DeclarativeHeader( this ) )
, m_trackView( new TrackView() )
, m_detailedView( new TrackView() )
, m_gridView( new GridView() )
, m_gridView( new QmlGridView() )
, m_model( 0 )
{
qRegisterMetaType< FlexibleViewMode >( "FlexibleViewMode" );
qRegisterMetaType< TomahawkUtils::ViewMode >( "TomahawkUtils::ViewMode" );
// m_trackView->setPlaylistInterface( m_playlistInterface );
m_detailedView->setPlaylistInterface( m_trackView->proxyModel()->playlistInterface() );
@@ -69,9 +69,10 @@ FlexibleView::FlexibleView( QWidget* parent )
m_stack->addWidget( m_detailedView );
m_stack->addWidget( m_gridView );
setCurrentMode( Flat );
setCurrentMode( TomahawkUtils::ViewModeFlat );
connect( m_header, SIGNAL( filterTextChanged( QString ) ), SLOT( setFilter( QString ) ) );
connect( m_header, SIGNAL(viewModeChanged(TomahawkUtils::ViewMode)), SLOT(setCurrentMode(TomahawkUtils::ViewMode)));
}
@@ -124,7 +125,7 @@ FlexibleView::setDetailedView( TrackView* view )
void
FlexibleView::setGridView( GridView* view )
FlexibleView::setGridView( QmlGridView *view )
{
if ( m_gridView )
{
@@ -178,26 +179,26 @@ FlexibleView::setPlaylistModel( PlaylistModel* model )
void
FlexibleView::setCurrentMode( FlexibleViewMode mode )
FlexibleView::setCurrentMode( TomahawkUtils::ViewMode mode )
{
m_mode = mode;
switch ( mode )
{
case Flat:
case TomahawkUtils::ViewModeFlat:
{
tDebug() << "m_trackView:" << m_trackView << m_stack->indexOf( m_trackView );
m_stack->setCurrentWidget( m_trackView );
break;
}
case Detailed:
case TomahawkUtils::ViewModeDetailed:
{
m_stack->setCurrentWidget( m_detailedView );
break;
}
case Grid:
case TomahawkUtils::ViewModeGrid:
{
m_stack->setCurrentWidget( m_gridView );
break;
@@ -229,10 +230,10 @@ FlexibleView::description() const
}
QPixmap
FlexibleView::pixmap() const
QString
FlexibleView::headerIconSource() const
{
return m_pixmap;
return m_headerIconSource;
}
@@ -270,22 +271,22 @@ FlexibleView::setEmptyTip( const QString& tip )
{
m_trackView->setEmptyTip( tip );
m_detailedView->setEmptyTip( tip );
m_gridView->setEmptyTip( tip );
// m_gridView->setEmptyTip( tip );
}
void
FlexibleView::setPixmap( const QPixmap& pixmap )
FlexibleView::setHeaderIconSource( const QString& headerIconSource )
{
m_pixmap = pixmap;
m_header->setPixmap( pixmap );
m_headerIconSource = headerIconSource;
m_header->setIconSource( headerIconSource );
}
void
FlexibleView::onModelChanged()
{
m_header->setPixmap( m_pixmap );
m_header->setIconSource( m_headerIconSource );
m_header->setCaption( m_model->title() );
m_header->setDescription( m_model->description() );

View File

@@ -25,20 +25,23 @@
class QStackedWidget;
class GridView;
class QmlGridView;
class TrackView;
class PlayableModel;
class PlaylistModel;
class FlexibleHeader;
namespace Tomahawk
{
class DeclarativeHeader;
}
using namespace Tomahawk;
class DLLEXPORT FlexibleView : public QWidget, public Tomahawk::ViewPage
{
Q_OBJECT
public:
enum FlexibleViewMode
{ Flat = 0, Detailed = 1, Grid = 2 };
explicit FlexibleView( QWidget* parent = 0 );
~FlexibleView();
@@ -47,33 +50,33 @@ public:
virtual QString title() const;
virtual QString description() const;
virtual QPixmap pixmap() const;
virtual QString headerIconSource() const;
virtual bool showInfoBar() const { return false; }
virtual bool jumpToCurrentTrack();
TrackView* trackView() const { return m_trackView; }
TrackView* detailedView() const { return m_detailedView; }
GridView* gridView() const { return m_gridView; }
QmlGridView* gridView() const { return m_gridView; }
void setGuid( const QString& guid );
void setTrackView( TrackView* view );
void setDetailedView( TrackView* view );
void setGridView( GridView* view );
void setGridView( QmlGridView* view );
void setPlayableModel( PlayableModel* model );
void setPlaylistModel( PlaylistModel* model );
void setPixmap( const QPixmap& pixmap );
void setHeaderIconSource( const QString& headerIconSource );
void setEmptyTip( const QString& tip );
public slots:
void setCurrentMode( FlexibleViewMode mode );
void setCurrentMode( TomahawkUtils::ViewMode mode );
virtual bool setFilter( const QString& pattern );
signals:
void modeChanged( FlexibleViewMode mode );
void modeChanged( TomahawkUtils::ViewMode mode );
void destroyed( QWidget* widget );
private slots:
@@ -81,19 +84,17 @@ private slots:
void onWidgetDestroyed( QWidget* widget );
private:
FlexibleHeader* m_header;
QPixmap m_pixmap;
DeclarativeHeader* m_header;
QString m_headerIconSource;
TrackView* m_trackView;
TrackView* m_detailedView;
GridView* m_gridView;
QmlGridView* m_gridView;
PlayableModel* m_model;
QStackedWidget* m_stack;
FlexibleViewMode m_mode;
TomahawkUtils::ViewMode m_mode;
};
Q_DECLARE_METATYPE( FlexibleView::FlexibleViewMode );
#endif // FLEXIBLEVIEW_H

View File

@@ -53,9 +53,11 @@ PlayableItem::PlayableItem( const Tomahawk::album_ptr& album, PlayableItem* pare
: QObject( parent )
, m_album( album )
{
Q_ASSERT( !m_album.isNull() );
init( parent, row );
connect( album.data(), SIGNAL( updated() ), SIGNAL( dataChanged() ) );
connect( album.data(), SIGNAL( coverChanged() ), SIGNAL( coverChanged() ) );
}
@@ -63,9 +65,11 @@ PlayableItem::PlayableItem( const Tomahawk::artist_ptr& artist, PlayableItem* pa
: QObject( parent )
, m_artist( artist )
{
Q_ASSERT( !m_artist.isNull() );
init( parent, row );
connect( artist.data(), SIGNAL( updated() ), SIGNAL( dataChanged() ) );
connect( artist.data(), SIGNAL( coverChanged() ), SIGNAL( coverChanged() ) );
}
@@ -73,6 +77,7 @@ PlayableItem::PlayableItem( const Tomahawk::result_ptr& result, PlayableItem* pa
: QObject( parent )
, m_result( result )
{
Q_ASSERT( !m_result.isNull() );
init( parent, row );
connect( result.data(), SIGNAL( updated() ),
@@ -84,6 +89,7 @@ PlayableItem::PlayableItem( const Tomahawk::query_ptr& query, PlayableItem* pare
: QObject( parent )
, m_query( query )
{
Q_ASSERT( !m_query.isNull() );
init( parent, row );
connect( query.data(), SIGNAL( socialActionsLoaded() ),
@@ -94,6 +100,9 @@ PlayableItem::PlayableItem( const Tomahawk::query_ptr& query, PlayableItem* pare
connect( query.data(), SIGNAL( resultsChanged() ),
SLOT( onResultsChanged() ) );
connect( query->displayQuery().data(), SIGNAL( coverChanged() ), SIGNAL( coverChanged() ) );
connect( query->displayQuery().data(), SIGNAL( coverChanged() ), SIGNAL( dataChanged() ) );
}
@@ -102,6 +111,7 @@ PlayableItem::PlayableItem( const Tomahawk::plentry_ptr& entry, PlayableItem* pa
, m_entry( entry )
{
m_query = entry->query();
Q_ASSERT( !m_query.isNull() );
init( parent, row );
connect( m_query.data(), SIGNAL( socialActionsLoaded() ),
@@ -112,6 +122,9 @@ PlayableItem::PlayableItem( const Tomahawk::plentry_ptr& entry, PlayableItem* pa
connect( m_query.data(), SIGNAL( resultsChanged() ),
SLOT( onResultsChanged() ) );
connect( m_query->displayQuery().data(), SIGNAL( coverChanged() ), SIGNAL( coverChanged() ) );
connect( m_query->displayQuery().data(), SIGNAL( coverChanged() ), SIGNAL( dataChanged() ) );
}
@@ -146,7 +159,16 @@ void
PlayableItem::onResultsChanged()
{
if ( !m_query->results().isEmpty() )
{
m_result = m_query->results().first();
if ( m_query->displayQuery()->coverLoaded() )
{
emit coverChanged();
}
connect( m_query->displayQuery().data(), SIGNAL( coverChanged() ), SIGNAL( dataChanged() ) );
connect( m_query->displayQuery().data(), SIGNAL( coverChanged() ), SIGNAL( coverChanged() ) );
}
else
m_result = result_ptr();
@@ -190,6 +212,14 @@ PlayableItem::artistName() const
{
return m_query->artist();
}
else if ( !m_album.isNull() )
{
return m_album->artist()->name();
}
else if ( !m_artist.isNull() )
{
return m_artist->name();
}
return QString();
}
@@ -206,6 +236,10 @@ PlayableItem::albumName() const
{
return m_query->album();
}
else if ( !m_album.isNull() )
{
return m_album->name();
}
return QString();
}

View File

@@ -30,7 +30,10 @@
class DLLEXPORT PlayableItem : public QObject
{
Q_OBJECT
Q_PROPERTY(QString name READ name NOTIFY dataChanged)
Q_PROPERTY(QString artistName READ artistName NOTIFY dataChanged)
Q_PROPERTY(QString albumName READ albumName NOTIFY dataChanged)
Q_PROPERTY(bool isPlaying READ isPlaying NOTIFY dataChanged)
public:
~PlayableItem();
@@ -64,6 +67,7 @@ public:
signals:
void dataChanged();
void coverChanged();
private slots:
void onResultsChanged();

View File

@@ -43,6 +43,13 @@ PlayableModel::PlayableModel( QObject* parent, bool loading )
, m_readOnly( true )
, m_loading( loading )
{
QHash<int, QByteArray> roleNames;
roleNames.insert( ArtistRole, "artistName" );
roleNames.insert( TrackRole, "trackName" );
roleNames.insert( CoverIDRole, "coverID" );
roleNames.insert( IsPlayingRole, "isPlaying" );
setRoleNames( roleNames );
connect( AudioEngine::instance(), SIGNAL( started( Tomahawk::result_ptr ) ), SLOT( onPlaybackStarted( Tomahawk::result_ptr ) ), Qt::DirectConnection );
connect( AudioEngine::instance(), SIGNAL( stopped() ), SLOT( onPlaybackStopped() ), Qt::DirectConnection );
@@ -144,6 +151,12 @@ PlayableModel::parent( const QModelIndex& child ) const
QVariant
PlayableModel::artistData( const artist_ptr& artist, int role ) const
{
if ( role == CoverIDRole )
{
artist->cover( QSize( 0, 0 ) );
return artist->coverId();
}
if ( role != Qt::DisplayRole ) // && role != Qt::ToolTipRole )
return QVariant();
@@ -154,6 +167,12 @@ PlayableModel::artistData( const artist_ptr& artist, int role ) const
QVariant
PlayableModel::albumData( const album_ptr& album, int role ) const
{
if ( role == CoverIDRole )
{
album->cover( QSize( 0, 0 ) );
return album->coverId();
}
if ( role != Qt::DisplayRole ) // && role != Qt::ToolTipRole )
return QVariant();
@@ -164,6 +183,12 @@ PlayableModel::albumData( const album_ptr& album, int role ) const
QVariant
PlayableModel::queryData( const query_ptr& query, int column, int role ) const
{
if ( role == CoverIDRole )
{
query->cover( QSize( 0, 0 ) );
return query->coverId();
}
if ( role != Qt::DisplayRole ) // && role != Qt::ToolTipRole )
return QVariant();
@@ -274,15 +299,26 @@ PlayableModel::data( const QModelIndex& index, int role ) const
{
return QVariant();
}
if ( role == Qt::TextAlignmentRole )
else if ( role == Qt::TextAlignmentRole )
{
return QVariant( columnAlignment( index.column() ) );
}
else if ( role == IsPlayingRole )
{
return entry->isPlaying();
}
int column = index.column();
if ( role < CoverIDRole && role >= Qt::UserRole )
{
// Map user-role to column
column = role - Qt::UserRole;
role = Qt::DisplayRole;
}
if ( !entry->query().isNull() )
{
return queryData( entry->query()->displayQuery(), index.column(), role );
return queryData( entry->query()->displayQuery(), column, role );
}
else if ( !entry->artist().isNull() )
{
@@ -322,10 +358,13 @@ PlayableModel::headerData( int section, Qt::Orientation orientation, int role )
void
PlayableModel::setCurrentIndex( const QModelIndex& index )
{
PlayableItem* oldEntry = itemFromIndex( m_currentIndex );
if ( oldEntry )
if ( m_currentIndex.isValid() )
{
oldEntry->setIsPlaying( false );
PlayableItem* oldEntry = itemFromIndex( m_currentIndex );
if ( oldEntry )
{
oldEntry->setIsPlaying( false );
}
}
PlayableItem* entry = itemFromIndex( index );
@@ -745,6 +784,13 @@ PlayableModel::itemFromIndex( const QModelIndex& index ) const
}
PlayableItem*
PlayableModel::itemFromIndex( int itemIndex ) const
{
return itemFromIndex( index( itemIndex, 0, QModelIndex() ) );
}
void
PlayableModel::appendArtist( const Tomahawk::artist_ptr& artist )
{

View File

@@ -55,6 +55,26 @@ public:
Name = 12
};
enum PlayableRoles
{
ArtistRole = Qt::UserRole,
TrackRole,
ComposerRole,
AlbumRole,
AlbumPosRole,
DurationRole,
BitrateRole,
AgeRole,
YearRole,
FilesizeRole,
OriginRole,
ScoreRole,
NameRole,
CoverIDRole,
IsPlayingRole
};
explicit PlayableModel( QObject* parent = 0, bool loading = true );
virtual ~PlayableModel();
@@ -104,6 +124,7 @@ public:
virtual PlayableItem* itemFromIndex( const QModelIndex& index ) const;
virtual PlayableItem* itemFromQuery( const Tomahawk::query_ptr& query ) const;
virtual PlayableItem* itemFromResult( const Tomahawk::result_ptr& result ) const;
Q_INVOKABLE PlayableItem* itemFromIndex( int itemIndex ) const;
/// Returns a flat list of all tracks in this model
QList< Tomahawk::query_ptr > queries() const;

View File

@@ -632,6 +632,15 @@ PlayableProxyModel::setFilter( const QString& pattern )
}
PlayableItem*
PlayableProxyModel::itemFromIndex( int itemIndex ) const
{
// qDebug() << "returning item" << sourceModel()->itemFromIndex( itemIndex )->name();
QModelIndex modelIndex = index( itemIndex, 0 );
return sourceModel()->itemFromIndex( mapToSource( modelIndex ) );
}
void
PlayableProxyModel::setCurrentIndex( const QModelIndex& index )
{

View File

@@ -36,7 +36,7 @@ public:
{ Detailed = 0, Short = 1, ShortWithAvatars = 2, Large = 3, Collection = 4 };
enum PlayableProxyModelRole
{ StyleRole = Qt::UserRole + 1 };
{ StyleRole = Qt::UserRole + 100 };
explicit PlayableProxyModel ( QObject* parent = 0 );
virtual ~PlayableProxyModel() {}
@@ -71,6 +71,7 @@ public:
virtual PlayableItem* itemFromIndex( const QModelIndex& index ) const { return sourceModel()->itemFromIndex( index ); }
virtual PlayableItem* itemFromQuery( const Tomahawk::query_ptr& query ) const { return sourceModel()->itemFromQuery( query ); }
virtual PlayableItem* itemFromResult( const Tomahawk::result_ptr& result ) const { return sourceModel()->itemFromResult( result ); }
Q_INVOKABLE virtual PlayableItem* itemFromIndex( int itemIndex ) const;
virtual Tomahawk::playlistinterface_ptr playlistInterface() const;
void setPlaylistInterface( const Tomahawk::playlistinterface_ptr& playlistInterface );

View File

@@ -139,7 +139,6 @@ PlayableProxyModelPlaylistInterface::siblingIndex( int itemsAway, qint64 rootInd
return -1;
PlayableProxyModel* proxyModel = m_proxyModel.data();
while ( m_shuffleHistory.count() && m_shuffleHistory.count() >= proxyModel->rowCount() )
{
m_shuffleHistory.removeFirst();

View File

@@ -163,8 +163,8 @@ PlaylistView::onMenuTriggered( int action )
}
QPixmap
PlaylistView::pixmap() const
QString
PlaylistView::iconSource() const
{
return TomahawkUtils::defaultPixmap( TomahawkUtils::Playlist );
return TomahawkUtils::defaultImagePath( TomahawkUtils::Playlist );
}

View File

@@ -40,7 +40,7 @@ public:
virtual QList<Tomahawk::PlaylistUpdaterInterface*> updaters() const;
virtual QPixmap pixmap() const;
virtual QString iconSource() const;
virtual bool isTemporaryPage() const;
signals:

View File

@@ -0,0 +1,124 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2012, Michael Zanetti <mzanetti@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 "QmlGridView.h"
#include "widgets/DeclarativeCoverArtProvider.h"
#include "PlayableProxyModelPlaylistInterface.h"
#include "ViewManager.h"
#include "audio/AudioEngine.h"
#include <QDeclarativeContext>
using namespace Tomahawk;
class QmlGridPlaylistInterface : public PlayableProxyModelPlaylistInterface
{
Q_OBJECT
public:
explicit QmlGridPlaylistInterface( PlayableProxyModel* proxy, QmlGridView* view ) : PlayableProxyModelPlaylistInterface( proxy ), m_view( view ) {}
virtual bool hasChildInterface( playlistinterface_ptr playlistInterface )
{
// if ( m_view.isNull() || !m_view.data()->m_playing.isValid() )
// {
// return false;
// }
// PlayableItem* item = m_view.data()->model()->itemFromIndex( m_view.data()->proxyModel()->mapToSource( m_view.data()->m_playing ) );
// if ( item )
// {
// if ( !item->album().isNull() )
// return item->album()->playlistInterface( Tomahawk::Mixed ) == playlistInterface;
// else if ( !item->artist().isNull() )
// return item->artist()->playlistInterface( Tomahawk::Mixed ) == playlistInterface;
// else if ( !item->query().isNull() && !playlistInterface.dynamicCast< SingleTrackPlaylistInterface >().isNull() )
// return item->query() == playlistInterface.dynamicCast< SingleTrackPlaylistInterface >()->track();
// }
return false;
}
private:
QWeakPointer<QmlGridView> m_view;
};
QmlGridView::QmlGridView(QWidget *parent) : DeclarativeView(parent)
{
m_proxyModel = new PlayableProxyModel( this );
m_playlistInterface = playlistinterface_ptr( new QmlGridPlaylistInterface( m_proxyModel, this ) );
rootContext()->setContextProperty( "mainModel", m_proxyModel );
setSource( QUrl( "qrc" RESPATH "qml/QmlGridView.qml" ) );
}
QmlGridView::~QmlGridView()
{
}
void QmlGridView::setPlayableModel(PlayableModel *model)
{
// m_inited = false;
m_model = model;
if ( m_proxyModel )
{
m_proxyModel->setSourcePlayableModel( m_model );
m_proxyModel->sort( 0 );
}
emit modelChanged();
}
void QmlGridView::setPlaylistInterface(playlistinterface_ptr playlistInterface)
{
proxyModel()->setPlaylistInterface( playlistInterface );
}
void
QmlGridView::onItemClicked( int index )
{
PlayableItem* item = m_model->itemFromIndex( m_proxyModel->mapToSource( m_proxyModel->index( index, 0) ) );
if ( item )
{
if ( !item->album().isNull() )
ViewManager::instance()->show( item->album() );
else if ( !item->artist().isNull() )
ViewManager::instance()->show( item->artist() );
else if ( !item->query().isNull() )
ViewManager::instance()->show( item->query() );
}
}
void QmlGridView::onItemPlayClicked(int index)
{
PlayableItem* item = m_model->itemFromIndex( m_proxyModel->mapToSource( m_proxyModel->index( index, 0) ) );
if ( item )
{
if ( !item->query().isNull() )
AudioEngine::instance()->playItem( Tomahawk::playlistinterface_ptr(), item->query() );
else if ( !item->album().isNull() )
AudioEngine::instance()->playItem( item->album() );
else if ( !item->artist().isNull() )
AudioEngine::instance()->playItem( item->artist() );
}
}
#include "QmlGridView.moc"

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>
* Copyright 2010-2012, Jeff Mitchell <jeff@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 QMLGRIDVIEW_H
#define QMLGRIDVIEW_H
#include <widgets/DeclarativeView.h>
#include <QSortFilterProxyModel>
#include <QTimer>
#include "ViewPage.h"
#include "PlayableProxyModel.h"
#include "widgets/OverlayWidget.h"
#include "DllMacro.h"
namespace Tomahawk
{
class ContextMenu;
};
class AnimatedSpinner;
class GridItemDelegate;
class PlayableModel;
class GridPlaylistInterface;
class DLLEXPORT QmlGridView : public Tomahawk::DeclarativeView, public Tomahawk::ViewPage
{
Q_OBJECT
public:
QmlGridView( QWidget *parent = 0);
~QmlGridView();
void setPlayableModel( PlayableModel* model );
// void setModel( QAbstractItemModel* model );
PlayableProxyModel* proxyModel() const { return m_proxyModel; }
QWidget *widget() { return this; }
virtual void setPlaylistInterface(Tomahawk::playlistinterface_ptr playlistInterface);
virtual Tomahawk::playlistinterface_ptr playlistInterface() const { return m_playlistInterface; }
virtual QString title() const { return m_model->title(); }
virtual QString description() const { return m_model->description(); }
virtual bool jumpToCurrentTrack() { return false; }
Q_INVOKABLE void onItemClicked( int index );
Q_INVOKABLE void onItemPlayClicked( int index );
private:
PlayableModel* m_model;
PlayableProxyModel* m_proxyModel;
Tomahawk::playlistinterface_ptr m_playlistInterface;
signals:
void modelChanged();
};
#endif

View File

@@ -60,7 +60,7 @@ ViewHeader::visibleSectionCount() const
void
ViewHeader::onSectionsChanged()
{
tDebug( LOGVERBOSE ) << "Saving columns state for view guid:" << m_guid;
tDebug() << "Saving columns state for view guid:" << m_guid;
if ( !m_guid.isEmpty() )
TomahawkSettings::instance()->setPlaylistColumnSizes( m_guid, saveState() );
}
@@ -76,7 +76,7 @@ ViewHeader::checkState()
disconnect( this, SIGNAL( sectionResized( int, int, int ) ), this, SLOT( onSectionsChanged() ) );
QByteArray state;
tDebug( LOGVERBOSE ) << "Restoring columns state for view:" << m_guid;
tDebug() << "Restoring columns state for view:" << m_guid;
if ( !m_guid.isEmpty() )
state = TomahawkSettings::instance()->playlistColumnSizes( m_guid );
@@ -87,7 +87,7 @@ ViewHeader::checkState()
}
else
{
tDebug( LOGVERBOSE ) << "Giving columns initial weighting:" << m_columnWeights;
tDebug() << "Giving columns initial weighting:" << m_columnWeights;
for ( int i = 0; i < count() - 1; i++ )
{
if ( isSectionHidden( i ) )
@@ -97,6 +97,7 @@ ViewHeader::checkState()
double nw = (double)m_parent->width() * m_columnWeights.at( i );
resizeSection( i, qMax( minimumSectionSize(), int( nw - 0.5 ) ) );
tDebug() << "Initial size for column" << i << qMax( minimumSectionSize(), int( nw - 0.5 ) );
}
}

View File

@@ -154,27 +154,30 @@ DynamicModel::trackResolveFinished( bool success )
Query* q = qobject_cast<Query*>( sender() );
tDebug() << "Got resolveFinished in DynamicModel" << q->track() << q->artist();
tDebug() << "Got resolveFinished in DynamicModel" << q->toString();
if ( !m_waitingFor.contains( q ) )
return;
if ( !q->playable() )
{
tDebug() << "Got not playable or resolved track:" << q->track() << q->artist() << m_lastResolvedRow << m_currentAttempts;
tDebug() << "Got not playable or resolved track:" << q->toString() << m_lastResolvedRow << m_currentAttempts;
m_currentAttempts++;
int curAttempts = m_startingAfterFailed ? m_currentAttempts - 20 : m_currentAttempts; // if we just failed, m_currentAttempts includes those failures
if( curAttempts < 20 ) {
if ( curAttempts < 20 )
{
qDebug() << "FETCHING MORE!";
m_playlist->generator()->fetchNext();
} else {
}
else
{
m_startingAfterFailed = true;
emit trackGenerationFailure( tr( "Could not find a playable track.\n\nPlease change the filters or try again." ) );
}
}
else
{
qDebug() << "Got successful resolved track:" << q->track() << q->artist() << m_lastResolvedRow << m_currentAttempts;
qDebug() << "Got successful resolved track:" << q->toString() << m_lastResolvedRow << m_currentAttempts;
if ( m_currentAttempts > 0 ) {
qDebug() << "EMITTING AN ASK FOR COLLAPSE:" << m_lastResolvedRow << m_currentAttempts;

View File

@@ -36,33 +36,32 @@ using namespace Tomahawk;
DynamicPlaylist::DynamicPlaylist( const Tomahawk::source_ptr& author, const QString& type )
: Playlist( author )
{
qDebug() << Q_FUNC_INFO << "JSON";
tDebug( LOGVERBOSE ) << Q_FUNC_INFO << "JSON";
m_generator = geninterface_ptr( GeneratorFactory::create( type ) );
}
DynamicPlaylist::~DynamicPlaylist()
{
}
// Called by loadAllPlaylists command
DynamicPlaylist::DynamicPlaylist ( const Tomahawk::source_ptr& src,
const QString& currentrevision,
const QString& title,
const QString& info,
const QString& creator,
uint createdOn,
const QString& type,
GeneratorMode mode,
bool shared,
int lastmod,
const QString& guid )
DynamicPlaylist::DynamicPlaylist( const Tomahawk::source_ptr& src,
const QString& currentrevision,
const QString& title,
const QString& info,
const QString& creator,
uint createdOn,
const QString& type,
GeneratorMode mode,
bool shared,
int lastmod,
const QString& guid )
: Playlist( src, currentrevision, title, info, creator, createdOn, shared, lastmod, guid )
, m_autoLoad( false )
{
// qDebug() << "Creating Dynamic Playlist 1";
tDebug( LOGVERBOSE ) << "Creating Dynamic Playlist 1";
// TODO instantiate generator
m_generator = geninterface_ptr( GeneratorFactory::create( type ) );
m_generator->setMode( mode );
@@ -82,7 +81,7 @@ DynamicPlaylist::DynamicPlaylist( const Tomahawk::source_ptr& author,
: Playlist ( author, guid, title, info, creator, shared )
, m_autoLoad( autoLoad )
{
// qDebug() << "Creating Dynamic Playlist 2";
tDebug( LOGVERBOSE ) << "Creating Dynamic Playlist 2";
m_generator = geninterface_ptr( GeneratorFactory::create( type ) );
m_generator->setMode( mode );
}
@@ -128,7 +127,7 @@ DynamicPlaylist::load( const QString& guid )
{
dynplaylist_ptr p;
foreach( const Tomahawk::source_ptr& source, SourceList::instance()->sources() )
foreach ( const Tomahawk::source_ptr& source, SourceList::instance()->sources() )
{
p = source->collection()->autoPlaylist( guid );
if ( p.isNull() )
@@ -143,7 +142,7 @@ DynamicPlaylist::load( const QString& guid )
dynplaylist_ptr
DynamicPlaylist::create( const Tomahawk::source_ptr& author,
DynamicPlaylist::create(const Tomahawk::source_ptr& author,
const QString& guid,
const QString& title,
const QString& info,
@@ -151,17 +150,21 @@ DynamicPlaylist::create( const Tomahawk::source_ptr& author,
GeneratorMode mode,
bool shared,
const QString& type,
bool autoLoad
)
bool autoLoad,
bool temporary )
{
dynplaylist_ptr dynplaylist = Tomahawk::dynplaylist_ptr( new DynamicPlaylist( author, guid, title, info, creator, type, mode, shared, autoLoad ), &QObject::deleteLater );
dynplaylist->setWeakSelf( dynplaylist.toWeakRef() );
DatabaseCommand_CreateDynamicPlaylist* cmd = new DatabaseCommand_CreateDynamicPlaylist( author, dynplaylist, autoLoad );
connect( cmd, SIGNAL(finished()), dynplaylist.data(), SIGNAL(created()) );
Database::instance()->enqueue( QSharedPointer<DatabaseCommand>(cmd) );
if ( autoLoad )
dynplaylist->reportCreated( dynplaylist );
if ( !temporary )
{
DatabaseCommand_CreateDynamicPlaylist* cmd = new DatabaseCommand_CreateDynamicPlaylist( author, dynplaylist, autoLoad );
connect( cmd, SIGNAL( finished() ), dynplaylist.data(), SIGNAL( created() ) );
Database::instance()->enqueue( QSharedPointer<DatabaseCommand>(cmd) );
if ( autoLoad )
dynplaylist->reportCreated( dynplaylist );
}
return dynplaylist;
}
@@ -185,7 +188,7 @@ void
DynamicPlaylist::createNewRevision( const QString& newrev,
const QString& oldrev,
const QString& type,
const QList< dyncontrol_ptr>& controls,
const QVariantList& controls,
const QList< plentry_ptr >& entries )
{
Q_ASSERT( m_source->isLocal() || newrev == oldrev );
@@ -233,7 +236,7 @@ void
DynamicPlaylist::createNewRevision( const QString& newrev,
const QString& oldrev,
const QString& type,
const QList< dyncontrol_ptr>& controls )
const QVariantList& controls )
{
Q_ASSERT( m_source->isLocal() || newrev == oldrev );
@@ -271,34 +274,15 @@ DynamicPlaylist::loadRevision( const QString& rev )
setBusy( true );
DatabaseCommand_LoadDynamicPlaylistEntries* cmd = new DatabaseCommand_LoadDynamicPlaylistEntries( rev.isEmpty() ? currentrevision() : rev );
if ( m_generator->mode() == OnDemand ) {
connect( cmd, SIGNAL( done( QString,
bool,
QString,
QList< QVariantMap >,
bool ) ),
SLOT( setRevision( QString,
bool,
QString,
QList< QVariantMap >,
bool) ) );
} else if ( m_generator->mode() == Static ) {
connect( cmd, SIGNAL( done( QString,
QList< QString >,
QList< QString >,
QString,
QList< QVariantMap >,
bool,
QMap< QString, Tomahawk::plentry_ptr >,
bool ) ),
SLOT( setRevision( QString,
QList< QString >,
QList< QString >,
QString,
QList< QVariantMap >,
bool,
QMap< QString, Tomahawk::plentry_ptr >,
bool ) ) );
if ( m_generator->mode() == OnDemand )
{
connect( cmd, SIGNAL( done( QString, bool, QString, QVariantList, bool ) ),
SLOT( setRevision( QString, bool, QString, QVariantList, bool) ) );
}
else if ( m_generator->mode() == Static )
{
connect( cmd, SIGNAL( done( QString, QList< QString >, QList< QString >, QString, QVariantList, bool, QMap< QString, Tomahawk::plentry_ptr >, bool ) ),
SLOT( setRevision( QString, QList< QString >, QList< QString >, QString, QVariantList, bool, QMap< QString, Tomahawk::plentry_ptr >, bool ) ) );
}
Database::instance()->enqueue( QSharedPointer<DatabaseCommand>( cmd ) );
@@ -320,10 +304,10 @@ DynamicPlaylist::remove( const Tomahawk::dynplaylist_ptr& playlist )
void
DynamicPlaylist::reportCreated( const Tomahawk::dynplaylist_ptr& self )
{
// qDebug() << Q_FUNC_INFO;
Q_ASSERT( self.data() == this );
Q_ASSERT( !author().isNull() );
Q_ASSERT( !author()->collection().isNull() );
// will emit Collection::playlistCreated(...)
// qDebug() << "Creating dynplaylist belonging to:" << author().data() << author().isNull();
// qDebug() << "REPORTING DYNAMIC PLAYLIST CREATED:" << this << author()->friendlyName();
@@ -337,8 +321,8 @@ DynamicPlaylist::reportCreated( const Tomahawk::dynplaylist_ptr& self )
void
DynamicPlaylist::reportDeleted( const Tomahawk::dynplaylist_ptr& self )
{
// qDebug() << Q_FUNC_INFO;
Q_ASSERT( self.data() == this );
// will emit Collection::playlistDeleted(...)
if ( self->mode() == Static )
author()->collection()->deleteAutoPlaylist( self );
@@ -355,7 +339,6 @@ DynamicPlaylist::addEntries( const QList< query_ptr >& queries, const QString& o
Q_ASSERT( m_generator->mode() == Static );
QList<plentry_ptr> el = entriesFromQueries( queries );
QString newrev = uuid();
createNewRevision( newrev, oldrev, m_generator->type(), m_generator->controls(), el );
}
@@ -376,7 +359,7 @@ DynamicPlaylist::setRevision( const QString& rev,
const QList< QString >& neworderedguids,
const QList< QString >& oldorderedguids,
const QString& type,
const QList< dyncontrol_ptr >& controls,
const QVariantList& controls,
bool is_newest_rev,
const QMap< QString, plentry_ptr >& addedmap,
bool applied )
@@ -391,14 +374,16 @@ DynamicPlaylist::setRevision( const QString& rev,
Q_ARG( QList<QString> , neworderedguids ),
Q_ARG( QList<QString> , oldorderedguids ),
Q_ARG( QString , type ),
QGenericArgument( "QList< Tomahawk::dyncontrol_ptr > " , (const void*)&controls ),
Q_ARG( QVariantList, controls ),
Q_ARG( bool, is_newest_rev ),
QGenericArgument( "QMap< QString,Tomahawk::plentry_ptr > " , (const void*)&addedmap ),
Q_ARG( bool, applied ) );
return;
}
if ( m_generator->type() != type ) { // new generator needed
if ( m_generator->type() != type )
{
// new generator needed
m_generator = GeneratorFactory::create( type );
}
@@ -414,48 +399,16 @@ DynamicPlaylist::setRevision( const QString& rev,
if ( applied )
setCurrentrevision( rev );
// qDebug() << "EMITTING REVISION LOADED 1!";
setBusy( false );
emit dynamicRevisionLoaded( dpr );
}
void
DynamicPlaylist::setRevision( const QString& rev,
const QList< QString >& neworderedguids,
const QList< QString >& oldorderedguids,
const QString& type,
const QList< QVariantMap>& controlsV,
bool is_newest_rev,
const QMap< QString, Tomahawk::plentry_ptr >& addedmap,
bool applied )
{
if ( QThread::currentThread() != thread() )
{
QMetaObject::invokeMethod( this,
"setRevision",
Qt::BlockingQueuedConnection,
Q_ARG( QString, rev ),
Q_ARG( QList<QString> , neworderedguids ),
Q_ARG( QList<QString> , oldorderedguids ),
Q_ARG( QString , type ),
QGenericArgument( "QList< QVariantMap > " , (const void*)&controlsV ),
Q_ARG( bool, is_newest_rev ),
QGenericArgument( "QMap< QString,Tomahawk::plentry_ptr > " , (const void*)&addedmap ),
Q_ARG( bool, applied ) );
return;
}
QList<dyncontrol_ptr> controls = variantsToControl( controlsV );
setRevision( rev, neworderedguids, oldorderedguids, type, controls, is_newest_rev, addedmap, applied );
}
void
DynamicPlaylist::setRevision( const QString& rev,
bool is_newest_rev,
const QString& type,
const QList< dyncontrol_ptr >& controls,
const QVariantList& controls,
bool applied )
{
if ( QThread::currentThread() != thread() )
@@ -466,7 +419,7 @@ DynamicPlaylist::setRevision( const QString& rev,
Q_ARG( QString, rev ),
Q_ARG( bool, is_newest_rev ),
Q_ARG( QString, type ),
QGenericArgument( "QList< Tomahawk::dyncontrol_ptr >" , (const void*)&controls ),
Q_ARG( QVariantList, controls ),
Q_ARG( bool, applied ) );
return;
}
@@ -490,51 +443,11 @@ DynamicPlaylist::setRevision( const QString& rev,
if ( applied )
setCurrentrevision( rev );
// qDebug() << "EMITTING REVISION LOADED 2!";
setBusy( false );
emit dynamicRevisionLoaded( dpr );
}
void
DynamicPlaylist::setRevision( const QString& rev,
bool is_newest_rev,
const QString& type,
const QList< QVariantMap >& controlsV,
bool applied )
{
if ( QThread::currentThread() != thread() )
{
QMetaObject::invokeMethod( this,
"setRevision",
Qt::BlockingQueuedConnection,
Q_ARG( QString, rev ),
Q_ARG( bool, is_newest_rev ),
Q_ARG( QString, type ),
QGenericArgument( "QList< QVariantMap >" , (const void*)&controlsV ),
Q_ARG( bool, applied ) );
return;
}
QList<dyncontrol_ptr> controls = variantsToControl( controlsV );
setRevision( rev, is_newest_rev, type, controls, applied );
}
QList< dyncontrol_ptr >
DynamicPlaylist::variantsToControl( const QList< QVariantMap >& controlsV )
{
QList<dyncontrol_ptr> realControls;
foreach( QVariantMap controlV, controlsV ) {
dyncontrol_ptr control = GeneratorFactory::createControl( controlV.value( "type" ).toString(), controlV.value( "selectedType" ).toString() );
// qDebug() << "Creating control with data:" << controlV;
QJson::QObjectHelper::qvariant2qobject( controlV, control.data() );
realControls << control;
}
return realControls;
}
void
DynamicPlaylist::checkRevisionQueue()
{

View File

@@ -49,10 +49,10 @@ class DatabaseCommand_LoadDynamicPlaylist;
struct DynQueueItem : RevisionQueueItem
{
QString type;
QList <dyncontrol_ptr> controls;
QVariantList controls;
int mode;
DynQueueItem( const QString& nRev, const QString& oRev, const QString& typ, const QList< dyncontrol_ptr >& ctrls, int m, const QList< plentry_ptr >& e, bool latest ) :
DynQueueItem( const QString& nRev, const QString& oRev, const QString& typ, const QVariantList& ctrls, int m, const QList< plentry_ptr >& e, bool latest ) :
RevisionQueueItem( nRev, oRev, e, latest ), type( typ ), controls( ctrls ), mode( m ) {}
};
@@ -69,7 +69,7 @@ class DLLEXPORT DynamicPlaylist : public Tomahawk::Playlist
friend class ::DatabaseCommand_CreateDynamicPlaylist;
friend class Tomahawk::DatabaseCommand_LoadDynamicPlaylist;
friend class ::DatabaseCommand_LoadAllSortedPlaylists;
friend class ::DatabaseCollection; /// :-(
friend class ::DatabaseCollection;
public:
virtual ~DynamicPlaylist();
@@ -85,7 +85,8 @@ public:
GeneratorMode mode,
bool shared,
const QString& type = QString(),
bool autoLoad = true
bool autoLoad = true,
bool temporary = false
);
static void remove( const dynplaylist_ptr& playlist );
@@ -95,7 +96,7 @@ public:
int mode() const;
QString type() const;
geninterface_ptr generator() const;
bool autoLoad() const { return m_autoLoad; }
bool autoLoad() const { return m_autoLoad; }
// Creates a new revision from the playlist in memory. Use this is you change the controls or
// mode of a playlist and want to save it to db/others.
@@ -125,32 +126,22 @@ public slots:
// want to update the playlist from the model?
// generate a newrev using uuid() and call this:
// if this is a static playlist, pass it a new list of entries. implicitly sets mode to static
void createNewRevision( const QString& newrev, const QString& oldrev, const QString& type, const QList< dyncontrol_ptr>& controls, const QList< plentry_ptr >& entries );
void createNewRevision( const QString& newrev, const QString& oldrev, const QString& type, const QVariantList& controls, const QList< plentry_ptr >& entries );
// if it is ondemand, no entries are needed implicitly sets mode to ondemand
void createNewRevision( const QString& newrev, const QString& oldrev, const QString& type, const QList< dyncontrol_ptr>& controls );
void createNewRevision( const QString& newrev, const QString& oldrev, const QString& type, const QVariantList& controls );
void reportCreated( const Tomahawk::dynplaylist_ptr& self );
void reportDeleted( const Tomahawk::dynplaylist_ptr& self );
// called from setdynamicplaylistrevision db cmd
// 4 options, because dbcmds can't create qwidgets:
// static version, qvariant controls
// static version, dyncontrol_ptr controls
// ondemand version, qvariant controls
// ondemand version, dyncontrol_ptr controls
// 2 options:
// static version
// ondemand version
void setRevision( const QString& rev,
const QList<QString>& neworderedguids,
const QList<QString>& oldorderedguids,
const QString& type,
const QList< QVariantMap >& controls,
bool is_newest_rev,
const QMap< QString, Tomahawk::plentry_ptr >& addedmap,
bool applied );
void setRevision( const QString& rev,
const QList<QString>& neworderedguids,
const QList<QString>& oldorderedguids,
const QString& type,
const QList< Tomahawk::dyncontrol_ptr >& controls,
const QVariantList& controls,
bool is_newest_rev,
const QMap< QString, Tomahawk::plentry_ptr >& addedmap,
bool applied );
@@ -158,12 +149,7 @@ public slots:
void setRevision( const QString& rev,
bool is_newest_rev,
const QString& type,
const QList< QVariantMap>& controls,
bool applied );
void setRevision( const QString& rev,
bool is_newest_rev,
const QString& type,
const QList< Tomahawk::dyncontrol_ptr>& controls,
const QVariantList& controls,
bool applied );
private:
// called from loadAllPlaylists DB cmd via databasecollection (in GUI thread)
@@ -192,8 +178,6 @@ private:
void checkRevisionQueue();
QList< dyncontrol_ptr > variantsToControl( const QList< QVariantMap >& controlsV );
geninterface_ptr m_generator;
bool m_autoLoad;

View File

@@ -29,7 +29,7 @@ struct DLLEXPORT DynamicPlaylistRevision : PlaylistRevision
{
public:
QList< dyncontrol_ptr > controls;
QVariantList controls;
Tomahawk::GeneratorMode mode;
QString type;

View File

@@ -40,16 +40,6 @@ GeneratorFactory::create ( const QString& type )
}
dyncontrol_ptr
GeneratorFactory::createControl( const QString& generatorType, const QString& controlType )
{
if( generatorType.isEmpty() || !s_factories.contains( generatorType ) )
return dyncontrol_ptr();
return s_factories.value( generatorType )->createControl( controlType );
}
void
GeneratorFactory::registerFactory ( const QString& type, GeneratorFactoryInterface* interface )
{
@@ -63,12 +53,3 @@ GeneratorFactory::types()
return s_factories.keys();
}
QStringList
GeneratorFactory::typeSelectors( const QString& type )
{
if( !s_factories.contains( type ) )
return QStringList();
return s_factories.value( type )->typeSelectors();
}

View File

@@ -42,13 +42,7 @@ public:
virtual ~GeneratorFactoryInterface() {}
virtual GeneratorInterface* create() = 0;
/**
* Create a control for this generator, not tied to this generator itself. Used when loading dynamic
* playlists from a dbcmd.
*/
virtual dyncontrol_ptr createControl( const QString& controlType = QString() ) = 0;
virtual QStringList typeSelectors() const = 0;
};
/**

View File

@@ -34,17 +34,6 @@ Tomahawk::GeneratorInterface::~GeneratorInterface()
}
QList< Tomahawk::dyncontrol_ptr >
Tomahawk::GeneratorInterface::controls()
{
// if( m_controls.isEmpty() ) { // return a default control (so the user can add more)
// return QList< Tomahawk::dyncontrol_ptr >() << createControl();
// }
return m_controls;
}
QPixmap
Tomahawk::GeneratorInterface::logo()
{
@@ -52,38 +41,15 @@ Tomahawk::GeneratorInterface::logo()
}
void
Tomahawk::GeneratorInterface::addControl( const Tomahawk::dyncontrol_ptr& control )
QVariantList
Tomahawk::GeneratorInterface::controls() const
{
m_controls << control;
return m_controls;
}
void
Tomahawk::GeneratorInterface::clearControls()
{
m_controls.clear();
}
void
Tomahawk::GeneratorInterface::setControls( const QList< Tomahawk::dyncontrol_ptr >& controls )
Tomahawk::GeneratorInterface::setControls( const QVariantList& controls )
{
m_controls = controls;
}
void
Tomahawk::GeneratorInterface::removeControl( const Tomahawk::dyncontrol_ptr& control )
{
m_controls.removeAll( control );
}
Tomahawk::dyncontrol_ptr
Tomahawk::GeneratorInterface::createControl( const QString& type )
{
Q_UNUSED( type );
Q_ASSERT( false );
return dyncontrol_ptr();
}

View File

@@ -47,6 +47,7 @@ class DLLEXPORT GeneratorInterface : public QObject
{
Q_OBJECT
Q_PROPERTY( QString type READ type )
Q_PROPERTY( QString summary READ sentenceSummary)
/// oh qjson.
Q_PROPERTY( int mode READ mode WRITE setMode )
@@ -55,12 +56,6 @@ public:
explicit GeneratorInterface( QObject* parent = 0 );
virtual ~GeneratorInterface();
// Can't make it pure otherwise we can't shove it in QVariants :-/
// empty QString means use default
/// The generator will keep track of all the controls it creates. No need to tell it about controls
/// you ask it to create
virtual dyncontrol_ptr createControl( const QString& type = QString() );
/// A logo to display for this generator, if it has one
virtual QPixmap logo();
@@ -98,17 +93,17 @@ public:
*/
virtual bool onDemandSteerable() const { return false; }
/**
* Returns a widget used to steer the OnDemand dynamic playlist.
* If this generator doesn't support this (and returns false for
* \c onDemandSteerable) this will be null. The generator is responsible
* for reacting to changes in the widget.
*
* Steering widgets may emit a \c steeringChanged() signal, which will cause the model to toss any
* upcoming tracks and re-fetch them.
*
* Returns the controls for this station.
*/
virtual QWidget* steeringWidget() { return 0; }
virtual QVariantList controls() const;
/**
* Sets the controls (for example when loaded from database)
*/
virtual void setControls( const QVariantList& controls );
/// The type of this generator
QString type() const { return m_type; }
@@ -116,22 +111,20 @@ public:
int mode() const { return (int)m_mode; }
void setMode( int mode ) { m_mode = (GeneratorMode)mode; }
// control functions
QList< dyncontrol_ptr > controls();
void addControl( const dyncontrol_ptr& control );
void clearControls();
void setControls( const QList< dyncontrol_ptr>& controls );
void removeControl( const dyncontrol_ptr& control );
virtual bool startFromTrack( const Tomahawk::query_ptr& query ) = 0;
virtual bool startFromArtist( const Tomahawk::artist_ptr& artist ) = 0;
virtual bool startFromGenre( const QString& genre ) = 0;
signals:
void error( const QString& title, const QString& body);
void generated( const QList< Tomahawk::query_ptr>& queries );
void nextTrackGenerated( const Tomahawk::query_ptr& track );
void summaryChanged();
protected:
QString m_type;
GeneratorMode m_mode;
QList< dyncontrol_ptr > m_controls;
QVariantList m_controls;
};
typedef QSharedPointer<GeneratorInterface> geninterface_ptr;

View File

@@ -34,13 +34,6 @@ DatabaseFactory::create()
}
dyncontrol_ptr
DatabaseFactory::createControl ( const QString& controlType )
{
return dyncontrol_ptr( new DatabaseControl( controlType, typeSelectors() ) );
}
dyncontrol_ptr
DatabaseFactory::createControl ( const QString& sql, DatabaseCommand_GenericSelect::QueryType type, const QString& summary )
{
@@ -102,9 +95,6 @@ DatabaseGenerator::generate( int number )
return;
}
foreach ( const dyncontrol_ptr& ctrl, m_controls )
qDebug() << ctrl->selectedType() << ctrl->match() << ctrl->input();
// TODO for now, we just support the special "SQL" control, not meant to be shown to the user. Just does a raw query.
bool hasSql = false;
bool hasOther = false;

View File

@@ -18,7 +18,6 @@
#include "playlist/dynamic/echonest/EchonestGenerator.h"
#include "playlist/dynamic/echonest/EchonestControl.h"
#include "playlist/dynamic/echonest/EchonestSteerer.h"
#include "Query.h"
#include "utils/TomahawkUtils.h"
#include "TomahawkSettings.h"
@@ -53,23 +52,6 @@ EchonestFactory::create()
}
dyncontrol_ptr
EchonestFactory::createControl( const QString& controlType )
{
return dyncontrol_ptr( new EchonestControl( controlType, typeSelectors() ) );
}
QStringList
EchonestFactory::typeSelectors() const
{
QStringList types = QStringList() << "Artist" << "Artist Description" << "User Radio" << "Song" << "Mood" << "Style" << "Adventurousness" << "Variety" << "Tempo" << "Duration" << "Loudness"
<< "Danceability" << "Energy" << "Artist Familiarity" << "Artist Hotttnesss" << "Song Hotttnesss"
<< "Longitude" << "Latitude" << "Mode" << "Key" << "Sorting";
return types;
}
CatalogManager::CatalogManager( QObject* parent )
: QObject( parent )
{
@@ -152,14 +134,6 @@ EchonestGenerator::setupCatalogs()
// qDebug() << "ECHONEST:" << m_logo.size();
}
dyncontrol_ptr
EchonestGenerator::createControl( const QString& type )
{
m_controls << dyncontrol_ptr( new EchonestControl( type, GeneratorFactory::typeSelectors( m_type ) ) );
return m_controls.last();
}
QPixmap EchonestGenerator::logo()
{
return m_logo;
@@ -168,33 +142,42 @@ QPixmap EchonestGenerator::logo()
void
EchonestGenerator::knownCatalogsChanged()
{
// Refresh all contrls
foreach( const dyncontrol_ptr& control, m_controls )
{
control.staticCast< EchonestControl >()->updateWidgetsFromData();
}
// // Refresh all contrls
// foreach( const dyncontrol_ptr& control, m_controls )
// {
// control.staticCast< EchonestControl >()->updateWidgetsFromData();
// }
}
void
EchonestGenerator::generate( int number )
{
// convert to an echonest query, and fire it off
qDebug() << Q_FUNC_INFO;
qDebug() << "Generating playlist with" << m_controls.size();
foreach( const dyncontrol_ptr& ctrl, m_controls )
qDebug() << ctrl->selectedType() << ctrl->match() << ctrl->input();
// // convert to an echonest query, and fire it off
// qDebug() << Q_FUNC_INFO;
// qDebug() << "Generating playlist with" << m_controls.size() << "controls";
setProperty( "number", number ); //HACK
// setProperty( "number", number ); //HACK
connect( this, SIGNAL( paramsGenerated( Echonest::DynamicPlaylist::PlaylistParams ) ), this, SLOT( doGenerate(Echonest::DynamicPlaylist::PlaylistParams ) ) );
// connect( this, SIGNAL( paramsGenerated( Echonest::DynamicPlaylist::PlaylistParams ) ), this, SLOT( doGenerate(Echonest::DynamicPlaylist::PlaylistParams ) ) );
// try {
// getParams();
// } catch( std::runtime_error& e ) {
// qWarning() << "Got invalid controls!" << e.what();
// emit error( "Filters are not valid", e.what() );
// }
QList< query_ptr > queries;
queries << Query::get("Colour Haze", "All", QString(), uuid(), true);
queries << Query::get("Colour Haze", "Sun", QString(), uuid(), true);
queries << Query::get("Colour Haze", "Zen", QString(), uuid(), true);
queries << Query::get("Colour Haze", "Outside", QString(), uuid(), true);
queries << Query::get("Colour Haze", "Dirt", QString(), uuid(), true);
emit generated( queries );
try {
getParams();
} catch( std::runtime_error& e ) {
qWarning() << "Got invalid controls!" << e.what();
emit error( "Filters are not valid", e.what() );
}
}
@@ -208,6 +191,7 @@ EchonestGenerator::startOnDemand()
connect( deleteReply, SIGNAL( finished() ), deleteReply, SLOT( deleteLater() ) );
}
tDebug() << Q_FUNC_INFO;
connect( this, SIGNAL( paramsGenerated( Echonest::DynamicPlaylist::PlaylistParams ) ), this, SLOT( doStartOnDemand( Echonest::DynamicPlaylist::PlaylistParams ) ) );
try {
getParams();
@@ -218,6 +202,81 @@ EchonestGenerator::startOnDemand()
}
bool
EchonestGenerator::startFromTrack( const Tomahawk::query_ptr& query )
{
tDebug() << "Generating station content by query:" << query->toString();
Echonest::DynamicPlaylist::PlaylistParamData data;
data.first = Echonest::DynamicPlaylist::SongId;
data.second = query->artist() + " " + query->track();
Echonest::DynamicPlaylist::PlaylistParams params;
params.append( Echonest::DynamicPlaylist::PlaylistParamData( Echonest::DynamicPlaylist::Type, Echonest::DynamicPlaylist::SongRadioType ) );
params << data;
// FIXME!
return true;
}
bool
EchonestGenerator::startFromArtist( const Tomahawk::artist_ptr& artist )
{
tDebug() << "Generating station content by artist:" << artist->name();
if ( !m_dynPlaylist->sessionId().isNull() )
{
// Running session, delete it
QNetworkReply* deleteReply = m_dynPlaylist->deleteSession();
connect( deleteReply, SIGNAL( finished() ), deleteReply, SLOT( deleteLater() ) );
}
connect( this, SIGNAL( paramsGenerated( Echonest::DynamicPlaylist::PlaylistParams ) ), this, SLOT( doStartOnDemand( Echonest::DynamicPlaylist::PlaylistParams ) ) );
Echonest::DynamicPlaylist::PlaylistParamData data;
data.first = Echonest::DynamicPlaylist::Artist;
data.second = artist->name();
Echonest::DynamicPlaylist::PlaylistParams params;
params << data;
// params.append( Echonest::DynamicPlaylist::PlaylistParamData( Echonest::DynamicPlaylist::Type, Echonest::DynamicPlaylist::SongRadioType ) );
emit paramsGenerated( params );
return true;
}
bool
EchonestGenerator::startFromGenre( const QString& genre )
{
tDebug() << "Generating station content by genre:" << genre;
if ( !m_dynPlaylist->sessionId().isNull() )
{
// Running session, delete it
QNetworkReply* deleteReply = m_dynPlaylist->deleteSession();
connect( deleteReply, SIGNAL( finished() ), deleteReply, SLOT( deleteLater() ) );
}
connect( this, SIGNAL( paramsGenerated( Echonest::DynamicPlaylist::PlaylistParams ) ), this, SLOT( doGenerate( Echonest::DynamicPlaylist::PlaylistParams ) ) );
setProperty( "number", 20 );
Echonest::DynamicPlaylist::PlaylistParamData data;
data.first = Echonest::DynamicPlaylist::Description;
data.second = genre;
Echonest::DynamicPlaylist::PlaylistParams params;
params << data;
params.append( Echonest::DynamicPlaylist::PlaylistParamData( Echonest::DynamicPlaylist::Type, Echonest::DynamicPlaylist::ArtistDescriptionType ) );
emit paramsGenerated( params );
return true;
}
void
EchonestGenerator::doGenerate( const Echonest::DynamicPlaylist::PlaylistParams& paramsIn )
{
@@ -229,7 +288,7 @@ EchonestGenerator::doGenerate( const Echonest::DynamicPlaylist::PlaylistParams&
Echonest::DynamicPlaylist::PlaylistParams params = paramsIn;
params.append( Echonest::DynamicPlaylist::PlaylistParamData( Echonest::DynamicPlaylist::Results, number ) );
QNetworkReply* reply = Echonest::DynamicPlaylist::staticPlaylist( params );
qDebug() << "Generating a static playlist from echonest!" << reply->url().toString();
tDebug() << "Generating a static playlist from echonest!" << reply->url().toString() << params;
connect( reply, SIGNAL( finished() ), this, SLOT( staticFinished() ) );
}
@@ -240,7 +299,7 @@ EchonestGenerator::doStartOnDemand( const Echonest::DynamicPlaylist::PlaylistPar
disconnect( this, SIGNAL( paramsGenerated( Echonest::DynamicPlaylist::PlaylistParams ) ), this, SLOT( doStartOnDemand( Echonest::DynamicPlaylist::PlaylistParams ) ) );
QNetworkReply* reply = m_dynPlaylist->create( params );
qDebug() << "starting a dynamic playlist from echonest!" << reply->url().toString();
tDebug() << Q_FUNC_INFO << "Starting a dynamic playlist from echonest!" << reply->url().toString();
connect( reply, SIGNAL( finished() ), this, SLOT( dynamicStarted() ) );
}
@@ -300,42 +359,44 @@ void
EchonestGenerator::getParams() throw( std::runtime_error )
{
Echonest::DynamicPlaylist::PlaylistParams params;
foreach( const dyncontrol_ptr& control, m_controls ) {
params.append( control.dynamicCast<EchonestControl>()->toENParam() );
}
// foreach( const dyncontrol_ptr& control, m_controls ) {
// params.append( control.dynamicCast<EchonestControl>()->toENParam() );
// }
if( appendRadioType( params ) == Echonest::DynamicPlaylist::SongRadioType ) {
// we need to do another pass, converting all song queries to song-ids.
m_storedParams = params;
qDeleteAll( m_waiting );
m_waiting.clear();
// if( appendRadioType( params ) == Echonest::DynamicPlaylist::SongRadioType ) {
// // we need to do another pass, converting all song queries to song-ids.
// m_storedParams = params;
// qDeleteAll( m_waiting );
// m_waiting.clear();
// one query per track
for( int i = 0; i < params.count(); i++ ) {
const Echonest::DynamicPlaylist::PlaylistParamData param = params.value( i );
// // one query per track
// for( int i = 0; i < params.count(); i++ ) {
// const Echonest::DynamicPlaylist::PlaylistParamData param = params.value( i );
if( param.first == Echonest::DynamicPlaylist::SongId ) { // this is a song type enum
QString text = param.second.toString();
// if( param.first == Echonest::DynamicPlaylist::SongId ) { // this is a song type enum
// QString text = param.second.toString();
Echonest::Song::SearchParams q;
q.append( Echonest::Song::SearchParamData( Echonest::Song::Combined, text ) ); // search with the free text "combined" parameter
QNetworkReply* r = Echonest::Song::search( q );
r->setProperty( "index", i );
r->setProperty( "search", text );
// Echonest::Song::SearchParams q;
// q.append( Echonest::Song::SearchParamData( Echonest::Song::Combined, text ) ); // search with the free text "combined" parameter
// QNetworkReply* r = Echonest::Song::search( q );
// r->setProperty( "index", i );
// r->setProperty( "search", text );
m_waiting.insert( r );
connect( r, SIGNAL( finished() ), this, SLOT( songLookupFinished() ) );
}
}
// m_waiting.insert( r );
// connect( r, SIGNAL( finished() ), this, SLOT( songLookupFinished() ) );
// }
// }
if( m_waiting.isEmpty() ) {
m_storedParams.clear();
emit paramsGenerated( params );
}
// if( m_waiting.isEmpty() ) {
// m_storedParams.clear();
// emit paramsGenerated( params );
// }
} else {
emit paramsGenerated( params );
}
// } else {
// emit paramsGenerated( params );
// }
tDebug() << "ParamsGenerated" << params;
}
@@ -400,6 +461,7 @@ EchonestGenerator::dynamicFetched()
Q_ASSERT( qobject_cast< QNetworkReply* >( sender() ) );
QNetworkReply* reply = qobject_cast< QNetworkReply* >( sender() );
tDebug() << "dynamicFetched";
try
{
Echonest::DynamicPlaylist::FetchPair fetched = m_dynPlaylist->parseNext( reply );
@@ -432,60 +494,38 @@ EchonestGenerator::userCatalogs()
return s_catalogs->catalogs().keys();
}
bool
EchonestGenerator::onlyThisArtistType( Echonest::DynamicPlaylist::ArtistTypeEnum type ) const throw( std::runtime_error )
{
bool only = true;
bool some = false;
foreach( const dyncontrol_ptr& control, m_controls ) {
if( ( control->selectedType() == "Artist" || control->selectedType() == "Artist Description" || control->selectedType() == "Song" ) && static_cast<Echonest::DynamicPlaylist::ArtistTypeEnum>( control->match().toInt() ) != type ) {
only = false;
} else if( ( control->selectedType() == "Artist" || control->selectedType() == "Artist Description" || control->selectedType() == "Song" ) && static_cast<Echonest::DynamicPlaylist::ArtistTypeEnum>( control->match().toInt() ) == type ) {
some = true;
}
}
if( some && only ) {
return true;
} else if( some && !only ) {
throw std::runtime_error( "All artist and song match types must be the same" );
}
return false;
}
Echonest::DynamicPlaylist::ArtistTypeEnum
EchonestGenerator::appendRadioType( Echonest::DynamicPlaylist::PlaylistParams& params ) const throw( std::runtime_error )
{
/**
* So we try to match the best type of echonest playlist, based on the controls
* the types are artist, artist-radio, artist-description, catalog, catalog-radio, song-radio. we don't care about the catalog ones
*
*/
// /**
// * So we try to match the best type of echonest playlist, based on the controls
// * the types are artist, artist-radio, artist-description, catalog, catalog-radio, song-radio. we don't care about the catalog ones
// *
// */
/// 1. catalog-radio: If any the entries are catalog types.
/// 2. artist: If all the artist controls are Limit-To. If some were but not all, error out.
/// 3. artist-description: If all the artist entries are Description. If some were but not all, error out.
/// 4. artist-radio: If all the artist entries are Similar To. If some were but not all, error out.
/// 5. song-radio: If all the artist entries are Similar To. If some were but not all, error out.
bool someCatalog = false;
foreach( const dyncontrol_ptr& control, m_controls ) {
if ( control->selectedType() == "User Radio" )
someCatalog = true;
}
if( someCatalog )
params.append( Echonest::DynamicPlaylist::PlaylistParamData( Echonest::DynamicPlaylist::Type, Echonest::DynamicPlaylist::CatalogRadioType ) );
else if( onlyThisArtistType( Echonest::DynamicPlaylist::ArtistType ) )
params.append( Echonest::DynamicPlaylist::PlaylistParamData( Echonest::DynamicPlaylist::Type, Echonest::DynamicPlaylist::ArtistType ) );
else if( onlyThisArtistType( Echonest::DynamicPlaylist::ArtistDescriptionType ) )
params.append( Echonest::DynamicPlaylist::PlaylistParamData( Echonest::DynamicPlaylist::Type, Echonest::DynamicPlaylist::ArtistDescriptionType ) );
else if( onlyThisArtistType( Echonest::DynamicPlaylist::ArtistRadioType ) )
params.append( Echonest::DynamicPlaylist::PlaylistParamData( Echonest::DynamicPlaylist::Type, Echonest::DynamicPlaylist::ArtistRadioType ) );
else if( onlyThisArtistType( Echonest::DynamicPlaylist::SongRadioType ) )
params.append( Echonest::DynamicPlaylist::PlaylistParamData( Echonest::DynamicPlaylist::Type, Echonest::DynamicPlaylist::SongRadioType ) );
else // no artist or song or description types. default to artist-description
params.append( Echonest::DynamicPlaylist::PlaylistParamData( Echonest::DynamicPlaylist::Type, Echonest::DynamicPlaylist::ArtistDescriptionType ) );
// /// 1. catalog-radio: If any the entries are catalog types.
// /// 2. artist: If all the artist controls are Limit-To. If some were but not all, error out.
// /// 3. artist-description: If all the artist entries are Description. If some were but not all, error out.
// /// 4. artist-radio: If all the artist entries are Similar To. If some were but not all, error out.
// /// 5. song-radio: If all the artist entries are Similar To. If some were but not all, error out.
// bool someCatalog = false;
// foreach( const dyncontrol_ptr& control, m_controls ) {
// if ( control->selectedType() == "User Radio" )
// someCatalog = true;
// }
// if( someCatalog )
// params.append( Echonest::DynamicPlaylist::PlaylistParamData( Echonest::DynamicPlaylist::Type, Echonest::DynamicPlaylist::CatalogRadioType ) );
// else if( onlyThisArtistType( Echonest::DynamicPlaylist::ArtistType ) )
// params.append( Echonest::DynamicPlaylist::PlaylistParamData( Echonest::DynamicPlaylist::Type, Echonest::DynamicPlaylist::ArtistType ) );
// else if( onlyThisArtistType( Echonest::DynamicPlaylist::ArtistDescriptionType ) )
// params.append( Echonest::DynamicPlaylist::PlaylistParamData( Echonest::DynamicPlaylist::Type, Echonest::DynamicPlaylist::ArtistDescriptionType ) );
// else if( onlyThisArtistType( Echonest::DynamicPlaylist::ArtistRadioType ) )
// params.append( Echonest::DynamicPlaylist::PlaylistParamData( Echonest::DynamicPlaylist::Type, Echonest::DynamicPlaylist::ArtistRadioType ) );
// else if( onlyThisArtistType( Echonest::DynamicPlaylist::SongRadioType ) )
// params.append( Echonest::DynamicPlaylist::PlaylistParamData( Echonest::DynamicPlaylist::Type, Echonest::DynamicPlaylist::SongRadioType ) );
// else // no artist or song or description types. default to artist-description
// params.append( Echonest::DynamicPlaylist::PlaylistParamData( Echonest::DynamicPlaylist::Type, Echonest::DynamicPlaylist::ArtistDescriptionType ) );
return static_cast< Echonest::DynamicPlaylist::ArtistTypeEnum >( params.last().second.toInt() );
}
@@ -516,90 +556,91 @@ EchonestGenerator::sentenceSummary()
* NOTE / TODO: In order for the sentence to be grammatically correct, we must follow the EN API rules. That means we can't have multiple of some types of filters,
* and all Artist types must be the same. The filters aren't checked at the moment until Generate / Play is pressed. Consider doing a check on hide as well.
*/
QList< dyncontrol_ptr > allcontrols = m_controls;
QString sentence = "Songs ";
// QList< dyncontrol_ptr > allcontrols = m_controls;
// QString sentence = "Songs ";
/// 1. Collect all required filters
/// 2. Get the sorted by filter if it exists.
QList< dyncontrol_ptr > required;
dyncontrol_ptr sorting;
foreach( const dyncontrol_ptr& control, allcontrols ) {
if( control->selectedType() == "Artist" || control->selectedType() == "Artist Description" || control->selectedType() == "Song" )
required << control;
else if( control->selectedType() == "Sorting" )
sorting = control;
}
if( !sorting.isNull() )
allcontrols.removeAll( sorting );
// /// 1. Collect all required filters
// /// 2. Get the sorted by filter if it exists.
// QList< dyncontrol_ptr > required;
// dyncontrol_ptr sorting;
// foreach( const dyncontrol_ptr& control, allcontrols ) {
// if( control->selectedType() == "Artist" || control->selectedType() == "Artist Description" || control->selectedType() == "Song" )
// required << control;
// else if( control->selectedType() == "Sorting" )
// sorting = control;
// }
// if( !sorting.isNull() )
// allcontrols.removeAll( sorting );
/// Skip empty artists
QList< dyncontrol_ptr > empty;
foreach( const dyncontrol_ptr& artistOrTrack, required ) {
QString summary = artistOrTrack.dynamicCast< EchonestControl >()->summary();
if( summary.lastIndexOf( "~" ) == summary.length() - 1 )
empty << artistOrTrack;
}
foreach( const dyncontrol_ptr& toremove, empty ) {
required.removeAll( toremove );
allcontrols.removeAll( toremove );
}
// /// Skip empty artists
// QList< dyncontrol_ptr > empty;
// foreach( const dyncontrol_ptr& artistOrTrack, required ) {
// QString summary = artistOrTrack.dynamicCast< EchonestControl >()->summary();
// if( summary.lastIndexOf( "~" ) == summary.length() - 1 )
// empty << artistOrTrack;
// }
// foreach( const dyncontrol_ptr& toremove, empty ) {
// required.removeAll( toremove );
// allcontrols.removeAll( toremove );
// }
/// If there are no artists and no filters, show some help text
if( required.isEmpty() && allcontrols.isEmpty() )
sentence = "No configured filters!";
// /// If there are no artists and no filters, show some help text
// if( required.isEmpty() && allcontrols.isEmpty() )
// sentence = "No configured filters!";
/// Do the assembling. Start with the artists if there are any, then do all the rest.
for( int i = 0; i < required.size(); i++ ) {
dyncontrol_ptr artist = required.value( i );
allcontrols.removeAll( artist ); // remove from pool while we're here
// /// Do the assembling. Start with the artists if there are any, then do all the rest.
// for( int i = 0; i < required.size(); i++ ) {
// dyncontrol_ptr artist = required.value( i );
// allcontrols.removeAll( artist ); // remove from pool while we're here
/// Collapse artist lists
QString center, suffix;
QString summary = artist.dynamicCast< EchonestControl >()->summary();
// /// Collapse artist lists
// QString center, suffix;
// QString summary = artist.dynamicCast< EchonestControl >()->summary();
if( i == 0 ) { // if it's the first.. special casez
center = summary.remove( "~" );
if( required.size() == 2 ) // special case for 2, no comma. ( X and Y )
suffix = " and ";
else if( required.size() > 2 ) // in a list with more after
suffix = ", ";
else if( allcontrols.isEmpty() && sorting.isNull() ) // the last one, and no more controls, so put a period
suffix = ".";
else
suffix = " ";
} else {
center = summary.mid( summary.indexOf( "~" ) + 1 );
if( i == required.size() - 1 ) { // if there are more, add an " and "
if( !( allcontrols.isEmpty() && sorting.isNull() ) )
suffix = ", ";
else
suffix = ".";
} else if ( i < required.size() - 2 ) // An item in the list that is before the second to last one, don't use ", and", we only want that for the last item
suffix += ", ";
else
suffix += ", and ";
}
sentence += center + suffix;
}
/// Add each filter individually
for( int i = 0; i < allcontrols.size(); i++ ) {
/// end case: if this is the last AND there is not a sorting filter (so this is the real last one)
const bool last = ( i == allcontrols.size() - 1 && sorting.isNull() );
QString prefix, suffix;
if( last ) { // only if there is not just 1
if( !( required.isEmpty() && allcontrols.size() == 1 ) )
prefix = "and ";
suffix = ".";
} else
suffix = ", ";
sentence += prefix + allcontrols.value( i ).dynamicCast< EchonestControl >()->summary() + suffix;
}
// if( i == 0 ) { // if it's the first.. special casez
// center = summary.remove( "~" );
// if( required.size() == 2 ) // special case for 2, no comma. ( X and Y )
// suffix = " and ";
// else if( required.size() > 2 ) // in a list with more after
// suffix = ", ";
// else if( allcontrols.isEmpty() && sorting.isNull() ) // the last one, and no more controls, so put a period
// suffix = ".";
// else
// suffix = " ";
// } else {
// center = summary.mid( summary.indexOf( "~" ) + 1 );
// if( i == required.size() - 1 ) { // if there are more, add an " and "
// if( !( allcontrols.isEmpty() && sorting.isNull() ) )
// suffix = ", ";
// else
// suffix = ".";
// } else if ( i < required.size() - 2 ) // An item in the list that is before the second to last one, don't use ", and", we only want that for the last item
// suffix += ", ";
// else
// suffix += ", and ";
// }
// sentence += center + suffix;
// }
// /// Add each filter individually
// for( int i = 0; i < allcontrols.size(); i++ ) {
// /// end case: if this is the last AND there is not a sorting filter (so this is the real last one)
// const bool last = ( i == allcontrols.size() - 1 && sorting.isNull() );
// QString prefix, suffix;
// if( last ) { // only if there is not just 1
// if( !( required.isEmpty() && allcontrols.size() == 1 ) )
// prefix = "and ";
// suffix = ".";
// } else
// suffix = ", ";
// sentence += prefix + allcontrols.value( i ).dynamicCast< EchonestControl >()->summary() + suffix;
// }
if( !sorting.isNull() ) {
sentence += "and " + sorting.dynamicCast< EchonestControl >()->summary() + ".";
}
// if( !sorting.isNull() ) {
// sentence += "and " + sorting.dynamicCast< EchonestControl >()->summary() + ".";
// }
return sentence;
// return sentence;
return "This is a station!";
}
void

View File

@@ -60,8 +60,6 @@ public:
EchonestFactory();
virtual GeneratorInterface* create();
virtual dyncontrol_ptr createControl( const QString& controlType = QString() );
virtual QStringList typeSelectors() const;
};
class DLLEXPORT EchonestGenerator : public GeneratorInterface
@@ -71,7 +69,6 @@ public:
explicit EchonestGenerator( QObject* parent = 0 );
virtual ~EchonestGenerator();
virtual dyncontrol_ptr createControl( const QString& type = QString() );
virtual QPixmap logo();
virtual void generate ( int number = -1 );
virtual void startOnDemand();
@@ -79,13 +76,17 @@ public:
virtual QString sentenceSummary();
virtual bool onDemandSteerable() const { return false; }
virtual QWidget* steeringWidget() { return 0; }
virtual bool startFromTrack( const Tomahawk::query_ptr& query );
virtual bool startFromArtist( const Tomahawk::artist_ptr& artist );
virtual bool startFromGenre( const QString& genre );
static QStringList styles();
Q_INVOKABLE static QStringList styles();
static QStringList moods();
static QStringList userCatalogs();
static QByteArray catalogId( const QString& collectionId );
static void setupCatalogs();
signals:
void paramsGenerated( const Echonest::DynamicPlaylist::PlaylistParams& );
@@ -102,13 +103,13 @@ private slots:
void knownCatalogsChanged();
void songLookupFinished();
private:
// get result from signal paramsGenerated
void getParams() throw( std::runtime_error );
query_ptr queryFromSong( const Echonest::Song& song );
Echonest::DynamicPlaylist::ArtistTypeEnum appendRadioType( Echonest::DynamicPlaylist::PlaylistParams& params ) const throw( std::runtime_error );
bool onlyThisArtistType( Echonest::DynamicPlaylist::ArtistTypeEnum type ) const throw( std::runtime_error );
void loadStylesAndMoods();
void saveStylesAndMoods();

View File

@@ -0,0 +1,70 @@
#include "EchonestStation.h"
#include "playlist/dynamic/DynamicPlaylist.h"
#include "playlist/PlayableItem.h"
#include "audio/AudioEngine.h"
#include <echonest/Playlist.h>
namespace Tomahawk
{
EchonestStation::EchonestStation( PlayableProxyModel *model, dynplaylist_ptr playlist, QObject *parent )
: QObject( parent )
, m_name( model->sourceModel()->title() )
, m_model( model )
, m_playlist( playlist )
{
connect(m_playlist->generator().data(), SIGNAL(controlAdded(const dyncontrol_ptr&)), SLOT(controlsChanged()));
}
QString EchonestStation::name() const
{
return m_playlist->title();
}
void EchonestStation::setName(const QString &name)
{
m_playlist->setTitle( name );
emit nameChanged();
}
bool EchonestStation::configured()
{
return true;
}
int EchonestStation::minTempo() const
{
}
int EchonestStation::maxTempo() const
{
}
void EchonestStation::playItem(int row)
{
QModelIndex index = m_model->index( row, 0);
if( index.isValid() ) {
PlayableItem* item = m_model->itemFromIndex( m_model->mapToSource(index) );
if ( item && !item->query().isNull() )
{
m_model->setCurrentIndex( index );
AudioEngine::instance()->playItem( m_model->playlistInterface(), item->query() );
}
}
}
void EchonestStation::setTempo(int min, int max)
{
}
void EchonestStation::controlsChanged()
{
Q_ASSERT(false);
}
}

View File

@@ -0,0 +1,56 @@
#ifndef ECHONESTSTATION_H
#define ECHONESTSTATION_H
#include "playlist/PlayableProxyModel.h"
#include "playlist/dynamic/GeneratorInterface.h"
namespace Tomahawk
{
class EchonestStation: public QObject
{
Q_OBJECT
Q_ENUMS(StationType)
Q_PROPERTY( QString name READ name WRITE setName NOTIFY nameChanged )
Q_PROPERTY( bool configured READ configured NOTIFY configurationChanged )
Q_PROPERTY( int minTempo READ minTempo NOTIFY configurationChanged )
Q_PROPERTY( int maxTempo READ maxTempo NOTIFY configurationChanged )
public:
enum StationType {
StationTypeStyle,
StationTypeArtist
};
EchonestStation( PlayableProxyModel *model, dynplaylist_ptr playlist, QObject *parent = 0);
QString name() const;
void setName( const QString &name );
bool configured();
int minTempo() const;
int maxTempo() const;
public slots:
void playItem( int row );
void setTempo( int min, int max );
signals:
void nameChanged();
void configurationChanged();
private:
dyncontrol_ptr findControl( const QString &selectedType, const QString &match ) const;
private slots:
void controlsChanged();
private:
QString m_name;
PlayableProxyModel *m_model;
dynplaylist_ptr m_playlist;
};
}
#endif

View File

@@ -1,272 +0,0 @@
/* === 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 "playlist/dynamic/echonest/EchonestSteerer.h"
#include "Source.h"
#include "playlist/dynamic/widgets/DynamicWidget.h"
#include "utils/ImageRegistry.h"
#include "utils/TomahawkUtils.h"
#include "utils/Logger.h"
#include <echonest/Playlist.h>
#include <QPaintEvent>
#include <QHBoxLayout>
#include <QLineEdit>
#include <QComboBox>
#include <QLabel>
#include <QPainter>
#include <QToolButton>
#include <QPropertyAnimation>
using namespace Tomahawk;
#define ANIM_DURATION 300
EchonestSteerer::EchonestSteerer( QWidget* parent )
: QWidget( parent )
, m_layout( new QHBoxLayout )
, m_amplifier( 0 )
, m_field( 0 )
, m_description( 0 )
, m_textL( new QVBoxLayout )
, m_steerTop( 0 )
, m_steerBottom( 0 )
, m_reset( 0 )
, m_expanding( true )
{
m_layout->setContentsMargins( 8, 8, 8, 8 );
m_textL->setSpacing( 0 );
m_steerTop = new QLabel( tr( "Steer this station:" ), this );
QFont f = m_steerTop->font();
f.setPointSize( f.pointSize() + 2 );
f.setBold( true );
m_steerTop->setFont( f );
m_textL->addWidget( m_steerTop );
// m_steerBottom = new QLabel( tr( "Takes effect on track change" ), this );
// f.setPointSize( f.pointSize() - 3 );
// m_steerBottom->setFont( f );
// m_textL->addWidget( m_steerBottom );
QPalette p = m_steerTop->palette();
#ifdef Q_OS_MAC
p.setBrush( QPalette::WindowText, Qt::white );
#else
p.setBrush( QPalette::WindowText, palette().highlightedText() );
#endif
m_steerTop->setPalette( p );
m_layout->addLayout( m_textL, 1 );
m_amplifier = new QComboBox( this );
m_amplifier->addItem( tr( "Much less" ), "^.1" );
m_amplifier->addItem( tr( "Less" ), "^.5" );
m_amplifier->addItem( tr( "A bit less" ), "^.75" );
m_amplifier->addItem( tr( "Keep at current", "" ) );
m_amplifier->addItem( tr( "A bit more" ), "^1.25" );
m_amplifier->addItem( tr( "More" ), "^1.5" );
m_amplifier->addItem( tr( "Much more" ), "^2" );
m_amplifier->setCurrentIndex( 3 );
m_field = new QComboBox( this );
m_field->addItem( tr( "Tempo" ), "tempo");
m_field->addItem( tr( "Loudness" ), "loudness");
m_field->addItem( tr( "Danceability" ), "danceability");
m_field->addItem( tr( "Energy" ), "energy");
m_field->addItem( tr( "Song Hotttnesss" ), "tempo");
m_field->addItem( tr( "Artist Hotttnesss" ), "artist_hotttnesss");
m_field->addItem( tr( "Artist Familiarity" ), "artist_familiarity");
m_field->addItem( tr( "By Description" ), "desc");
m_layout->addWidget( m_amplifier );
m_layout->addWidget( m_field );
connect( m_amplifier, SIGNAL( currentIndexChanged( int ) ), this, SLOT( changed() ) );
connect( m_field, SIGNAL( currentIndexChanged( int ) ), this, SLOT( changed() ) );
m_description = new QLineEdit( this );
m_description->setPlaceholderText( tr( "Enter a description" ) );
m_description->hide();
connect( m_description, SIGNAL( textChanged( QString ) ), this, SLOT( changed() ) );
m_apply = initButton( this );
m_apply->setIcon( ImageRegistry::instance()->icon( RESPATH "images/apply-check.svg" ) );
m_apply->setToolTip( tr( "Apply steering command" ) );
m_layout->addWidget( m_apply );
connect( m_apply, SIGNAL( clicked( bool ) ), this, SLOT( applySteering() ) );
m_reset = initButton( this );
m_reset->setIcon( ImageRegistry::instance()->icon( RESPATH "images/view-refresh.svg" ) );
m_reset->setToolTip( tr( "Reset all steering commands" ) );
m_layout->addWidget( m_reset );
connect( m_reset, SIGNAL( clicked( bool ) ), this, SLOT( resetSteering( bool ) ) );
setLayout( m_layout );
setSizePolicy( QSizePolicy::Fixed, QSizePolicy::Fixed );
m_resizeAnim.setDuration( ANIM_DURATION );
m_resizeAnim.setEasingCurve( QEasingCurve::InOutQuad );
m_resizeAnim.setDirection( QTimeLine::Forward );
m_resizeAnim.setUpdateInterval( 8 );
connect( &m_resizeAnim, SIGNAL( frameChanged( int ) ), this, SLOT( resizeFrame( int ) ) );
m_fadeAnim = new QPropertyAnimation( this, "opacity", this );
m_fadeAnim->setDuration( ANIM_DURATION );
m_fadeAnim->setStartValue( 0 );
m_fadeAnim->setEndValue( .7 );
resize( sizeHint() );
}
void
EchonestSteerer::paintEvent( QPaintEvent* )
{
QPainter p( this );
QRect r = contentsRect();
QPalette pal = palette();
DynamicWidget::paintRoundedFilledRect( p, pal, r, m_opacity );
}
void
EchonestSteerer::setOpacity( qreal opacity )
{
m_opacity = opacity;
if( m_opacity == 0 )
hide();
repaint();
}
void
EchonestSteerer::fadeIn()
{
m_fadeAnim->setDirection( QAbstractAnimation::Forward );
m_fadeAnim->start();
show();
}
void
EchonestSteerer::fadeOut()
{
m_fadeAnim->setDirection( QAbstractAnimation::Backward );
m_fadeAnim->start();
}
void
EchonestSteerer::changed()
{
if( m_field->itemData( m_field->currentIndex() ).toString() != "desc" ) {
// if description was shown, animate to shrink
if( m_layout->indexOf( m_description ) > 0 ) {
m_expanding = false;
int start = width();
int end = start - m_layout->spacing() - m_description->sizeHint().width();;
m_layout->removeWidget( m_description );
m_description->hide();
m_layout->setStretchFactor( m_textL, 1 );
m_resizeAnim.setFrameRange( start, end );
m_resizeAnim.start();
}
} else { // description, so put in the description field
if( m_layout->indexOf( m_description ) == -1 ) {
// animate to expand
m_layout->insertWidget( m_layout->count() - 1, m_description, 1 );
m_layout->setStretchFactor( m_textL, 0 );
m_description->show();
m_expanding = true;
int start = width();
int end = start + m_layout->spacing() + m_description->sizeHint().width();
m_resizeAnim.setFrameRange( start, end );
m_resizeAnim.start();
}
}
}
void
EchonestSteerer::applySteering()
{
if ( m_field->itemData( m_field->currentIndex() ).toString() != "desc" )
{
QString steer = m_field->itemData( m_field->currentIndex() ).toString() + m_amplifier->itemData( m_amplifier->currentIndex() ).toString();
emit steerField( steer );
}
else
{
if ( !m_description->text().isEmpty() )
{
QString steer = m_description->text() + m_amplifier->itemData( m_amplifier->currentIndex() ).toString();
emit steerDescription( steer );
}
}
emit steeringChanged();
resetSteering( true );
}
void
EchonestSteerer::resizeFrame( int width )
{
// qDebug() << "RESIZING TO:" << width;
resize( width, sizeHint().height() );
repaint();
emit resized();
}
void
EchonestSteerer::resetSteering( bool automatic )
{
m_amplifier->setCurrentIndex( 3 );
if( !automatic ) {
m_description->clear();
m_field->setCurrentIndex( 0 );
emit reset();
}
}
QToolButton*
EchonestSteerer::initButton( QWidget* parent )
{
QToolButton* btn = new QToolButton( parent );
btn->setSizePolicy( QSizePolicy::Fixed, QSizePolicy::Fixed );
btn->setIconSize( QSize( 14, 14 ) );
btn->setToolButtonStyle( Qt::ToolButtonIconOnly );
btn->setAutoRaise( true );
btn->setContentsMargins( 0, 0, 0, 0 );
return btn;
}

View File

@@ -1,97 +0,0 @@
/* === 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 ECHONEST_STEERER_H
#define ECHONEST_STEERER_H
#include <QWidget>
#include <QTimeLine>
class QPropertyAnimation;
class QToolButton;
class QLabel;
class QComboBox;
class QVBoxLayout;
class QLineEdit;
class QHBoxLayout;
namespace Tomahawk
{
class EchonestSteerer : public QWidget
{
Q_OBJECT
Q_PROPERTY( qreal opacity READ opacity WRITE setOpacity )
public:
EchonestSteerer( QWidget* parent = 0 );
virtual void paintEvent(QPaintEvent* );
public slots:
void applySteering();
void resetSteering( bool automatic = false );
void fadeIn();
void fadeOut();
qreal opacity() const { return m_opacity; }
void setOpacity( qreal opacity );
signals:
void steerField( const QString& field );
void steerDescription( const QString& desc );
void reset();
void resized();
// interface to DynamicWidget
void steeringChanged();
private slots:
void changed();
void resizeFrame( int );
private:
QToolButton* initButton( QWidget* parent );
QHBoxLayout* m_layout;
QComboBox* m_amplifier;
QComboBox* m_field;
QLineEdit* m_description;
// text on the left
QVBoxLayout* m_textL;
QLabel* m_steerTop;
QLabel* m_steerBottom;
// icons on the right
QToolButton* m_apply;
QToolButton* m_reset;
// animations
QTimeLine m_resizeAnim;
bool m_expanding;
QPropertyAnimation* m_fadeAnim;
qreal m_opacity;
};
};
#endif

View File

@@ -1,217 +0,0 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2010-2011, Leo Franchi <lfranchi@kde.org>
* Copyright 2010-2011, Jeff Mitchell <jeff@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 "CollapsibleControls.h"
#include "DynamicControlList.h"
#include "DynamicControlWrapper.h"
#include "playlist/dynamic/GeneratorInterface.h"
#include "playlist/dynamic/DynamicControl.h"
#include "utils/ImageRegistry.h"
#include "utils/TomahawkUtils.h"
#include "widgets/ElidedLabel.h"
#include "Source.h"
#include <QLabel>
#include <QStackedLayout>
#include <QToolButton>
#include <QEasingCurve>
#include <QTimeLine>
#include <QPaintEvent>
#include <QPainter>
#include "utils/Logger.h"
using namespace Tomahawk;
CollapsibleControls::CollapsibleControls( QWidget* parent )
: QWidget( parent )
, m_isLocal( true )
{
init();
}
CollapsibleControls::CollapsibleControls( const dynplaylist_ptr& playlist, bool isLocal, QWidget* parent )
: QWidget( parent )
, m_dynplaylist( playlist )
, m_isLocal( isLocal )
{
init();
setControls( m_dynplaylist, m_isLocal );
}
Tomahawk::CollapsibleControls::~CollapsibleControls()
{
}
void
CollapsibleControls::init()
{
m_timeline = new QTimeLine( 250, this );
m_timeline->setUpdateInterval( 5 );
m_animHeight = -1;
m_collapseAnimation = false;
connect( m_timeline, SIGNAL( frameChanged( int ) ), this, SLOT( onAnimationStep( int ) ) );
connect( m_timeline, SIGNAL( finished() ), this, SLOT( onAnimationFinished() ) );
m_layout = new QStackedLayout;
setContentsMargins( 0, 0, 0, 0 );
m_layout->setContentsMargins( 0, 0, 0, 0 );
m_layout->setSpacing( 0 );
m_controls = new Tomahawk::DynamicControlList( this );
m_layout->addWidget( m_controls );
connect( m_controls, SIGNAL( toggleCollapse() ), this, SLOT( toggleCollapse() ) );
m_summaryWidget = new QWidget( this );
m_summaryWidget->setMinimumHeight( 24 );
m_summaryWidget->setMaximumHeight( 24 );
m_summaryWidget->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Fixed );
m_summaryLayout = new QHBoxLayout;
m_summaryWidget->setLayout( m_summaryLayout );
m_summaryLayout->setMargin( 0 );
m_summaryWidget->setContentsMargins( 3, 0, 0, 0 );
m_summary = new ElidedLabel( m_summaryWidget );
QFont f = m_summary->font();
f.setPointSize( f.pointSize() + 1 );
f.setBold( true );
m_summary->setFont( f );
m_summaryLayout->addWidget( m_summary, 1 );
m_summaryExpand = DynamicControlWrapper::initButton( this );
m_summaryExpand->setIcon( ImageRegistry::instance()->icon( RESPATH "images/arrow-down-double.svg" ) );
m_expandL = new QStackedLayout;
m_expandL->setContentsMargins( 0, 0, 0, 0 );
m_expandL->setMargin( 0 );
m_expandL->addWidget( m_summaryExpand );
m_expandL->addWidget( DynamicControlWrapper::createDummy( m_summaryExpand, this ) );
m_summaryLayout->addLayout( m_expandL );
if( m_isLocal )
m_expandL->setCurrentIndex( 0 );
else
m_expandL->setCurrentIndex( 1 );
m_layout->addWidget( m_summaryWidget );
connect( m_summaryExpand, SIGNAL( clicked( bool ) ), this, SLOT( toggleCollapse() ) );
if( m_isLocal )
m_layout->setCurrentWidget( m_controls );
else
m_layout->setCurrentWidget( m_summary );
connect( m_controls, SIGNAL( controlChanged( Tomahawk::dyncontrol_ptr ) ), SIGNAL( controlChanged( Tomahawk::dyncontrol_ptr ) ) );
connect( m_controls, SIGNAL( controlsChanged( bool ) ), SIGNAL( controlsChanged( bool ) ) );
setLayout( m_layout );
setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Fixed );
}
QList< DynamicControlWrapper* >
Tomahawk::CollapsibleControls::controls() const
{
return m_controls->controls();
}
void
CollapsibleControls::setControls( const dynplaylist_ptr& playlist, bool isLocal )
{
m_dynplaylist = playlist;
m_isLocal = isLocal;
m_controls->setControls( m_dynplaylist->generator(), m_dynplaylist->generator()->controls() );
if( !m_isLocal ) {
m_expandL->setCurrentIndex( 1 );
m_summary->setText( m_dynplaylist->generator()->sentenceSummary() );
m_layout->setCurrentWidget( m_summaryWidget );
setMaximumHeight( m_summaryWidget->sizeHint().height() );
} else {
m_expandL->setCurrentIndex( 0 );
}
}
void
CollapsibleControls::toggleCollapse()
{
// qDebug() << "TOGGLING SIZEHINTS:" << m_controls->height() << m_summaryWidget->sizeHint();
m_timeline->setEasingCurve( QEasingCurve::OutBack );
m_timeline->setFrameRange( m_summaryWidget->sizeHint().height(), m_controls->height() );
if( m_layout->currentWidget() == m_controls ) {
m_summary->setText( m_dynplaylist->generator()->sentenceSummary() );
m_controls->setSizePolicy( QSizePolicy::Ignored, QSizePolicy::Ignored );
m_timeline->setDirection( QTimeLine::Backward );
m_timeline->start();
m_collapseAnimation = true;
} else {
m_summaryWidget->setSizePolicy( QSizePolicy::Ignored, QSizePolicy::Ignored );
m_layout->setCurrentWidget( m_controls );
m_timeline->setDirection( QTimeLine::Forward );
m_timeline->start();
m_collapseAnimation = false;
}
}
void
CollapsibleControls::onAnimationStep( int step )
{
// qDebug() << "ANIMATION STEP:" << step;
resize( width(), step );
m_animHeight = step;
setMaximumHeight( m_animHeight );
}
void
CollapsibleControls::onAnimationFinished()
{
// qDebug() << "ANIMATION DONE:" << m_animHeight;
setMaximumHeight( m_animHeight );
m_animHeight = -1;
if( m_collapseAnimation ) {
m_layout->setCurrentWidget( m_summaryWidget );
} else {
setMaximumHeight( QWIDGETSIZE_MAX );
}
}
QSize
CollapsibleControls::sizeHint() const
{
if( m_animHeight >= 0 ) {
return QSize( QWidget::sizeHint().width(), m_animHeight );
} else {
return QWidget::sizeHint();
}
}

View File

@@ -1,83 +0,0 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2010-2011, Leo Franchi <lfranchi@kde.org>
* Copyright 2010-2011, Jeff Mitchell <jeff@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 COLLAPSIBLE_CONTROLS_H
#define COLLAPSIBLE_CONTROLS_H
#include "Typedefs.h"
#include <QtGui/QWidget>
class QPaintEvent;
class QHBoxLayout;
class QTimeLine;
class QToolButton;
class ElidedLabel;
class QStackedLayout;
namespace Tomahawk
{
class DynamicControlWrapper;
class DynamicControlList;
class CollapsibleControls : public QWidget
{
Q_OBJECT
public:
CollapsibleControls( QWidget* parent );
CollapsibleControls( const dynplaylist_ptr& playlist, bool isLocal, QWidget* parent = 0 );
virtual ~CollapsibleControls();
void setControls( const dynplaylist_ptr& playlist, bool isLocal );
QList< DynamicControlWrapper* > controls() const;
virtual QSize sizeHint() const;
signals:
void controlsChanged( bool added );
void controlChanged( const Tomahawk::dyncontrol_ptr& control );
private slots:
void toggleCollapse();
void onAnimationStep( int );
void onAnimationFinished();
private:
void init();
dynplaylist_ptr m_dynplaylist;
QStackedLayout* m_layout;
DynamicControlList* m_controls;
bool m_isLocal;
QWidget* m_summaryWidget;
QHBoxLayout* m_summaryLayout;
ElidedLabel* m_summary;
QStackedLayout* m_expandL;
QToolButton* m_summaryExpand;
// animations!
QTimeLine* m_timeline;
int m_animHeight;
bool m_collapseAnimation;
};
}
#endif

View File

@@ -1,189 +0,0 @@
/* === 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 "DynamicControlList.h"
#include "DynamicControlWrapper.h"
#include "playlist/dynamic/GeneratorInterface.h"
#include "utils/ImageRegistry.h"
#include "utils/TomahawkUtils.h"
#include "utils/Logger.h"
#include "Source.h"
#include <QLayout>
#include <QLabel>
#include <QPaintEvent>
#include <QPushButton>
#include <QToolButton>
#include <QPainter>
#include <QGridLayout>
#include <QHBoxLayout>
using namespace Tomahawk;
DynamicControlList::DynamicControlList( QWidget* parent )
: QWidget( parent )
, m_layout( new QGridLayout )
{
init();
}
DynamicControlList::DynamicControlList( const geninterface_ptr& generator, const QList< dyncontrol_ptr >& controls, QWidget* parent )
: QWidget( parent )
, m_generator( generator )
, m_layout( new QGridLayout )
{
init();
setControls( generator, controls );
}
DynamicControlList::~DynamicControlList()
{
}
void
DynamicControlList::init()
{
qDebug() << "GRIDLAYOUT: " << m_layout->rowCount();
setContentsMargins( 0, 0, 0, 0 );
setLayout( m_layout );
m_layout->setColumnStretch( 2, 1 );
m_layout->setMargin( 0 );
m_layout->setVerticalSpacing( 0 );
#ifdef Q_WS_MAC // on OS X we don't want the right edge of the toolbuttons against the window
m_layout->setContentsMargins( 0, 0, 3, 0 );
#else
m_layout->setContentsMargins( 0, 0, 0, 0 );
#endif
m_layout->setSizeConstraint( QLayout::SetMinimumSize );
m_collapseLayout = new QHBoxLayout();
m_collapseLayout->setContentsMargins( 0, 0, 0, 0 );
m_collapseLayout->setMargin( 0 );
m_collapseLayout->setSpacing( 0 );
m_collapse = new QPushButton( tr( "Click to collapse" ), this );
m_collapse->setAttribute( Qt::WA_LayoutUsesWidgetRect );
m_collapseLayout->addWidget( m_collapse );
m_addControl = new QToolButton( this );
m_addControl->setAttribute( Qt::WA_LayoutUsesWidgetRect );
m_addControl->setIcon( ImageRegistry::instance()->icon( RESPATH "images/list-add.svg" ) );
m_addControl->setSizePolicy( QSizePolicy::Fixed, QSizePolicy::Fixed );
m_addControl->setIconSize( QSize( 16, 16 ) );
m_addControl->setToolButtonStyle( Qt::ToolButtonIconOnly );
m_addControl->setAutoRaise( true );
m_addControl->setContentsMargins( 0, 0, 0, 0 );
m_collapseLayout->addWidget( m_addControl );
m_collapse->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Fixed );
connect( m_collapse, SIGNAL( clicked() ), this, SIGNAL( toggleCollapse() ) );
connect( m_addControl, SIGNAL( clicked() ), this, SLOT( addNewControl() ) );
setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Fixed );
}
void
DynamicControlList::setControls( const geninterface_ptr& generator, const QList< dyncontrol_ptr >& controls )
{
if( m_controls.size() == controls.size() && controls.size() > 0 ) { // check if we're setting the same controls we already have, and exit if we are
bool different = false;
for( int i = 0; i < m_controls.size(); i++ ) {
if( m_controls.value( i )->control().data() != controls.value( i ).data() ) {
different = true;
break;
}
}
if( !different ) { // no work to do
return;
}
}
if( !m_controls.isEmpty() ) {
qDeleteAll( m_controls );
m_controls.clear();
}
m_layout->removeItem( m_collapseLayout );
m_generator = generator;
if( controls.isEmpty() ) {
qDebug() << "CREATING DEFAULT CONTROL";
DynamicControlWrapper* ctrlW = new DynamicControlWrapper( generator->createControl(), m_layout, m_controls.size(), this );
connect( ctrlW, SIGNAL( removeControl() ), this, SLOT( removeControl() ) );
connect( ctrlW, SIGNAL( changed() ), this, SLOT( controlChanged() ) );
m_controls << ctrlW;
} else
{
foreach( const dyncontrol_ptr& control, controls ) {
DynamicControlWrapper* ctrlW = new DynamicControlWrapper( control, m_layout, m_controls.size(), this );
connect( ctrlW, SIGNAL( removeControl() ), this, SLOT( removeControl() ) );
connect( ctrlW, SIGNAL( changed() ), this, SLOT( controlChanged() ) );
m_controls << ctrlW;
}
}
m_layout->addItem( m_collapseLayout, m_layout->rowCount(), 0, 1, 4, Qt::AlignCenter );
}
void
DynamicControlList::addNewControl()
{
m_layout->removeItem( m_collapseLayout );
dyncontrol_ptr control = m_generator->createControl();
m_controls.append( new DynamicControlWrapper( control, m_layout, m_layout->rowCount(), this ) );
connect( m_controls.last(), SIGNAL( removeControl() ), this, SLOT( removeControl() ) );
connect( m_controls.last(), SIGNAL( changed() ), this, SLOT( controlChanged() ) );
m_layout->addItem( m_collapseLayout, m_layout->rowCount(), 0, 1, 4, Qt::AlignCenter );
emit controlsChanged( true );
}
void
DynamicControlList::removeControl()
{
DynamicControlWrapper* w = qobject_cast<DynamicControlWrapper*>( sender() );
w->removeFromLayout();
m_controls.removeAll( w );
m_generator->removeControl( w->control() );
w->deleteLater();
emit controlsChanged( false );
}
void
DynamicControlList::controlChanged()
{
Q_ASSERT( sender() && qobject_cast<DynamicControlWrapper*>(sender()) );
DynamicControlWrapper* widget = qobject_cast<DynamicControlWrapper*>(sender());
qDebug() << "control changed!";
foreach( DynamicControlWrapper* c, m_controls )
qDebug() << c->control()->id() << c->control()->selectedType() << c->control()->match() << c->control()->input();
emit controlChanged( widget->control() );
}

View File

@@ -1,81 +0,0 @@
/* === 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 DYNAMIC_CONTROL_LIST_H
#define DYNAMIC_CONTROL_LIST_H
#include "Typedefs.h"
#include "playlist/dynamic/DynamicPlaylist.h"
#include <QStackedWidget>
class QEvent;
class QGridLayout;
class QPushButton;
class QHBoxLayout;
class QVBoxLayout;
class QToolButton;
namespace Tomahawk
{
class DynamicControlWrapper;
/**
* This widget encapsulates the list of dynamic controls. It can hide or show the controls.
*/
class DynamicControlList : public QWidget
{
Q_OBJECT
public:
DynamicControlList( QWidget* parent = 0 );
explicit DynamicControlList( const geninterface_ptr& generator, const QList< dyncontrol_ptr >& controls, QWidget* parent = 0 );
virtual ~DynamicControlList();
void setControls( const geninterface_ptr& generator, const QList< dyncontrol_ptr >& controls );
QList< DynamicControlWrapper* > controls() const { return m_controls; }
signals:
void controlsChanged( bool added );
void controlChanged( const Tomahawk::dyncontrol_ptr& control );
void toggleCollapse();
public slots:
void addNewControl();
void removeControl();
void controlChanged();
private:
void init();
geninterface_ptr m_generator;
QGridLayout* m_layout;
QList< DynamicControlWrapper* > m_controls;
QHBoxLayout* m_collapseLayout;
QPushButton* m_collapse;
QToolButton* m_addControl;
};
};
#endif

View File

@@ -1,197 +0,0 @@
/* === 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 "DynamicControlWrapper.h"
#include "playlist/dynamic/DynamicControl.h"
#include "utils/TomahawkUtilsGui.h"
#include "utils/Logger.h"
#include <QHBoxLayout>
#include <QComboBox>
#include <QLayout>
#include <QToolButton>
#include <QPaintEvent>
#include <QPainter>
#include <QStackedLayout>
using namespace Tomahawk;
DynamicControlWrapper::DynamicControlWrapper( const Tomahawk::dyncontrol_ptr& control, QGridLayout* layout, int row, QWidget* parent )
: QObject( parent )
, m_parent( parent )
, m_row( row )
, m_minusButton( 0 )
, m_control( control )
, m_typeSelector( 0 )
, m_layout( QWeakPointer< QGridLayout >( layout ) )
{
m_typeSelector = new QComboBox( m_parent );
m_matchSelector = QWeakPointer<QWidget>( control->matchSelector() );
m_entryWidget = QWeakPointer<QWidget>( control->inputField() );
m_minusButton = initButton( m_parent );
m_minusButton->setIcon( TomahawkUtils::defaultPixmap( TomahawkUtils::ListRemove ) );
connect( m_minusButton, SIGNAL( clicked( bool ) ), this, SIGNAL( removeControl() ) );
m_plusL = new QStackedLayout();
m_plusL->setContentsMargins( 0, 0, 0, 0 );
m_plusL->setMargin( 0 );
m_plusL->addWidget( m_minusButton );
m_plusL->addWidget( createDummy( m_minusButton, m_parent ) ); // :-(
connect( m_typeSelector, SIGNAL( activated( QString) ), SLOT( typeSelectorChanged( QString ) ) );
connect( m_control.data(), SIGNAL( changed() ), this, SIGNAL( changed() ) );
m_layout.data()->addWidget( m_typeSelector, row, 0, Qt::AlignLeft );
if( !control.isNull() ) {
foreach( const QString& type, control->typeSelectors() )
m_typeSelector->addItem( type );
}
typeSelectorChanged( m_control.isNull() ? "" : m_control->selectedType(), true );
m_layout.data()->addLayout( m_plusL, m_row, 3, Qt::AlignCenter );
m_plusL->setCurrentIndex( 0 );
}
DynamicControlWrapper::~DynamicControlWrapper()
{
// remove the controls widgets from our layout so they are not parented
// we don't want to auto-delete them since the control should own them
// if we delete them, then the control will be holding on to null ptrs
removeFromLayout();
if( !m_entryWidget.isNull() )
m_control->inputField()->setParent( 0 );
if( !m_matchSelector.isNull() )
m_control->matchSelector()->setParent( 0 );
delete m_typeSelector;
delete m_minusButton;
}
dyncontrol_ptr
DynamicControlWrapper::control() const
{
return m_control;
}
void
DynamicControlWrapper::removeFromLayout()
{
if( m_layout.isNull() )
return;
if( !m_matchSelector.isNull() )
m_layout.data()->removeWidget( m_matchSelector.data() );
if( !m_entryWidget.isNull() )
m_layout.data()->removeWidget( m_entryWidget.data() );
m_layout.data()->removeWidget( m_typeSelector );
m_layout.data()->removeItem( m_plusL );
}
QToolButton*
DynamicControlWrapper::initButton( QWidget* parent )
{
QToolButton* btn = new QToolButton( parent );
btn->setAttribute( Qt::WA_LayoutUsesWidgetRect );
btn->setSizePolicy( QSizePolicy::Fixed, QSizePolicy::Fixed );
btn->setIconSize( QSize( 16, 16 ) );
btn->setToolButtonStyle( Qt::ToolButtonIconOnly );
btn->setAutoRaise( true );
btn->setContentsMargins( 0, 0, 0, 0 );
return btn;
}
QWidget*
DynamicControlWrapper::createDummy( QWidget* fromW, QWidget* parent )
{
QWidget* dummy = new QWidget( parent );
dummy->setAttribute( Qt::WA_LayoutUsesWidgetRect );
dummy->setContentsMargins( 0, 0, 0, 0 );
dummy->setSizePolicy( QSizePolicy::Fixed, QSizePolicy::Fixed );
dummy->setMinimumSize( fromW->sizeHint() );
dummy->setMaximumSize( fromW->sizeHint() );
return dummy;
}
void
DynamicControlWrapper::typeSelectorChanged( const QString& type, bool firstLoad )
{
Q_ASSERT( !m_layout.isNull() );
m_layout.data()->removeWidget( m_matchSelector.data() );
m_layout.data()->removeWidget( m_entryWidget.data() );
if( m_control->selectedType() != type && !firstLoad )
m_control->setSelectedType( type );
int idx = m_typeSelector->findText( type );
if( idx > -1 )
m_typeSelector->setCurrentIndex( idx );
if( m_control->matchSelector() ) {
m_matchSelector = QWeakPointer<QWidget>( m_control->matchSelector() );
m_layout.data()->addWidget( m_matchSelector.data(), m_row, 1, Qt::AlignCenter );
m_matchSelector.data()->show();
}
if( m_control->inputField() ) {
m_entryWidget = QWeakPointer<QWidget>( m_control->inputField() );
m_layout.data()->addWidget( m_entryWidget.data(), m_row, 2 );
m_entryWidget.data()->show();
}
emit changed();
}
/*
void
DynamicControlWrapper::enterEvent(QEvent* ev)
{
m_mouseOver = true;
if( m_isLocal )
m_plusL->setCurrentIndex( 0 );
if( ev )
QObject::enterEvent( ev );
}
void
DynamicControlWrapper::leaveEvent(QEvent* ev)
{
m_mouseOver = true;
if( m_isLocal )
m_plusL->setCurrentIndex( 1 );
if( ev )
QWidget::leaveEvent( ev );
}
*/

View File

@@ -1,84 +0,0 @@
/* === 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 DYNAMIC_CONTROL_WRAPPER_H
#define DYNAMIC_CONTROL_WRAPPER_H
#include <QWidget>
#include "Typedefs.h"
class QGridLayout;
class QStackedLayout;
class QEvent;
class QToolButton;
class QHBoxLayout;
class QComboBox;
class QLabel;;
namespace Tomahawk
{
/**
* This abstraction object manages the widgets for 1 dynamic playlist control, laid out in the desired layout
*/
class DynamicControlWrapper : public QObject
{
Q_OBJECT
public:
explicit DynamicControlWrapper( const dyncontrol_ptr& control, QGridLayout* layout, int row, QWidget* parent = 0 );
virtual ~DynamicControlWrapper();
// virtual void enterEvent(QEvent* );
// virtual void leaveEvent(QEvent* );
dyncontrol_ptr control() const;
void removeFromLayout();
static QToolButton* initButton( QWidget* parent );
static QWidget* createDummy( QWidget* fromW, QWidget* parent );
signals:
void collapse();
void removeControl();
void changed();
private slots:
void typeSelectorChanged( const QString& selectedType, bool firstLoad = false );
private:
QWidget* m_parent;
int m_row;
QStackedLayout* m_plusL;
QToolButton* m_minusButton;
dyncontrol_ptr m_control;
QComboBox* m_typeSelector;
QWeakPointer<QWidget> m_matchSelector;
QWeakPointer<QWidget> m_entryWidget;
QWeakPointer<QGridLayout> m_layout;
};
};
#endif
class QPaintEvent;
class QMouseEvent;

View File

@@ -0,0 +1,266 @@
#include "DynamicQmlWidget.h"
#include "playlist/dynamic/DynamicModel.h"
#include "playlist/PlayableProxyModel.h"
#include "playlist/dynamic/DynamicModel.h"
#include "playlist/dynamic/echonest/EchonestControl.h"
#include "playlist/dynamic/GeneratorInterface.h"
#include "playlist/PlayableItem.h"
#include "Source.h"
#include "SourceList.h"
#include "audio/AudioEngine.h"
#include "database/Database.h"
#include "database/DatabaseCommand_PlaybackCharts.h"
#include "widgets/DeclarativeCoverArtProvider.h"
#include "utils/TomahawkUtilsGui.h"
#include "utils/Logger.h"
#include <QUrl>
#include <QSize>
#include <qdeclarative.h>
#include <QDeclarativeContext>
namespace Tomahawk
{
DynamicQmlWidget::DynamicQmlWidget( const dynplaylist_ptr& playlist, QWidget* parent )
: DeclarativeView( parent )
, m_playlist( playlist )
, m_runningOnDemand( false )
, m_activePlaylist( false )
, m_playNextResolved( false )
{
m_model = new DynamicModel( this );
m_proxyModel = new PlayableProxyModel( this );
m_proxyModel->setSourcePlayableModel( m_model );
m_proxyModel->setShowOfflineResults( false );
m_model->loadPlaylist( m_playlist );
m_artistChartsModel = new PlayableModel( this );
qmlRegisterUncreatableType<GeneratorInterface>("tomahawk", 1, 0, "Generator", "you cannot create it on your own - should be set in context");
rootContext()->setContextProperty( "dynamicModel", m_proxyModel );
rootContext()->setContextProperty( "artistChartsModel", m_artistChartsModel );
rootContext()->setContextProperty( "generator", m_playlist->generator().data() );
rootContext()->setContextProperty( "currentlyPlayedIndex", QVariant::fromValue( 0 ) );
setSource( QUrl( "qrc" RESPATH "qml/StationView.qml" ) );
connect( m_model, SIGNAL( currentItemChanged(QPersistentModelIndex)), SLOT( currentIndexChanged( QPersistentModelIndex ) ) );
connect( m_model, SIGNAL( loadingStarted() ), SIGNAL(loadingChanged() ) );
connect( m_model, SIGNAL( loadingFinished() ), SIGNAL(loadingChanged() ) );
connect( m_model, SIGNAL( changed() ), SIGNAL( titleChanged() ) );
connect( m_playlist->generator().data(), SIGNAL( generated( QList<Tomahawk::query_ptr> ) ), this, SLOT( tracksGenerated( QList<Tomahawk::query_ptr> ) ) );
connect( m_playlist->generator().data(), SIGNAL( nextTrackGenerated( Tomahawk::query_ptr ) ), this, SLOT( nextTrackGenerated( Tomahawk::query_ptr ) ) );
connect( m_playlist.data(), SIGNAL( dynamicRevisionLoaded( Tomahawk::DynamicPlaylistRevision ) ), this, SLOT( onRevisionLoaded( Tomahawk::DynamicPlaylistRevision ) ) );
connect( m_playlist->generator().data(), SIGNAL( error( QString, QString )), SLOT( error(QString,QString) ) );
connect( AudioEngine::instance(), SIGNAL( started( Tomahawk::result_ptr ) ), this, SLOT( trackStarted() ) );
connect( AudioEngine::instance(), SIGNAL( playlistChanged( Tomahawk::playlistinterface_ptr ) ), this, SLOT( playlistChanged( Tomahawk::playlistinterface_ptr ) ) );
// m_playlist->generator()->generate( 20 );
loadArtistCharts();
}
DynamicQmlWidget::~DynamicQmlWidget()
{
}
Tomahawk::playlistinterface_ptr
DynamicQmlWidget::playlistInterface() const
{
return m_proxyModel->playlistInterface();
}
QString
DynamicQmlWidget::title() const
{
return m_model->title();
}
QString
DynamicQmlWidget::description() const
{
return m_model->description();
}
QString
DynamicQmlWidget::iconSource() const
{
return QLatin1String( RESPATH "images/station.png" );
}
bool
DynamicQmlWidget::jumpToCurrentTrack()
{
return true;
}
playlist_ptr DynamicQmlWidget::playlist() const
{
return m_model->playlist();
}
bool DynamicQmlWidget::loading()
{
// Why does isLoading() not reset to true when cleared and station started again?
// return m_model->isLoading();
return m_playNextResolved && m_proxyModel->rowCount() == 0;
}
bool DynamicQmlWidget::configured()
{
return !m_playlist->generator()->controls().isEmpty();
}
void DynamicQmlWidget::playItem(int index)
{
tDebug() << "playItem called for cover" << index;
AudioEngine::instance()->playItem( m_proxyModel->playlistInterface(), m_proxyModel->itemFromIndex( index )->result() );
}
void DynamicQmlWidget::pause()
{
AudioEngine::instance()->pause();
}
void DynamicQmlWidget::startStationFromArtist(const QString &artist)
{
m_model->clear();
m_playNextResolved = true;
m_playlist->generator()->startFromArtist(Artist::get(artist));
emit loadingChanged();
emit configuredChanged();
}
void DynamicQmlWidget::startStationFromGenre(const QString &genre)
{
tDebug() << "should start startion from genre" << genre;
m_model->clear();
m_playNextResolved = true;
m_playlist->generator()->startFromGenre( genre );
emit loadingChanged();
emit configuredChanged();
}
void DynamicQmlWidget::currentIndexChanged( const QPersistentModelIndex &currentIndex )
{
tDebug() << "current index is" << currentIndex.row();
rootContext()->setContextProperty( "currentlyPlayedIndex", m_proxyModel->mapFromSource( currentIndex ).row() );
}
void
DynamicQmlWidget::tracksGenerated( const QList< query_ptr >& queries )
{
m_model->tracksGenerated( queries, queries.count() );
m_playlist->resolve();
}
void DynamicQmlWidget::nextTrackGenerated(const query_ptr &track)
{
m_model->tracksGenerated( QList<query_ptr>() << track );
m_playlist->resolve();
connect( track.data(), SIGNAL( resolvingFinished( bool )), SLOT( resolvingFinished( bool ) ) );
}
void DynamicQmlWidget::error(const QString &title, const QString &body)
{
qDebug() << "got a generator error:" << title << body;
// m_playlist->generator()->fetchNext();
}
void DynamicQmlWidget::onRevisionLoaded(DynamicPlaylistRevision)
{
m_playlist->resolve();
}
void DynamicQmlWidget::resolvingFinished(bool hasResults)
{
Q_UNUSED(hasResults)
qDebug() << "next track generated" << m_proxyModel->rowCount() << m_proxyModel->currentIndex().row();
if( m_proxyModel->rowCount() <= m_proxyModel->currentIndex().row() + 8 ) {
qDebug() << "fetching next one";
m_playlist->generator()->fetchNext();
}
if( m_playNextResolved && m_proxyModel->rowCount() > 0 ) {
playItem( 0 );
m_playNextResolved = false;
}
}
void DynamicQmlWidget::trackStarted()
{
if ( m_activePlaylist && !m_playlist.isNull() &&
m_playlist->mode() == OnDemand && !m_runningOnDemand )
{
startStation();
}
}
void
DynamicQmlWidget::playlistChanged( Tomahawk::playlistinterface_ptr pl )
{
if ( pl == m_proxyModel->playlistInterface() ) // same playlist
m_activePlaylist = true;
else
{
m_activePlaylist = false;
// user started playing something somewhere else, so give it a rest
if ( m_runningOnDemand )
{
stopStation( false );
}
}
}
void
DynamicQmlWidget::stopStation( bool stopPlaying )
{
m_model->stopOnDemand( stopPlaying );
m_runningOnDemand = false;
}
void
DynamicQmlWidget::startStation()
{
m_runningOnDemand = true;
m_model->startOnDemand();
}
void
DynamicQmlWidget::loadArtistCharts()
{
DatabaseCommand_PlaybackCharts* cmd = new DatabaseCommand_PlaybackCharts( SourceList::instance()->getLocal(), this );
cmd->setLimit( 15 );
connect( cmd, SIGNAL( artists( QList<Tomahawk::artist_ptr> ) ), SLOT( onArtistCharts( QList< Tomahawk::artist_ptr > ) ), Qt::UniqueConnection );
Database::instance()->enqueue( QSharedPointer<DatabaseCommand>( cmd ) );
}
void
DynamicQmlWidget::onArtistCharts( const QList< Tomahawk::artist_ptr >& artists )
{
m_artistChartsModel->clear();
m_artistChartsModel->appendArtists( artists );
}
}

View File

@@ -0,0 +1,109 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2012, Michael Zanetti <mzanetti@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 DYNAMIC_QML_WIDGET_H
#define DYNAMIC_QML_WIDGET_H
#include "ViewPage.h"
#include "Typedefs.h"
#include "widgets/DeclarativeView.h"
#include <QDeclarativeImageProvider>
class PlayableModel;
class PlayableProxyModel;
namespace Tomahawk
{
class DynamicModel;
class DynamicQmlWidget : public DeclarativeView, public Tomahawk::ViewPage
{
Q_OBJECT
Q_PROPERTY(QString title READ title NOTIFY titleChanged)
Q_PROPERTY(bool loading READ loading NOTIFY loadingChanged)
Q_PROPERTY(bool configured READ configured NOTIFY configuredChanged)
public:
explicit DynamicQmlWidget( const dynplaylist_ptr& playlist, QWidget* parent = 0 );
virtual ~DynamicQmlWidget();
virtual QWidget* widget() { return this; }
virtual Tomahawk::playlistinterface_ptr playlistInterface() const;
virtual QString title() const;
virtual QString description() const;
virtual QString iconSource() const;
virtual bool showInfoBar() const { return false; }
virtual bool showModes() const { return false; }
virtual bool showFilter() const { return false; }
virtual bool jumpToCurrentTrack();
playlist_ptr playlist() const;
bool loading();
bool configured();
signals:
void loadingChanged();
void configuredChanged();
void titleChanged();
public slots:
void playItem(int index);
void pause();
void startStationFromArtist(const QString &artist);
void startStationFromGenre(const QString &genre);
private slots:
void currentIndexChanged( const QPersistentModelIndex &currentIndex );
void tracksGenerated( const QList< Tomahawk::query_ptr>& queries );
void nextTrackGenerated( const Tomahawk::query_ptr& track );
void error( const QString& title, const QString& body);
void onRevisionLoaded( Tomahawk::DynamicPlaylistRevision );
void playlistChanged( Tomahawk::playlistinterface_ptr pl );
void resolvingFinished( bool hasResults );
void trackStarted();
void startStation();
void stopStation( bool stopPlaying );
void loadArtistCharts();
void onArtistCharts( const QList< Tomahawk::artist_ptr >& artists );
private:
DynamicModel* m_model;
PlayableProxyModel* m_proxyModel;
dynplaylist_ptr m_playlist;
PlayableModel* m_artistChartsModel;
bool m_runningOnDemand;
bool m_activePlaylist;
bool m_playNextResolved;
};
}
#endif // DYNAMIC_QML_WIDGET_H

View File

@@ -1,164 +0,0 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2010-2011, Leo Franchi <lfranchi@kde.org>
* Copyright 2010-2011, Jeff Mitchell <jeff@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 "DynamicSetupWidget.h"
#include "ReadOrWriteWidget.h"
#include "playlist/dynamic/DynamicPlaylist.h"
#include "playlist/dynamic/GeneratorFactory.h"
#include "DynamicWidget.h"
#include "Source.h"
#include <QHBoxLayout>
#include <QComboBox>
#include <QLabel>
#include <QPushButton>
#include <QSpinBox>
#include <QPropertyAnimation>
#include <QPaintEvent>
#include <QPainter>
#include "utils/Logger.h"
using namespace Tomahawk;
DynamicSetupWidget::DynamicSetupWidget( const Tomahawk::dynplaylist_ptr& playlist, QWidget* parent )
: QWidget( parent )
, m_playlist( playlist )
, m_headerText( 0 )
, m_layout( new QHBoxLayout )
, m_generatorCombo( 0 )
, m_logo( 0 )
, m_generateButton( 0 )
, m_genNumber( 0 )
{
setContentsMargins( 0, 0, 0, 0 );
m_headerText = new QLabel( tr( "Type:" ), this );
m_layout->addWidget( m_headerText );
QComboBox * genCombo = new QComboBox( this );
foreach( const QString& type, GeneratorFactory::types() )
genCombo->addItem( type );
m_generatorCombo = new ReadOrWriteWidget( genCombo, m_playlist->author()->isLocal(), this );
m_generatorCombo->setLabel( playlist->generator()->type().replace( 0, 1, playlist->generator()->type().at( 0 ).toUpper() ) );
m_layout->addWidget( m_generatorCombo );
// TODO until there are more... no point in choices
m_headerText->hide();
m_generatorCombo->hide();
m_generateButton = new QPushButton( tr( "Generate" ), this );
m_generateButton->setAttribute( Qt::WA_LayoutUsesWidgetRect );
connect( m_generateButton, SIGNAL( clicked( bool ) ), this, SLOT( generatePressed( bool ) ) );
if( m_playlist->mode() == OnDemand )
m_generateButton->hide();
else
m_layout->addWidget( m_generateButton );
m_genNumber = new QSpinBox( this );
m_genNumber->setValue( 15 );
m_genNumber->setMinimum( 0 );
if( m_playlist->mode() == OnDemand )
m_genNumber->hide();
else
m_layout->addWidget( m_genNumber );
if( m_playlist->mode() == Static )
m_layout->addSpacing( 30 );
m_logo = new QLabel( this );
if( !m_playlist->generator()->logo().isNull() ) {
QPixmap p = m_playlist->generator()->logo().scaledToHeight( 22, Qt::SmoothTransformation );
m_logo->setPixmap( p );
}
m_layout->addWidget(m_logo);
setLayout( m_layout );
m_fadeAnim = new QPropertyAnimation( this, "opacity" );
m_fadeAnim->setDuration( 250 );
m_fadeAnim->setStartValue( 0.00 );
m_fadeAnim->setEndValue( .70 );
setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Fixed );
resize( sizeHint() );
}
DynamicSetupWidget::~DynamicSetupWidget()
{
}
void
DynamicSetupWidget::setPlaylist( const Tomahawk::dynplaylist_ptr& playlist )
{
Q_UNUSED( playlist );
}
void
DynamicSetupWidget::fadeIn()
{
m_fadeAnim->setDirection( QAbstractAnimation::Forward );
m_fadeAnim->start();
show();
}
void
DynamicSetupWidget::fadeOut()
{
m_fadeAnim->setDirection( QAbstractAnimation::Backward );
m_fadeAnim->start();
}
void
DynamicSetupWidget::generatePressed( bool )
{
emit generatePressed( m_genNumber->value() );
}
void
DynamicSetupWidget::setOpacity( qreal opacity )
{
m_opacity = opacity;
if( m_opacity == 0 )
hide();
repaint();
}
void
DynamicSetupWidget::paintEvent( QPaintEvent* e )
{
QPainter p( this );
QRect r = contentsRect();
QPalette pal = palette();
DynamicWidget::paintRoundedFilledRect( p, pal, r, m_opacity );
QWidget::paintEvent( e );
}

View File

@@ -1,82 +0,0 @@
/* === 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 DYNAMIC_SETUP_WIDGET_H
#define DYNAMIC_SETUP_WIDGET_H
#include <QWidget>
#include <Typedefs.h>
class QPropertyAnimation;
class QPaintEvent;
class QHBoxLayout;
class QSpinBox;
class QPushButton;
class QLabel;
class ReadOrWriteWidget;
class QLabel;
namespace Tomahawk
{
/**
* Widget used to choose a type of dynamic playlist, and to set the number/generate if it's a static one.
*/
class DynamicSetupWidget : public QWidget
{
Q_OBJECT
Q_PROPERTY( qreal opacity READ opacity WRITE setOpacity )
public:
DynamicSetupWidget( const Tomahawk::dynplaylist_ptr& playlist, QWidget* parent = 0 );
virtual ~DynamicSetupWidget();
void setPlaylist( const dynplaylist_ptr& playlist );
qreal opacity() const { return m_opacity; }
void setOpacity( qreal opacity );
virtual void paintEvent( QPaintEvent* );
public slots:
void fadeIn();
void fadeOut();
signals:
void generatePressed( int num );
void typeChanged( const QString& playlistType );
private slots:
void generatePressed( bool );
private:
dynplaylist_ptr m_playlist;
QLabel* m_headerText;
QHBoxLayout* m_layout;
ReadOrWriteWidget* m_generatorCombo;
QLabel* m_logo;
QPushButton* m_generateButton;
QSpinBox* m_genNumber;
QPropertyAnimation* m_fadeAnim;
qreal m_opacity;
};
};
#endif

View File

@@ -1,550 +0,0 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2010-2011, Leo Franchi <lfranchi@kde.org>
* Copyright 2010-2012, Jeff Mitchell <jeff@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 "DynamicWidget.h"
#include "DynamicControlList.h"
#include "playlist/dynamic/DynamicModel.h"
#include "playlist/PlayableProxyModel.h"
#include "playlist/PlayableItem.h"
#include "playlist/dynamic/GeneratorInterface.h"
#include "playlist/dynamic/GeneratorFactory.h"
#include "Pipeline.h"
#include "Source.h"
#include "audio/AudioEngine.h"
#include "ReadOrWriteWidget.h"
#include "CollapsibleControls.h"
#include "DynamicControlWrapper.h"
#include "ViewManager.h"
#include "playlist/dynamic/DynamicView.h"
#include "DynamicSetupWidget.h"
#include "utils/AnimatedSpinner.h"
#include "utils/TomahawkUtilsGui.h"
#include "utils/Logger.h"
#include <QVBoxLayout>
#include <QLabel>
#include <QComboBox>
#include <QPushButton>
#include <QSpinBox>
#include <QEvent>
#include <QPainter>
using namespace Tomahawk;
DynamicWidget::DynamicWidget( const Tomahawk::dynplaylist_ptr& playlist, QWidget* parent )
: QWidget( parent )
, m_layout( new QVBoxLayout )
, m_resolveOnNextLoad( false )
, m_seqRevLaunched( 0 )
, m_activePlaylist( false )
, m_setup( 0 )
, m_runningOnDemand( false )
, m_controlsChanged( false )
, m_steering( 0 )
, m_controls( 0 )
, m_view( 0 )
, m_model()
{
m_controls = new CollapsibleControls( this );
m_layout->addWidget( m_controls );
setContentsMargins( 0, 0, 0, 1 ); // to align the bottom with the bottom of the sourcelist
m_model = new DynamicModel( this );
m_view = new DynamicView( this );
m_view->setDynamicModel( m_model );
m_view->setContentsMargins( 0, 0, 0, 0 );
m_layout->addWidget( m_view, 1 );
connect( m_model, SIGNAL( collapseFromTo( int, int ) ), m_view, SLOT( collapseEntries( int, int ) ) );
connect( m_model, SIGNAL( trackGenerationFailure( QString ) ), this, SLOT( stationFailed( QString ) ) );
m_loading = new AnimatedSpinner( m_view );
connect( m_model, SIGNAL( tracksAdded() ), m_loading, SLOT( fadeOut() ) );
m_setup = new DynamicSetupWidget( playlist, this );
m_setup->fadeIn();
connect( m_model, SIGNAL( tracksAdded() ), this, SLOT( tracksAdded() ) );
loadDynamicPlaylist( playlist );
m_layout->setContentsMargins( 0, 0, 0, 0 );
m_layout->setMargin( 0 );
m_layout->setSpacing( 0 );
setLayout( m_layout );
connect( m_setup, SIGNAL( generatePressed( int ) ), this, SLOT( generate( int ) ) );
connect( m_setup, SIGNAL( typeChanged( QString ) ), this, SLOT( playlistTypeChanged( QString ) ) );
layoutFloatingWidgets();
connect( m_controls, SIGNAL( controlChanged( Tomahawk::dyncontrol_ptr ) ), this, SLOT( controlChanged( Tomahawk::dyncontrol_ptr ) ), Qt::QueuedConnection );
connect( m_controls, SIGNAL( controlsChanged( bool ) ), this, SLOT( controlsChanged( bool ) ), Qt::QueuedConnection );
connect( AudioEngine::instance(), SIGNAL( started( Tomahawk::result_ptr ) ), this, SLOT( trackStarted() ) );
connect( AudioEngine::instance(), SIGNAL( playlistChanged( Tomahawk::playlistinterface_ptr ) ), this, SLOT( playlistChanged( Tomahawk::playlistinterface_ptr ) ) );
}
DynamicWidget::~DynamicWidget()
{
}
dynplaylist_ptr
DynamicWidget::playlist()
{
return m_playlist;
}
void
DynamicWidget::loadDynamicPlaylist( const Tomahawk::dynplaylist_ptr& playlist )
{
// special case: if we have launched multiple setRevision calls, and the number of controls is different, it means that we're getting an intermediate setRevision
// called after the user has already created more revisions. ignore in that case.
if ( m_playlist.data() == playlist.data() && m_seqRevLaunched > 0
&& m_controls->controls().size() != playlist->generator()->controls().size() // different number of controls
&& qAbs( m_playlist->generator()->controls().size() - playlist->generator()->controls().size() ) < m_seqRevLaunched )
{ // difference in controls has to be less than how many revisions we launched
return;
}
m_seqRevLaunched = 0;
// if we're being told to load the same dynamic playlist over again, only do it if the controls have a different number
if ( !m_playlist.isNull() && ( m_playlist.data() == playlist.data() ) // same playlist pointer
&& m_playlist->generator()->controls().size() == playlist->generator()->controls().size() )
{
// we can skip our work. just let the dynamiccontrollist show the difference
m_controls->setControls( m_playlist, m_playlist->author()->isLocal() );
m_playlist = playlist;
if ( !m_runningOnDemand )
m_model->loadPlaylist( m_playlist );
else if ( !m_controlsChanged ) // if the controls changed, we already dealt with that and don't want to change station yet
m_model->changeStation();
m_controlsChanged = false;
return;
}
if ( !m_playlist.isNull() )
{
disconnect( m_playlist->generator().data(), SIGNAL( generated( QList<Tomahawk::query_ptr> ) ), this, SLOT( tracksGenerated( QList<Tomahawk::query_ptr> ) ) );
disconnect( m_playlist.data(), SIGNAL( dynamicRevisionLoaded( Tomahawk::DynamicPlaylistRevision) ), this, SLOT(onRevisionLoaded( Tomahawk::DynamicPlaylistRevision) ) );
disconnect( m_playlist->generator().data(), SIGNAL( error( QString, QString ) ), this, SLOT( generatorError( QString, QString ) ) );
disconnect( m_playlist.data(), SIGNAL( deleted( Tomahawk::dynplaylist_ptr ) ), this, SLOT( onDeleted() ) );
disconnect( m_playlist.data(), SIGNAL( changed() ), this, SLOT( onChanged() ) );
}
m_playlist = playlist;
m_view->setOnDemand( m_playlist->mode() == OnDemand );
m_view->setReadOnly( !m_playlist->author()->isLocal() );
m_model->loadPlaylist( m_playlist );
m_controlsChanged = false;
m_setup->setPlaylist( m_playlist );
if ( !m_playlist->author()->isLocal() ) // hide controls, as we show the description in the summary
m_layout->removeWidget( m_controls );
else if ( m_layout->indexOf( m_controls ) == -1 )
m_layout->insertWidget( 0, m_controls );
connect( m_playlist->generator().data(), SIGNAL( generated( QList<Tomahawk::query_ptr> ) ), this, SLOT( tracksGenerated( QList<Tomahawk::query_ptr> ) ) );
connect( m_playlist.data(), SIGNAL( dynamicRevisionLoaded( Tomahawk::DynamicPlaylistRevision ) ), this, SLOT( onRevisionLoaded( Tomahawk::DynamicPlaylistRevision ) ) );
connect( m_playlist->generator().data(), SIGNAL( error( QString, QString ) ), this, SLOT( generatorError( QString, QString ) ) );
connect( m_playlist.data(), SIGNAL( deleted( Tomahawk::dynplaylist_ptr ) ), this, SLOT( onDeleted() ) );
connect( m_playlist.data(), SIGNAL( changed() ), this, SLOT( onChanged() ) );
if ( m_playlist->mode() == OnDemand && !m_playlist->generator()->controls().isEmpty() )
showPreview();
if ( !m_playlist.isNull() )
m_controls->setControls( m_playlist, m_playlist->author()->isLocal() );
}
void
DynamicWidget::onRevisionLoaded( const Tomahawk::DynamicPlaylistRevision& rev )
{
Q_UNUSED( rev );
qDebug() << "DynamicWidget::onRevisionLoaded";
if ( m_model->ignoreRevision( rev.revisionguid ) )
{
m_model->removeRevisionFromIgnore( rev.revisionguid );
return;
}
loadDynamicPlaylist( m_playlist );
if( m_resolveOnNextLoad || !m_playlist->author()->isLocal() )
{
m_playlist->resolve();
m_resolveOnNextLoad = false;
}
}
Tomahawk::playlistinterface_ptr
DynamicWidget::playlistInterface() const
{
return m_view->proxyModel()->playlistInterface();
}
QSize
DynamicWidget::sizeHint() const
{
// We want to take up as much room as the animated splitter containing us and the queue editor will allow. So we return a bogus huge sizehint
// to avoid having to calculate it which is slow
return QSize( 5000, 5000 );
}
void
DynamicWidget::resizeEvent(QResizeEvent* )
{
layoutFloatingWidgets();
}
void
DynamicWidget::layoutFloatingWidgets()
{
if ( !m_runningOnDemand )
{
int x = ( width() / 2 ) - ( m_setup->size().width() / 2 );
int y = height() - m_setup->size().height() - 40; // padding
m_setup->move( x, y );
}
else if( m_runningOnDemand && m_steering )
{
int x = ( width() / 2 ) - ( m_steering->size().width() / 2 );
int y = height() - m_steering->size().height() - 40; // padding
m_steering->move( x, y );
}
}
void
DynamicWidget::playlistChanged( Tomahawk::playlistinterface_ptr pl )
{
if ( pl == m_view->proxyModel()->playlistInterface() ) // same playlist
m_activePlaylist = true;
else
{
m_activePlaylist = false;
// user started playing something somewhere else, so give it a rest
if ( m_runningOnDemand )
{
stopStation( false );
}
}
}
void
DynamicWidget::showEvent(QShowEvent* )
{
if ( !m_playlist.isNull() && !m_runningOnDemand )
m_setup->fadeIn();
}
void
DynamicWidget::generate( int num )
{
// get the items from the generator, and put them in the playlist
m_view->setDynamicWorking( true );
m_loading->fadeIn();
m_playlist->generator()->generate( num );
}
void
DynamicWidget::stationFailed( const QString& msg )
{
m_view->setDynamicWorking( false );
m_view->showMessage( msg );
m_loading->fadeOut();
stopStation( false );
}
void
DynamicWidget::trackStarted()
{
if ( m_activePlaylist && !m_playlist.isNull() &&
m_playlist->mode() == OnDemand && !m_runningOnDemand )
{
startStation();
}
}
void
DynamicWidget::tracksAdded()
{
if ( m_playlist->mode() == OnDemand && m_runningOnDemand && m_setup->isVisible() )
m_setup->fadeOut();
}
void
DynamicWidget::stopStation( bool stopPlaying )
{
m_model->stopOnDemand( stopPlaying );
m_runningOnDemand = false;
// TODO until i add a qwidget interface
QMetaObject::invokeMethod( m_steering, "fadeOut", Qt::DirectConnection );
m_setup->fadeIn();
}
void
DynamicWidget::startStation()
{
m_runningOnDemand = true;
m_model->startOnDemand();
m_setup->fadeOut();
// show the steering controls
if ( m_playlist->generator()->onDemandSteerable() )
{
// position it horizontally centered, above the botton.
m_steering = m_playlist->generator()->steeringWidget();
Q_ASSERT( m_steering );
connect( m_steering, SIGNAL( steeringChanged() ), this, SLOT( steeringChanged() ) );
int x = ( width() / 2 ) - ( m_steering->size().width() / 2 );
int y = height() - m_steering->size().height() - 40; // padding
m_steering->setParent( this );
m_steering->move( x, y );
// TODO until i add a qwidget interface
QMetaObject::invokeMethod( m_steering, "fadeIn", Qt::DirectConnection );
connect( m_steering, SIGNAL( resized() ), this, SLOT( layoutFloatingWidgets() ) );
}
}
void
DynamicWidget::playlistTypeChanged( QString )
{
// TODO
}
void
DynamicWidget::tracksGenerated( const QList< query_ptr >& queries )
{
int limit = -1; // only limit the "preview" of a station
if ( m_playlist->author()->isLocal() && m_playlist->mode() == Static )
{
m_resolveOnNextLoad = true;
}
else if ( m_playlist->mode() == OnDemand )
{
limit = 5;
}
if ( m_playlist->mode() != OnDemand )
m_loading->fadeOut();
m_model->tracksGenerated( queries, limit );
}
void
DynamicWidget::controlsChanged( bool added )
{
// controlsChanged() is emitted when a control is added or removed
// in the case of addition, it's blank by default... so to avoid an error
// when playing a station just ignore it till we're ready and get a controlChanged()
m_controlsChanged = true;
if ( !m_playlist->author()->isLocal() )
return;
m_playlist->createNewRevision();
m_seqRevLaunched++;
if ( !added )
showPreview();
emit descriptionChanged( m_playlist->generator()->sentenceSummary() );
}
void
DynamicWidget::controlChanged( const Tomahawk::dyncontrol_ptr& control )
{
Q_UNUSED( control );
if ( !m_playlist->author()->isLocal() )
return;
m_playlist->createNewRevision();
m_seqRevLaunched++;
showPreview();
emit descriptionChanged( m_playlist->generator()->sentenceSummary() );
}
void
DynamicWidget::steeringChanged()
{
// When steering changes, toss all the tracks that are upcoming, and re-fetch.
// We have to find the currently playing item
QModelIndex playing;
for ( int i = 0; i < m_view->proxyModel()->rowCount( QModelIndex() ); ++i )
{
const QModelIndex cur = m_view->proxyModel()->index( i, 0, QModelIndex() );
PlayableItem* item = m_view->proxyModel()->itemFromIndex( m_view->proxyModel()->mapToSource( cur ) );
if ( item && item->isPlaying() )
{
playing = cur;
break;
}
}
if ( !playing.isValid() )
return;
const int upcoming = m_view->proxyModel()->rowCount( QModelIndex() ) - 1 - playing.row();
tDebug() << "Removing tracks after current in station, found" << upcoming;
QModelIndexList toRemove;
for ( int i = playing.row() + 1; i < m_view->proxyModel()->rowCount( QModelIndex() ); i++ )
{
toRemove << m_view->proxyModel()->index( i, 0, QModelIndex() );
}
m_view->proxyModel()->removeIndexes( toRemove );
m_playlist->generator()->fetchNext();
}
void
DynamicWidget::showPreview()
{
if ( m_playlist->mode() == OnDemand && !m_runningOnDemand )
{
// if this is a not running station, preview matching tracks
m_model->clear();
generate( 20 ); // ask for more, we'll filter how many we actually want
}
}
void
DynamicWidget::generatorError( const QString& title, const QString& content )
{
m_view->setDynamicWorking( false );
m_loading->fadeOut();
if ( m_runningOnDemand )
{
stopStation( false );
m_view->showMessage( tr( "Station ran out of tracks!\n\nTry tweaking the filters for a new set of songs to play." ) );
}
else
m_view->showMessageTimeout( title, content );
}
void
DynamicWidget::paintRoundedFilledRect( QPainter& p, QPalette& /* pal */, QRect& r, qreal opacity )
{
p.setBackgroundMode( Qt::TransparentMode );
p.setRenderHint( QPainter::Antialiasing );
p.setOpacity( opacity );
QColor c( 30, 30, 30 );
QPen pen( c.darker(), .5 );
p.setPen( pen );
p.setBrush( c );
p.drawRoundedRect( r, 10, 10 );
p.setOpacity( opacity + .2 );
p.setBrush( QBrush() );
p.setPen( pen );
p.drawRoundedRect( r, 10, 10 );
}
QString
DynamicWidget::description() const
{
return m_model->description();
}
QString
DynamicWidget::title() const
{
return m_model->title();
}
QPixmap
DynamicWidget::pixmap() const
{
if ( m_playlist->mode() == OnDemand )
return TomahawkUtils::defaultPixmap( TomahawkUtils::Station );
else if ( m_playlist->mode() == Static )
return TomahawkUtils::defaultPixmap( TomahawkUtils::AutomaticPlaylist );
else
return QPixmap();
}
bool
DynamicWidget::jumpToCurrentTrack()
{
m_view->scrollTo( m_view->proxyModel()->currentIndex(), QAbstractItemView::PositionAtCenter );
return true;
}
void
DynamicWidget::onDeleted()
{
emit destroyed( widget() );
}
void
DynamicWidget::onChanged()
{
if ( !m_playlist.isNull() &&
ViewManager::instance()->currentPage() == this )
emit nameChanged( m_playlist->title() );
}

View File

@@ -1,140 +0,0 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2010-2011, Leo Franchi <lfranchi@kde.org>
* Copyright 2010-2011, Jeff Mitchell <jeff@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 DYNAMIC_WIDGET_H
#define DYNAMIC_WIDGET_H
#include <QWidget>
#include "Typedefs.h"
#include "ViewPage.h"
#include "playlist/dynamic/DynamicPlaylistRevision.h"
class AnimatedSpinner;
class QShowEvent;
class QHideEvent;
class QSpinBox;
class QVBoxLayout;
class QHBoxLayout;
class QPushButton;
class QComboBox;
class PlaylistModel;
class PlaylistView;
class AnimatedSplitter;
class QLabel;
class ReadOrWriteWidget;
namespace Tomahawk
{
class DynamicModel;
class DynamicControl;
class DynamicSetupWidget;
class DynamicView;
class CollapsibleControls;
/**
* This class contains the dynamic playlist config and the playlist view itself
*/
class DynamicWidget : public QWidget, public Tomahawk::ViewPage
{
Q_OBJECT
public:
explicit DynamicWidget( const dynplaylist_ptr& playlist, QWidget* parent = 0);
virtual ~DynamicWidget();
void loadDynamicPlaylist( const dynplaylist_ptr& playlist );
dynplaylist_ptr playlist();
virtual Tomahawk::playlistinterface_ptr playlistInterface() const;
virtual QSize sizeHint() const;
virtual void resizeEvent( QResizeEvent* );
virtual void showEvent(QShowEvent* );
static void paintRoundedFilledRect( QPainter& p, QPalette& pal, QRect& r, qreal opacity = .95 );
virtual QWidget* widget() { return this; }
virtual QString title() const;
virtual QString description() const;
virtual QPixmap pixmap() const;
virtual bool jumpToCurrentTrack();
public slots:
void onRevisionLoaded( const Tomahawk::DynamicPlaylistRevision& rev );
void playlistTypeChanged(QString);
void startStation();
void stopStation( bool stopPlaying = true );
void trackStarted();
void stationFailed( const QString& );
void playlistChanged( Tomahawk::playlistinterface_ptr );
void tracksAdded();
signals:
void nameChanged( const QString& name );
void descriptionChanged( const QString& caption );
void destroyed( QWidget* widget );
private slots:
void generate( int = -1 );
void tracksGenerated( const QList< Tomahawk::query_ptr>& queries );
void generatorError( const QString& title, const QString& content );
void controlsChanged( bool added );
void controlChanged( const Tomahawk::dyncontrol_ptr& control );
void steeringChanged();
void showPreview();
void layoutFloatingWidgets();
void onDeleted();
void onChanged();
private:
dynplaylist_ptr m_playlist;
QVBoxLayout* m_layout;
bool m_resolveOnNextLoad;
int m_seqRevLaunched; // if we shoot off multiple createRevision calls, we don'y want to set one of the middle ones
bool m_activePlaylist;
// loading animation
AnimatedSpinner* m_loading;
// setup controls
DynamicSetupWidget* m_setup;
// used in OnDemand mode
bool m_runningOnDemand;
bool m_controlsChanged;
QWidget* m_steering;
CollapsibleControls* m_controls;
DynamicView* m_view;
DynamicModel* m_model;
};
};
#endif

View File

@@ -56,12 +56,14 @@ QSearchField::QSearchField(QWidget *parent) : QWidget(parent)
lineEdit->setStyleSheet( "QLineEdit { border: 1px solid gray; border-radius: 6px; }" );
lineEdit->setMinimumHeight(27);
setFixedHeight(27);
QFontMetrics fm(lineEdit->font());
setFixedHeight(fm.height() + fm.xHeight() * 2);
#ifdef Q_WS_MAC
lineEdit->setContentsMargins(0, 0, 0, 0);
#else
lineEdit->setContentsMargins(2, 2, 2, 2);
const int margins = fm.xHeight() / 2;
lineEdit->setContentsMargins(margins, margins, margins, margins);
#endif
}

View File

@@ -14,7 +14,7 @@ class DLLEXPORT QSearchField : public QWidget
Q_PROPERTY(QString placeholderText READ placeholderText WRITE setPlaceholderText);
public:
explicit QSearchField(QWidget *parent);
explicit QSearchField(QWidget *parent = 0);
QString text() const;
QString placeholderText() const;

View File

@@ -132,6 +132,12 @@ namespace TomahawkUtils
RoundedCorners
};
enum ViewMode
{
ViewModeFlat = 0,
ViewModeDetailed = 1,
ViewModeGrid = 2
};
class DLLEXPORT NetworkProxyFactory : public QNetworkProxyFactory
{
@@ -215,5 +221,6 @@ namespace TomahawkUtils
DLLEXPORT void crash();
}
Q_DECLARE_METATYPE( TomahawkUtils::ViewMode );
#endif // TOMAHAWKUTILS_H

View File

@@ -413,299 +413,306 @@ alphaBlend( const QColor& colorFrom, const QColor& colorTo, float opacity )
}
QPixmap
defaultPixmap( ImageType type, ImageMode mode, const QSize& size )
QString
defaultImagePath( ImageType type, ImageMode mode )
{
QPixmap pixmap;
switch ( type )
{
case DefaultAlbumCover:
if ( mode == CoverInCase )
pixmap = ImageRegistry::instance()->pixmap( RESPATH "images/no-album-art-placeholder.svg", size );
return QLatin1String( RESPATH "images/no-album-art-placeholder.svg" );
else if ( mode == Grid )
pixmap = ImageRegistry::instance()->pixmap( RESPATH "images/album-placeholder-grid.svg", size );
return QLatin1String( RESPATH "images/album-placeholder-grid.svg" );
else
pixmap = ImageRegistry::instance()->pixmap( RESPATH "images/album-icon.svg", size );
return QLatin1String( RESPATH "images/album-icon.svg" );
break;
case DefaultArtistImage:
if ( mode == Grid )
pixmap = ImageRegistry::instance()->pixmap( RESPATH "images/artist-placeholder-grid.svg", size );
return QLatin1String( RESPATH "images/artist-placeholder-grid.svg" );
else
pixmap = ImageRegistry::instance()->pixmap( RESPATH "images/artist-icon.svg", size );
return QLatin1String( RESPATH "images/artist-icon.svg" );
break;
case DefaultTrackImage:
if ( mode == Grid )
pixmap = ImageRegistry::instance()->pixmap( RESPATH "images/track-placeholder-grid.svg", size );
return QLatin1String( RESPATH "images/track-placeholder-grid.svg" );
else if ( mode == RoundedCorners )
pixmap = ImageRegistry::instance()->pixmap( RESPATH "images/track-icon.svg", size, TomahawkUtils::RoundedCorners );
return QLatin1String( RESPATH "images/track-icon.svg" );
else
pixmap = ImageRegistry::instance()->pixmap( RESPATH "images/track-icon.svg", size );
return QLatin1String( RESPATH "images/track-icon.svg" );
break;
case DefaultSourceAvatar:
if ( mode == RoundedCorners )
pixmap = ImageRegistry::instance()->pixmap( RESPATH "images/user-avatar.svg", size, TomahawkUtils::RoundedCorners );
else
pixmap = ImageRegistry::instance()->pixmap( RESPATH "images/user-avatar.svg", size );
return QLatin1String( RESPATH "images/user-avatar.svg" );
break;
case DefaultResolver:
if ( mode == RoundedCorners )
pixmap = ImageRegistry::instance()->pixmap( RESPATH "images/resolver-default.svg", size, TomahawkUtils::RoundedCorners );
else
pixmap = ImageRegistry::instance()->pixmap( RESPATH "images/resolver-default.svg", size );
return QLatin1String( RESPATH "images/resolver-default.svg" );
break;
case DefaultCollection:
pixmap = ImageRegistry::instance()->pixmap( RESPATH "images/collection.svg", size );
return QLatin1String( RESPATH "images/collection.svg" );
break;
case NowPlayingSpeaker:
pixmap = ImageRegistry::instance()->pixmap( RESPATH "images/now-playing-speaker.svg", size );
return QLatin1String( RESPATH "images/now-playing-speaker.svg" );
break;
case NowPlayingSpeakerDark:
pixmap = ImageRegistry::instance()->pixmap( RESPATH "images/now-playing-speaker-dark.svg", size );
return QLatin1String( RESPATH "images/now-playing-speaker-dark.svg" );
break;
case InfoIcon:
pixmap = ImageRegistry::instance()->pixmap( RESPATH "images/info.svg", size );
return QLatin1String( RESPATH "images/info.svg" );
break;
case PlayButton:
pixmap = ImageRegistry::instance()->pixmap( RESPATH "images/play-rest.svg", size );
return QLatin1String( RESPATH "images/play-rest.svg" );
break;
case PlayButtonPressed:
pixmap = ImageRegistry::instance()->pixmap( RESPATH "images/play-pressed.svg", size );
return QLatin1String( RESPATH "images/play-pressed.svg" );
break;
case PauseButton:
pixmap = ImageRegistry::instance()->pixmap( RESPATH "images/pause-rest.svg", size );
return QLatin1String( RESPATH "images/pause-rest.svg" );
break;
case PauseButtonPressed:
pixmap = ImageRegistry::instance()->pixmap( RESPATH "images/pause-pressed.svg", size );
return QLatin1String( RESPATH "images/pause-pressed.svg" );
break;
case PrevButton:
pixmap = ImageRegistry::instance()->pixmap( RESPATH "images/back-rest.svg", size );
return QLatin1String( RESPATH "images/back-rest.svg" );
break;
case PrevButtonPressed:
pixmap = ImageRegistry::instance()->pixmap( RESPATH "images/back-pressed.svg", size );
return QLatin1String( RESPATH "images/back-pressed.svg" );
break;
case NextButton:
pixmap = ImageRegistry::instance()->pixmap( RESPATH "images/skip-rest.svg", size );
return QLatin1String( RESPATH "images/skip-rest.svg" );
break;
case NextButtonPressed:
pixmap = ImageRegistry::instance()->pixmap( RESPATH "images/skip-pressed.svg", size );
return QLatin1String( RESPATH "images/skip-pressed.svg" );
break;
case ShuffleOff:
pixmap = ImageRegistry::instance()->pixmap( RESPATH "images/shuffle-off-rest.svg", size );
return QLatin1String( RESPATH "images/shuffle-off-rest.svg" );
break;
case ShuffleOffPressed:
pixmap = ImageRegistry::instance()->pixmap( RESPATH "images/shuffle-off-pressed.svg", size );
return QLatin1String( RESPATH "images/shuffle-off-pressed.svg" );
break;
case ShuffleOn:
pixmap = ImageRegistry::instance()->pixmap( RESPATH "images/shuffle-on-rest.svg", size );
return QLatin1String( RESPATH "images/shuffle-on-rest.svg" );
break;
case ShuffleOnPressed:
pixmap = ImageRegistry::instance()->pixmap( RESPATH "images/shuffle-on-pressed.svg", size );
return QLatin1String( RESPATH "images/shuffle-on-pressed.svg" );
break;
case RepeatOne:
pixmap = ImageRegistry::instance()->pixmap( RESPATH "images/repeat-1-on-rest.svg", size );
return QLatin1String( RESPATH "images/repeat-1-on-rest.svg" );
break;
case RepeatOnePressed:
pixmap = ImageRegistry::instance()->pixmap( RESPATH "images/repeat-1-on-pressed.svg", size );
return QLatin1String( RESPATH "images/repeat-1-on-pressed.svg" );
break;
case RepeatAll:
pixmap = ImageRegistry::instance()->pixmap( RESPATH "images/repeat-all-on-rest.svg", size );
return QLatin1String( RESPATH "images/repeat-all-on-rest.svg" );
break;
case RepeatAllPressed:
pixmap = ImageRegistry::instance()->pixmap( RESPATH "images/repeat-all-on-pressed.svg", size );
return QLatin1String( RESPATH "images/repeat-all-on-pressed.svg" );
break;
case RepeatOff:
pixmap = ImageRegistry::instance()->pixmap( RESPATH "images/repeat-off-rest.svg", size );
return QLatin1String( RESPATH "images/repeat-off-rest.svg" );
break;
case RepeatOffPressed:
pixmap = ImageRegistry::instance()->pixmap( RESPATH "images/repeat-off-pressed.svg", size );
return QLatin1String( RESPATH "images/repeat-off-pressed.svg" );
break;
case VolumeMuted:
pixmap = ImageRegistry::instance()->pixmap( RESPATH "images/volume-icon-muted.svg", size );
return QLatin1String( RESPATH "images/volume-icon-muted.svg" );
break;
case VolumeFull:
pixmap = ImageRegistry::instance()->pixmap( RESPATH "images/volume-icon-full.svg", size );
return QLatin1String( RESPATH "images/volume-icon-full.svg" );
break;
case Share:
pixmap = ImageRegistry::instance()->pixmap( RESPATH "images/share.svg", size );
return QLatin1String( RESPATH "images/share.svg" );
break;
case NotLoved:
pixmap = ImageRegistry::instance()->pixmap( RESPATH "images/not-loved.svg", size );
return QLatin1String( RESPATH "images/not-loved.svg" );
break;
case Loved:
pixmap = ImageRegistry::instance()->pixmap( RESPATH "images/loved.svg", size );
return QLatin1String( RESPATH "images/loved.svg" );
break;
case Configure:
pixmap = ImageRegistry::instance()->pixmap( RESPATH "images/configure.svg", size );
return QLatin1String( RESPATH "images/configure.svg" );
break;
case GreenDot:
pixmap = ImageRegistry::instance()->pixmap( RESPATH "images/green-dot.svg", size );
return QLatin1String( RESPATH "images/green-dot.svg" );
break;
case AddContact:
pixmap = ImageRegistry::instance()->pixmap( RESPATH "images/add-contact.svg", size );
return QLatin1String( RESPATH "images/add-contact.svg" );
break;
case SubscribeOn:
pixmap = ImageRegistry::instance()->pixmap( RESPATH "images/subscribe-on.svg", size );
return QLatin1String( RESPATH "images/subscribe-on.svg" );
break;
case SubscribeOff:
pixmap = ImageRegistry::instance()->pixmap( RESPATH "images/subscribe-off.svg", size );
return QLatin1String( RESPATH "images/subscribe-off.svg" );
break;
case JumpLink:
pixmap = ImageRegistry::instance()->pixmap( RESPATH "images/jump-link.svg", size );
return QLatin1String( RESPATH "images/jump-link.svg" );
break;
case ProcessStop:
pixmap = ImageRegistry::instance()->pixmap( RESPATH "images/process-stop.svg", size );
return QLatin1String( RESPATH "images/process-stop.svg" );
break;
case HeadphonesOn:
pixmap = ImageRegistry::instance()->pixmap( RESPATH "images/headphones.svg", size );
return QLatin1String( RESPATH "images/headphones.svg" );
break;
case HeadphonesOff:
pixmap = ImageRegistry::instance()->pixmap( RESPATH "images/headphones-off.svg", size );
return QLatin1String( RESPATH "images/headphones-off.svg" );
break;
case PadlockClosed:
pixmap = ImageRegistry::instance()->pixmap( RESPATH "images/closed-padlock.svg", size );
return QLatin1String( RESPATH "images/closed-padlock.svg" );
break;
case PadlockOpen:
pixmap = ImageRegistry::instance()->pixmap( RESPATH "images/open-padlock.svg", size );
return QLatin1String( RESPATH "images/open-padlock.svg" );
break;
case Downloading:
pixmap = ImageRegistry::instance()->pixmap( RESPATH "images/downloading.svg", size );
return QLatin1String( RESPATH "images/downloading.svg" );
break;
case Uploading:
pixmap = ImageRegistry::instance()->pixmap( RESPATH "images/uploading.svg", size );
return QLatin1String( RESPATH "images/uploading.svg" );
break;
case ViewRefresh:
pixmap = ImageRegistry::instance()->pixmap( RESPATH "images/view-refresh.svg", size );
return QLatin1String( RESPATH "images/view-refresh.svg" );
break;
case SuperCollection:
pixmap = ImageRegistry::instance()->pixmap( RESPATH "images/supercollection.svg", size );
return QLatin1String( RESPATH "images/supercollection.svg" );
break;
case LovedPlaylist:
pixmap = ImageRegistry::instance()->pixmap( RESPATH "images/loved_playlist.svg", size );
return QLatin1String( RESPATH "images/loved_playlist.svg" );
break;
case NewReleases:
pixmap = ImageRegistry::instance()->pixmap( RESPATH "images/new-releases.svg", size );
return QLatin1String( RESPATH "images/new-releases.svg" );
break;
case NewAdditions:
pixmap = ImageRegistry::instance()->pixmap( RESPATH "images/new-additions.svg", size );
return QLatin1String( RESPATH "images/new-additions.svg" );
break;
case RecentlyPlayed:
pixmap = ImageRegistry::instance()->pixmap( RESPATH "images/recently-played.svg", size );
return QLatin1String( RESPATH "images/recently-played.svg" );
break;
case Charts:
pixmap = ImageRegistry::instance()->pixmap( RESPATH "images/charts.svg", size );
return QLatin1String( RESPATH "images/charts.svg" );
break;
case AutomaticPlaylist:
pixmap = ImageRegistry::instance()->pixmap( RESPATH "images/automatic-playlist.svg", size );
return QLatin1String( RESPATH "images/automatic-playlist.svg" );
break;
case Station:
pixmap = ImageRegistry::instance()->pixmap( RESPATH "images/station.svg", size );
return QLatin1String( RESPATH "images/station.svg" );
break;
case Playlist:
pixmap = ImageRegistry::instance()->pixmap( RESPATH "images/playlist-icon.svg", size );
return QLatin1String( RESPATH "images/playlist-icon.svg" );
break;
case Search:
pixmap = ImageRegistry::instance()->pixmap( RESPATH "images/search-icon.svg", size );
return QLatin1String( RESPATH "images/search-icon.svg" );
break;
case Add:
pixmap = ImageRegistry::instance()->pixmap( RESPATH "images/add.svg", size );
return QLatin1String( RESPATH "images/add.svg" );
break;
case ListAdd:
pixmap = ImageRegistry::instance()->pixmap( RESPATH "images/list-add.svg", size );
return QLatin1String( RESPATH "images/list-add.svg" );
break;
case ListRemove:
pixmap = ImageRegistry::instance()->pixmap( RESPATH "images/list-remove.svg", size );
return QLatin1String( RESPATH "images/list-remove.svg" );
break;
case AdvancedSettings:
pixmap = ImageRegistry::instance()->pixmap( RESPATH "images/advanced-settings.svg", size );
return QLatin1String( RESPATH "images/advanced-settings.svg" );
break;
case AccountSettings:
pixmap = ImageRegistry::instance()->pixmap( RESPATH "images/account-settings.svg", size );
return QLatin1String( RESPATH "images/account-settings.svg" );
break;
case MusicSettings:
pixmap = ImageRegistry::instance()->pixmap( RESPATH "images/music-settings.svg", size );
return QLatin1String( RESPATH "images/music-settings.svg" );
break;
case DropSong:
pixmap = ImageRegistry::instance()->pixmap( RESPATH "images/drop-song.svg", size );
return QLatin1String( RESPATH "images/drop-song.svg" );
break;
case DropAlbum:
pixmap = ImageRegistry::instance()->pixmap( RESPATH "images/drop-album.svg", size );
return QLatin1String( RESPATH "images/drop-album.svg" );
break;
case DropAllSongs:
pixmap = ImageRegistry::instance()->pixmap( RESPATH "images/drop-all-songs.svg", size );
return QLatin1String( RESPATH "images/drop-all-songs.svg" );
break;
case DropLocalSongs:
pixmap = ImageRegistry::instance()->pixmap( RESPATH "images/drop-local-songs.svg", size );
return QLatin1String( RESPATH "images/drop-local-songs.svg" );
break;
case DropTopSongs:
pixmap = ImageRegistry::instance()->pixmap( RESPATH "images/drop-top-songs.svg", size );
return QLatin1String( RESPATH "images/drop-top-songs.svg" );
break;
case Starred:
pixmap = ImageRegistry::instance()->pixmap( RESPATH "images/starred.svg", size );
return QLatin1String( RESPATH "images/starred.svg" );
break;
case Unstarred:
pixmap = ImageRegistry::instance()->pixmap( RESPATH "images/star-unstarred.svg", size );
return QLatin1String( RESPATH "images/star-unstarred.svg" );
break;
case StarHovered:
pixmap = ImageRegistry::instance()->pixmap( RESPATH "images/star-hover.svg", size );
return QLatin1String( RESPATH "images/star-hover.svg" );
break;
case SipPluginOnline:
pixmap = ImageRegistry::instance()->pixmap( RESPATH "images/sipplugin-online.svg", size );
return QLatin1String( RESPATH "images/sipplugin-online.svg" );
break;
case SipPluginOffline:
pixmap = ImageRegistry::instance()->pixmap( RESPATH "images/sipplugin-offline.svg", size );
return QLatin1String( RESPATH "images/sipplugin-offline.svg" );
break;
case AccountNone:
pixmap = ImageRegistry::instance()->pixmap( RESPATH "images/account-none.svg", size );
return QLatin1String( RESPATH "images/account-none.svg" );
break;
case LastfmIcon:
pixmap = ImageRegistry::instance()->pixmap( RESPATH "images/lastfm-icon.svg", size );
return QLatin1String( RESPATH "images/lastfm-icon.svg" );
break;
case SpotifyIcon:
pixmap = ImageRegistry::instance()->pixmap( RESPATH "images/spotify-sourceicon.svg", size );
return QLatin1String( RESPATH "images/spotify-sourceicon.svg" );
break;
case SoundcloudIcon:
pixmap = ImageRegistry::instance()->pixmap( RESPATH "images/soundcloud.svg", size );
return QLatin1String( RESPATH "images/soundcloud.svg" );
break;
default:
break;
}
Q_ASSERT(false);
return QString();
}
QPixmap
defaultPixmap( ImageType type, ImageMode mode, const QSize& size )
{
QPixmap pixmap;
if ( mode == RoundedCorners ) {
pixmap = ImageRegistry::instance()->pixmap( defaultImagePath( type, mode ), size, TomahawkUtils::RoundedCorners );
} else {
pixmap = ImageRegistry::instance()->pixmap( defaultImagePath( type, mode ), size );
}
if ( pixmap.isNull() )
{
Q_ASSERT( false );
@@ -713,6 +720,7 @@ defaultPixmap( ImageType type, ImageMode mode, const QSize& size )
}
return pixmap;
}

View File

@@ -69,6 +69,7 @@ namespace TomahawkUtils
DLLEXPORT void styleScrollBar( QScrollBar* scrollBar );
DLLEXPORT QPixmap defaultPixmap( ImageType type, ImageMode mode = TomahawkUtils::Original, const QSize& size = QSize( 0, 0 ) );
DLLEXPORT QString defaultImagePath( ImageType type, ImageMode mode = TomahawkUtils::Original );
DLLEXPORT QPixmap createTiledPixmap( int width, int height, const QImage& src );
DLLEXPORT QPixmap addDropShadow( const QPixmap& sourceImage, const QSize& targetSize );
DLLEXPORT QPixmap squareCenterPixmap( const QPixmap& sourceImage );

View File

@@ -0,0 +1,142 @@
#include "DeclarativeCoverArtProvider.h"
#include "playlist/PlayableItem.h"
#include "playlist/PlayableProxyModel.h"
#include "Query.h"
#include "Album.h"
#include "Artist.h"
#include "utils/TomahawkUtilsGui.h"
#include "utils/Logger.h"
#include <QDeclarativeImageProvider>
#include <QModelIndex>
#include <QDebug>
#include <QPainter>
namespace Tomahawk
{
DeclarativeCoverArtProvider::DeclarativeCoverArtProvider( )
: QDeclarativeImageProvider( QDeclarativeImageProvider::Pixmap )
{
}
DeclarativeCoverArtProvider::~DeclarativeCoverArtProvider()
{
}
QPixmap DeclarativeCoverArtProvider::requestPixmap(const QString &id, QSize *size, const QSize &requestedSize)
{
// We always can generate it in the requested size
int width = requestedSize.width() > 0 ? requestedSize.width() : 230;
int height = requestedSize.height() > 0 ? requestedSize.height() : 230;
if( size )
*size = QSize( width, height );
QPixmap cover;
tDebug() << "DeclarativeCoverArtprovider: Getting album art by id:" << id << requestedSize;
bool mirrored = false;
bool labeled = false;
QString coverId = id;
if(coverId.contains("-mirror")) {
coverId.remove("-mirror");
mirrored = true;
}
if(coverId.contains("-labels")) {
coverId.remove("-labels");
labeled = true;
}
artist_ptr artist = Artist::getByCoverId( coverId );
if ( !artist.isNull() )
{
tDebug() << "Returning artist cover:" << artist->cover( *size ).isNull();
cover = artist->cover( *size );
if ( cover.isNull() )
{
tDebug() << Q_FUNC_INFO << "Returning default artist image";
cover = TomahawkUtils::defaultPixmap( TomahawkUtils::DefaultArtistImage, TomahawkUtils::Original, *size );
}
}
if ( cover.isNull() )
{
album_ptr album = Album::getByCoverId( coverId );
if ( !album.isNull() )
{
tDebug() << "Returning album cover:" << album->cover( *size ).isNull();
cover = album->cover( *size );
if ( cover.isNull() )
{
tDebug() << Q_FUNC_INFO << "Returning default album image";
cover = TomahawkUtils::defaultPixmap( TomahawkUtils::DefaultAlbumCover, TomahawkUtils::Original, *size );
}
}
}
if ( cover.isNull() )
{
tDebug() << Q_FUNC_INFO << "Returning default track image";
cover = TomahawkUtils::defaultPixmap( TomahawkUtils::DefaultTrackImage, TomahawkUtils::Original, *size );
}
QImage image(*size, QImage::Format_ARGB32);
if(labeled) {
QImage coverImage(*size, QImage::Format_RGB32);
QPainter bgPainter(&coverImage);
bgPainter.drawPixmap(0, 0, size->width(), size->height(), cover);
QColor c1;
c1.setRgb( 0, 0, 0 );
c1.setAlphaF( 0.00 );
QColor c2;
c2.setRgb( 0, 0, 0 );
c2.setAlphaF( 0.88 );
QLinearGradient gradient( QPointF( 0, 0 ), QPointF( 0, 1 ) );
gradient.setCoordinateMode( QGradient::ObjectBoundingMode );
gradient.setColorAt( 0.0, c1 );
gradient.setColorAt( 0.6, c2 );
gradient.setColorAt( 1.0, c2 );
bgPainter.setPen( Qt::transparent );
bgPainter.setBrush(QBrush(gradient));
bgPainter.drawRect(0, size->height() * 0.7, size->width(), size->height() * 0.3);
cover = QPixmap::fromImage(coverImage);
}
QPainter painter(&image);
if(!mirrored) {
image.fill(Qt::white);
painter.drawPixmap(0, 0, size->width(), size->height(), cover);
} else {
image.fill(QColor(0, 0, 0, 0));
// Lets paint half of the image in a fragment per line
int mirrorHeight = size->height() / 2;
int fragmentCount = mirrorHeight;
int fragmentHeight = mirrorHeight / fragmentCount;
QPainter::PixmapFragment fragments[fragmentCount];
qreal fragmentOpacity = 0;
int fragmentStartY = size->height() - mirrorHeight;
for(int i = 0; i < fragmentCount; ++i) {
QPointF point = QPointF(size->width() / 2, fragmentStartY + (fragmentHeight / 2));
QRectF sourceRect = QRectF(0, fragmentStartY, size->width(), fragmentHeight);
fragments[i] = QPainter::PixmapFragment::create(point, sourceRect, 1, 1, 0, fragmentOpacity);
fragmentOpacity += 0.5 / fragmentCount;
fragmentStartY += fragmentHeight;
}
painter.drawPixmapFragments(fragments, fragmentCount, cover);
}
return QPixmap::fromImage(image);
}
}

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