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

Compare commits

...

64 Commits

Author SHA1 Message Date
Christian Muehlhaeuser
588b9898dd * Backported breakpad building fix. 2013-06-05 09:17:59 +02:00
Teo Mrnjavac
072a2e8d78 Avoid crash in SuperCollection. 2013-05-10 10:04:29 +02:00
Christian Muehlhaeuser
247c184ba3 * Updated ChangeLog. 2013-05-07 04:31:32 +02:00
Christian Muehlhaeuser
af6f96728f * Worked around Lucene memory leak and improved our own memory foot-print during indexing. 2013-05-07 04:30:24 +02:00
Christian Muehlhaeuser
03b581856f * Updated README.md. 2013-05-05 04:59:06 +02:00
Teo Mrnjavac
7a869ef332 There's no need for this connection to be Queued any more.
This fixes Listening along for some users.
2013-05-01 21:26:27 +02:00
Christian Muehlhaeuser
93ec8a300d * Updated ChangeLog. 2013-04-29 02:03:04 +02:00
Christian Muehlhaeuser
b4752ba7a6 * Fixed displaying filename in properties dialog. 2013-04-29 01:59:55 +02:00
Christian Muehlhaeuser
87311cfbd7 * Fixed TWK-1292: Issues playing fils with '#' in the filename. 2013-04-29 01:59:43 +02:00
Christian Muehlhaeuser
1a75a46f78 * Added new translations. 2013-04-27 04:50:39 +02:00
Christian Muehlhaeuser
2867314465 * Updated ChangeLog for 0.7. 2013-04-27 04:48:30 +02:00
Christian Muehlhaeuser
760c8b664d * Updated ChangeLog. 2013-04-27 02:42:57 +02:00
Christian Muehlhaeuser
16aae53436 * Fixed PlaylistItemDelegate swallowing mouse events, which prevented TrackView from triggering D&D operations. 2013-04-26 06:07:15 +02:00
Christian Muehlhaeuser
214e2ac924 * Bumped libechonest requirement to 2.0.3.
This reverts commit b2b1ff1cba.
2013-04-26 04:26:04 +02:00
Christian Muehlhaeuser
627c32be9f * Updated pngs. 2013-04-24 19:38:33 +02:00
Jason Herskowitz
911d611701 Give the logo SVG a cream filling 2013-04-24 19:23:37 +02:00
Jason Herskowitz
efc89621b6 Try getting a shadow on SVG logo similar to PNG 2013-04-24 19:23:28 +02:00
Christian Muehlhaeuser
f552f20322 * Updated pngs. 2013-04-24 18:55:28 +02:00
Jason Herskowitz
1cef0c9ac4 Make SVG Icon square 2013-04-24 18:53:22 +02:00
Jason Herskowitz
8d069f83c8 Fix Tomahawk icon SVG 2013-04-24 18:53:22 +02:00
Christian Muehlhaeuser
0849e24c5a * Updated ChangeLog. 2013-04-24 17:26:26 +02:00
Mihail Stoykov
deb81ac8d4 fix .desktop file
as per the specification:
" Multiple keys in the same group may not have the same name. Keys in different groups may have the same name. "
2013-04-23 01:19:57 +02:00
Christian Muehlhaeuser
3af985b798 * Only keep one temporary cover art image. 2013-04-22 11:57:42 +02:00
Christian Muehlhaeuser
3aaa951b41 * Set a contact id when creating peerinfo for zeroconf in Servent. 2013-04-22 11:55:36 +02:00
Christian Muehlhaeuser
4a1c5aef33 * Set a contact id on Zeroconf peers. 2013-04-18 17:12:46 +02:00
Christian Muehlhaeuser
0b4765c728 * Don't try to re-load avatars. 2013-04-18 17:12:46 +02:00
Christian Muehlhaeuser
aa0dc80efc * Fix and saftey checks for avatar handling. 2013-04-18 16:40:59 +02:00
Christian Muehlhaeuser
616f02087e * Fixed avatar caching going bonkers. 2013-04-18 16:40:59 +02:00
Christian Muehlhaeuser
d2bfda03aa * Updated Album- & PlaylistChartItemDelegate. 2013-04-17 06:45:39 +02:00
Christian Muehlhaeuser
d76580ab5f * Request item repaint via PlayableItem. 2013-04-17 04:10:29 +02:00
Christian Muehlhaeuser
d0b71a6c00 * Request item repaint via PlayableItem. 2013-04-17 04:10:29 +02:00
Christian Muehlhaeuser
1c5635dad5 * Don't use hard-coded color values. 2013-04-17 03:36:57 +02:00
Christian Muehlhaeuser
63196bc550 * It's not called PlaylistManager anymore ;-) 2013-04-17 03:31:25 +02:00
Christian Muehlhaeuser
757d9f98d6 * Reset the delegates' hover-index after a wheel-event. 2013-04-17 03:31:25 +02:00
Christian Muehlhaeuser
9c1db4970a * Delegates can now reset the hover-index upon request. 2013-04-17 03:31:24 +02:00
Christian Muehlhaeuser
49d50e5487 * Updated ChangeLog. 2013-04-17 02:07:20 +02:00
Christian Muehlhaeuser
c6b87a09b6 * Cleanups. 2013-04-16 12:43:28 +02:00
Christian Muehlhaeuser
858bbaa3d8 * Double-click expands artists & albums. We now show an info button for those items. 2013-04-16 12:43:28 +02:00
Christian Muehlhaeuser
27d522165d * Don't assert here. 2013-04-16 11:43:22 +02:00
Christian Muehlhaeuser
1b08c197e3 * ViewManager now deletes the ViewPages for us. 2013-04-16 11:43:22 +02:00
Christian Muehlhaeuser
c37ab1779e * Emit signals before and after deleting a viewpage. 2013-04-16 11:43:22 +02:00
Christian Muehlhaeuser
f2a0c05413 * Check control states after a viewpage has been deleted. 2013-04-16 11:43:22 +02:00
Christian Muehlhaeuser
bc3ae087eb * Init AudioControls after ViewManager. 2013-04-16 11:43:22 +02:00
Christian Muehlhaeuser
4e92cb5e48 * Only delete ControlConnection if we don't expect any incoming connections. 2013-04-16 11:00:26 +02:00
Christian Muehlhaeuser
552a830284 * Fixed foreground color. 2013-04-16 11:00:26 +02:00
Christian Muehlhaeuser
3f9356a0e0 * Never wrap text in sidebar. 2013-04-16 10:39:51 +02:00
Christian Muehlhaeuser
e7fe517e9a * Don't allow to collapse the sidebar entirely. 2013-04-16 10:30:15 +02:00
Christian Muehlhaeuser
822c84b32a * Don't use hard-coded colors. 2013-04-16 10:25:44 +02:00
Christian Muehlhaeuser
e8322cef91 * Store PeerInfos in a temporary ControlConnection while the SIP is pending, so they don't get deleted again immediately. 2013-04-16 09:47:33 +02:00
Teo Mrnjavac
1ad39e3ef6 Remove PeerInfo from cache on dtor. 2013-04-16 09:38:15 +02:00
Teo Mrnjavac
1ec2ccabea Make the PeerInfo cache contain weak pointers. 2013-04-16 09:37:58 +02:00
Christian Muehlhaeuser
769f02b7da * Style cleanup. 2013-04-16 06:11:11 +02:00
Christian Muehlhaeuser
94dc1cfd5e * Moved info button handling out of the views. 2013-04-16 06:11:05 +02:00
Christian Muehlhaeuser
75e9b78883 * Moved info button handling into TreeItemDelegate. 2013-04-16 06:10:58 +02:00
Christian Muehlhaeuser
117b091e4a * Moved info button handling into PlaylistItemDelegate. 2013-04-16 06:10:52 +02:00
Leo Franchi
b0fb51eed7 Don't crash if artist is missing from chart album data 2013-04-16 06:10:41 +02:00
Uwe L. Korn
6a86f843bc Sort playlist alphabetically in ContextMenu 2013-04-16 06:10:29 +02:00
Christian Muehlhaeuser
3a27edbe38 * Fixed TWK-1215: Dupe tracks used to caused playback to loop. 2013-04-16 02:45:07 +02:00
Uwe L. Korn
8771fd6f0e Define QXT_STATIC in header so that parent projects use it. 2013-04-09 12:07:46 +02:00
Christian Muehlhaeuser
c6b82dc30d * Temporary work-around for losing the shared-pointer. 2013-04-09 12:07:25 +02:00
Uwe L. Korn
c3f2eff5cb Compile qxt with QXT_STATIC 2013-04-09 12:07:13 +02:00
Uwe L. Korn
7132ef85cd Fix QxtFifo and move qxtweb modules in their respective folder 2013-04-09 12:06:42 +02:00
Teo Mrnjavac
6bab0f7b6f Hide debug tooltips unless --verbose. 2013-04-08 15:15:21 +02:00
Christian Muehlhaeuser
663d7af61b * Bumped to 0.7. 2013-04-07 19:47:47 +02:00
128 changed files with 768 additions and 2140 deletions

View File

@@ -22,8 +22,8 @@ ELSE()
ENDIF()
SET( TOMAHAWK_VERSION_MAJOR 0 )
SET( TOMAHAWK_VERSION_MINOR 6 )
SET( TOMAHAWK_VERSION_PATCH 99 )
SET( TOMAHAWK_VERSION_MINOR 7 )
SET( TOMAHAWK_VERSION_PATCH 0 )
#SET( TOMAHAWK_VERSION_RC 0 )
SET( TOMAHAWK_TRANSLATION_LANGUAGES ar bg bn_IN ca cs de en el es fi fr hi_IN hu gl it ja lt pl pt_BR ru sv tr zh_CN zh_TW )
@@ -191,8 +191,8 @@ if(PHONON_FOUND)
message(STATUS "Phonon found; ensure that phonon-vlc is at least 0.4")
endif()
macro_optional_find_package(Echonest 2.0.2)
macro_log_feature(ECHONEST_FOUND "Echonest" "Qt library for communicating with The Echo Nest" "http://projects.kde.org/libechonest" TRUE "" "libechonest 2.0.2 is needed for dynamic playlists and the infosystem")
macro_optional_find_package(Echonest 2.0.3)
macro_log_feature(ECHONEST_FOUND "Echonest" "Qt library for communicating with The Echo Nest" "http://projects.kde.org/libechonest" TRUE "" "libechonest 2.0.3 is needed for dynamic playlists and the infosystem")
macro_optional_find_package(CLucene 0.9.23)
macro_log_feature(CLucene_FOUND "CLucene" "The open-source, C++ search engine" "http://clucene.sf.net" TRUE "" "CLucene is used for indexing the collection")
@@ -241,7 +241,7 @@ ENDIF( WIN32 )
#TODO: support external qxt
set(QXTWEB_FOUND TRUE)
set(QXTWEB_LIBRARIES qxtweb-standalone)
set(QXTWEB_INCLUDE_DIRS ${THIRDPARTY_DIR}/qxt/qxtweb-standalone/qxtweb ${CMAKE_CURRENT_BINARY_DIR})
set(QXTWEB_INCLUDE_DIRS ${THIRDPARTY_DIR}/qxt/qxtweb-standalone/web ${THIRDPARTY_DIR}/qxt/qxtweb-standalone/network ${THIRDPARTY_DIR}/qxt/qxtweb-standalone/core ${CMAKE_CURRENT_BINARY_DIR})
### libportfwd
set(LIBPORTFWD_INCLUDE_DIR ${THIRDPARTY_DIR}/libportfwd/include)

View File

@@ -1,4 +1,26 @@
Version 0.7.1:
* Heavily reduced memory footprint during and after indexing the database.
Version 0.7.0:
* JavaScript Resolvers can now expose collections.
* Introduced bundle system for JavaScript Resolvers, called "axes".
* Fixed playback-loop caused by duplicate tracks in a playlist.
* Improved peer handling.
* Taking an account offline now also disconnects associated peers.
* Improved spotify protocol handling.
* Added "Append to Playlist" context menu item.
* User-friendlier collection handling: added info-buttons for artists and
albums. Double-clicking an item expands it now.
* Charts only load on-demand now.
* Fixed Diagnostics information not correctly updating.
* Fixed issue with hidden sidebar and panels.
* Fixed issues with some color schemes.
* Fixed playing files with special characters in the filename.
* Improved stability.
* Added translations for Catalan, Czech, Galician, Greek, Italian and
Chinese.
* (Windows) Smoother and more responsive audio playback.
* (Linux) Fixed grid issues with GTK-styles.
Version 0.6.1:
* Improved stability.

View File

@@ -44,7 +44,7 @@ Required dependencies:
* TagLib 1.6.2 - http://developer.kde.org/~wheeler/taglib.html
* Boost 1.3 - http://www.boost.org/
* CLucene 0.9.23 (0.9.21 will fail) - http://clucene.sourceforge.net/download.shtml
* libechonest 2.0.2 - http://projects.kde.org/projects/playground/libs/libechonest/
* libechonest 2.0.3 - http://projects.kde.org/projects/playground/libs/libechonest/
* Attica 0.4.0 - ftp://ftp.kde.org/pub/kde/stable/attica/
* QuaZip 0.4.3 - http://quazip.sourceforge.net/
* liblastfm 1.0.1 - https://github.com/lastfm/liblastfm/

View File

@@ -16,5 +16,4 @@ Comment[se]=Tomahawk — Den Sociala Musikspelaren
Icon=tomahawk
Terminal=false
Categories=Qt;AudioVideo;Audio;Player;
MimeType=x-scheme-handler/tomahawk;
MimeType=x-scheme-handler/spotify;
MimeType=x-scheme-handler/tomahawk;x-scheme-handler/spotify;

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 822 B

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 77 KiB

After

Width:  |  Height:  |  Size: 93 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.3 KiB

After

Width:  |  Height:  |  Size: 10 KiB

View File

@@ -1,101 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="606.248px" height="595.5px" viewBox="0 0 606.248 595.5" enable-background="new 0 0 606.248 595.5" xml:space="preserve">
<g id="Layer_3">
<rect x="-20.376" y="-15.75" stroke="#E63E30" stroke-miterlimit="10" width="688" height="700"/>
</g>
<g id="Layer_2">
<path id="path9_1_" inkscape:connector-curvature="0" fill="none" stroke="#E63E30" stroke-miterlimit="10" d="M429.977,493.576
V154.483h-65.371h-6.949v115.065L194.409,156.07c-2.608-1.802-6.036-2.046-8.93-0.661c-2.895,1.493-4.692,4.437-4.692,7.56v266.003
c0,3.123,1.798,6.037,4.692,7.488c1.234,0.695,2.608,0.977,3.953,0.977c1.725,0,3.522-0.561,4.977-1.529l163.248-113.472v201.091
c-17.532,4.084-35.744,6.375-54.511,6.375c-130.896,0-237.345-104.598-237.345-233.146c0-128.547,106.442-233.117,237.345-233.117
c130.858,0,237.344,104.562,237.344,233.116C540.482,379.515,496.305,452.197,429.977,493.576L429.977,493.576z"/>
<ellipse fill="#FFFFFF" stroke="#E63E30" stroke-miterlimit="10" cx="318.624" cy="295.75" rx="286" ry="270.5"/>
</g>
<g id="Layer_1" sodipodi:docname="tomahawk-icon.svg" inkscape:version="0.48.2 r9819" 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:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape">
<sodipodi:namedview fit-margin-bottom="0" fit-margin-right="0" inkscape:snap-page="false" inkscape:current-layer="Layer_1" inkscape:window-maximized="0" inkscape:window-y="0" inkscape:window-x="0" inkscape:window-height="794" inkscape:window-width="1440" inkscape:pageshadow="2" inkscape:pageopacity="0" fit-margin-left="0" fit-margin-top="0" inkscape:cy="336.94553" inkscape:cx="431.12548" inkscape:zoom="1" showgrid="false" guidetolerance="10" gridtolerance="10" objecttolerance="10" bordercolor="#666666" pagecolor="#ffffff" borderopacity="1" id="namedview11">
<sodipodi:guide orientation="0,841.89001" position="-3.5728101e-05,-1.501866e-05" id="guide2994"></sodipodi:guide>
<sodipodi:guide orientation="-595.28003,0" position="841.88997,-1.501866e-05" id="guide2996"></sodipodi:guide>
<sodipodi:guide orientation="0,-841.89001" position="841.88997,595.28001" id="guide2998"></sodipodi:guide>
<sodipodi:guide orientation="595.28003,0" position="-3.5728101e-05,595.28001" id="guide3000"></sodipodi:guide>
</sodipodi:namedview>
<g id="g3_1_" transform="matrix(7.3080643,0,0,7.1790074,-2773.1692,-1839.0104)">
<g>
<g enable-background="new ">
<g>
<polygon fill="#4C1410" points="430.355,293.516 429.444,293.909 429.416,279.061 430.323,277.603 "/>
</g>
<g>
<polygon fill="#4C1410" points="430.428,328.913 429.507,326.921 429.457,300.757 430.371,300.856 "/>
</g>
<g enable-background="new ">
<defs>
<path id="SVGID_1_" enable-background="new " d="M440.229,324.595l-1.574-1.692c2.083-1.367,3.987-2.983,5.669-4.803
c1.678-1.816,3.136-3.836,4.332-6.018c1.194-2.177,2.127-4.516,2.758-6.973c0.63-2.454,0.959-5.028,0.945-7.679
c-0.022-4.114-0.867-8.035-2.377-11.605c-1.51-3.571-3.686-6.797-6.372-9.519c-2.69-2.726-5.895-4.949-9.458-6.505
c-3.573-1.561-7.51-2.454-11.652-2.511c-4.16-0.058-8.14,0.731-11.777,2.212c-3.65,1.486-6.953,3.668-9.739,6.386
c-2.795,2.728-5.069,5.995-6.649,9.637c-1.584,3.651-2.47,7.677-2.486,11.907c-0.016,4.236,0.842,8.276,2.405,11.95
c1.563,3.673,3.829,6.974,6.627,9.736c2.794,2.757,6.114,4.973,9.791,6.483c3.666,1.506,7.683,2.31,11.88,2.251
c0.601-0.008,1.197-0.036,1.787-0.08c0.59-0.045,1.176-0.107,1.756-0.186c0.58-0.079,1.154-0.174,1.723-0.286
c0.569-0.111,1.132-0.238,1.689-0.379l0.921,1.992c-0.597,0.152-1.201,0.288-1.81,0.408c-0.61,0.12-1.226,0.223-1.847,0.308
c-0.622,0.085-1.249,0.153-1.883,0.201c-0.634,0.049-1.272,0.078-1.917,0.088c-4.502,0.068-8.811-0.79-12.745-2.404
c-3.946-1.618-7.511-3.994-10.51-6.951c-3.004-2.963-5.437-6.506-7.115-10.448c-1.678-3.944-2.599-8.28-2.58-12.827
c0.018-4.54,0.971-8.86,2.673-12.777c1.698-3.907,4.139-7.409,7.141-10.333c2.99-2.913,6.534-5.249,10.45-6.838
c3.9-1.583,8.167-2.424,12.626-2.357c4.439,0.066,8.656,1.027,12.483,2.703c3.815,1.671,7.247,4.055,10.127,6.976
c2.875,2.916,5.205,6.373,6.822,10.198c1.616,3.824,2.522,8.023,2.548,12.43c0.016,2.84-0.335,5.597-1.008,8.226
c-0.674,2.633-1.672,5.139-2.949,7.472c-1.279,2.338-2.839,4.504-4.636,6.452C444.498,321.393,442.46,323.127,440.229,324.595
z"/>
</defs>
<clipPath id="SVGID_2_">
<use xlink:href="#SVGID_1_" overflow="visible"/>
</clipPath>
<g clip-path="url(#SVGID_2_)" enable-background="new ">
<path fill="#4D1510" d="M440.229,324.595l-1.574-1.692c2.083-1.367,3.987-2.983,5.669-4.803
c1.678-1.816,3.136-3.836,4.332-6.018c1.194-2.177,2.127-4.516,2.758-6.973c0.63-2.454,0.959-5.028,0.945-7.679
c-0.022-4.114-0.867-8.035-2.377-11.605c-1.51-3.571-3.686-6.797-6.372-9.519c-2.69-2.726-5.895-4.949-9.458-6.505
c-3.573-1.561-7.51-2.454-11.652-2.511c-4.16-0.058-8.14,0.731-11.777,2.212c-3.65,1.486-6.953,3.668-9.739,6.386
c-2.795,2.728-5.069,5.995-6.649,9.637c-1.584,3.651-2.47,7.677-2.486,11.907c-0.016,4.236,0.842,8.276,2.405,11.95
c1.563,3.673,3.829,6.974,6.627,9.736c2.794,2.757,6.114,4.973,9.791,6.483c3.666,1.506,7.683,2.31,11.88,2.251
c0.601-0.008,1.197-0.036,1.787-0.08c0.59-0.045,1.176-0.107,1.756-0.186c0.58-0.079,1.154-0.174,1.723-0.286
c0.569-0.111,1.132-0.238,1.689-0.379l0.921,1.992c-0.597,0.152-1.201,0.288-1.81,0.408c-0.61,0.12-1.226,0.223-1.847,0.308
c-0.622,0.085-1.249,0.153-1.883,0.201c-0.634,0.049-1.272,0.078-1.917,0.088c-4.502,0.068-8.811-0.79-12.745-2.404
c-3.946-1.618-7.511-3.994-10.51-6.951c-3.004-2.963-5.437-6.506-7.115-10.448c-1.678-3.944-2.599-8.28-2.58-12.827
c0.018-4.54,0.971-8.86,2.673-12.777c1.698-3.907,4.139-7.409,7.141-10.333c2.99-2.913,6.534-5.249,10.45-6.838
c3.9-1.583,8.167-2.424,12.626-2.357c4.439,0.066,8.656,1.027,12.483,2.703c3.815,1.671,7.247,4.055,10.127,6.976
c2.875,2.916,5.205,6.373,6.822,10.198c1.616,3.824,2.522,8.023,2.548,12.43c0.016,2.84-0.335,5.597-1.008,8.226
c-0.674,2.633-1.672,5.139-2.949,7.472c-1.279,2.338-2.839,4.504-4.636,6.452C444.498,321.393,442.46,323.127,440.229,324.595
"/>
</g>
</g>
<g>
<path fill="#E53E30" d="M422.903,256.08c5.658,0.108,11.029,1.353,15.897,3.504c4.85,2.143,9.21,5.189,12.868,8.915
c3.65,3.717,6.608,8.117,8.662,12.983c2.053,4.864,3.208,10.204,3.247,15.808c0.04,5.613-1.043,10.98-3.039,15.883
c-2.002,4.92-4.927,9.381-8.565,13.159c-3.654,3.795-8.032,6.904-12.92,9.095c-4.913,2.202-10.344,3.478-16.073,3.588
c-5.761,0.111-11.281-0.966-16.325-3.014c-5.064-2.056-9.641-5.088-13.494-8.871c-3.862-3.792-6.988-8.332-9.143-13.388
c-2.155-5.058-3.333-10.622-3.302-16.455c0.032-5.822,1.266-11.358,3.463-16.374c2.19-4.998,5.333-9.474,9.191-13.203
c3.84-3.711,8.385-6.68,13.4-8.69C411.761,257.022,417.213,255.972,422.903,256.08z M440.229,324.595
c2.231-1.468,4.269-3.201,6.069-5.154c1.796-1.948,3.356-4.114,4.636-6.452c1.277-2.334,2.274-4.84,2.949-7.472
c0.673-2.629,1.024-5.386,1.008-8.226c-0.025-4.407-0.931-8.606-2.548-12.43c-1.617-3.826-3.947-7.282-6.822-10.198
c-2.88-2.922-6.312-5.305-10.127-6.976c-3.827-1.676-8.044-2.637-12.483-2.703c-4.458-0.067-8.726,0.775-12.626,2.357
c-3.916,1.589-7.46,3.925-10.45,6.838c-3.001,2.924-5.443,6.426-7.141,10.333c-1.702,3.917-2.655,8.237-2.673,12.777
c-0.018,4.547,0.902,8.884,2.58,12.827c1.678,3.942,4.111,7.485,7.115,10.448c2.999,2.957,6.563,5.333,10.51,6.951
c3.934,1.613,8.243,2.471,12.745,2.404c0.644-0.01,1.283-0.039,1.917-0.088c0.633-0.049,1.261-0.116,1.883-0.201
c0.622-0.085,1.237-0.188,1.847-0.308c0.609-0.12,1.213-0.256,1.81-0.408l-0.057-28.056l-22.413,16.004
c-0.051,0.034-0.104,0.065-0.16,0.092c-0.056,0.027-0.113,0.05-0.172,0.069c-0.059,0.019-0.119,0.033-0.179,0.043
c-0.06,0.01-0.121,0.016-0.18,0.016c-0.047,0.001-0.094-0.001-0.141-0.006c-0.047-0.005-0.094-0.012-0.14-0.023
c-0.046-0.011-0.092-0.024-0.137-0.041c-0.045-0.017-0.089-0.037-0.132-0.061c-0.1-0.05-0.191-0.114-0.271-0.187
c-0.08-0.074-0.149-0.157-0.205-0.249c-0.056-0.091-0.1-0.19-0.13-0.294s-0.045-0.212-0.045-0.322l0.06-37.332
c0-0.109,0.016-0.216,0.046-0.319c0.03-0.103,0.074-0.201,0.13-0.292c0.056-0.091,0.125-0.175,0.205-0.249
c0.08-0.074,0.17-0.138,0.27-0.189c0.043-0.02,0.087-0.038,0.131-0.053s0.09-0.028,0.135-0.037
c0.046-0.01,0.091-0.017,0.137-0.022c0.046-0.005,0.092-0.007,0.138-0.006c0.061,0.001,0.122,0.006,0.183,0.015
c0.06,0.01,0.12,0.024,0.178,0.042c0.058,0.018,0.116,0.042,0.171,0.069c0.055,0.027,0.109,0.059,0.161,0.095l22.346,15.897
l-0.033-15.913l0.94,0.009l8.801,0.081L440.229,324.595"/>
</g>
</g>
</g>
</g>
</g>
</svg>
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg width="3100px" height="3100px" viewBox="0 0 3100 3100" 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>tomahawk-icon</title>
<description>Created with Sketch (http://www.bohemiancoding.com/sketch)</description>
<defs>
<filter x="-20%" y="-20%" width="140%" height="140%" filterUnits="objectBoundingBox" id="filter-1">
<feOffset dx="0" dy="2" in="SourceAlpha" result="shadowOffsetOuter1"></feOffset>
<feGaussianBlur stdDeviation="2" in="shadowOffsetOuter1" result="shadowBlurOuter1"></feGaussianBlur>
<feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.7 0" in="shadowBlurOuter1" type="matrix" result="shadowMatrixOuter1"></feColorMatrix>
<feMerge>
<feMergeNode in="shadowMatrixOuter1"></feMergeNode>
<feMergeNode in="SourceGraphic"></feMergeNode>
</feMerge>
</filter>
</defs>
<g id="Page 1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
<circle d="M1546,3051 C2374.42717,3051 3046,2379.42717 3046,1551 C3046,722.572834 2374.42717,51 1546,51 C717.572834,51 46,722.572834 46,1551 C46,2379.42717 717.572834,3051 1546,3051 Z M1546,3051" id="Oval 1" stroke="#979797" fill="#FFFFFF" sketch:type="MSShapeGroup" cx="1546" cy="1551" r="1500"></circle>
<g id="Group" sketch:type="MSLayerGroup" transform="translate(46.000000, 48.000000)">
<path d="M1500.00026,326.562245 C852.333832,326.562245 325.623384,853.409757 325.623384,1501.14467 C325.623384,2148.84339 852.297589,2675.87184 1500.00026,2675.87184 C1592.85982,2675.87184 1682.97119,2664.36909 1769.71967,2643.75096 L1769.71967,1630.53256 L961.97175,2202.26973 C954.812018,2207.15297 945.916593,2209.9744 937.346612,2209.9744 C930.693123,2209.9744 923.894994,2208.56369 917.783909,2205.05499 C903.464446,2197.74821 894.56902,2183.06231 894.56902,2167.32742 L894.56902,827.040286 C894.56902,811.305396 903.464445,796.474809 917.783909,788.951001 C932.103373,781.969774 949.026377,783.199628 961.97175,792.27884 L1769.71967,1364.0522 L1769.71967,784.284802 L1804.10807,784.284802 L2127.56161,784.284802 L2127.56161,2492.84071 C2455.75216,2284.34437 2674.34095,1918.13743 2674.34095,1501.10857 C2674.34087,853.446289 2147.48558,326.562245 1500.00026,326.562245 L1500.00026,326.562245 L1500.00026,326.562245" id="Shape" sketch:type="MSShapeGroup"></path>
<path d="M1500.00026,0.904613118 C672.870406,0.904613118 0.0726019231,673.923876 0.0726019231,1501.14494 C0.0726019231,2328.4023 672.870475,3001.38527 1500.00026,3001.38527 C2326.94928,3001.38527 2999.78337,2328.40223 2999.78337,1501.14494 C2999.74681,673.923394 2326.94887,0.904613118 1500.00026,0.904613118 L1500.00026,0.904613118 L1500.00026,0.904613118 M2127.56146,2492.87708 L2127.56146,784.321176 L1804.10793,784.321176 L1769.71952,784.321176 L1769.71952,1364.08857 L961.971604,792.315213 C949.06239,783.236001 932.103228,782.006148 917.783763,788.987375 C903.464299,796.511184 894.568874,811.341769 894.568874,827.076659 L894.568874,2167.3638 C894.568874,2183.09869 903.464299,2197.78459 917.783763,2205.09136 C923.894847,2208.60006 930.692976,2210.01078 937.346465,2210.01078 C945.880287,2210.01078 954.775711,2207.18935 961.971604,2202.30611 L1769.71952,1630.56893 L1769.71952,2643.78733 C1682.97105,2664.36929 1592.85968,2675.90821 1500.00012,2675.90821 C852.333686,2675.90821 325.623237,2148.87983 325.623237,1501.18104 C325.623237,853.482317 852.297442,326.598618 1500.00012,326.598618 C2147.48578,326.598618 2674.377,853.44613 2674.377,1501.18104 C2674.34087,1918.17376 2455.75215,2284.38047 2127.56146,2492.87708 L2072.56159,399.051282" id="Shape" fill="#E63E30" filter="url(#filter-1)" sketch:type="MSShapeGroup"></path>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 9.1 KiB

After

Width:  |  Height:  |  Size: 3.9 KiB

View File

@@ -1,16 +1,23 @@
<!DOCTYPE RCC><RCC version="1.0">
<qresource prefix="/lang">
<file>tomahawk_en.qm</file>
<file>tomahawk_ar.qm</file>
<file>tomahawk_bg.qm</file>
<file>tomahawk_ca.qm</file>
<file>tomahawk_ca@valencia.qm</file>
<file>tomahawk_cs.qm</file>
<file>tomahawk_de.qm</file>
<file>tomahawk_el.qm</file>
<file>tomahawk_en.qm</file>
<file>tomahawk_es.qm</file>
<file>tomahawk_fi.qm</file>
<file>tomahawk_fr.qm</file>
<file>tomahawk_gl.qm</file>
<file>tomahawk_it.qm</file>
<file>tomahawk_ja.qm</file>
<file>tomahawk_pl.qm</file>
<file>tomahawk_pt_BR.qm</file>
<file>tomahawk_ru.qm</file>
<file>tomahawk_es.qm</file>
<file>tomahawk_sv.qm</file>
<file>tomahawk_ja.qm</file>
<file>tomahawk_ar.qm</file>
<file>tomahawk_zh_CN.qm</file>
</qresource>
</RCC>

View File

@@ -64,7 +64,6 @@ AudioControls::AudioControls( QWidget* parent )
ui->artistTrackLabel->setFont( font );
ui->artistTrackLabel->setElideMode( Qt::ElideMiddle );
ui->artistTrackLabel->setType( QueryLabel::Track );
ui->artistTrackLabel->setJumpLinkVisible( true );
font.setPointSize( TomahawkUtils::defaultFontSize() );
ui->albumLabel->setFont( font );
@@ -74,7 +73,7 @@ AudioControls::AudioControls( QWidget* parent )
queryLabelsPalette.setColor( QPalette::Foreground, Qt::black );
ui->artistTrackLabel->setPalette( queryLabelsPalette );
ui->albumLabel->setPalette( queryLabelsPalette );
font.setWeight( QFont::Normal );
ui->timeLabel->setFont( font );
ui->timeLeftLabel->setFont( font );
@@ -136,7 +135,6 @@ AudioControls::AudioControls( QWidget* parent )
connect( ui->loveButton, SIGNAL( clicked( bool ) ), SLOT( onLoveButtonClicked( bool ) ) );
connect( ui->ownerButton, SIGNAL( clicked() ), SLOT( onOwnerButtonClicked() ) );
// <From AudioEngine>
connect( AudioEngine::instance(), SIGNAL( loading( Tomahawk::result_ptr ) ), SLOT( onPlaybackLoading( Tomahawk::result_ptr ) ) );
connect( AudioEngine::instance(), SIGNAL( started( Tomahawk::result_ptr ) ), SLOT( onPlaybackStarted( Tomahawk::result_ptr ) ) );
connect( AudioEngine::instance(), SIGNAL( paused() ), SLOT( onPlaybackPaused() ) );
@@ -149,6 +147,8 @@ AudioControls::AudioControls( QWidget* parent )
connect( AudioEngine::instance(), SIGNAL( repeatModeChanged( Tomahawk::PlaylistModes::RepeatMode ) ), SLOT( onRepeatModeChanged( Tomahawk::PlaylistModes::RepeatMode ) ) );
connect( AudioEngine::instance(), SIGNAL( shuffleModeChanged( bool ) ), SLOT( onShuffleModeChanged( bool ) ) );
connect( ViewManager::instance(), SIGNAL( viewPageDestroyed() ), SLOT( onControlStateChanged() ) );
ui->buttonAreaLayout->setSpacing( 0 );
ui->stackedLayout->setSpacing( 0 );
ui->stackedLayout->setContentsMargins( 0, 0, 0, 0 );
@@ -216,6 +216,10 @@ AudioControls::onControlStateChanged()
ui->prevButton->setEnabled( AudioEngine::instance()->canGoPrevious() );
ui->nextButton->setEnabled( AudioEngine::instance()->canGoNext() );
// If the ViewManager doesn't know a page for the current interface, we can't offer the jump link
ui->artistTrackLabel->setJumpLinkVisible( AudioEngine::instance()->currentTrackPlaylist()
&& ViewManager::instance()->pageForInterface( AudioEngine::instance()->currentTrackPlaylist() ) );
}
@@ -239,7 +243,7 @@ AudioControls::onPlaybackStarted( const Tomahawk::result_ptr& result )
ui->timeLabel->setText( TomahawkUtils::timeToString( 0 ) );
ui->timeLeftLabel->setText( "-" + TomahawkUtils::timeToString( 0 ) );
m_sliderTimeLine.setDuration( duration );
m_sliderTimeLine.setFrameRange( 0, duration );
m_sliderTimeLine.setCurveShape( QTimeLine::LinearCurve );
@@ -299,9 +303,6 @@ AudioControls::onPlaybackLoading( const Tomahawk::result_ptr& result )
ui->seekSlider->setVisible( true );
m_sliderTimeLine.stop();
// If the ViewManager doesn't know a page for the current interface, we can't offer the jump link
ui->artistTrackLabel->setJumpLinkVisible( ( ViewManager::instance()->pageForInterface( AudioEngine::instance()->currentTrackPlaylist() ) ) );
onControlStateChanged();
QPixmap sourceIcon = result->sourceIcon( TomahawkUtils::RoundedCorners, ui->ownerButton->size() );

View File

@@ -107,7 +107,6 @@ TomahawkWindow::TomahawkWindow( QWidget* parent )
#endif
, ui( new Ui::TomahawkWindow )
, m_searchWidget( 0 )
, m_audioControls( new AudioControls( this ) )
, m_trayIcon( new TomahawkTrayIcon( this ) )
, m_settingsDialog( 0 )
, m_audioRetryCounter( 0 )
@@ -115,6 +114,8 @@ TomahawkWindow::TomahawkWindow( QWidget* parent )
setWindowIcon( QIcon( RESPATH "icons/tomahawk-icon-128x128.png" ) );
ViewManager* vm = new ViewManager( this );
m_audioControls = new AudioControls( this );
connect( vm, SIGNAL( showQueueRequested() ), SLOT( showQueue() ) );
connect( vm, SIGNAL( hideQueueRequested() ), SLOT( hideQueue() ) );
connect( APP, SIGNAL( tomahawkLoaded() ), vm, SLOT( setTomahawkLoaded() ) ); // Pass loaded signal into libtomahawk so components in there can connect to ViewManager
@@ -415,6 +416,7 @@ TomahawkWindow::setupSideBar()
ui->splitter->addWidget( sidebarWidget );
ui->splitter->addWidget( ViewManager::instance()->widget() );
ui->splitter->setCollapsible( 0, false );
ui->splitter->setCollapsible( 1, false );
ActionCollection::instance()->getAction( "showOfflineSources" )

View File

@@ -880,7 +880,7 @@ XmppSipPlugin::onNewIq( const Jreen::IQ& iq )
handlePeerStatus( jid, Jreen::Presence::Available );
}
}
else if ( context == RequestVersion)
else if ( context == RequestVersion )
{
Jreen::SoftwareVersion::Ptr softwareVersion = iq.payload<Jreen::SoftwareVersion>();
if ( softwareVersion )
@@ -932,7 +932,7 @@ XmppSipPlugin::onNewIq( const Jreen::IQ& iq )
Tomahawk::peerinfo_ptr peerInfo = PeerInfo::get( this, iq.from().full() );
if ( peerInfo.isNull() )
{
tDebug() << Q_FUNC_INFO << "no valid peerInfo for " << iq.from().full();
tDebug() << Q_FUNC_INFO << "no valid peerInfo for" << iq.from().full();
return;
}
peerInfo->setSipInfo( info );

View File

@@ -162,6 +162,7 @@ ZeroconfPlugin::lanHostFound( const QString& host, int port, const QString& name
Tomahawk::peerinfo_ptr peerInfo = Tomahawk::PeerInfo::get( this, host, Tomahawk::PeerInfo::AutoCreate );
peerInfo->setSipInfo( sipInfo );
peerInfo->setContactId( host );
peerInfo->setFriendlyName( name );
peerInfo->setType( PeerInfo::Local );
peerInfo->setStatus( PeerInfo::Online );

View File

@@ -78,6 +78,10 @@ ContextMenu::addToPlaylist( int playlistIdx )
playlist->addEntries( m_queries, playlist->currentrevision() );
}
bool caseInsensitiveLessThan(Tomahawk::playlist_ptr &s1, Tomahawk::playlist_ptr &s2)
{
return s1->title().toLower() < s2->title().toLower();
}
void
ContextMenu::setQueries( const QList<Tomahawk::query_ptr>& queries )
@@ -97,7 +101,9 @@ ContextMenu::setQueries( const QList<Tomahawk::query_ptr>& queries )
if ( m_supportedActions & ActionPlaylist ) {
// Get the current list of all playlists.
m_playlists = SourceList::instance()->getLocal()->dbCollection()->playlists();
m_playlists = QList< Tomahawk::playlist_ptr >( SourceList::instance()->getLocal()->dbCollection()->playlists() );
// Sort the playlist
qSort( m_playlists.begin(), m_playlists.end(), caseInsensitiveLessThan );
m_playlists_sigmap = new QSignalMapper( this );
// Build the menu listing all available playlists

View File

@@ -58,8 +58,8 @@ PlaylistInterface::nextResult() const
}
Tomahawk::result_ptr
PlaylistInterface::siblingResult( int itemsAway, qint64 rootIndex ) const
qint64
PlaylistInterface::siblingResultIndex( int itemsAway, qint64 rootIndex ) const
{
qint64 idx = siblingIndex( itemsAway, rootIndex );
QList< qint64 > safetyCheck;
@@ -73,12 +73,49 @@ PlaylistInterface::siblingResult( int itemsAway, qint64 rootIndex ) const
if ( query && query->playable() )
{
return query->results().first();
return idx;
}
idx = siblingIndex( itemsAway < 0 ? -1 : 1, idx );
}
return -1;
}
Tomahawk::result_ptr
PlaylistInterface::siblingResult( int itemsAway, qint64 rootIndex ) const
{
qint64 idx = siblingResultIndex( itemsAway, rootIndex );
if ( idx >= 0 )
{
Tomahawk::query_ptr query = queryAt( idx );
if ( query && query->playable() )
{
return query->results().first();
}
}
return Tomahawk::result_ptr();
}
Tomahawk::result_ptr
PlaylistInterface::setSiblingResult( int itemsAway, qint64 rootIndex )
{
qint64 idx = siblingResultIndex( itemsAway, rootIndex );
if ( idx >= 0 )
{
Tomahawk::query_ptr query = queryAt( idx );
if ( query && query->playable() )
{
setCurrentIndex( idx );
return query->results().first();
}
}
return Tomahawk::result_ptr();
}

View File

@@ -53,8 +53,10 @@ public:
virtual Tomahawk::result_ptr previousResult() const;
virtual qint64 siblingIndex( int itemsAway, qint64 rootIndex = -1 ) const = 0;
virtual qint64 siblingResultIndex( int itemsAway, qint64 rootIndex = -1 ) const;
virtual Tomahawk::result_ptr siblingResult( int itemsAway, qint64 rootIndex = -1 ) const;
virtual Tomahawk::result_ptr setSiblingResult( int itemsAway, qint64 rootIndex = -1 );
virtual Tomahawk::result_ptr resultAt( qint64 index ) const = 0;
virtual Tomahawk::query_ptr queryAt( qint64 index ) const = 0;
virtual qint64 indexOfResult( const Tomahawk::result_ptr& result ) const = 0;

View File

@@ -61,6 +61,7 @@ namespace Tomahawk
typedef QSharedPointer<Album> album_ptr;
typedef QWeakPointer<Album> album_wptr;
typedef QSharedPointer<PeerInfo> peerinfo_ptr;
typedef QWeakPointer<PeerInfo> peerinfo_wptr;
typedef QSharedPointer<DynamicControl> dyncontrol_ptr;
typedef QSharedPointer<GeneratorInterface> geninterface_ptr;

View File

@@ -508,6 +508,7 @@ ViewManager::destroyPage( ViewPage* page )
return;
tDebug() << Q_FUNC_INFO << "Deleting page:" << page->title();
if ( historyPages().contains( page ) )
{
m_pageHistoryBack.removeAll( page );
@@ -523,6 +524,10 @@ ViewManager::destroyPage( ViewPage* page )
historyBack();
}
emit viewPageAboutToBeDestroyed( page );
delete page;
emit viewPageDestroyed();
}
@@ -659,6 +664,7 @@ ViewManager::onWidgetDestroyed( QWidget* widget )
m_pageHistoryBack.removeAll( page );
m_pageHistoryFwd.removeAll( page );
break;
}
m_stack->removeWidget( widget );

View File

@@ -17,8 +17,8 @@
* along with Tomahawk. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef PLAYLISTMANAGER_H
#define PLAYLISTMANAGER_H
#ifndef VIEWMANAGER_H
#define VIEWMANAGER_H
#include <QObject>
#include <QHash>
@@ -115,6 +115,8 @@ signals:
void tempPageActivated( Tomahawk::ViewPage* );
void viewPageActivated( Tomahawk::ViewPage* );
void viewPageAboutToBeDestroyed( Tomahawk::ViewPage* );
void viewPageDestroyed();
void showQueueRequested();
void hideQueueRequested();
@@ -206,4 +208,4 @@ private:
static ViewManager* s_instance;
};
#endif // PLAYLISTMANAGER_H
#endif // VIEWMANAGER_H

View File

@@ -45,7 +45,6 @@
#include <QtCore/QUrl>
#include <QDir>
#include <QtNetwork/QNetworkReply>
#include <QTemporaryFile>
using namespace Tomahawk;
@@ -72,6 +71,7 @@ AudioEngine::AudioEngine()
, m_expectStop( false )
, m_waitingOnNewTrack( false )
, m_state( Stopped )
, m_coverTempFile( 0 )
{
s_instance = this;
tDebug() << "Init AudioEngine";
@@ -395,24 +395,23 @@ AudioEngine::onNowPlayingInfoReady( const Tomahawk::InfoSystem::InfoType type )
{
playInfo["cover"] = cover;
QTemporaryFile* coverTempFile = new QTemporaryFile( QDir::toNativeSeparators( QDir::tempPath() + "/" + m_currentTrack->artist()->name() + "_" + m_currentTrack->album()->name() + "_tomahawk_cover.png" ) );
if ( !coverTempFile->open() )
delete m_coverTempFile;
m_coverTempFile = new QTemporaryFile( QDir::toNativeSeparators( QDir::tempPath() + "/" + m_currentTrack->artist()->name() + "_" + m_currentTrack->album()->name() + "_tomahawk_cover.png" ) );
if ( !m_coverTempFile->open() )
{
tDebug() << Q_FUNC_INFO << "WARNING: could not write temporary file for cover art!";
}
else
{
// Finally, save the image to the new temp file
coverTempFile->setAutoRemove( false );
if ( cover.save( coverTempFile, "PNG" ) )
if ( cover.save( m_coverTempFile, "PNG" ) )
{
tDebug() << Q_FUNC_INFO << "Saving cover image to:" << QFileInfo( *coverTempFile ).absoluteFilePath();
playInfo["coveruri"] = QFileInfo( *coverTempFile ).absoluteFilePath();
tDebug() << Q_FUNC_INFO << "Saving cover image to:" << QFileInfo( *m_coverTempFile ).absoluteFilePath();
playInfo["coveruri"] = QFileInfo( *m_coverTempFile ).absoluteFilePath();
}
else
tDebug() << Q_FUNC_INFO << "Failed to save cover image!";
}
delete coverTempFile;
}
else
tDebug() << Q_FUNC_INFO << "Cover from query is null!";
@@ -504,12 +503,11 @@ AudioEngine::performLoadTrack( const Tomahawk::result_ptr& result, QSharedPointe
else
{
QString furl = m_currentTrack->url();
#ifdef Q_WS_WIN
if ( furl.startsWith( "file://" ) )
furl = furl.right( furl.length() - 7 );
#endif
tLog( LOGVERBOSE ) << "Passing to Phonon:" << furl << furl.toLatin1();
m_mediaObject->setCurrentSource( furl );
tLog( LOGVERBOSE ) << "Passing to Phonon:" << QUrl::fromLocalFile( furl );
m_mediaObject->setCurrentSource( QUrl::fromLocalFile( furl ) );
}
m_mediaObject->currentSource().setAutoDelete( true );
@@ -558,7 +556,7 @@ AudioEngine::loadPreviousTrack()
Tomahawk::result_ptr result;
if ( m_playlist.data()->previousResult() )
{
result = m_playlist.data()->previousResult();
result = m_playlist.data()->setSiblingResult( -1 );
m_currentTrackPlaylist = m_playlist;
}
@@ -599,7 +597,7 @@ AudioEngine::loadNextTrack()
if ( m_playlist.data()->nextResult() )
{
result = m_playlist.data()->nextResult();
result = m_playlist.data()->setSiblingResult( 1 );
m_currentTrackPlaylist = m_playlist;
}
}
@@ -1004,7 +1002,7 @@ AudioEngine::setCurrentTrack( const Tomahawk::result_ptr& result )
if ( result )
{
if ( m_playlist )
if ( m_playlist && m_playlist->currentItem() != result )
{
m_playlist->setCurrentIndex( m_playlist->indexOfResult( result ) );
}

View File

@@ -35,6 +35,7 @@
#include <QtCore/QObject>
#include <QtCore/QTimer>
#include <QtCore/QQueue>
#include <QTemporaryFile>
class DLLEXPORT AudioEngine : public QObject
@@ -178,6 +179,8 @@ private:
uint_fast8_t m_underrunCount;
bool m_underrunNotified;
QTemporaryFile* m_coverTempFile;
static AudioEngine* s_instance;
};

View File

@@ -1,6 +1,6 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2010-2011, Christian Muehlhaeuser <muesli@tomahawk-player.org>
* Copyright 2010-2013, Christian Muehlhaeuser <muesli@tomahawk-player.org>
* Copyright 2012 Leo Franchi <lfranchi@kde.org>
*
* Tomahawk is free software: you can redistribute it and/or modify
@@ -49,6 +49,8 @@ DatabaseCommand_UpdateSearchIndex::DatabaseCommand_UpdateSearchIndex()
DatabaseCommand_UpdateSearchIndex::~DatabaseCommand_UpdateSearchIndex()
{
tDebug() << Q_FUNC_INFO;
#ifndef ENABLE_HEADLESS
if ( ! m_statusJob.isNull() )
m_statusJob.data()->done();
@@ -61,35 +63,30 @@ DatabaseCommand_UpdateSearchIndex::exec( DatabaseImpl* db )
{
db->m_fuzzyIndex->beginIndexing();
QMap< unsigned int, QMap< QString, QString > > data;
TomahawkSqlQuery q = db->newquery();
q.exec( "SELECT track.id, track.name, artist.name, artist.id FROM track, artist WHERE artist.id = track.artist" );
while ( q.next() )
{
QMap< QString, QString > track;
track.insert( "track", q.value( 1 ).toString() );
track.insert( "artist", q.value( 2 ).toString() );
track.insert( "artistid", q.value( 3 ).toString() );
IndexData ida;
ida.id = q.value( 0 ).toUInt();
ida.artistId = q.value( 3 ).toUInt();
ida.track = q.value( 1 ).toString();
ida.artist = q.value( 2 ).toString();
data.insert( q.value( 0 ).toUInt(), track );
db->m_fuzzyIndex->appendFields( ida );
}
db->m_fuzzyIndex->appendFields( data );
data.clear();
q.exec( "SELECT album.id, album.name FROM album" );
while ( q.next() )
{
QMap< QString, QString > album;
album.insert( "album", q.value( 1 ).toString() );
IndexData ida;
ida.id = q.value( 0 ).toUInt();
ida.album = q.value( 1 ).toString();
data.insert( q.value( 0 ).toUInt(), album );
db->m_fuzzyIndex->appendFields( ida );
}
db->m_fuzzyIndex->appendFields( data );
qDebug() << "Building index finished.";
tDebug( LOGVERBOSE ) << "Building index finished.";
db->m_fuzzyIndex->endIndexing();
}

View File

@@ -1,6 +1,6 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2010-2011, Christian Muehlhaeuser <muesli@tomahawk-player.org>
* Copyright 2010-2013, Christian Muehlhaeuser <muesli@tomahawk-player.org>
*
* Tomahawk is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -25,6 +25,15 @@
class IndexingJobItem;
struct IndexData
{
unsigned int id;
unsigned int artistId;
QString artist;
QString album;
QString track;
};
class DLLEXPORT DatabaseCommand_UpdateSearchIndex : public DatabaseCommand
{
Q_OBJECT

View File

@@ -1,6 +1,6 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2010-2011, Christian Muehlhaeuser <muesli@tomahawk-player.org>
* Copyright 2010-2013, Christian Muehlhaeuser <muesli@tomahawk-player.org>
*
* Tomahawk is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -24,7 +24,6 @@
#include <CLucene.h>
#include <CLucene/queryParser/MultiFieldQueryParser.h>
#include "DatabaseCommand_UpdateSearchIndex.h"
#include "DatabaseImpl.h"
#include "Database.h"
#include "utils/TomahawkUtils.h"
@@ -112,10 +111,11 @@ FuzzyIndex::beginIndexing()
try
{
qDebug() << Q_FUNC_INFO << "Starting indexing.";
tDebug( LOGVERBOSE ) << Q_FUNC_INFO << "Starting indexing.";
if ( m_luceneReader != 0 )
{
qDebug() << "Deleting old lucene stuff.";
tDebug( LOGVERBOSE ) << "Deleting old lucene stuff.";
m_luceneSearcher->close();
m_luceneReader->close();
delete m_luceneSearcher;
@@ -124,8 +124,8 @@ FuzzyIndex::beginIndexing()
m_luceneReader = 0;
}
qDebug() << "Creating new index writer.";
IndexWriter luceneWriter( m_luceneDir, m_analyzer, true );
tDebug( LOGVERBOSE ) << "Creating new index writer.";
m_luceneWriter = new IndexWriter( m_luceneDir, m_analyzer, true );
}
catch( CLuceneError& error )
{
@@ -138,62 +138,52 @@ FuzzyIndex::beginIndexing()
void
FuzzyIndex::endIndexing()
{
m_luceneWriter->optimize();
m_luceneWriter->close();
delete m_luceneWriter;
m_luceneWriter = 0;
m_mutex.unlock();
emit indexReady();
}
void
FuzzyIndex::appendFields( const QMap< unsigned int, QMap< QString, QString > >& trackData )
FuzzyIndex::appendFields( const IndexData& data )
{
try
{
tDebug() << "Appending to index:" << trackData.count();
bool create = !IndexReader::indexExists( TomahawkUtils::appDataDir().absoluteFilePath( "tomahawk.lucene" ).toStdString().c_str() );
IndexWriter luceneWriter( m_luceneDir, m_analyzer, create );
Document doc;
QMapIterator< unsigned int, QMap< QString, QString > > it( trackData );
while ( it.hasNext() )
if ( !data.track.isEmpty() )
{
it.next();
unsigned int id = it.key();
QMap< QString, QString > values = it.value();
doc.add( *( _CLNEW Field( _T( "fulltext" ), DatabaseImpl::sortname( QString( "%1 %2" ).arg( data.artist ).arg( data.track ) ).toStdWString().c_str(),
Field::STORE_NO | Field::INDEX_UNTOKENIZED ) ) );
if ( values.contains( "track" ) )
{
doc.add( *( _CLNEW Field( _T( "fulltext" ), DatabaseImpl::sortname( QString( "%1 %2" ).arg( values.value( "artist" ) ).arg( values.value( "track" ) ) ).toStdWString().c_str(),
Field::STORE_NO | Field::INDEX_UNTOKENIZED ) ) );
doc.add( *( _CLNEW Field( _T( "track" ), DatabaseImpl::sortname( data.track ).toStdWString().c_str(),
Field::STORE_NO | Field::INDEX_UNTOKENIZED ) ) );
doc.add( *( _CLNEW Field( _T( "track" ), DatabaseImpl::sortname( values.value( "track" ) ).toStdWString().c_str(),
Field::STORE_NO | Field::INDEX_UNTOKENIZED ) ) );
doc.add( *( _CLNEW Field( _T( "artist" ), DatabaseImpl::sortname( data.artist ).toStdWString().c_str(),
Field::STORE_NO | Field::INDEX_UNTOKENIZED ) ) );
doc.add( *( _CLNEW Field( _T( "artist" ), DatabaseImpl::sortname( values.value( "artist" ) ).toStdWString().c_str(),
Field::STORE_NO | Field::INDEX_UNTOKENIZED ) ) );
doc.add( *( _CLNEW Field( _T( "artistid" ), QString::number( data.artistId ).toStdWString().c_str(),
Field::STORE_YES | Field::INDEX_NO ) ) );
doc.add( *( _CLNEW Field( _T( "artistid" ), values.value( "artistid" ).toStdWString().c_str(),
Field::STORE_YES | Field::INDEX_NO ) ) );
doc.add( *( _CLNEW Field( _T( "trackid" ), QString::number( id ).toStdWString().c_str(),
Field::STORE_YES | Field::INDEX_NO ) ) );
}
else if ( values.contains( "album" ) )
{
doc.add( *( _CLNEW Field( _T( "album" ), DatabaseImpl::sortname( values.value( "album" ) ).toStdWString().c_str(),
Field::STORE_NO | Field::INDEX_UNTOKENIZED ) ) );
doc.add( *( _CLNEW Field( _T( "albumid" ), QString::number( id ).toStdWString().c_str(),
Field::STORE_YES | Field::INDEX_NO ) ) );
}
else
Q_ASSERT( false );
luceneWriter.addDocument( &doc );
doc.clear();
doc.add( *( _CLNEW Field( _T( "trackid" ), QString::number( data.id ).toStdWString().c_str(),
Field::STORE_YES | Field::INDEX_NO ) ) );
}
else if ( !data.album.isEmpty() )
{
doc.add( *( _CLNEW Field( _T( "album" ), DatabaseImpl::sortname( data.album ).toStdWString().c_str(),
Field::STORE_NO | Field::INDEX_UNTOKENIZED ) ) );
luceneWriter.optimize();
luceneWriter.close();
doc.add( *( _CLNEW Field( _T( "albumid" ), QString::number( data.id ).toStdWString().c_str(),
Field::STORE_YES | Field::INDEX_NO ) ) );
}
else
Q_ASSERT( false );
m_luceneWriter->addDocument( &doc );
}
catch( CLuceneError& error )
{
@@ -223,7 +213,7 @@ FuzzyIndex::search( const Tomahawk::query_ptr& query )
{
if ( !IndexReader::indexExists( TomahawkUtils::appDataDir().absoluteFilePath( "tomahawk.lucene" ).toStdString().c_str() ) )
{
qDebug() << Q_FUNC_INFO << "index didn't exist.";
tDebug( LOGVERBOSE ) << Q_FUNC_INFO << "index didn't exist.";
return resultsmap;
}
@@ -314,7 +304,7 @@ FuzzyIndex::searchAlbum( const Tomahawk::query_ptr& query )
{
if ( !IndexReader::indexExists( TomahawkUtils::appDataDir().absoluteFilePath( "tomahawk.lucene" ).toStdString().c_str() ) )
{
qDebug() << Q_FUNC_INFO << "index didn't exist.";
tDebug( LOGVERBOSE ) << Q_FUNC_INFO << "index didn't exist.";
return resultsmap;
}

View File

@@ -1,6 +1,6 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2010-2011, Christian Muehlhaeuser <muesli@tomahawk-player.org>
*
* Copyright 2010-2013, Christian Muehlhaeuser <muesli@tomahawk-player.org>
*
* Tomahawk is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -26,6 +26,7 @@
#include <QMutex>
#include "Query.h"
#include "DatabaseCommand_UpdateSearchIndex.h"
namespace lucene
{
@@ -60,7 +61,7 @@ public:
void beginIndexing();
void endIndexing();
void appendFields( const QMap< unsigned int, QMap< QString, QString > >& trackData );
void appendFields( const IndexData& data );
signals:
void indexReady();
@@ -82,6 +83,7 @@ private:
lucene::analysis::SimpleAnalyzer* m_analyzer;
lucene::store::Directory* m_luceneDir;
lucene::index::IndexReader* m_luceneReader;
lucene::index::IndexWriter* m_luceneWriter;
lucene::search::IndexSearcher* m_luceneSearcher;
};

View File

@@ -35,8 +35,6 @@
#include "utils/Logger.h"
#include "taglib/fileref.h"
#include <QDialog>
#include <QDialogButtonBox>
#include <QFileInfo>
@@ -231,7 +229,11 @@ MetadataEditor::loadResult( const Tomahawk::result_ptr& result )
if ( result->collection() && result->collection()->source()->isLocal() )
{
QFileInfo fi( QUrl( m_result->url() ).toLocalFile() );
QString furl = m_result->url();
if ( furl.startsWith( "file://" ) )
furl = furl.right( furl.length() - 7 );
QFileInfo fi( furl );
setFileName( fi.absoluteFilePath() );
setFileSize( TomahawkUtils::filesizeToString( fi.size() ) );
}

View File

@@ -298,7 +298,7 @@ Servent::registerPeer( const Tomahawk::peerinfo_ptr& peerInfo )
{
if ( peerInfo->hasControlConnection() )
{
peerInfoDebug( peerInfo ) << "already had control connection, not doin nuffin: " << peerInfo->controlConnection()->name();
peerInfoDebug( peerInfo ) << "already had control connection, doing nothing: " << peerInfo->controlConnection()->name();
tLog() << "existing control connection has following peers:";
foreach ( const peerinfo_ptr& otherPeerInfo, peerInfo->controlConnection()->peerInfos() )
{
@@ -311,7 +311,7 @@ Servent::registerPeer( const Tomahawk::peerinfo_ptr& peerInfo )
if ( peerInfo->type() == Tomahawk::PeerInfo::Local )
{
peerInfoDebug(peerInfo) << "YAY, we need to establish the connection now.. thinking";
peerInfoDebug(peerInfo) << "we need to establish the connection now... thinking";
if ( !connectedToSession( peerInfo->sipInfo().nodeId() ) )
{
connectToPeer( peerInfo );
@@ -336,17 +336,17 @@ Servent::registerPeer( const Tomahawk::peerinfo_ptr& peerInfo )
else
{
SipInfo info;
QString peerId = peerInfo->id();
QString key = uuid();
ControlConnection* conn = new ControlConnection( this );
const QString& nodeid = Database::instance()->impl()->dbid();
conn->setName( peerInfo->contactId() );
conn->setId( nodeid );
conn->addPeerInfo( peerInfo );
if ( visibleExternally() )
{
QString peerId = peerInfo->id();
QString key = uuid();
ControlConnection* conn = new ControlConnection( this );
const QString& nodeid = Database::instance()->impl()->dbid();
conn->setName( peerInfo->contactId() );
conn->setId( nodeid );
conn->addPeerInfo( peerInfo );
registerOffer( key, conn );
info.setVisible( true );
info.setHost( externalAddress() );
@@ -414,6 +414,12 @@ void Servent::handleSipInfo( const Tomahawk::peerinfo_ptr& peerInfo )
else
{
tDebug() << Q_FUNC_INFO << "They are not visible, doing nothing atm";
if ( !visibleExternally() )
{
if ( peerInfo->controlConnection() )
delete peerInfo->controlConnection();
}
}
}
@@ -714,10 +720,13 @@ Servent::connectToPeer( const peerinfo_ptr& peerInfo )
SipInfo sipInfo = peerInfo->sipInfo();
peerInfoDebug( peerInfo ) << "connectToPeer: search for already established connections to the same nodeid:" << m_controlconnections.count() << "connections";
if ( peerInfo->controlConnection() )
delete peerInfo->controlConnection();
bool isDupe = false;
ControlConnection* conn = 0;
// try to find a ControlConnection with the same SipInfo, then we dont need to try to connect again
foreach ( ControlConnection* c, m_controlconnections )
{
Q_ASSERT( c );
@@ -769,7 +778,6 @@ Servent::connectToPeer( const peerinfo_ptr& peerInfo )
m["nodeid"] = Database::instance()->impl()->dbid();
peerInfoDebug(peerInfo) << "No match found, creating a new ControlConnection...";
conn = new ControlConnection( this );
conn->addPeerInfo( peerInfo );
conn->setFirstMessage( m );
@@ -787,7 +795,7 @@ Servent::connectToPeer( const peerinfo_ptr& peerInfo )
void
Servent::connectToPeer( const QString& ha, int port, const QString &key, Connection* conn )
Servent::connectToPeer( const QString& ha, int port, const QString& key, Connection* conn )
{
tDebug( LOGVERBOSE ) << "Servent::connectToPeer:" << ha << ":" << port
<< thread() << QThread::currentThread();
@@ -902,6 +910,7 @@ Servent::claimOffer( ControlConnection* cc, const QString &nodeid, const QString
// this is terrible, actually there should be a way to let this be created by the zeroconf plugin
// because this way we rely on the ip being used as id in two totally different parts of the code
Tomahawk::peerinfo_ptr peerInfo = Tomahawk::PeerInfo::get( account->sipPlugin(), peer.toString(), Tomahawk::PeerInfo::AutoCreate );
peerInfo->setContactId( peer.toString() );
peerInfoDebug( peerInfo );
conn->addPeerInfo( peerInfo );
return conn;

View File

@@ -118,8 +118,8 @@ public slots:
public:
void connectToPeer( const Tomahawk::peerinfo_ptr& ha );
void connectToPeer( const QString& ha, int port, const QString &key, Connection* conn );
void reverseOfferRequest( ControlConnection* orig_conn, const QString &theirdbid, const QString& key, const QString& theirkey );
void connectToPeer( const QString& ha, int port, const QString& key, Connection* conn );
void reverseOfferRequest( ControlConnection* orig_conn, const QString& theirdbid, const QString& key, const QString& theirkey );
bool visibleExternally() const { return !m_externalHostname.isNull() || (m_externalPort > 0 && !m_externalAddress.isNull()); }
QString externalAddress() const { return !m_externalHostname.isNull() ? m_externalHostname : m_externalAddress.toString(); }

View File

@@ -119,21 +119,28 @@ AlbumItemDelegate::paint( QPainter* painter, const QStyleOptionViewItem& option,
painter->setFont( boldFont );
painter->setPen( option.palette.text().color().lighter( 450 ) );
QRect figureRect = r.adjusted( 4, 0, 0, 0 );
figureRect.setWidth( QFontMetrics( painter->font() ).width( "888" ) );
painter->drawText( figureRect, QString::number( index.row() + 1 ), QTextOption( Qt::AlignCenter ) );
r.adjust( figureRect.width() + 12, 0, 0, 0 );
QRect leftRect = r.adjusted( 0, 0, -48, 0 );
QRect rightRect = r.adjusted( r.width() - smallBoldFontMetrics.width( TomahawkUtils::timeToString( duration ) ), 0, 0, 0 );
QString text = painter->fontMetrics().elidedText( track, Qt::ElideRight, leftRect.width() );
painter->setPen( opt.palette.text().color() );
painter->drawText( leftRect, text, m_centerOption );
QRect leftRect = r.adjusted( 0, 0, -( rightRect.width() + 8 ), 0 );
const int sourceIconSize = r.height();
if ( q->numResults() && !q->results().first()->sourceIcon( TomahawkUtils::RoundedCorners, QSize( sourceIconSize, sourceIconSize ) ).isNull() )
if ( hoveringOver() == index && index.column() == 0 )
{
const QPixmap infoIcon = TomahawkUtils::defaultPixmap( TomahawkUtils::InfoIcon, TomahawkUtils::Original, QSize( sourceIconSize, sourceIconSize ) );
QRect arrowRect = QRect( rightRect.right() - sourceIconSize, r.center().y() - sourceIconSize / 2, infoIcon.width(), infoIcon.height() );
painter->drawPixmap( arrowRect, infoIcon );
setInfoButtonRect( index, arrowRect );
rightRect.moveLeft( rightRect.left() - infoIcon.width() - 8 );
leftRect.adjust( 0, 0, -( infoIcon.width() + 8 ), 0 );
}
else if ( q->numResults() && !q->results().first()->sourceIcon( TomahawkUtils::RoundedCorners, QSize( sourceIconSize, sourceIconSize ) ).isNull() )
{
const QPixmap sourceIcon = q->results().first()->sourceIcon( TomahawkUtils::RoundedCorners, QSize( sourceIconSize, sourceIconSize ) );
painter->setOpacity( 0.8 );
@@ -142,6 +149,10 @@ AlbumItemDelegate::paint( QPainter* painter, const QStyleOptionViewItem& option,
rightRect.moveLeft( rightRect.left() - sourceIcon.width() - 8 );
}
QString text = painter->fontMetrics().elidedText( track, Qt::ElideRight, leftRect.width() );
painter->setPen( opt.palette.text().color() );
painter->drawText( leftRect, text, m_centerOption );
if ( duration > 0 )
{
painter->setPen( opt.palette.text().color() );

View File

@@ -29,18 +29,16 @@
#include "Query.h"
#include "Result.h"
#include "Source.h"
#include "audio/AudioEngine.h"
#include "utils/TomahawkUtilsGui.h"
#include "utils/PixmapDelegateFader.h"
#include <utils/Closure.h>
#include "playlist/PlayableItem.h"
#include "playlist/PlayableProxyModel.h"
#include "GridView.h"
#include "ViewManager.h"
#include "utils/AnimatedSpinner.h"
#include "audio/AudioEngine.h"
#include "playlist/PlayableItem.h"
#include "playlist/PlayableProxyModel.h"
#include "widgets/ImageButton.h"
#include "utils/TomahawkUtilsGui.h"
#include "utils/PixmapDelegateFader.h"
#include "utils/Closure.h"
#include "utils/AnimatedSpinner.h"
#include "utils/Logger.h"
namespace {
@@ -377,7 +375,6 @@ GridItemDelegate::editorEvent( QEvent* event, QAbstractItemModel* model, const Q
if ( hoveringArtist )
{
if ( event->type() == QEvent::MouseButtonRelease )
{
PlayableItem* item = m_model->sourceModel()->itemFromIndex( m_model->mapToSource( index ) );

View File

@@ -53,6 +53,7 @@ public:
void setIsPlaying( bool b ) { m_isPlaying = b; emit dataChanged(); }
bool fetchingMore() const { return m_fetchingMore; }
void setFetchingMore( bool b ) { m_fetchingMore = b; }
void requestRepaint() { emit dataChanged(); }
QString name() const;
QString artistName() const;

View File

@@ -97,7 +97,6 @@ PlayableProxyModelPlaylistInterface::onCurrentIndexChanged()
void
PlayableProxyModelPlaylistInterface::setCurrentIndex( qint64 index )
{
Q_ASSERT( m_proxyModel );
if ( m_proxyModel.isNull() )
return;

View File

@@ -43,7 +43,7 @@ using namespace Tomahawk;
PlaylistChartItemDelegate::PlaylistChartItemDelegate( TrackView* parent, PlayableProxyModel* proxy )
: QStyledItemDelegate( (QObject*)parent )
: PlaylistItemDelegate( parent, proxy )
, m_view( parent )
, m_model( proxy )
{
@@ -59,11 +59,8 @@ PlaylistChartItemDelegate::PlaylistChartItemDelegate( TrackView* parent, Playabl
m_bottomOption = QTextOption( Qt::AlignBottom );
m_bottomOption.setWrapMode( QTextOption::NoWrap );
connect( this, SIGNAL( updateIndex( QModelIndex ) ), parent, SLOT( update( QModelIndex ) ) );
connect( m_model, SIGNAL( modelReset() ), this, SLOT( modelChanged() ) );
if ( PlaylistView* plView = qobject_cast< PlaylistView* >( parent ) )
connect( plView, SIGNAL( modelChanged() ), this, SLOT( modelChanged() ) );
connect( proxy, SIGNAL( modelReset() ), SLOT( modelChanged() ) );
connect( parent, SIGNAL( modelChanged() ), SLOT( modelChanged() ) );
}
@@ -99,25 +96,6 @@ PlaylistChartItemDelegate::sizeHint( const QStyleOptionViewItem& option, const Q
}
QWidget*
PlaylistChartItemDelegate::createEditor( QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index ) const
{
Q_UNUSED( parent );
Q_UNUSED( option );
Q_UNUSED( index );
return 0;
}
void
PlaylistChartItemDelegate::prepareStyleOption( QStyleOptionViewItemV4* option, const QModelIndex& index, PlayableItem* item ) const
{
initStyleOption( option, index );
TomahawkUtils::prepareStyleOption( option, index, item );
}
void
PlaylistChartItemDelegate::paint( QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index ) const
{
@@ -143,7 +121,7 @@ PlaylistChartItemDelegate::paint( QPainter* painter, const QStyleOptionViewItem&
painter->save();
{
QRect r = opt.rect.adjusted( 4, 6, 0, -6 );
// Paint Now Playing Speaker Icon
if ( item->isPlaying() )
{
@@ -214,7 +192,29 @@ PlaylistChartItemDelegate::paint( QPainter* painter, const QStyleOptionViewItem&
painter->drawPixmap( pixmapRect, pixmap );
r.adjust( pixmapRect.width() + figureRect.width() + 18, 1, -28, 0 );
QRect leftRect = r.adjusted( 0, 0, -durationFontMetrics.width( TomahawkUtils::timeToString( duration ) ) - 8, 0 );
QRect rightRect = r.adjusted( r.width() - durationFontMetrics.width( TomahawkUtils::timeToString( duration ) ), 0, 0, 0 );
QRect leftRect = r.adjusted( 0, 0, -( rightRect.width() + 8 ), 0 );
/* const int sourceIconSize = r.height();
if ( hoveringOver() == index && index.column() == 0 )
{
const QPixmap infoIcon = TomahawkUtils::defaultPixmap( TomahawkUtils::InfoIcon, TomahawkUtils::Original, QSize( sourceIconSize, sourceIconSize ) );
QRect arrowRect = QRect( rightRect.right() - sourceIconSize, r.center().y() - sourceIconSize / 2, infoIcon.width(), infoIcon.height() );
painter->drawPixmap( arrowRect, infoIcon );
setInfoButtonRect( index, arrowRect );
rightRect.moveLeft( rightRect.left() - infoIcon.width() - 8 );
leftRect.adjust( 0, 0, -( infoIcon.width() + 8 ), 0 );
}
else if ( q->numResults() && !q->results().first()->sourceIcon( TomahawkUtils::RoundedCorners, QSize( sourceIconSize, sourceIconSize ) ).isNull() )
{
const QPixmap sourceIcon = q->results().first()->sourceIcon( TomahawkUtils::RoundedCorners, QSize( sourceIconSize, sourceIconSize ) );
painter->setOpacity( 0.8 );
painter->drawPixmap( QRect( rightRect.right() - sourceIconSize, r.center().y() - sourceIconSize / 2, sourceIcon.width(), sourceIcon.height() ), sourceIcon );
painter->setOpacity( 1.0 );
rightRect.moveLeft( rightRect.left() - sourceIcon.width() - 8 );
}*/
painter->setFont( boldFont );
QString text = painter->fontMetrics().elidedText( track, Qt::ElideRight, leftRect.width() );
@@ -226,10 +226,9 @@ PlaylistChartItemDelegate::paint( QPainter* painter, const QStyleOptionViewItem&
if ( duration > 0 )
{
painter->setPen( opt.palette.text().color() );
painter->setFont( durationFont );
QRect rightRect = r.adjusted( r.width() - durationFontMetrics.width( TomahawkUtils::timeToString( duration ) ), 0, 0, 0 );
text = painter->fontMetrics().elidedText( TomahawkUtils::timeToString( duration ), Qt::ElideRight, rightRect.width() );
painter->drawText( rightRect, text, m_centerRightOption );
painter->drawText( rightRect, TomahawkUtils::timeToString( duration ), m_centerRightOption );
}
}
painter->restore();
@@ -239,7 +238,8 @@ PlaylistChartItemDelegate::paint( QPainter* painter, const QStyleOptionViewItem&
void
PlaylistChartItemDelegate::doUpdateIndex( const QPersistentModelIndex& idx )
{
emit updateIndex( idx );
if ( idx.isValid() )
emit updateIndex( idx );
}

View File

@@ -19,9 +19,9 @@
#ifndef PLAYLISTCHARTITEMDELEGATE_H
#define PLAYLISTCHARTITEMDELEGATE_H
#include <QStyledItemDelegate>
#include <QTextOption>
#include "PlaylistItemDelegate.h"
#include "DllMacro.h"
#include "Typedefs.h"
@@ -35,37 +35,32 @@ class PlayableItem;
class PlayableProxyModel;
class TrackView;
class DLLEXPORT PlaylistChartItemDelegate : public QStyledItemDelegate
class DLLEXPORT PlaylistChartItemDelegate : public PlaylistItemDelegate
{
Q_OBJECT
public:
PlaylistChartItemDelegate( TrackView* parent = 0, PlayableProxyModel* proxy = 0 );
signals:
void updateIndex( const QModelIndex& idx );
virtual QSize sizeHint( const QStyleOptionViewItem& option, const QModelIndex& index ) const;
protected:
void paint( QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index ) const;
QSize sizeHint( const QStyleOptionViewItem& option, const QModelIndex& index ) const;
QWidget* createEditor( QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index ) const;
private slots:
void modelChanged();
void doUpdateIndex( const QPersistentModelIndex& idx );
void modelChanged();
private:
void prepareStyleOption( QStyleOptionViewItemV4* option, const QModelIndex& index, PlayableItem* item ) const;
QTextOption m_topOption;
QTextOption m_centerOption;
QTextOption m_centerRightOption;
QTextOption m_bottomOption;
mutable QHash< QPersistentModelIndex, QSharedPointer< Tomahawk::PixmapDelegateFader > > m_pixmaps;
TrackView* m_view;
PlayableProxyModel* m_model;
mutable QHash< QPersistentModelIndex, QSharedPointer< Tomahawk::PixmapDelegateFader > > m_pixmaps;
};
#endif // PLAYLISTCHARTITEMDELEGATE_H

View File

@@ -21,10 +21,12 @@
#include <QApplication>
#include <QPainter>
#include <QMouseEvent>
#include "Query.h"
#include "Result.h"
#include "Artist.h"
#include "Album.h"
#include "Source.h"
#include "SourceList.h"
@@ -33,6 +35,7 @@
#include "PlayableProxyModel.h"
#include "TrackView.h"
#include "ViewHeader.h"
#include "ViewManager.h"
#include "utils/TomahawkUtilsGui.h"
#include "utils/Logger.h"
@@ -82,16 +85,6 @@ PlaylistItemDelegate::sizeHint( const QStyleOptionViewItem& option, const QModel
}
QWidget*
PlaylistItemDelegate::createEditor( QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index ) const
{
Q_UNUSED( parent );
Q_UNUSED( option );
Q_UNUSED( index );
return 0;
}
void
PlaylistItemDelegate::prepareStyleOption( QStyleOptionViewItemV4* option, const QModelIndex& index, PlayableItem* item ) const
{
@@ -210,6 +203,7 @@ PlaylistItemDelegate::paintShort( QPainter* painter, const QStyleOptionViewItem&
text = painter->fontMetrics().elidedText( lowerText, Qt::ElideRight, r.width() );
painter->drawText( r.adjusted( 0, 1, 0, 0 ), text, m_bottomOption );
}
painter->restore();
}
@@ -228,7 +222,7 @@ PlaylistItemDelegate::paintDetailed( QPainter* painter, const QStyleOptionViewIt
opt.text.clear();
qApp->style()->drawControl( QStyle::CE_ItemViewItem, &opt, painter );
if ( m_view->hoveredIndex().row() == index.row() && m_view->hoveredIndex().column() == index.column() && !index.data().toString().isEmpty() &&
if ( m_hoveringOver == index && !index.data().toString().isEmpty() &&
( index.column() == PlayableModel::Artist || index.column() == PlayableModel::Album || index.column() == PlayableModel::Track ) )
{
opt.rect.setWidth( opt.rect.width() - opt.rect.height() - 2 );
@@ -236,6 +230,8 @@ PlaylistItemDelegate::paintDetailed( QPainter* painter, const QStyleOptionViewIt
QPixmap infoIcon = TomahawkUtils::defaultPixmap( TomahawkUtils::InfoIcon, TomahawkUtils::Original, arrowRect.size() );
painter->drawPixmap( arrowRect, infoIcon );
m_infoButtonRects[ index ] = arrowRect;
}
painter->save();
@@ -289,3 +285,103 @@ PlaylistItemDelegate::paintDetailed( QPainter* painter, const QStyleOptionViewIt
painter->restore();
}
bool
PlaylistItemDelegate::editorEvent( QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option, const QModelIndex& index )
{
QStyledItemDelegate::editorEvent( event, model, option, index );
if ( event->type() != QEvent::MouseButtonRelease &&
event->type() != QEvent::MouseMove &&
event->type() != QEvent::Leave )
{
return false;
}
bool hoveringInfo = false;
if ( m_infoButtonRects.contains( index ) )
{
const QRect infoRect = m_infoButtonRects[ index ];
const QMouseEvent* ev = static_cast< QMouseEvent* >( event );
hoveringInfo = infoRect.contains( ev->pos() );
}
if ( event->type() == QEvent::MouseMove )
{
if ( hoveringInfo )
m_view->setCursor( Qt::PointingHandCursor );
else
m_view->setCursor( Qt::ArrowCursor );
if ( m_hoveringOver != index )
{
PlayableItem* item = m_model->sourceModel()->itemFromIndex( m_model->mapToSource( index ) );
item->requestRepaint();
m_hoveringOver = index;
emit updateIndex( m_hoveringOver );
}
// We return false here so the view can still decide to process/trigger things like D&D events
return false;
}
// reset mouse cursor. we switch to a pointing hand cursor when hovering an info button
m_view->setCursor( Qt::ArrowCursor );
if ( hoveringInfo )
{
if ( event->type() == QEvent::MouseButtonRelease )
{
PlayableItem* item = m_model->sourceModel()->itemFromIndex( m_model->mapToSource( index ) );
if ( !item )
return false;
if ( m_model->style() != PlayableProxyModel::Detailed )
{
if ( item->query() )
ViewManager::instance()->show( item->query()->displayQuery() );
}
else
{
switch ( index.column() )
{
case PlayableModel::Artist:
{
ViewManager::instance()->show( Artist::get( item->query()->displayQuery()->artist() ) );
break;
}
case PlayableModel::Album:
{
artist_ptr artist = Artist::get( item->query()->displayQuery()->artist() );
ViewManager::instance()->show( Album::get( artist, item->query()->displayQuery()->album() ) );
break;
}
case PlayableModel::Track:
{
ViewManager::instance()->show( item->query()->displayQuery() );
break;
}
default:
break;
}
}
event->accept();
return true;
}
}
return false;
}
void
PlaylistItemDelegate::resetHoverIndex()
{
m_hoveringOver = QModelIndex();
m_infoButtonRects.clear();
}

View File

@@ -40,6 +40,9 @@ public:
virtual QSize sizeHint( const QStyleOptionViewItem& option, const QModelIndex& index ) const;
public slots:
void resetHoverIndex();
signals:
void updateIndex( const QModelIndex& idx );
@@ -47,7 +50,10 @@ protected:
void prepareStyleOption( QStyleOptionViewItemV4* option, const QModelIndex& index, PlayableItem* item ) const;
void paint( QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index ) const;
QWidget* createEditor( QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index ) const;
bool editorEvent( QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option, const QModelIndex& index );
QPersistentModelIndex hoveringOver() const { return m_hoveringOver; }
void setInfoButtonRect( const QPersistentModelIndex& index, const QRect& rect ) const { m_infoButtonRects[ index ] = rect; }
private:
void paintDetailed( QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index ) const;
@@ -56,6 +62,9 @@ private:
QTextOption m_topOption;
QTextOption m_bottomOption;
mutable QHash< QPersistentModelIndex, QRect > m_infoButtonRects;
QPersistentModelIndex m_hoveringOver;
TrackView* m_view;
PlayableProxyModel* m_model;
};

View File

@@ -190,8 +190,31 @@ PlaylistLargeItemDelegate::paint( QPainter* painter, const QStyleOptionViewItem&
smallFont.setPointSize( TomahawkUtils::defaultFontSize() - 1 );
r.adjust( pixmapRect.width() + 12, 1, - 16, 0 );
QRect leftRect = r.adjusted( 0, 0, -48, 0 );
QRect rightRect = r.adjusted( r.width() - smallBoldFontMetrics.width( TomahawkUtils::timeToString( duration ) ), 0, 0, 0 );
QRect leftRect = r.adjusted( 0, 0, -( rightRect.width() + 8 ), 0 );
const int sourceIconSize = avatarRect.width() - 6;
if ( hoveringOver() == index && !index.data().toString().isEmpty() && index.column() == 0 )
{
const QPixmap infoIcon = TomahawkUtils::defaultPixmap( TomahawkUtils::InfoIcon, TomahawkUtils::Original, QSize( sourceIconSize, sourceIconSize ) );
QRect arrowRect = QRect( rightRect.right() - sourceIconSize, r.center().y() - sourceIconSize / 2, infoIcon.width(), infoIcon.height() );
painter->drawPixmap( arrowRect, infoIcon );
setInfoButtonRect( index, arrowRect );
rightRect.moveLeft( rightRect.left() - infoIcon.width() - 8 );
leftRect.adjust( 0, 0, -( infoIcon.width() + 8 ), 0 );
}
else if ( q->numResults() && !q->results().first()->sourceIcon( TomahawkUtils::RoundedCorners, QSize( sourceIconSize, sourceIconSize ) ).isNull() )
{
const QPixmap sourceIcon = q->results().first()->sourceIcon( TomahawkUtils::RoundedCorners, QSize( sourceIconSize, sourceIconSize ) );
painter->setOpacity( 0.8 );
painter->drawPixmap( QRect( rightRect.right() - sourceIconSize, r.center().y() - sourceIconSize / 2, sourceIcon.width(), sourceIcon.height() ), sourceIcon );
painter->setOpacity( 1.0 );
rightRect.moveLeft( rightRect.left() - sourceIcon.width() - 8 );
leftRect.adjust( 0, 0, -( sourceIcon.width() + 8 ), 0 );
}
painter->setFont( boldFont );
QString text = painter->fontMetrics().elidedText( track, Qt::ElideRight, leftRect.width() );
@@ -207,7 +230,8 @@ PlaylistLargeItemDelegate::paint( QPainter* painter, const QStyleOptionViewItem&
textDoc.setDefaultFont( painter->font() );
textDoc.setDefaultTextOption( m_topOption );
drawRichText( painter, opt, leftRect.adjusted( 0, boldFontMetrics.height() + 1, 0, 0 ), Qt::AlignTop, textDoc );
if ( textDoc.idealWidth() <= leftRect.width() )
drawRichText( painter, opt, leftRect.adjusted( 0, boldFontMetrics.height() + 1, 0, 0 ), Qt::AlignTop, textDoc );
if ( !( option.state & QStyle::State_Selected || item->isPlaying() ) )
painter->setPen( Qt::gray );
@@ -222,16 +246,6 @@ PlaylistLargeItemDelegate::paint( QPainter* painter, const QStyleOptionViewItem&
drawRichText( painter, opt, leftRect, Qt::AlignBottom, textDoc );
const int sourceIconSize = avatarRect.width() - 6;
if ( q->numResults() && !q->results().first()->sourceIcon( TomahawkUtils::RoundedCorners, QSize( sourceIconSize, sourceIconSize ) ).isNull() )
{
const QPixmap sourceIcon = q->results().first()->sourceIcon( TomahawkUtils::RoundedCorners, QSize( sourceIconSize, sourceIconSize ) );
painter->setOpacity( 0.8 );
painter->drawPixmap( QRect( rightRect.right() - sourceIconSize, r.center().y() - sourceIconSize / 2, sourceIcon.width(), sourceIcon.height() ), sourceIcon );
painter->setOpacity( 1.0 );
rightRect.moveLeft( rightRect.left() - sourceIcon.width() - 8 );
}
if ( duration > 0 )
{
painter->setPen( opt.palette.text().color() );

View File

@@ -25,8 +25,6 @@
#include "PlayableProxyModel.h"
#include "PlayableItem.h"
#include "DropJob.h"
#include "Artist.h"
#include "Album.h"
#include "Source.h"
#include "TomahawkSettings.h"
#include "audio/AudioEngine.h"
@@ -154,14 +152,14 @@ TrackView::setProxyModel( PlayableProxyModel* model )
disconnect( m_proxyModel, SIGNAL( rowsInserted( QModelIndex, int, int ) ), this, SLOT( verifySize() ) );
disconnect( m_proxyModel, SIGNAL( rowsRemoved( QModelIndex, int, int ) ), this, SLOT( verifySize() ) );
}
m_proxyModel = model;
connect( m_proxyModel, SIGNAL( filterChanged( QString ) ), SLOT( onFilterChanged( QString ) ) );
connect( m_proxyModel, SIGNAL( rowsInserted( QModelIndex, int, int ) ), SLOT( onViewChanged() ) );
connect( m_proxyModel, SIGNAL( rowsInserted( QModelIndex, int, int ) ), SLOT( verifySize() ) );
connect( m_proxyModel, SIGNAL( rowsRemoved( QModelIndex, int, int ) ), SLOT( verifySize() ) );
m_delegate = new PlaylistItemDelegate( this, m_proxyModel );
setItemDelegate( m_delegate );
@@ -360,6 +358,7 @@ TrackView::tryToPlayItem( const QModelIndex& index )
PlayableItem* item = m_model->itemFromIndex( m_proxyModel->mapToSource( index ) );
if ( item && !item->query().isNull() )
{
m_model->setCurrentIndex( m_proxyModel->mapToSource( index ) );
AudioEngine::instance()->playItem( playlistInterface(), item->query() );
return true;
@@ -563,6 +562,16 @@ TrackView::paintEvent( QPaintEvent* event )
}
void
TrackView::wheelEvent( QWheelEvent* event )
{
QTreeView::wheelEvent( event );
m_delegate->resetHoverIndex();
repaint();
}
void
TrackView::onFilterChanged( const QString& )
{
@@ -683,105 +692,6 @@ TrackView::onMenuTriggered( int action )
}
void
TrackView::updateHoverIndex( const QPoint& pos )
{
QModelIndex idx = indexAt( pos );
if ( idx != m_hoveredIndex )
{
m_hoveredIndex = idx;
repaint();
}
if ( !m_model || m_proxyModel->style() != PlayableProxyModel::Detailed )
return;
if ( idx.column() == PlayableModel::Artist || idx.column() == PlayableModel::Album || idx.column() == PlayableModel::Track )
{
if ( pos.x() > header()->sectionViewportPosition( idx.column() ) + header()->sectionSize( idx.column() ) - 16 &&
pos.x() < header()->sectionViewportPosition( idx.column() ) + header()->sectionSize( idx.column() ) )
{
setCursor( Qt::PointingHandCursor );
return;
}
}
if ( cursor().shape() != Qt::ArrowCursor )
setCursor( Qt::ArrowCursor );
}
void
TrackView::wheelEvent( QWheelEvent* event )
{
QTreeView::wheelEvent( event );
if ( m_hoveredIndex.isValid() )
{
m_hoveredIndex = QModelIndex();
repaint();
}
}
void
TrackView::leaveEvent( QEvent* event )
{
QTreeView::leaveEvent( event );
updateHoverIndex( QPoint( -1, -1 ) );
}
void
TrackView::mouseMoveEvent( QMouseEvent* event )
{
QTreeView::mouseMoveEvent( event );
updateHoverIndex( event->pos() );
}
void
TrackView::mousePressEvent( QMouseEvent* event )
{
QTreeView::mousePressEvent( event );
if ( !m_model || m_proxyModel->style() != PlayableProxyModel::Detailed )
return;
QModelIndex idx = indexAt( event->pos() );
if ( event->pos().x() > header()->sectionViewportPosition( idx.column() ) + header()->sectionSize( idx.column() ) - 16 &&
event->pos().x() < header()->sectionViewportPosition( idx.column() ) + header()->sectionSize( idx.column() ) )
{
PlayableItem* item = proxyModel()->itemFromIndex( proxyModel()->mapToSource( idx ) );
switch ( idx.column() )
{
case PlayableModel::Artist:
{
ViewManager::instance()->show( Artist::get( item->query()->displayQuery()->artist() ) );
break;
}
case PlayableModel::Album:
{
artist_ptr artist = Artist::get( item->query()->displayQuery()->artist() );
ViewManager::instance()->show( Album::get( artist, item->query()->displayQuery()->album() ) );
break;
}
case PlayableModel::Track:
{
ViewManager::instance()->show( item->query()->displayQuery() );
break;
}
default:
break;
}
}
}
Tomahawk::playlistinterface_ptr
TrackView::playlistInterface() const
{

View File

@@ -75,7 +75,6 @@ public:
virtual bool setFilter( const QString& filter );
virtual bool jumpToCurrentTrack();
QModelIndex hoveredIndex() const { return m_hoveredIndex; }
QModelIndex contextMenuIndex() const { return m_contextMenuIndex; }
void setContextMenuIndex( const QModelIndex& idx ) { m_contextMenuIndex = idx; }
@@ -112,12 +111,9 @@ protected:
virtual void dragMoveEvent( QDragMoveEvent* event );
virtual void dropEvent( QDropEvent* event );
virtual void wheelEvent( QWheelEvent* event );
virtual void mouseMoveEvent( QMouseEvent* event );
virtual void mousePressEvent( QMouseEvent* event );
virtual void leaveEvent( QEvent* event );
virtual void paintEvent( QPaintEvent* event );
virtual void keyPressEvent( QKeyEvent* event );
virtual void wheelEvent( QWheelEvent* event );
protected slots:
virtual void currentChanged( const QModelIndex& current, const QModelIndex& previous );
@@ -131,7 +127,7 @@ private slots:
void autoPlayResolveFinished( const Tomahawk::query_ptr& query, int row );
void verifySize();
private:
void startAutoPlay( const QModelIndex& index );
bool tryToPlayItem( const QModelIndex& index );
@@ -153,7 +149,6 @@ private:
bool m_updateContextView;
bool m_autoResize;
QModelIndex m_hoveredIndex;
QModelIndex m_contextMenuIndex;
Tomahawk::query_ptr m_autoPlaying;

View File

@@ -23,6 +23,7 @@
#include <QPainter>
#include <QAbstractItemView>
#include <QHeaderView>
#include <QMouseEvent>
#include "Query.h"
#include "Result.h"
@@ -35,6 +36,7 @@
#include "PlayableItem.h"
#include "TreeProxyModel.h"
#include "TreeView.h"
#include "ViewManager.h"
#include "Typedefs.h"
@@ -73,7 +75,7 @@ TreeItemDelegate::sizeHint( const QStyleOptionViewItem& option, const QModelInde
break;
}
}
// artist per default
size.setHeight( option.fontMetrics.height() * 4 );
return size;
@@ -135,13 +137,15 @@ TreeItemDelegate::paint( QPainter* painter, const QStyleOptionViewItem& option,
if ( oldX > 0 )
o.rect.setX( oldX );
if ( m_view->hoveredIndex() == index && !index.data().toString().isEmpty() && index.column() == 0 )
if ( m_hoveringOver == index && !index.data().toString().isEmpty() && index.column() == 0 )
{
o.rect.setWidth( o.rect.width() - o.rect.height() );
QRect arrowRect( o.rect.x() + o.rect.width(), o.rect.y() + 1, o.rect.height() - 2, o.rect.height() - 2 );
QPixmap infoIcon = TomahawkUtils::defaultPixmap( TomahawkUtils::InfoIcon, TomahawkUtils::Original, arrowRect.size() );
painter->drawPixmap( arrowRect, infoIcon );
m_infoButtonRects[ index ] = arrowRect;
}
{
@@ -182,6 +186,15 @@ TreeItemDelegate::paint( QPainter* painter, const QStyleOptionViewItem& option,
opt.palette.setColor( QPalette::Text, opt.palette.color( QPalette::HighlightedText ) );
}
QRect arrowRect( m_view->viewport()->width() - option.rect.height(), option.rect.y() + 1, option.rect.height() - 2, option.rect.height() - 2 );
if ( m_hoveringOver.row() == index.row() && m_hoveringOver.parent() == index.parent() )
{
QPixmap infoIcon = TomahawkUtils::defaultPixmap( TomahawkUtils::InfoIcon, TomahawkUtils::Original, arrowRect.size() );
painter->drawPixmap( arrowRect, infoIcon );
m_infoButtonRects[ index ] = arrowRect;
}
if ( index.column() > 0 )
return;
@@ -209,7 +222,6 @@ TreeItemDelegate::paint( QPainter* painter, const QStyleOptionViewItem& option,
}
const QPixmap cover = m_pixmaps[ index ]->currentPixmap();
painter->drawPixmap( r, cover );
r = option.rect.adjusted( option.rect.height(), 6, -4, -option.rect.height() + 22 );
@@ -226,3 +238,88 @@ TreeItemDelegate::doUpdateIndex( const QPersistentModelIndex& index )
emit updateIndex( index );
}
bool
TreeItemDelegate::editorEvent( QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option, const QModelIndex& index )
{
Q_UNUSED( model );
Q_UNUSED( option );
if ( event->type() != QEvent::MouseButtonRelease &&
event->type() != QEvent::MouseMove &&
event->type() != QEvent::MouseButtonPress &&
event->type() != QEvent::Leave )
return false;
bool hoveringInfo = false;
if ( m_infoButtonRects.contains( index ) )
{
const QRect infoRect = m_infoButtonRects[ index ];
const QMouseEvent* ev = static_cast< QMouseEvent* >( event );
hoveringInfo = infoRect.contains( ev->pos() );
}
if ( event->type() == QEvent::MouseMove )
{
if ( hoveringInfo )
m_view->setCursor( Qt::PointingHandCursor );
else
m_view->setCursor( Qt::ArrowCursor );
if ( m_hoveringOver != index )
{
PlayableItem* item = m_model->sourceModel()->itemFromIndex( m_model->mapToSource( index ) );
item->requestRepaint();
m_hoveringOver = index;
emit updateIndex( m_hoveringOver );
}
event->accept();
return true;
}
// reset mouse cursor. we switch to a pointing hand cursor when hovering an info button
m_view->setCursor( Qt::ArrowCursor );
if ( hoveringInfo )
{
if ( event->type() == QEvent::MouseButtonRelease )
{
PlayableItem* item = m_model->sourceModel()->itemFromIndex( m_model->mapToSource( index ) );
if ( !item )
return false;
if ( item->query() )
{
ViewManager::instance()->show( item->query()->displayQuery() );
}
else if ( item->artist() )
{
ViewManager::instance()->show( item->artist() );
}
else if ( item->album() )
{
ViewManager::instance()->show( item->album() );
}
event->accept();
return true;
}
else if ( event->type() == QEvent::MouseButtonPress )
{
// Stop the whole item from having a down click action as we just want the info button to be clicked
event->accept();
return true;
}
}
return false;
}
void
TreeItemDelegate::resetHoverIndex()
{
m_hoveringOver = QModelIndex();
m_infoButtonRects.clear();
}

View File

@@ -40,8 +40,12 @@ public:
virtual QSize sizeHint( const QStyleOptionViewItem& option, const QModelIndex& index ) const;
public slots:
void resetHoverIndex();
protected:
void paint( QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index ) const;
bool editorEvent( QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option, const QModelIndex& index );
signals:
void updateIndex( const QModelIndex& idx );
@@ -54,6 +58,8 @@ private:
TreeProxyModel* m_model;
mutable QHash< QPersistentModelIndex, QSharedPointer< Tomahawk::PixmapDelegateFader > > m_pixmaps;
mutable QHash< QPersistentModelIndex, QRect > m_infoButtonRects;
QPersistentModelIndex m_hoveringOver;
};
#endif // TREEITEMDELEGATE_H

View File

@@ -27,6 +27,7 @@
#include "database/DatabaseImpl.h"
#include "collection/AlbumsRequest.h"
#include "collection/ArtistsRequest.h"
#include "database/DatabaseCommand_AllAlbums.h"
#include "PlayableItem.h"
#include "utils/Logger.h"
@@ -73,7 +74,11 @@ TreeProxyModel::onRowsInserted( const QModelIndex& parent, int /* start */, int
if ( pi->artist().isNull() )
return;
Tomahawk::AlbumsRequest* cmd = m_model->collection()->requestAlbums( pi->artist() );
Tomahawk::AlbumsRequest* cmd = 0;
if ( !m_model->collection().isNull() )
cmd = m_model->collection()->requestAlbums( pi->artist() );
else
cmd = new DatabaseCommand_AllAlbums( Tomahawk::collection_ptr(), pi->artist() );
cmd->setFilter( m_filter );
@@ -115,7 +120,11 @@ TreeProxyModel::setFilter( const QString& pattern )
}
else
{
Tomahawk::ArtistsRequest* cmd = m_model->collection()->requestArtists();
Tomahawk::ArtistsRequest* cmd = 0;
if ( !m_model->collection().isNull() )
cmd = m_model->collection()->requestArtists();
else
cmd = new DatabaseCommand_AllArtists(); //for SuperCollection, TODO: replace with a proper proxy-ArtistsRequest
cmd->setFilter( pattern );
m_artistsFilterCmd = cmd;

View File

@@ -53,6 +53,7 @@ TreeView::TreeView( QWidget* parent )
, m_overlay( new OverlayWidget( this ) )
, m_model( 0 )
, m_proxyModel( 0 )
, m_delegate( 0 )
, m_loadingSpinner( new LoadingSpinner( this ) )
, m_updateContextView( true )
, m_contextMenu( new ContextMenu( this ) )
@@ -99,9 +100,9 @@ void
TreeView::setProxyModel( TreeProxyModel* model )
{
m_proxyModel = model;
TreeItemDelegate* del = new TreeItemDelegate( this, m_proxyModel );
connect( del, SIGNAL( updateIndex( QModelIndex ) ), this, SLOT( update( QModelIndex ) ) );
setItemDelegate( del );
m_delegate = new TreeItemDelegate( this, m_proxyModel );
connect( m_delegate, SIGNAL( updateIndex( QModelIndex ) ), SLOT( update( QModelIndex ) ) );
setItemDelegate( m_delegate );
QTreeView::setModel( m_proxyModel );
}
@@ -234,7 +235,7 @@ TreeView::onItemActivated( const QModelIndex& index )
PlayableItem* item = m_model->itemFromIndex( m_proxyModel->mapToSource( index ) );
if ( item )
{
if ( !item->artist().isNull() )
/* if ( !item->artist().isNull() )
{
ViewManager::instance()->show( item->artist() );
}
@@ -242,7 +243,7 @@ TreeView::onItemActivated( const QModelIndex& index )
{
ViewManager::instance()->show( item->album() );
}
else if ( !item->result().isNull() && item->result()->isOnline() )
else */ if ( !item->result().isNull() && item->result()->isOnline() )
{
AudioEngine::instance()->playItem( m_proxyModel->playlistInterface(), item->result() );
}
@@ -285,6 +286,16 @@ TreeView::resizeEvent( QResizeEvent* event )
}
void
TreeView::wheelEvent( QWheelEvent* event )
{
QTreeView::wheelEvent( event );
m_delegate->resetHoverIndex();
repaint();
}
void
TreeView::onFilterChangeFinished()
{
@@ -427,96 +438,6 @@ TreeView::jumpToCurrentTrack()
}
void
TreeView::updateHoverIndex( const QPoint& pos )
{
QModelIndex idx = indexAt( pos );
if ( idx != m_hoveredIndex )
{
m_hoveredIndex = idx;
repaint();
}
if ( !m_model || m_proxyModel->style() != PlayableProxyModel::Collection )
return;
PlayableItem* item = proxyModel()->itemFromIndex( proxyModel()->mapToSource( idx ) );
if ( idx.column() == 0 && !item->query().isNull() )
{
if ( pos.x() > header()->sectionViewportPosition( idx.column() ) + header()->sectionSize( idx.column() ) - 16 &&
pos.x() < header()->sectionViewportPosition( idx.column() ) + header()->sectionSize( idx.column() ) )
{
setCursor( Qt::PointingHandCursor );
return;
}
}
if ( cursor().shape() != Qt::ArrowCursor )
setCursor( Qt::ArrowCursor );
}
void
TreeView::wheelEvent( QWheelEvent* event )
{
QTreeView::wheelEvent( event );
if ( m_hoveredIndex.isValid() )
{
m_hoveredIndex = QModelIndex();
repaint();
}
}
void
TreeView::leaveEvent( QEvent* event )
{
QTreeView::leaveEvent( event );
updateHoverIndex( QPoint( -1, -1 ) );
}
void
TreeView::mouseMoveEvent( QMouseEvent* event )
{
QTreeView::mouseMoveEvent( event );
updateHoverIndex( event->pos() );
}
void
TreeView::mousePressEvent( QMouseEvent* event )
{
QTreeView::mousePressEvent( event );
if ( !m_model || m_proxyModel->style() != PlayableProxyModel::Collection )
return;
QModelIndex idx = indexAt( event->pos() );
if ( event->pos().x() > header()->sectionViewportPosition( idx.column() ) + header()->sectionSize( idx.column() ) - 16 &&
event->pos().x() < header()->sectionViewportPosition( idx.column() ) + header()->sectionSize( idx.column() ) )
{
PlayableItem* item = proxyModel()->itemFromIndex( proxyModel()->mapToSource( idx ) );
if ( item->query().isNull() )
return;
switch ( idx.column() )
{
case 0:
{
ViewManager::instance()->show( item->query()->displayQuery() );
break;
}
default:
break;
}
}
}
QString
TreeView::guid() const
{

View File

@@ -40,6 +40,7 @@ class ViewHeader;
class AnimatedSpinner;
class OverlayWidget;
class TreeModel;
class TreeItemDelegate;
class DLLEXPORT TreeView : public QTreeView
{
@@ -65,8 +66,6 @@ public:
virtual bool jumpToCurrentTrack();
QModelIndex hoveredIndex() const { return m_hoveredIndex; }
bool updatesContextView() const { return m_updateContextView; }
void setUpdatesContextView( bool b ) { m_updateContextView = b; }
@@ -81,10 +80,7 @@ protected:
virtual void resizeEvent( QResizeEvent* event );
virtual void keyPressEvent( QKeyEvent* event );
void wheelEvent( QWheelEvent* event );
void mouseMoveEvent( QMouseEvent* event );
void mousePressEvent( QMouseEvent* event );
void leaveEvent( QEvent* event );
virtual void wheelEvent( QWheelEvent* event );
protected slots:
virtual void currentChanged( const QModelIndex& current, const QModelIndex& previous );
@@ -99,17 +95,15 @@ private slots:
void onMenuTriggered( int action );
private:
void updateHoverIndex( const QPoint& pos );
ViewHeader* m_header;
OverlayWidget* m_overlay;
TreeModel* m_model;
TreeProxyModel* m_proxyModel;
TreeItemDelegate* m_delegate;
AnimatedSpinner* m_loadingSpinner;
bool m_updateContextView;
QModelIndex m_hoveredIndex;
QModelIndex m_contextMenuIndex;
Tomahawk::ContextMenu* m_contextMenu;

View File

@@ -30,7 +30,7 @@
namespace Tomahawk
{
QHash< QString, peerinfo_ptr > PeerInfo::s_peersByCacheKey = QHash< QString, peerinfo_ptr >();
QHash< QString, peerinfo_wptr > PeerInfo::s_peersByCacheKey = QHash< QString, peerinfo_wptr >();
QHash< SipPlugin*, peerinfo_ptr > PeerInfo::s_selfPeersBySipPlugin = QHash< SipPlugin*, peerinfo_ptr >();
@@ -57,6 +57,7 @@ PeerInfo::getSelf( SipPlugin* parent, PeerInfo::GetOptions options )
peerinfo_ptr selfPeer( new PeerInfo( parent, "local peerinfo don't use this id for anything" ) );
selfPeer->setWeakRef( selfPeer.toWeakRef() );
selfPeer->setContactId( "localpeer" );
// parent->setSelfPeer( selfPeer );
s_selfPeersBySipPlugin.insert( parent, selfPeer );
@@ -76,9 +77,9 @@ Tomahawk::peerinfo_ptr
PeerInfo::get( SipPlugin* parent, const QString& id, GetOptions options )
{
const QString key = peerCacheKey( parent, id );
if ( s_peersByCacheKey.contains( key ) )
if ( s_peersByCacheKey.contains( key ) && !s_peersByCacheKey.value( key ).isNull() )
{
return s_peersByCacheKey.value( key );
return s_peersByCacheKey.value( key ).toStrongRef();
}
// if AutoCreate isn't enabled nothing to do here
@@ -89,7 +90,7 @@ PeerInfo::get( SipPlugin* parent, const QString& id, GetOptions options )
peerinfo_ptr peerInfo( new PeerInfo( parent, id ) );
peerInfo->setWeakRef( peerInfo.toWeakRef() );
s_peersByCacheKey.insert( key, peerInfo );
s_peersByCacheKey.insert( key, peerInfo.toWeakRef() );
return peerInfo;
}
@@ -98,7 +99,13 @@ PeerInfo::get( SipPlugin* parent, const QString& id, GetOptions options )
QList< Tomahawk::peerinfo_ptr >
PeerInfo::getAll()
{
return s_peersByCacheKey.values();
QList< Tomahawk::peerinfo_ptr > strongRefs;
foreach ( Tomahawk::peerinfo_wptr wptr, s_peersByCacheKey.values() )
{
if ( !wptr.isNull() )
strongRefs << wptr.toStrongRef();
}
return strongRefs;
}
@@ -117,6 +124,7 @@ PeerInfo::PeerInfo( SipPlugin* parent, const QString& id )
PeerInfo::~PeerInfo()
{
// tDebug() << Q_FUNC_INFO;
s_peersByCacheKey.remove( s_peersByCacheKey.key( weakRef() ) );
delete m_avatar;
delete m_fancyAvatar;
}
@@ -256,7 +264,7 @@ PeerInfo::setSipInfo( const SipInfo& sipInfo )
m_sipInfo = sipInfo;
tLog() << "id: " << id() << " info changed" << sipInfo;
tLog() << "id:" << id() << "info changed" << sipInfo;
emit sipInfoChanged();
}
@@ -297,13 +305,14 @@ PeerInfo::setAvatar( const QPixmap& avatar )
m_avatarHash = hash;
m_avatarBuffer = ba;
delete m_avatar;
delete m_fancyAvatar;
m_avatar = 0;
m_fancyAvatar = 0;
TomahawkUtils::Cache::instance()->putData( "Sources", 7776000000 /* 90 days */, id(), ba );
Q_ASSERT( !contactId().isEmpty() );
TomahawkUtils::Cache::instance()->putData( "Sources", 7776000000 /* 90 days */, contactId(), ba );
}
@@ -312,19 +321,15 @@ PeerInfo::avatar( TomahawkUtils::ImageMode style, const QSize& size ) const
{
if ( !m_avatar )
{
if ( m_avatarBuffer.isEmpty() )
m_avatarBuffer = TomahawkUtils::Cache::instance()->getData( "Sources", id() ).toByteArray();
tDebug() << "Avatar for:" << id();
Q_ASSERT( !contactId().isEmpty() );
if ( m_avatarBuffer.isEmpty() && !contactId().isEmpty() )
m_avatarBuffer = TomahawkUtils::Cache::instance()->getData( "Sources", contactId() ).toByteArray();
m_avatar = new QPixmap();
if ( !m_avatarBuffer.isEmpty() )
m_avatar->loadFromData( m_avatarBuffer );
if ( m_avatar->isNull() )
{
delete m_avatar;
m_avatar = 0;
}
m_avatarBuffer.clear();
}
@@ -336,7 +341,7 @@ PeerInfo::avatar( TomahawkUtils::ImageMode style, const QSize& size ) const
{
pixmap = *m_fancyAvatar;
}
else if ( m_avatar )
else if ( m_avatar && !m_avatar->isNull() )
{
pixmap = *m_avatar;
}

View File

@@ -119,7 +119,7 @@ private:
PeerInfo( SipPlugin* parent, const QString& id );
void announce();
static QHash< QString, peerinfo_ptr > s_peersByCacheKey;
static QHash< QString, peerinfo_wptr > s_peersByCacheKey;
static QHash< SipPlugin*, peerinfo_ptr > s_selfPeersBySipPlugin;
QWeakPointer< Tomahawk::PeerInfo > m_ownRef;

View File

@@ -35,12 +35,13 @@ public:
{
}
SipInfoPrivate( const SipInfoPrivate& other ) : QSharedData( other ),
visible(other.visible),
host(other.host),
port(other.port),
nodeId(other.nodeId),
key(other.key)
SipInfoPrivate( const SipInfoPrivate& other )
: QSharedData( other )
, visible( other.visible )
, host( other.host )
, port( other.port )
, nodeId( other.nodeId )
, key( other.key )
{
}
~SipInfoPrivate() { }
@@ -93,10 +94,10 @@ SipInfo::clear()
bool
SipInfo::isValid() const
{
// qDebug() << Q_FUNC_INFO << d->visible << d->host.hostName() << d->port << d->nodeId << d->key;
if( !d->visible.isNull() )
// tDebug() << Q_FUNC_INFO << d->visible << d->host << d->port << d->nodeId << d->key;
if ( !d->visible.isNull() )
{
if(
if (
// visible and all data available
( d->visible.toBool() && !d->host.isEmpty() && ( d->port > 0 ) && !d->nodeId.isNull() && !d->key.isNull() )
// invisible and no data available
@@ -112,7 +113,7 @@ SipInfo::isValid() const
void
SipInfo::setVisible( bool visible )
{
d->visible.setValue(visible);
d->visible.setValue( visible );
}
@@ -195,7 +196,7 @@ SipInfo::toJson() const
// build variant map
QVariantMap m;
m["visible"] = isVisible();
if( isVisible() )
if ( isVisible() )
{
m["ip"] = host();
m["port"] = port();
@@ -227,7 +228,7 @@ SipInfo::fromJson( QString json )
QVariantMap m = v.toMap();
info.setVisible( m["visible"].toBool() );
if( m["visible"].toBool() )
if ( m["visible"].toBool() )
{
info.setHost( m["host"].toString() );
info.setPort( m["port"].toInt() );
@@ -242,7 +243,7 @@ SipInfo::fromJson( QString json )
QDebug
operator<< ( QDebug dbg, const SipInfo& info )
{
if( !info.isValid() )
if ( !info.isValid() )
dbg.nospace() << "info is invalid";
else
dbg.nospace() << info.toJson();
@@ -250,7 +251,9 @@ operator<< ( QDebug dbg, const SipInfo& info )
return dbg.maybeSpace();
}
bool operator==( const SipInfo& one, const SipInfo& two )
bool
operator==( const SipInfo& one, const SipInfo& two )
{
// check valid/invalid combinations first, so we don't try to access any invalid sipInfos (->assert)
if ( ( one.isValid() && !two.isValid() ) || ( !one.isValid() && two.isValid() ) )
@@ -272,6 +275,7 @@ bool operator==( const SipInfo& one, const SipInfo& two )
return false;
}
const QString
SipInfo::debugString() const
{
@@ -281,6 +285,5 @@ SipInfo::debugString() const
.arg( d->port )
.arg( d->nodeId )
.arg( d->key );
}

View File

@@ -73,6 +73,9 @@ ChartDataLoader::go()
foreach ( const Tomahawk::InfoSystem::InfoStringHash& album, m_data )
{
if ( album["artist"].isEmpty() )
continue;
artist_ptr artistPtr = Artist::get( album[ "artist" ], false );
album_ptr albumPtr = Album::get( artistPtr, album[ "album" ], false );
album_ptrs << albumPtr;

View File

@@ -34,7 +34,9 @@
#ifndef ENABLE_HEADLESS
#include "TomahawkSettingsGui.h"
#include "breakpad/BreakPad.h"
#ifdef WITH_BREAKPAD
#include "breakpad/BreakPad.h"
#endif
#endif

View File

@@ -229,9 +229,13 @@ SourceDelegate::paintCollection( QPainter* painter, const QStyleOptionViewItem&
QRect textRect = option.rect.adjusted( iconRect.width() + 8, 6, -figWidth - ( figWidth ? 28 : 0 ), 0 );
QString text = painter->fontMetrics().elidedText( name, Qt::ElideRight, textRect.width() );
painter->drawText( textRect, text );
{
QTextOption to;
to.setWrapMode( QTextOption::NoWrap );
painter->drawText( textRect, text, to );
}
QColor descColor = QColor( "#8d8d8d" );
QColor descColor = option.palette.color( QPalette::Text ).lighter( 180 );
if ( type == SourcesModel::ScriptCollection && //you cannot select a non-script collection anyway
option.state.testFlag( QStyle::State_Selected ) )
{
@@ -300,7 +304,7 @@ SourceDelegate::paintCollection( QPainter* painter, const QStyleOptionViewItem&
m_lockRects.remove( index );
if ( isPlaying )
descColor = Qt::black;
descColor = option.palette.color( QPalette::Text );
}
}
@@ -312,10 +316,12 @@ SourceDelegate::paintCollection( QPainter* painter, const QStyleOptionViewItem&
}
textRect.adjust( 0, 0, 0, 2 );
text = painter->fontMetrics().elidedText( desc, Qt::ElideRight, textRect.width() - 8 );
QTextOption to( Qt::AlignVCenter );
to.setWrapMode( QTextOption::NoWrap );
painter->setPen( descColor );
painter->drawText( textRect, text, to );
{
QTextOption to( Qt::AlignVCenter );
to.setWrapMode( QTextOption::NoWrap );
painter->setPen( descColor );
painter->drawText( textRect, text, to );
}
bool shouldPaintTrackCount = false;
if ( type == SourcesModel::Collection )

View File

@@ -271,7 +271,7 @@ SourceTreeView::setupMenus()
connect( deletePlaylistAction, SIGNAL( triggered() ), SLOT( deletePlaylist() ) );
connect( copyPlaylistAction, SIGNAL( triggered() ), SLOT( copyPlaylistLink() ) );
connect( addToLocalAction, SIGNAL( triggered() ), SLOT( addToLocal() ) );
connect( latchOnAction, SIGNAL( triggered() ), SLOT( latchOnOrCatchUp() ), Qt::QueuedConnection );
connect( latchOnAction, SIGNAL( triggered() ), SLOT( latchOnOrCatchUp() ) );
}

View File

@@ -34,7 +34,6 @@ ScriptCollectionItem::~ScriptCollectionItem()
{
model()->removeSourceItemLink( this );
ViewManager::instance()->destroyPage( m_page );
dynamic_cast< QObject* >( m_page )->deleteLater();
}

View File

@@ -39,6 +39,7 @@
#include "utils/ImageRegistry.h"
#include "utils/TomahawkUtilsGui.h"
#include "utils/Logger.h"
#include "TomahawkApp.h"
/// SourceItem
@@ -156,23 +157,26 @@ SourceItem::tooltip() const
QString t;
// This is kind of debug output for now.
t.append( "<PRE>" );
QString narf("%1: %2\n");
t.append( narf.arg( "id" ).arg( m_source->id() ) );
t.append( narf.arg( "username" ).arg( m_source->nodeId() ) );
t.append( narf.arg( "friendlyname" ).arg( m_source->friendlyName() ) );
t.append( narf.arg( "dbfriendlyname" ).arg( m_source->dbFriendlyName() ) );
t.append("\n");
foreach( Tomahawk::peerinfo_ptr p, m_source->peerInfos() )
bool showDebugInfo = APP->arguments().contains( "--verbose" );
if ( showDebugInfo )
{
QString line( p->sipPlugin()->serviceName() + p->sipPlugin()->friendlyName() + ": " + p->id() + " " + p->friendlyName() );
t.append( line + "\n\n" );
// This is kind of debug output for now.
t.append( "<PRE>" );
QString narf("%1: %2\n");
t.append( narf.arg( "id" ).arg( m_source->id() ) );
t.append( narf.arg( "username" ).arg( m_source->nodeId() ) );
t.append( narf.arg( "friendlyname" ).arg( m_source->friendlyName() ) );
t.append( narf.arg( "dbfriendlyname" ).arg( m_source->dbFriendlyName() ) );
t.append("\n");
foreach( Tomahawk::peerinfo_ptr p, m_source->peerInfos() )
{
QString line( p->sipPlugin()->serviceName() + p->sipPlugin()->friendlyName() + ": " + p->id() + " " + p->friendlyName() );
t.append( line + "\n\n" );
}
t.append( "</PRE>" );
}
t.append( "</PRE>" );
if ( !m_source->currentTrack().isNull() )
t.append( m_source->textStatus() );

View File

@@ -126,8 +126,6 @@ TemporaryPageItem::IDValue() const
void
TemporaryPageItem::removeFromList()
{
ViewManager::instance()->destroyPage( m_page );
model()->removeSourceItemLink( this );
int idx = parent()->children().indexOf( this );
@@ -137,7 +135,7 @@ TemporaryPageItem::removeFromList()
emit removed();
delete m_page;
ViewManager::instance()->destroyPage( m_page );
deleteLater();
}

View File

@@ -201,16 +201,19 @@ Api_v1::sid( QxtWebRequestEvent* event, QString unused )
void
Api_v1::processSid( QxtWebRequestEvent* event, Tomahawk::result_ptr& rp, QSharedPointer< QIODevice >& iodev )
{
if ( !iodev || iodev.isNull() )
tDebug( LOGVERBOSE ) << Q_FUNC_INFO;
if ( !iodev || !rp )
{
return send404( event ); // 503?
}
m_ioDevice = iodev;
QxtWebPageEvent* e = new QxtWebPageEvent( event->sessionID, event->requestID, iodev.data() );
e->streaming = iodev->isSequential();
e->contentType = rp->mimetype().toLatin1();
if ( rp->size() > 0 )
e->headers.insert( "Content-Length", QString::number( rp->size() ) );
postEvent( e );
}

View File

@@ -78,7 +78,9 @@ public slots:
private:
void processSid( QxtWebRequestEvent* event, Tomahawk::result_ptr&, QSharedPointer< QIODevice >& );
QxtWebRequestEvent* m_storedEvent;
QSharedPointer< QIODevice > m_ioDevice;
};
#endif

View File

@@ -7,16 +7,18 @@ ENDIF( ${CMAKE_VERSION} VERSION_GREATER 2.8.3 )
setup_qt()
SET(qxtweb "qxtweb")
SET( qxtcore "core" )
SET( qxtnet "network" )
SET( qxtweb "web" )
ADD_DEFINITIONS(-Wall -O2 -DNDEBUG)
IF(NOT WIN32)
ADD_DEFINITIONS(-fPIC)
ADD_DEFINITIONS( -Wall -O2 -DNDEBUG )
IF( NOT WIN32 )
ADD_DEFINITIONS( -fPIC )
ENDIF()
ADD_DEFINITIONS( -DBUILD_QXT_CORE -DBUILD_QXT_WEB )
INCLUDE_DIRECTORIES( ${qxtweb} )
INCLUDE_DIRECTORIES( ${qxtweb} ${qxtcore} ${qxtnet} )
macro(create_qxtweb_fancy_header simpleHeader fancyHeader)
file(WRITE ${CMAKE_BINARY_DIR}/QxtWeb/${fancyHeader} "#include \"${simpleHeader}\"" )
@@ -41,24 +43,23 @@ SET( sources
${qxtweb}/qxtwebcontent.cpp
${qxtweb}/qxtwebevent.cpp
${qxtweb}/qxtwebservicedirectory.cpp
${qxtweb}/qxtwebslotservice.cpp
${qxtweb}/qxtwebcgiservice.cpp
${qxtweb}/qhttpheader.cpp
${qxtweb}/qxtwebslotservice.cpp
# Ripped bits of QxtCore:
${qxtweb}/qxtmetaobject.cpp
${qxtweb}/qxtnull.cpp
${qxtweb}/qxtfifo.cpp
${qxtcore}/qxtmetaobject.cpp
${qxtcore}/qxtnull.cpp
${qxtcore}/qxtfifo.cpp
# QxtNetwork
${qxtweb}/qxtsslserver.cpp
${qxtnet}/qxtsslserver.cpp
# automoc hack
${qxtweb}/qxtboundfunction.h
${qxtcore}/qxtboundfunction.h
)
ADD_LIBRARY(qxtweb-standalone STATIC
ADD_LIBRARY( qxtweb-standalone STATIC
${mocstuff}
${headers}
${sources}
@@ -69,6 +70,6 @@ target_link_libraries( qxtweb-standalone
${QT_LIBRARIES}
)
qt5_use_modules(qxtweb-standalone Network)
qt5_use_modules( qxtweb-standalone Network )
set_target_properties(qxtweb-standalone PROPERTIES AUTOMOC TRUE)
set_target_properties( qxtweb-standalone PROPERTIES AUTOMOC TRUE )

View File

@@ -137,17 +137,14 @@ QxtFifo::QxtFifo(const QByteArray &prime, QObject *parent) : QIODevice(parent)
QXT_INIT_PRIVATE(QxtFifo);
setOpenMode(QIODevice::ReadWrite);
// Since we're being constructed, access to the internals is safe
QxtFifoNode *head;
int available;
QxtFifoNode* node;
#if QT_VERSION >= 0x50000
head = qxt_d().head.load();
available = qxt_d().available.load();
node = qxt_d().head.load();
#else
head = qxt_d().head;
available = qxt_d().available;
node = qxt_d().head;
#endif
node->content = prime;
qxt_d().available.QXT_ADD( prime.size() );
}
/*!

View File

@@ -51,6 +51,7 @@
#define QXT_DLLEXPORT DO_NOT_USE_THIS_ANYMORE
// Define QXT_STATIC here so that the linker of projects that include the static library use the correct call method.
#define QXT_STATIC
#if !defined(QXT_STATIC) && !defined(QXT_DOXYGEN_RUN)

View File

@@ -1,27 +1,33 @@
/****************************************************************************
**
** Copyright (C) Qxt Foundation. Some rights reserved.
**
** This file is part of the QxtCore module of the Qxt library.
**
** This library is free software; you can redistribute it and/or modify it
** under the terms of the Common Public License, version 1.0, as published
** by IBM, and/or under the terms of the GNU Lesser General Public License,
** version 2.1, as published by the Free Software Foundation.
**
** This file is provided "AS IS", without WARRANTIES OR CONDITIONS OF ANY
** KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY
** WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR
** FITNESS FOR A PARTICULAR PURPOSE.
**
** You should have received a copy of the CPL and the LGPL along with this
** file. See the LICENSE file and the cpl1.0.txt/lgpl-2.1.txt files
** included with the source distribution for more information.
** If you did not receive a copy of the licenses, contact the Qxt Foundation.
**
** <http://libqxt.org> <foundation@libqxt.org>
**
****************************************************************************/
** Copyright (c) 2006 - 2011, the LibQxt project.
** See the Qxt AUTHORS file for a list of authors and copyright holders.
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are met:
** * Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** * Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in the
** documentation and/or other materials provided with the distribution.
** * Neither the name of the LibQxt project nor the
** names of its contributors may be used to endorse or promote products
** derived from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
** WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
** DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
** DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
** (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
** ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
** SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**
** <http://libqxt.org> <foundation@libqxt.org>
*****************************************************************************/
#ifndef QXTMETATYPE_H
#define QXTMETATYPE_H
@@ -32,13 +38,19 @@
#include <QtDebug>
#include <qxtglobal.h>
#if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0))
#define qxtcreate create
#else
#define qxtcreate construct
#endif
template <typename T>
class /*QXT_CORE_EXPORT*/ QxtMetaType
{
public:
static inline T* construct(const T* copy = 0)
{
return QMetaType::construct(qMetaTypeId<T>(), reinterpret_cast<const void*>(copy));
return QMetaType::qxtcreate(qMetaTypeId<T>(), reinterpret_cast<const void*>(copy));
}
static inline void destroy(T* data)
@@ -111,11 +123,7 @@ public:
inline void* qxtConstructByName(const char* typeName, const void* copy = 0)
{
#if QT_VERSION >= QT_VERSION_CHECK( 5, 0, 0 )
return QMetaType::create(QMetaType::type(typeName), copy);
#else
return QMetaType::construct(QMetaType::type(typeName), copy);
#endif
return QMetaType::qxtcreate(QMetaType::type(typeName), copy);
}
inline void qxtDestroyByName(const char* typeName, void* data)

View File

@@ -39,7 +39,7 @@
#include <QSslSocket>
class QxtSslServerPrivate;
class QxtSslServer : public QTcpServer
class QXT_NETWORK_EXPORT QxtSslServer : public QTcpServer
{
Q_OBJECT
public:

View File

@@ -1,2 +0,0 @@
#include "qxtabstracthttpconnector.h"

View File

@@ -1,2 +0,0 @@
#include "qxtabstractwebservice.h"

View File

@@ -1,2 +0,0 @@
#include "qxtabstractwebsessionmanager.h"

View File

@@ -1,2 +0,0 @@
#include "qxthtmltemplate.h"

View File

@@ -1 +0,0 @@
#include "qxtabstracthttpconnector.h"

View File

@@ -1,2 +0,0 @@
#include "qxthttpsessionmanager.h"

View File

@@ -1,2 +0,0 @@
#include "qxtmail.h"

View File

@@ -1 +0,0 @@
#include "qxtabstracthttpconnector.h"

View File

@@ -1,2 +0,0 @@
#include "qxtsendmail.h"

View File

@@ -1 +0,0 @@
#include "qxtweb.h"

View File

@@ -1 +0,0 @@
#include "qxtwebcgiservice.h"

View File

@@ -1,2 +0,0 @@
#include "qxtwebcontent.h"

View File

@@ -1,2 +0,0 @@
#include "qxtwebevent.h"

View File

@@ -1,2 +0,0 @@
#include "qxtwebevent.h"

View File

@@ -1 +0,0 @@
#include "qxtwebevent.h"

View File

@@ -1,2 +0,0 @@
#include "qxtwebevent.h"

View File

@@ -1,2 +0,0 @@
#include "qxtwebevent.h"

View File

@@ -1 +0,0 @@
#include "qxtwebevent.h"

View File

@@ -1,2 +0,0 @@
#include "qxtwebevent.h"

View File

@@ -1,2 +0,0 @@
#include "qxtwebservicedirectory.h"

View File

@@ -1,2 +0,0 @@
#include "qxtwebslotservice.h"

View File

@@ -1 +0,0 @@
#include "qxtwebevent.h"

View File

@@ -1,251 +0,0 @@
/****************************************************************************
** Copyright (c) 2006 - 2011, the LibQxt project.
** See the Qxt AUTHORS file for a list of authors and copyright holders.
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are met:
** * Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** * Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in the
** documentation and/or other materials provided with the distribution.
** * Neither the name of the LibQxt project nor the
** names of its contributors may be used to endorse or promote products
** derived from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
** WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
** DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
** DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
** (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
** ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
** SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**
** <http://libqxt.org> <foundation@libqxt.org>
*****************************************************************************/
#include "qxtglobal.h"
/*!
\headerfile <QxtGlobal>
\title Global Qxt Declarations
\inmodule QxtCore
\brief The <QxtGlobal> header provides basic declarations and
is included by all other Qxt headers.
*/
/*!
\macro QXT_VERSION
\relates <QxtGlobal>
This macro expands a numeric value of the form 0xMMNNPP (MM =
major, NN = minor, PP = patch) that specifies Qxt's version
number. For example, if you compile your application against Qxt
0.4.0, the QXT_VERSION macro will expand to 0x000400.
You can use QXT_VERSION to use the latest Qt features where
available. For example:
\code
#if QXT_VERSION >= 0x000400
qxtTabWidget->setTabMovementMode(QxtTabWidget::InPlaceMovement);
#endif
\endcode
\sa QXT_VERSION_STR, qxtVersion()
*/
/*!
\macro QXT_VERSION_STR
\relates <QxtGlobal>
This macro expands to a string that specifies Qxt's version number
(for example, "0.4.0"). This is the version against which the
application is compiled.
\sa qxtVersion(), QXT_VERSION
*/
/*!
\relates <QxtGlobal>
Returns the version number of Qxt at run-time as a string (for
example, "0.4.0"). This may be a different version than the
version the application was compiled against.
\sa QXT_VERSION_STR
*/
const char * qxtVersion()
{
return QXT_VERSION_STR;
}
/*!
\headerfile <QxtPimpl>
\title The Qxt private implementation
\inmodule QxtCore
\brief The <QxtPimpl> header provides tools for hiding
details of a class.
Application code generally doesn't have to be concerned about hiding its
implementation details, but when writing library code it is important to
maintain a constant interface, both source and binary. Maintaining a constant
source interface is easy enough, but keeping the binary interface constant
means moving implementation details into a private class. The PIMPL, or
d-pointer, idiom is a common method of implementing this separation. QxtPimpl
offers a convenient way to connect the public and private sides of your class.
\section1 Getting Started
Before you declare the public class, you need to make a forward declaration
of the private class. The private class must have the same name as the public
class, followed by the word Private. For example, a class named MyTest would
declare the private class with:
\code
class MyTestPrivate;
\endcode
\section1 The Public Class
Generally, you shouldn't keep any data members in the public class without a
good reason. Functions that are part of the public interface should be declared
in the public class, and functions that need to be available to subclasses (for
calling or overriding) should be in the protected section of the public class.
To connect the private class to the public class, include the
QXT_DECLARE_PRIVATE macro in the private section of the public class. In the
example above, the private class is connected as follows:
\code
private:
QXT_DECLARE_PRIVATE(MyTest)
\endcode
Additionally, you must include the QXT_INIT_PRIVATE macro in the public class's
constructor. Continuing with the MyTest example, your constructor might look
like this:
\code
MyTest::MyTest() {
// initialization
QXT_INIT_PRIVATE(MyTest);
}
\endcode
\section1 The Private Class
As mentioned above, data members should usually be kept in the private class.
This allows the memory layout of the private class to change without breaking
binary compatibility for the public class. Functions that exist only as
implementation details, or functions that need access to private data members,
should be implemented here.
To define the private class, inherit from the template QxtPrivate class, and
include the QXT_DECLARE_PUBLIC macro in its public section. The template
parameter should be the name of the public class. For example:
\code
class MyTestPrivate : public QxtPrivate<MyTest> {
public:
MyTestPrivate();
QXT_DECLARE_PUBLIC(MyTest)
};
\endcode
\section1 Accessing Private Members
Use the qxt_d() function (actually a function-like object) from functions in
the public class to access the private class. Similarly, functions in the
private class can invoke functions in the public class by using the qxt_p()
function (this one's actually a function).
For example, assume that MyTest has methods named getFoobar and doBaz(),
and MyTestPrivate has a member named foobar and a method named doQuux().
The code might resemble this example:
\code
int MyTest::getFoobar() {
return qxt_d().foobar;
}
void MyTestPrivate::doQuux() {
qxt_p().doBaz(foobar);
}
\endcode
*/
/*!
* \macro QXT_DECLARE_PRIVATE(PUB)
* \relates <QxtPimpl>
* Declares that a public class has a related private class.
*
* This shuold be put in the private section of the public class. The
* parameter \a PUB must be the name of the public class.
*/
/*!
* \macro QXT_DECLARE_PUBLIC(PUB)
* \relates <QxtPimpl>
* Declares that a private class has a related public class named \a PUB.
*
* This may be put anywhere in the declaration of the private class. The parameter is the name of the public class.
*/
/*!
* \macro QXT_INIT_PRIVATE(PUB)
* \relates <QxtPimpl>
* Initializes resources owned by the private class.
*
* This should be called from the public class's constructor,
* before qxt_d() is used for the first time. The parameter \a PUB must be
* the name of the public class.
*/
/*!
* \macro QXT_D(PUB)
* \relates <QxtPimpl>
* Returns a reference in the current scope named "d" to the private class
* associated with the public class \a PUB.
*
* This function is only available in a class using QXT_DECLARE_PRIVATE().
*/
/*!
* \macro QXT_P(PUB)
* \relates <QxtPimpl>
* Creates a reference in the current scope named "q" to the public class
* named \a PUB.
*
* This macro only works in a class using QXT_DECLARE_PUBLIC().
*/
/*!
* \fn QxtPrivate<PUB>& PUB::qxt_d()
* \relates <QxtPimpl>
* Returns a reference to the private class.
*
* This function is only available in a class using \a QXT_DECLARE_PRIVATE.
*/
/*!
* \fn const QxtPrivate<PUB>& PUB::qxt_d() const
* \relates <QxtPimpl>
* Returns a const reference to the private class.
*
* This function is only available in a class using \a QXT_DECLARE_PRIVATE.
* This overload will be automatically used in const functions.
*/
/*!
* \fn PUB& QxtPrivate::qxt_p()
* \relates <QxtPimpl>
* Returns a reference to the public class.
*
* This function is only available in a class using QXT_DECLARE_PUBLIC().
*/
/*!
* \fn const PUB& QxtPrivate::qxt_p() const
* \relates <QxtPimpl>
* Returns a const reference to the public class.
*
* This function is only available in a class using QXT_DECLARE_PUBLIC().
* This overload will be automatically used in const functions.
*/

View File

@@ -1,46 +0,0 @@
#ifndef QXTWEB_H_INCLUDED
/****************************************************************************
** Copyright (c) 2006 - 2011, the LibQxt project.
** See the Qxt AUTHORS file for a list of authors and copyright holders.
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are met:
** * Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** * Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in the
** documentation and/or other materials provided with the distribution.
** * Neither the name of the LibQxt project nor the
** names of its contributors may be used to endorse or promote products
** derived from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
** WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
** DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
** DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
** (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
** ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
** SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**
** <http://libqxt.org> <foundation@libqxt.org>
*****************************************************************************/
#define QXTWEB_H_INCLUDED
#include "qxtabstracthttpconnector.h"
#include "qxtabstractwebservice.h"
#include "qxtabstractwebsessionmanager.h"
#include "qxthtmltemplate.h"
#include "qxthttpsessionmanager.h"
#include "qxtwebcgiservice.h"
#include "qxtwebcontent.h"
#include "qxtwebevent.h"
#include "qxtwebjsonrpcservice.h"
#include "qxtwebservicedirectory.h"
#include "qxtwebslotservice.h"
#endif // QXTWEB_H_INCLUDED

View File

@@ -1,445 +0,0 @@
/****************************************************************************
** Copyright (c) 2006 - 2011, the LibQxt project.
** See the Qxt AUTHORS file for a list of authors and copyright holders.
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are met:
** * Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** * Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in the
** documentation and/or other materials provided with the distribution.
** * Neither the name of the LibQxt project nor the
** names of its contributors may be used to endorse or promote products
** derived from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
** WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
** DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
** DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
** (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
** ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
** SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**
** <http://libqxt.org> <foundation@libqxt.org>
*****************************************************************************/
/*!
\class QxtWebCgiService
\inmodule QxtWeb
\brief The QxtWebCgiService class provides a CGI/1.1 gateway for QxtWeb
TODO: write docs
TODO: implement timeout
*/
#include "qxtwebcgiservice.h"
#include "qxtwebcgiservice_p.h"
#include "qxtwebevent.h"
#include "qxtwebcontent.h"
#include <QMap>
#include <QFile>
#include <QProcess>
#include <QtDebug>
QxtCgiRequestInfo::QxtCgiRequestInfo() : sessionID(0), requestID(0), eventSent(false), terminateSent(false) {}
QxtCgiRequestInfo::QxtCgiRequestInfo(QxtWebRequestEvent* req) : sessionID(req->sessionID), requestID(req->requestID), eventSent(false), terminateSent(false) {}
/*!
* Constructs a QxtWebCgiService object with the specified session \a manager and \a parent.
* This service will invoke the specified \a binary to handle incoming requests.
*
* Often, the session manager will also be the parent, but this is not a requirement.
*/
QxtWebCgiService::QxtWebCgiService(const QString& binary, QxtAbstractWebSessionManager* manager, QObject* parent) : QxtAbstractWebService(manager, parent)
{
QXT_INIT_PRIVATE(QxtWebCgiService);
qxt_d().binary = binary;
QObject::connect(&qxt_d().timeoutMapper, SIGNAL(mapped(QObject*)), &qxt_d(), SLOT(terminateProcess(QObject*)));
}
/*!
* Returns the path to the CGI script that will be executed to handle requests.
*
* \sa setBinary()
*/
QString QxtWebCgiService::binary() const
{
return qxt_d().binary;
}
/*!
* Sets the path to the CGI script \a bin that will be executed to handle requests.
*
* \sa binary()
*/
void QxtWebCgiService::setBinary(const QString& bin)
{
if (!QFile::exists(bin) || !(QFile::permissions(bin) & (QFile::ExeUser | QFile::ExeGroup | QFile::ExeOther)))
{
qWarning() << "QxtWebCgiService::setBinary: " + bin + " does not appear to be executable.";
}
qxt_d().binary = bin;
}
/*!
* Returns the maximum time a CGI script may execute, in milliseconds.
*
* The default value is 0, which indicates that CGI scripts will not be terminated
* due to long running times.
*
* \sa setTimeout()
*/
int QxtWebCgiService::timeout() const
{
return qxt_d().timeout;
}
/*!
* Sets the maximum \a time a CGI script may execute, in milliseconds.
*
* The timer is started when the script is launched. After the timeout elapses once,
* the script will be asked to stop, as QProcess::terminate(). (That is, the script
* will receive WM_CLOSE on Windows or SIGTERM on UNIX.) If the process has still
* failed to terminate after another timeout, it will be forcibly terminated, as
* QProcess::kill(). (That is, the script will receive TerminateProcess on Windows
* or SIGKILL on UNIX.)
*
* Set the timeout to 0 to disable this behavior; scripts will not be terminated
* due to excessive run time. This is the default behavior.
*
* CAUTION: Keep in mind that the timeout applies to the real running time of the
* script, not processor time used. A script that initiates a lengthy download
* may be interrupted while transferring data to the web browser. To avoid this
* behavior, see the timeoutOverride property to allow the script to request
* an extended timeout, or use a different QxtAbstractWebService object for
* serving streaming content or large files.
*
*
* \sa timeout(), timeoutOverride(), setTimeoutOverride(), QProcess::terminate(), QProcess::kill()
*/
void QxtWebCgiService::setTimeout(int time)
{
qxt_d().timeout = time;
}
/*!
* Returns whether or not to allow scripts to override the timeout.
*
* \sa setTimeoutOverride(), setTimeout()
*/
bool QxtWebCgiService::timeoutOverride() const
{
return qxt_d().timeoutOverride;
}
/*!
* Sets whether or not to allow scripts to override the timeout.
* Scripts are allowed to override if \a enable is \c true.
*
* As an extension to the CGI/1.1 gateway specification, a CGI script may
* output a "X-QxtWeb-Timeout" header to change the termination timeout
* on a per-script basis. Only enable this option if you trust the scripts
* being executed.
*
* \sa timeoutOverride(), setTimeout()
*/
void QxtWebCgiService::setTimeoutOverride(bool enable)
{
qxt_d().timeoutOverride = enable;
}
/*!
* \reimp
*/
void QxtWebCgiService::pageRequestedEvent(QxtWebRequestEvent* event)
{
// Create the process object and initialize connections
QProcess* process = new QProcess(this);
qxt_d().requests[process] = QxtCgiRequestInfo(event);
qxt_d().processes[event->content] = process;
QxtCgiRequestInfo& requestInfo = qxt_d().requests[process];
QObject::connect(process, SIGNAL(readyRead()), &qxt_d(), SLOT(processReadyRead()));
QObject::connect(process, SIGNAL(finished(int, QProcess::ExitStatus)), &qxt_d(), SLOT(processFinished()));
QObject::connect(process, SIGNAL(error(QProcess::ProcessError)), &qxt_d(), SLOT(processFinished()));
requestInfo.timeout = new QTimer(process);
qxt_d().timeoutMapper.setMapping(requestInfo.timeout, process);
QObject::connect(requestInfo.timeout, SIGNAL(timeout()), &qxt_d().timeoutMapper, SLOT(map()));
// Initialize the system environment
QStringList s_env = process->systemEnvironment();
QMap<QString, QString> env;
foreach(const QString& entry, s_env)
{
int pos = entry.indexOf('=');
env[entry.left(pos)] = entry.mid(pos + 1);
}
// Populate CGI/1.1 environment variables
env["SERVER_SOFTWARE"] = QString("QxtWeb/" QXT_VERSION_STR);
env["SERVER_NAME"] = event->url.host();
env["GATEWAY_INTERFACE"] = "CGI/1.1";
if (event->headers.contains("X-Request-Protocol"))
env["SERVER_PROTOCOL"] = event->headers.value("X-Request-Protocol");
else
env.remove("SERVER_PROTOCOL");
if (event->url.port() != -1)
env["SERVER_PORT"] = QString::number(event->url.port());
else
env.remove("SERVER_PORT");
env["REQUEST_METHOD"] = event->method;
env["PATH_INFO"] = event->url.path();
env["PATH_TRANSLATED"] = event->url.path(); // CGI/1.1 says we should resolve this, but we have no logical interpretation
env["SCRIPT_NAME"] = event->originalUrl.path().remove(QRegExp(QRegExp::escape(event->url.path()) + '$'));
env["SCRIPT_FILENAME"] = qxt_d().binary; // CGI/1.1 doesn't define this but PHP demands it
env.remove("REMOTE_HOST");
env["REMOTE_ADDR"] = event->remoteAddress.toString();
// TODO: If we ever support HTTP authentication, we should use these
env.remove("AUTH_TYPE");
env.remove("REMOTE_USER");
env.remove("REMOTE_IDENT");
if (event->contentType.isEmpty())
{
env.remove("CONTENT_TYPE");
env.remove("CONTENT_LENGTH");
}
else
{
env["CONTENT_TYPE"] = event->contentType;
env["CONTENT_LENGTH"] = QString::number(event->content->unreadBytes());
}
#if QT_VERSION >= QT_VERSION_CHECK( 5, 0, 0 )
env["QUERY_STRING"] = event->url.query();
#else
env["QUERY_STRING"] = event->url.encodedQuery();
#endif
// Populate HTTP header environment variables
QMultiHash<QString, QString>::const_iterator iter = event->headers.constBegin();
while (iter != event->headers.constEnd())
{
QString key = "HTTP_" + iter.key().toUpper().replace('-', '_');
if (key != "HTTP_CONTENT_TYPE" && key != "HTTP_CONTENT_LENGTH")
env[key] = iter.value();
iter++;
}
// Populate HTTP_COOKIE parameter
iter = event->cookies.constBegin();
QString cookies;
while (iter != event->cookies.constEnd())
{
if (!cookies.isEmpty())
cookies += "; ";
cookies += iter.key() + '=' + iter.value();
iter++;
}
if (!cookies.isEmpty())
env["HTTP_COOKIE"] = cookies;
// Load environment into process space
QStringList p_env;
QMap<QString, QString>::iterator env_iter = env.begin();
while (env_iter != env.end())
{
p_env << env_iter.key() + '=' + env_iter.value();
env_iter++;
}
process->setEnvironment(p_env);
// Launch process
#if QT_VERSION >= QT_VERSION_CHECK( 5, 0, 0 )
if (event->url.hasQuery() && event->url.query().contains('='))
#else
if (event->url.hasQuery() && event->url.encodedQuery().contains('='))
#endif
{
// CGI/1.1 spec says to pass the query on the command line if there's no embedded = sign
#if QT_VERSION >= QT_VERSION_CHECK( 5, 0, 0 )
process->start(qxt_d().binary + ' ' + event->url.query(), QIODevice::ReadWrite);
#else
process->start(qxt_d().binary + ' ' + event->url.encodedQuery(), QIODevice::ReadWrite);
#endif
}
else
{
process->start(qxt_d().binary, QIODevice::ReadWrite);
}
// Start the timeout
if(qxt_d().timeout > 0)
{
requestInfo.timeout->start(qxt_d().timeout);
}
// Transmit POST data
if (event->content)
{
QObject::connect(event->content, SIGNAL(readyRead()), &qxt_d(), SLOT(browserReadyRead()));
qxt_d().browserReadyRead(event->content);
}
}
/*!
* \internal
*/
void QxtWebCgiServicePrivate::browserReadyRead(QObject* o_content)
{
if (!o_content) o_content = sender();
QxtWebContent* content = static_cast<QxtWebContent*>(o_content); // this is a private class, no worries about type safety
// Read POST data and copy it to the process
QByteArray data = content->readAll();
if (!data.isEmpty())
processes[content]->write(data);
// If no POST data remains unsent, clean up
if (!content->unreadBytes() && processes.contains(content))
{
processes[content]->closeWriteChannel();
processes.remove(content);
}
}
/*!
* \internal
*/
void QxtWebCgiServicePrivate::processReadyRead()
{
QProcess* process = static_cast<QProcess*>(sender());
QxtCgiRequestInfo& request = requests[process];
QByteArray line;
while (process->canReadLine())
{
// Read in a CGI/1.1 header line
line = process->readLine().replace(QByteArray("\r"), ""); //krazy:exclude=doublequote_chars
if (line == "\n")
{
// An otherwise-empty line indicates the end of CGI/1.1 headers and the start of content
QObject::disconnect(process, SIGNAL(readyRead()), this, 0);
QxtWebPageEvent* event = 0;
int code = 200;
if (request.headers.contains("status"))
{
// CGI/1.1 defines a "Status:" header that dictates the HTTP response code
code = request.headers["status"].left(3).toInt();
if (code >= 300 && code < 400) // redirect
{
event = new QxtWebRedirectEvent(request.sessionID, request.requestID, request.headers["location"], code);
}
}
// If a previous header (currently just status) hasn't created an event, create a normal page event here
if (!event)
{
event = new QxtWebPageEvent(request.sessionID, request.requestID, process);
event->status = code;
}
// Add other response headers passed from CGI (currently only Content-Type is supported)
if (request.headers.contains("content-type"))
event->contentType = request.headers["content-type"].toUtf8();
// TODO: QxtWeb doesn't support transmitting arbitrary HTTP headers right now, but it may be desirable
// for applications that know what kind of server frontend they're using to allow scripts to send
// protocol-specific headers.
// Post the event
qxt_p().postEvent(event);
request.eventSent = true;
return;
}
else
{
// Since we haven't reached the end of headers yet, parse a header
int pos = line.indexOf(": ");
QByteArray hdrName = line.left(pos).toLower();
QByteArray hdrValue = line.mid(pos + 2).replace(QByteArray("\n"), ""); //krazy:exclude=doublequote_chars
if (hdrName == "set-cookie")
{
// Parse a new cookie and post an event to send it to the client
QList<QByteArray> cookies = hdrValue.split(',');
foreach(const QByteArray& cookie, cookies)
{
int equals = cookie.indexOf("=");
int semi = cookie.indexOf(";");
QByteArray cookieName = cookie.left(equals);
int age = cookie.toLower().indexOf("max-age=", semi);
int secs = -1;
if (age >= 0)
secs = cookie.mid(age + 8, cookie.indexOf(";", age) - age - 8).toInt();
if (secs == 0)
{
qxt_p().postEvent(new QxtWebRemoveCookieEvent(request.sessionID, cookieName));
}
else
{
QByteArray cookieValue = cookie.mid(equals + 1, semi - equals - 1);
QDateTime cookieExpires;
if (secs != -1)
cookieExpires = QDateTime::currentDateTime().addSecs(secs);
qxt_p().postEvent(new QxtWebStoreCookieEvent(request.sessionID, cookieName, cookieValue, cookieExpires));
}
}
}
else if(hdrName == "x-qxtweb-timeout")
{
if(timeoutOverride)
request.timeout->setInterval(hdrValue.toInt());
}
else
{
// Store other headers for later inspection
request.headers[hdrName] = hdrValue;
}
}
}
}
/*!
* \internal
*/
void QxtWebCgiServicePrivate::processFinished()
{
QProcess* process = static_cast<QProcess*>(sender());
QxtCgiRequestInfo& request = requests[process];
if (!request.eventSent)
{
// If no event was posted, issue an internal error
qxt_p().postEvent(new QxtWebErrorEvent(request.sessionID, request.requestID, 500, "Internal Server Error"));
}
// Clean up data structures
process->close();
QxtWebContent* key = processes.key(process);
if (key) processes.remove(key);
timeoutMapper.removeMappings(request.timeout);
requests.remove(process);
}
/*!
* \internal
*/
void QxtWebCgiServicePrivate::terminateProcess(QObject* o_process)
{
QProcess* process = static_cast<QProcess*>(o_process);
QxtCgiRequestInfo& request = requests[process];
if(request.terminateSent)
{
// kill with fire
process->kill();
}
else
{
// kill nicely
process->terminate();
request.terminateSent = true;
}
}

View File

@@ -1,64 +0,0 @@
/****************************************************************************
** Copyright (c) 2006 - 2011, the LibQxt project.
** See the Qxt AUTHORS file for a list of authors and copyright holders.
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are met:
** * Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** * Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in the
** documentation and/or other materials provided with the distribution.
** * Neither the name of the LibQxt project nor the
** names of its contributors may be used to endorse or promote products
** derived from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
** WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
** DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
** DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
** (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
** ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
** SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**
** <http://libqxt.org> <foundation@libqxt.org>
*****************************************************************************/
#ifndef QXTWEBCGISERVICE_H
#define QXTWEBCGISERVICE_H
#include <QObject>
#include <qxtglobal.h>
#include "qxtabstractwebsessionmanager.h"
#include "qxtabstractwebservice.h"
class QxtWebEvent;
class QxtWebRequestEvent;
class QxtWebCgiServicePrivate;
class QXT_WEB_EXPORT QxtWebCgiService : public QxtAbstractWebService
{
Q_OBJECT
public:
QxtWebCgiService(const QString& binary, QxtAbstractWebSessionManager* manager, QObject* parent = 0);
QString binary() const;
void setBinary(const QString& bin);
int timeout() const;
void setTimeout(int time);
bool timeoutOverride() const;
void setTimeoutOverride(bool enable);
virtual void pageRequestedEvent(QxtWebRequestEvent* event);
private:
QXT_DECLARE_PRIVATE(QxtWebCgiService)
};
#endif // QXTWEBCGISERVICE_H

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