1
0
mirror of https://github.com/tomahawk-player/tomahawk.git synced 2025-09-21 03:11:30 +02:00

Compare commits

..

20 Commits

Author SHA1 Message Date
Jeff Mitchell
5afce9327f Fix proxying (on Qt 4.8? With current OpenSSH?) 2012-04-24 14:44:19 -04:00
Jeff Mitchell
db413b2049 truthiness++ 2012-04-22 13:52:22 -04:00
Jeff Mitchell
646d2fbfd3 I'm stupid. Put the right version first. 2012-04-21 15:18:27 -04:00
Jeff Mitchell
dcb46877f0 Changelogify 2012-04-21 14:54:02 -04:00
Jeff Mitchell
985b54d84d Add zerconf protocol v2 2012-04-21 14:52:59 -04:00
Jeff Mitchell
c6ca3d8660 I can't think of any reason that removing this wasn't a mistake, so I'm
putting it back
2012-04-07 18:58:10 -04:00
Christian Muehlhaeuser
a104e92471 * Merged ChangeLog. 2012-04-07 10:10:52 +02:00
Christian Muehlhaeuser
1929804541 * Merged audio backend fixes from master. 2012-04-07 10:10:10 +02:00
Christian Muehlhaeuser
a564b3b272 * Don't update seek slider too often, use less cpu. 2012-04-07 10:07:05 +02:00
Christian Muehlhaeuser
d083528ad5 * Updated ChangeLog. 2012-04-07 08:10:31 +02:00
Christian Muehlhaeuser
4d63a9462d * Updated translations. 2012-04-07 08:09:37 +02:00
Christian Muehlhaeuser
5a64886b7c * Bumped to 0.4.2 and updated ChangeLog. 2012-04-07 06:46:13 +02:00
Leo Franchi
76986e8908 Backport case-insensitivity fix from master 2012-04-06 22:07:38 -03:00
Christian Muehlhaeuser
9a9a7148af * Properly fix time display in stable. 2012-04-05 06:17:40 +02:00
Christian Muehlhaeuser
aec7a0b140 * Fixed crash in AudioControls. (merge) 2012-04-05 05:39:50 +02:00
Christian Muehlhaeuser
eff42af593 * Updated ChangeLog. 2012-04-04 02:44:18 +02:00
Christian Muehlhaeuser
ad2b54ad90 * Bump version to 0.4.1. 2012-04-04 01:55:00 +02:00
Christian Muehlhaeuser
4e316a48ea * Fixed non debug builds. 2012-04-04 01:54:14 +02:00
Christian Muehlhaeuser
87863ae7e2 * Fixed about dialog for non debug releases. 2012-04-04 01:54:05 +02:00
Jeff Mitchell
5e439b990f See if this fixes Chris' time-updating problem 2012-03-31 13:06:50 -04:00
820 changed files with 21305 additions and 70253 deletions

View File

@@ -1,9 +0,0 @@
[main]
host = https://www.transifex.net
[tomahawk.tomahawk-master]
file_filter = lang/tomahawk_<lang>.ts
source_file = lang/tomahawk_en.ts
source_lang = en
type = QT

View File

@@ -1,7 +1,10 @@
PROJECT( tomahawk )
CMAKE_MINIMUM_REQUIRED( VERSION 2.8.6 )
CMAKE_MINIMUM_REQUIRED( VERSION 2.8 )
SET( CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/CMakeModules" )
CMAKE_POLICY(SET CMP0017 NEW)
IF( ${CMAKE_VERSION} VERSION_GREATER 2.8.3 )
CMAKE_POLICY(SET CMP0017 NEW)
ENDIF( ${CMAKE_VERSION} VERSION_GREATER 2.8.3 )
###
### Tomahawk application info
@@ -13,7 +16,7 @@ SET( TOMAHAWK_DESCRIPTION_SUMMARY "The social media player" )
SET( TOMAHAWK_VERSION_MAJOR 0 )
SET( TOMAHAWK_VERSION_MINOR 4 )
SET( TOMAHAWK_VERSION_PATCH 99 )
SET( TOMAHAWK_VERSION_PATCH 2 )
#SET( TOMAHAWK_VERSION_RC 0 )
@@ -23,21 +26,8 @@ add_definitions( "-fvisibility=hidden" )
# build options
option(BUILD_GUI "Build Tomahawk with GUI" ON)
option(BUILD_RELEASE "Generate TOMAHAWK_VERSION without GIT info" OFF)
option(WITH_BREAKPAD "Build with breakpad integration" ON)
option(WITH_CRASHREPORTER "Build with CrashReporter" ON)
option(LEGACY_KDE_INTEGRATION "Install tomahawk.protocol file, deprecated since 4.6.0" OFF)
IF( CMAKE_SYSTEM_PROCESSOR MATCHES "arm" )
message(STATUS "Build of breakpad library disabled on this platform.")
SET(WITH_BREAKPAD OFF)
SET(WITH_CRASHREPORTER OFF)
ENDIF()
# add definitions based on build options
IF(WITH_BREAKPAD)
message(STATUS "Build with support for breakpad.")
ENDIF()
# generate version string
# base string used in release and unstable builds
@@ -50,7 +40,7 @@ ENDIF()
IF( NOT BUILD_RELEASE )
INCLUDE( CMakeDateStamp )
SET( TOMAHAWK_VERSION_DATE "${CMAKE_DATESTAMP_YEAR}${CMAKE_DATESTAMP_MONTH}${CMAKE_DATESTAMP_DAY}" )
IF( TOMAHAWK_VERSION_DATE GREATER 0)
IF( ${TOMAHAWK_VERSION_DATE} GREATER 0)
SET( TOMAHAWK_VERSION ${TOMAHAWK_VERSION}.${TOMAHAWK_VERSION_DATE} )
ENDIF()
@@ -121,17 +111,30 @@ macro_optional_find_package(QCA2)
macro_log_feature(QCA2_FOUND "QCA2" "Provides encryption and signing functions required for Grooveshark resolver" "http://delta.affinix.com/qca/" FALSE "" "")
macro_optional_find_package(LibAttica)
macro_log_feature(LIBATTICA_FOUND "libattica" "Provides support for automatic fetching and managing of resolvers from the tomahawk website" "https://projects.kde.org/projects/kdesupport/attica" TRUE "" "")
macro_log_feature(LIBATTICA_FOUND "libattica" "Provides support for automatic fetching and managing of resolvers from the tomahawk website" "https://projects.kde.org/projects/kdesupport/attica" FALSE "" "")
macro_optional_find_package(QuaZip)
macro_log_feature(QuaZip_FOUND "QuaZip" "Provides support for extracting downloaded resolvers automatically." "http://quazip.sourceforge.net/" TRUE "" "")
macro_log_feature(QuaZip_FOUND "QuaZip" "Provides support for extracting downloaded resolvers automatically." "http://quazip.sourceforge.net/" FALSE "" "")
macro_optional_find_package(Jreen 1.0.5)
macro_log_feature(JREEN_FOUND "Jreen" "Qt XMPP Library" "http://qutim.org/jreen / https://github.com/euroelessar/jreen" FALSE "" "Jreen is needed for the Jabber SIP plugin.\n")
macro_optional_find_package(Jreen)
macro_log_feature(LIBJREEN_FOUND "Jreen" "Qt XMPP Library" "https://github.com/euroelessar/jreen" FALSE "" "Jreen is needed for the Jabber SIP plugin.\n")
macro_optional_find_package(QTweetLib)
macro_log_feature(QTWEETLIB_FOUND "QTweetLib" "Qt Twitter Library" "https://github.com/minimoog/QTweetLib" FALSE "" "QTweetLib is needed for the Twitter SIP plugin.\n")
IF( NOT QuaZip_FOUND )
add_subdirectory( ${CMAKE_SOURCE_DIR}/src/libtomahawk/thirdparty/quazip )
SET( QuaZip_INCLUDE_DIR ${CMAKE_CURRENT_BINARY_DIR}/libtomahawk/thirdparty/quazip )
SET( QuaZip_LIBRARY quazip )
SET( QuaZip_LIBRARIES ${QuaZip_LIBRARY} )
SET( QuaZip_FOUND true )
macro_log_feature(QuaZip_FOUND "QuaZip" "Provides support for extracting downloaded resolvers automatically. Building internal copy" "http://quazip.sourceforge.net/" FALSE "" "")
# copy headers to build/quazip so we can use proper includes inside the code
FILE( COPY ${CMAKE_SOURCE_DIR}/src/libtomahawk/thirdparty/quazip/quazip/ DESTINATION ${CMAKE_BINARY_DIR}/libtomahawk/thirdparty/quazip )
ENDIF()
# required
#While we distribute our own liblastfm2, don't need to look for it
#macro_optional_find_package(LibLastFm 0.3.3)
@@ -210,3 +213,9 @@ ADD_SUBDIRECTORY( src/libtomahawk )
SET( TOMAHAWK_LIBRARIES tomahawklib )
ADD_SUBDIRECTORY( src )
ADD_SUBDIRECTORY( admin )
IF( BUILD_GUI )
IF( NOT DISABLE_CRASHREPORTER )
ADD_SUBDIRECTORY( src/breakpad/CrashReporter )
ENDIF()
ENDIF()

View File

@@ -1,99 +0,0 @@
MACRO(PARSE_ARGUMENTS prefix arg_names option_names)
SET(DEFAULT_ARGS)
FOREACH(arg_name ${arg_names})
SET(${prefix}_${arg_name})
ENDFOREACH(arg_name)
FOREACH(option ${option_names})
SET(${prefix}_${option} FALSE)
ENDFOREACH(option)
SET(current_arg_name DEFAULT_ARGS)
SET(current_arg_list)
FOREACH(arg ${ARGN})
SET(larg_names ${arg_names})
LIST(FIND larg_names "${arg}" is_arg_name)
IF (is_arg_name GREATER -1)
SET(${prefix}_${current_arg_name} ${current_arg_list})
SET(current_arg_name ${arg})
SET(current_arg_list)
ELSE (is_arg_name GREATER -1)
SET(loption_names ${option_names})
LIST(FIND loption_names "${arg}" is_option)
IF (is_option GREATER -1)
SET(${prefix}_${arg} TRUE)
ELSE (is_option GREATER -1)
SET(current_arg_list ${current_arg_list} ${arg})
ENDIF (is_option GREATER -1)
ENDIF (is_arg_name GREATER -1)
ENDFOREACH(arg)
SET(${prefix}_${current_arg_name} ${current_arg_list})
ENDMACRO(PARSE_ARGUMENTS)
MACRO(CAR var)
SET(${var} ${ARGV1})
ENDMACRO(CAR)
MACRO(CDR var junk)
SET(${var} ${ARGN})
ENDMACRO(CDR)
macro(add_tomahawk_plugin)
parse_arguments(PLUGIN
"SOURCES;UI;LINK_LIBRARIES;TYPE;EXPORT_MACRO;COMPILE_DEFINITIONS"
"NO_INSTALL"
${ARGN}
)
car(PLUGIN_NAME ${PLUGIN_DEFAULT_ARGS})
# message("*** Arguments for ${PLUGIN_NAME}")
# message("Sources: ${PLUGIN_SOURCES}")
# message("Link libraries: ${PLUGIN_LINK_LIBRARIES}")
# message("UI: ${PLUGIN_UI}")
# message("TYPE: ${PLUGIN_TYPE}")
# message("EXPORT_MACRO: ${PLUGIN_EXPORT_MACRO}")
# create target name once for convenience
set(target "tomahawk_${PLUGIN_TYPE}_${PLUGIN_NAME}")
# qt stuff
include_directories(${CMAKE_CURRENT_BINARY_DIR})
if(PLUGIN_UI)
qt4_wrap_ui(PLUGIN_UI_SOURCES ${PLUGIN_UI})
list(APPEND PLUGIN_SOURCES ${PLUGIN_UI_SOURCES})
endif()
if(EXISTS "${CMAKE_CURRENT_LIST_DIR}/resources.qrc")
qt4_add_resources(PLUGIN_RC_SOURCES "resources.qrc")
list(APPEND PLUGIN_SOURCES ${PLUGIN_RC_SOURCES})
unset(PLUGIN_RC_SOURCES)
endif()
# add target
add_library(${target} MODULE ${PLUGIN_SOURCES})
# definitions - can this be moved into set_target_properties below?
add_definitions(${QT_DEFINITIONS})
set_target_properties(${target} PROPERTIES AUTOMOC TRUE COMPILE_DEFINITIONS ${PLUGIN_EXPORT_MACRO})
if(PLUGIN_COMPILE_DEFINITIONS)
# Dear CMake, i hate you! Sincerely, domme
# At least in CMake 2.8.8, you CANNOT set more than one COMPILE_DEFINITIONS value
# only takes the first one if called multiple times or bails out with wrong number of arguments
# when passing in a list, thus i redefine the export macro here in hope it won't mess up other targets
add_definitions( "-D${PLUGIN_EXPORT_MACRO}" )
set_target_properties(${target} PROPERTIES COMPILE_DEFINITIONS ${PLUGIN_COMPILE_DEFINITIONS})
endif()
# add link targets
target_link_libraries(${target} tomahawklib)
if(PLUGIN_LINK_LIBRARIES)
target_link_libraries(${target} ${PLUGIN_LINK_LIBRARIES})
endif()
# make installation optional, maybe useful for dummy plugins one day
if(NOT PLUGIN_NO_INSTALL)
include(GNUInstallDirs)
install(TARGETS ${target} DESTINATION ${CMAKE_INSTALL_LIBDIR})
endif()
endmacro()

View File

@@ -5,32 +5,40 @@
# LIBJREEN_LIBRARY, the path to libjreen
# LIBJREEN_FOUND, whether libjreen was found
FIND_PACKAGE(PkgConfig QUIET)
PKG_CHECK_MODULES(PC_JREEN QUIET libjreen)
FIND_PATH(JREEN_INCLUDE_DIR NAMES jreen/jreen.h
HINTS
${PC_JREEN_INCLUDEDIR}
${PC_JREEN_INCLUDE_DIRS}
${CMAKE_INSTALL_INCLUDEDIR}
${KDE4_INCLUDE_DIR}
find_path(LIBJREEN_INCLUDE_DIR NAMES jreen/jreen.h
HINTS
~/usr/include
/opt/local/include
/usr/include
/usr/local/include
/opt/kde4/include
${CMAKE_INSTALL_PREFIX}/include
${KDE4_INCLUDE_DIR}
)
FIND_LIBRARY(JREEN_LIBRARIES NAMES jreen
HINTS
${PC_JREEN_LIBDIR}
${PC_JREEN_LIBRARY_DIRS}
${CMAKE_INSTALL_LIBDIR}
${KDE4_LIB_DIR}
find_library( LIBJREEN_LIBRARY NAMES jreen
PATHS
~/usr/lib
/opt/local/lib
/usr/lib
/usr/lib64
/usr/local/lib
/opt/kde4/lib
${CMAKE_INSTALL_PREFIX}/lib
${CMAKE_INSTALL_PREFIX}/lib64
${KDE4_LIB_DIR}
)
IF(JREEN_LIBRARIES AND JREEN_INCLUDE_DIR AND NOT PC_JREEN_VERSION)
MESSAGE(WARNING "You don't have pkg-config and so the Jreen version check does not work!")
ENDIF()
INCLUDE(FindPackageHandleStandardArgs)
FIND_PACKAGE_HANDLE_STANDARD_ARGS(Jreen
REQUIRED_VARS JREEN_LIBRARIES JREEN_INCLUDE_DIR
VERSION_VAR PC_JREEN_VERSION)
if(LIBJREEN_INCLUDE_DIR AND LIBJREEN_LIBRARY)
set(LIBJREEN_FOUND TRUE)
message(STATUS "Found libjreen: ${LIBJREEN_INCLUDE_DIR}, ${LIBJREEN_LIBRARY}")
else(LIBJREEN_INCLUDE_DIR AND LIBJREEN_LIBRARY)
set(LIBJREEN_FOUND FALSE)
if (LIBJREEN_FIND_REQUIRED)
message(FATAL_ERROR "Could NOT find required package libjreen")
endif(LIBJREEN_FIND_REQUIRED)
endif(LIBJREEN_INCLUDE_DIR AND LIBJREEN_LIBRARY)
MARK_AS_ADVANCED(JREEN_INCLUDE_DIR JREEN_LIBRARIES)
mark_as_advanced(LIBJREEN_INCLUDE_DIR LIBJREEN_LIBRARY)

View File

@@ -38,9 +38,7 @@ else (QCA2_INCLUDE_DIR AND QCA2_LIBRARIES)
find_path(QCA2_INCLUDE_DIR qca.h
HINTS ${PC_QCA2_INCLUDEDIR} ${PC_QCA2_INCLUDE_DIRS}
PATH_SUFFIXES QtCrypto
PATHS /usr/local/lib/qca.framework/Headers/
)
PATH_SUFFIXES QtCrypto)
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(QCA2 DEFAULT_MSG QCA2_LIBRARIES QCA2_INCLUDE_DIR)

View File

@@ -280,8 +280,7 @@ Section "Tomahawk Player" SEC_TOMAHAWK_PLAYER
File "${INSTALL_PATH}\bin\libtomahawk_portfwd.dll"
File "${INSTALL_PATH}\bin\libtomahawk_lastfm2.dll"
File "${INSTALL_PATH}\bin\libtomahawklib.dll"
; plugins
File "${INSTALL_PATH}\lib\libtomahawk_*_*.dll"
File "${INSTALL_PATH}\lib\libtomahawk_sip*.dll"
!endif
!ifndef INSTALL_PATH
;Main executable.
@@ -294,8 +293,7 @@ Section "Tomahawk Player" SEC_TOMAHAWK_PLAYER
File "${BUILD_PATH}\libqxtweb-standalone.dll"
File "${BUILD_PATH}\libtomahawk_portfwd.dll"
File "${BUILD_PATH}\libtomahawk_lastfm2.dll"
; plugins
File "${BUILD_PATH}\libtomahawk_*_*.dll"
File "${BUILD_PATH}\libtomahawk_sip*.dll"
!endif
;License & release notes.

View File

@@ -1,15 +1,6 @@
Version 0.5.0:
* Added "Stop playback after this track" context menu items.
* You can now import your entire Last.fm playback history into Tomahawk.
* Support for multimedia keys (Play, Pause, Next etc.) on Windows & Linux.
* When listening privately scrobbling to Last.fm is now disabled.
* Added a toolbar with page back / forward buttons and the global search.
* New grid view with direct playback controls.
* Added a track page showing a song's lyrics and other similar tracks.
* Separate Loved Tracks and Recently Played views per source.
Version 0.4.2:
* Fix ZeroConf protocol showing IP addresses instead of host names.
Sometimes. Full fix coming later.
* Updated translations for various languages.
* Resuming playback restores correct volume settings.
* Reduced CPU usage during playback.
@@ -31,7 +22,7 @@ Version 0.4.0:
* Fixed icons not appearing in resolvers list.
* Fixed various UI glitches and stray error messages in stations.
* Fixed bug where album page would resolve bottom-to-top.
* Fixed bug where Footnotes would not update when changing selected album.
* Fixed bug where Footnotes would not update when changing selected album in Album View.
* Fixed dragging albums and artists from charts, album, and artist views.
* Fixed bug where filter text would be one step behind filter value.
* Fixed bug where resolvers would enable themselves after auto-updating.

8
README
View File

@@ -13,7 +13,7 @@ Compiling Tomahawk
Detailed building instructions for Ubuntu
-----------------------------------------
See: http://wiki.tomahawk-player.org/mediawiki/index.php/Building_Ubuntu_Binary_on_Precise_(12.04)
See: http://wiki.tomahawk-player.org/mediawiki/index.php/Building_Ubuntu_Binary_on_Maverick_(10.10)
Detailed building instructions for OS X
---------------------------------------
@@ -27,7 +27,7 @@ Doxygen Documentation
Dependencies
------------
CMake 2.8.6 - http://www.cmake.org/
CMake 2.8.0 - http://www.cmake.org/
Qt 4.7.0 - http://qt.nokia.com/
QJson 0.7.1 - http://qjson.sourceforge.net/
SQLite 3.6.22 - http://www.sqlite.org/
@@ -38,9 +38,9 @@ Dependencies
The following dependencies are optional, but recommended:
Attica 0.3.0 - ftp://ftp.kde.org/pub/kde/stable/attica/
Attica 0.2.0 - ftp://ftp.kde.org/pub/kde/stable/attica/
QuaZip 0.4.3 - http://quazip.sourceforge.net/
Jreen 1.0.5 - http://qutim.org/jreen / https://github.com/euroelessar/jreen
Jreen 1.0.3 - https://github.com/euroelessar/jreen
QTweetLib 0.5.0 - https://github.com/minimoog/QTweetLib
Third party libraries that we ship with our source:

View File

@@ -1,6 +1,6 @@
#!/usr/bin/python
# This file is part of Tomahawk.
# It was inspired in large part by the macdeploy script in Clementine.
# This file is part of Clementine.
#
# Clementine is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -199,10 +199,11 @@ QT_PLUGINS = [
]
TOMAHAWK_PLUGINS = [
'libtomahawk_account_xmpp.so',
'libtomahawk_account_google.so',
'libtomahawk_account_twitter.so',
'libtomahawk_account_zeroconf.so',
'libtomahawk_sipjabber.dylib',
'libtomahawk_sipgoogle.dylib',
'libtomahawk_siptwitter.dylib',
'libtomahawk_sipzeroconf.dylib',
'libtomahawk_qtweetlib.dylib',
]
QT_PLUGINS_SEARCH_PATH=[
@@ -495,6 +496,11 @@ for plugin in VLC_PLUGINS:
for plugin in TOMAHAWK_PLUGINS:
FixPlugin(plugin, '../MacOS')
try:
FixPlugin('spotify_tomahawkresolver', '../MacOS')
except:
print 'Failed to find spotify resolver'
try:
FixPlugin('tomahawk_crash_reporter', '../MacOS')
except:

Binary file not shown.

Before

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 68 KiB

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 68 KiB

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 68 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 91 KiB

After

Width:  |  Height:  |  Size: 91 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 356 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 68 KiB

After

Width:  |  Height:  |  Size: 8.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 339 B

BIN
data/images/avatar-dude.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 286 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 68 KiB

After

Width:  |  Height:  |  Size: 4.4 KiB

BIN
data/images/collapse.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 68 KiB

After

Width:  |  Height:  |  Size: 68 KiB

BIN
data/images/home.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.0 KiB

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 68 KiB

After

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 68 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 MiB

After

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 68 KiB

After

Width:  |  Height:  |  Size: 5.2 KiB

BIN
data/images/post.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 68 KiB

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 91 KiB

After

Width:  |  Height:  |  Size: 91 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 68 KiB

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 68 KiB

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 68 KiB

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 MiB

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 19 KiB

View File

@@ -1,20 +0,0 @@
-----BEGIN PUBLIC KEY-----
MIIDOzCCAi0GByqGSM44BAEwggIgAoIBAQDRltnNbKWFroVCsG1nTSdlTDmo7fjl
tgOuQ0YB2s0a1bcqgQ5YJRE59pFvF/z2pkHEHdyBA6USd9N7/T9lolwNcJoByJpO
MobUNs04elqZXliriaAdoSb2g6ZpxiedppbbyNP/BlK6o+zpyn0LVYXDI/OwJFzS
xjGXM+rBEWdUJnogZxV31gF9W3yD1Quz6icBulT9V/Soo6me9Mc60ooKSYj4Zgqd
3ln8tG90RFnWfbb0nbrITvR3ll6XXLfn081tjhymcXqHcgvaaqcmpKWL6ZWwX1mH
3t1pImnif/tSSZPG21KGE3FtuQ/+YFo19apQ6U6l8kaSFxqcDLAYzBy9AhUA/QfN
8WEIvzOEZ9uSWT7lYy64mUkCggEABsUmcs3kwjrmszIAAmPIowA0DBrxWZL03JBV
bDKT6tNHZaFFlCufVSjiL1EFZjRARC16OWYaDcElUsZYFMcsNIIa8LyDQaq6+SSm
quhMO5heeJiYPrutDiJzbJr0+HoY77Ll+Q4/cEkl0UAN4Ovp18WKwaq6GpHAvBnv
71LunLGAKsVb5joXBQ8In6zQkibJhgiBJwzLK90/j0OTiDaaOwM3PsAegORBVlVE
TAk4AQmawmF8nBGLzTyKXl83J571ku1Mm2JTl16jMYziKARKXYBmkcP1at0YddVK
WWpAwRKSxOucVJYfV58JqmjZqst8BBeH6esQKr5dklUvvDMaEwOCAQYAAoIBAQCw
5mo+8/R3S9cNYg9o8JNJGdSbMhSkurILHh9WNElsIC3RNtPcpijmAnWtXTVDhe6w
77wLj37tUuFGbsu2qPXtZoup35emf9DDshZ5w5UOclPaZ9HYjlC1H64c6d66Rllk
fY6FRDv9qVfjT84APbvMDrk6csJ5YHxFPDaqeQaFB0nxFiCMVwjEx+ZSvQNK1jJ2
o2gtuOvSPVSphsMeJ72DDNxO+SRRVnOmWaxg9rlmFuGle6Z+UJ2FItfmPEvhSBMY
hzndUbC7Wi4sIpBzbm9O5MiPYMv0VmN+0t1156EiC9uR4f7AKH2S94dnQob/YeY0
jMH+XxU/wzGUCmsOx1lx
-----END PUBLIC KEY-----

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,14 +1,11 @@
<!DOCTYPE RCC><RCC version="1.0">
<qresource prefix="/lang">
<file>tomahawk_en.qm</file>
<file>tomahawk_bg.qm</file>
<file>tomahawk_de.qm</file>
<file>tomahawk_fr.qm</file>
<file>tomahawk_sv.qm</file>
<file>tomahawk_es.qm</file>
<file>tomahawk_bg.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>
</qresource>
</RCC>

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,7 @@
<RCC>
<qresource prefix="/">
<file>data/images/avatar-dude-plus.png</file>
<file>data/images/avatar-dude.png</file>
<file>data/images/back-pressed.png</file>
<file>data/images/back-rest.png</file>
<file>data/images/filter.png</file>
@@ -7,8 +9,6 @@
<file>data/images/not-loved.png</file>
<file>data/images/no-album-art-placeholder.png</file>
<file>data/images/no-artist-image-placeholder.png</file>
<file>data/images/artist-placeholder-grid.png</file>
<file>data/images/album-placeholder-grid.png</file>
<file>data/images/track-placeholder.png</file>
<file>data/images/now-playing-panel.png</file>
<file>data/images/now-playing-speaker.png</file>
@@ -66,10 +66,12 @@
<file>data/images/echonest_logo.png</file>
<file>data/images/loading-animation.gif</file>
<file>data/images/info.png</file>
<file>data/images/home.png</file>
<file>data/images/back.png</file>
<file>data/images/forward.png</file>
<file>data/images/music-icon.png</file>
<file>data/images/configure.png</file>
<file>data/images/create-playlist.png</file>
<file>data/images/private-listening.png</file>
<file>data/images/add.png</file>
<file>data/images/recently-played.png</file>
@@ -85,7 +87,6 @@
<file>data/images/station.png</file>
<file>data/images/new-additions.png</file>
<file>data/images/charts.png</file>
<file>data/images/new-releases.png</file>
<file>data/images/loved_playlist.png</file>
<file>data/images/dashboard.png</file>
<file>data/images/artist-icon.png</file>
@@ -133,14 +134,7 @@
<file>data/images/no-album-no-case.png</file>
<file>data/images/rdio.png</file>
<file>data/images/grooveshark.png</file>
<file>data/images/lastfm-icon.png</file>
<file>data/images/spotifycore-logo.png</file>
<file>data/images/playlist-header-tiled.png</file>
<file>data/images/share.png</file>
<file>data/sql/dbmigrate-27_to_28.sql</file>
<file>data/images/process-stop.png</file>
<file>data/icons/tomahawk-icon-128x128-grayscale.png</file>
<file>data/images/collection.png</file>
<file>data/misc/tomahawk_pubkey.pem</file>
</qresource>
</RCC>

View File

@@ -1,784 +0,0 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2011-2012 Leo Franchi <lfranchi@kde.org>
*
* Tomahawk is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Tomahawk is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Tomahawk. If not, see <http://www.gnu.org/licenses/>.
*/
#include "AccountDelegate.h"
#include <QApplication>
#include <QPainter>
#include <QMouseEvent>
#include "accounts/AccountModel.h"
#include "accounts/Account.h"
#include "accounts/AccountManager.h"
#include "utils/TomahawkUtils.h"
#include "utils/Logger.h"
#include "utils/AnimatedSpinner.h"
#include "utils/Closure.h"
#include "Source.h"
#define CHILD_ACCOUNT_HEIGHT 24
#define PADDING 4
#define PADDING_BETWEEN_STARS 2
#define STAR_SIZE 12
#ifdef Q_OS_MAC
#define ROW_HEIGHT_MULTIPLIER 4.9
#else
#define ROW_HEIGHT_MULTIPLIER 5.7
#endif
#define ICONSIZE 40
#define WRENCH_SIZE 24
#define SMALL_WRENCH_SIZE 16
#define STATUS_ICON_SIZE 13
#define CHECK_LEFT_EDGE 8
#define REMOVE_ICON_SIZE 12
using namespace Tomahawk;
using namespace Accounts;
AccountDelegate::AccountDelegate( QObject* parent )
: QStyledItemDelegate ( parent )
, m_accountRowHeight( -1 )
, m_model( 0 )
{
m_defaultCover.load( RESPATH "images/sipplugin-online.png" );
m_ratingStarPositive.load( RESPATH "images/starred.png" );
m_ratingStarNegative.load( RESPATH "images/star-unstarred.png" );
m_onHoverStar.load( RESPATH "images/star-hover.png" );
m_onlineIcon.load( RESPATH "images/sipplugin-online.png" );
m_offlineIcon.load( RESPATH "images/sipplugin-offline.png" );
m_removeIcon.load( RESPATH "images/list-remove.png" );
m_ratingStarPositive = m_ratingStarPositive.scaled( STAR_SIZE, STAR_SIZE, Qt::KeepAspectRatio, Qt::SmoothTransformation );
m_ratingStarNegative = m_ratingStarNegative.scaled( STAR_SIZE, STAR_SIZE, Qt::KeepAspectRatio, Qt::SmoothTransformation );
m_onlineIcon = m_onlineIcon.scaled( STATUS_ICON_SIZE, STATUS_ICON_SIZE, Qt::KeepAspectRatio, Qt::SmoothTransformation );
m_offlineIcon = m_offlineIcon.scaled( STATUS_ICON_SIZE, STATUS_ICON_SIZE, Qt::KeepAspectRatio, Qt::SmoothTransformation );
m_onHoverStar = m_onHoverStar.scaled( STAR_SIZE, STAR_SIZE, Qt::KeepAspectRatio, Qt::SmoothTransformation );
m_removeIcon = m_removeIcon.scaled( REMOVE_ICON_SIZE, REMOVE_ICON_SIZE, Qt::KeepAspectRatio, Qt::SmoothTransformation );
m_defaultCover = m_defaultCover.scaled( ICONSIZE, ICONSIZE, Qt::KeepAspectRatio, Qt::SmoothTransformation );
}
QSize
AccountDelegate::sizeHint( const QStyleOptionViewItem& option, const QModelIndex& index ) const
{
AccountModel::RowType rowType = static_cast< AccountModel::RowType >( index.data( AccountModel::RowTypeRole ).toInt() );
if ( m_accountRowHeight < 0 )
{
// Haven't calculated normal item height yet, do it once and save it
QStyleOptionViewItemV4 opt( option );
initStyleOption( &opt, index );
m_accountRowHeight = ROW_HEIGHT_MULTIPLIER * opt.fontMetrics.height();
}
if ( rowType == AccountModel::TopLevelAccount || rowType == AccountModel::UniqueFactory || rowType == AccountModel::CustomAccount )
{
return QSize( 200, m_accountRowHeight );
}
else if ( rowType == AccountModel::TopLevelFactory )
{
// Make more space for each account we have to show.
AccountFactory* fac = qobject_cast< AccountFactory* >( index.data( AccountModel::AccountData ).value< QObject* >() );
if ( fac->isUnique() )
return QSize( 200, m_accountRowHeight );
const QList< Account* > accts = index.data( AccountModel::ChildrenOfFactoryRole ).value< QList< Tomahawk::Accounts::Account* > >();
const QSize s = QSize( 200, m_accountRowHeight + 12 * accts.size()-1 );
if ( s != m_sizeHints[ index ] )
const_cast< AccountDelegate* >( this )->sizeHintChanged( index ); // FU KTHBBQ
m_sizeHints[ index ] = s;
return s;
}
return QSize();
}
void
AccountDelegate::paint ( QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index ) const
{
QStyleOptionViewItemV4 opt = option;
initStyleOption( &opt, index );
// draw the background
const QWidget* w = opt.widget;
QStyle* style = w ? w->style() : QApplication::style();
style->drawPrimitive( QStyle::PE_PanelItemViewItem, &opt, painter, w );
painter->setRenderHint( QPainter::Antialiasing );
if ( m_model == 0 || m_model != index.model() )
m_model = const_cast<QAbstractItemModel*>( index.model() );
QFont titleFont = opt.font;
titleFont.setBold( true );
titleFont.setPointSize( titleFont.pointSize() + 2 );
const QFontMetrics titleMetrics( titleFont );
QFont authorFont = opt.font;
authorFont.setItalic( true );
authorFont.setPointSize( authorFont.pointSize() - 1 );
#ifdef Q_OS_MAC
authorFont.setPointSize( authorFont.pointSize() - 1 );
#endif
const QFontMetrics authorMetrics( authorFont );
QFont descFont = authorFont;
descFont.setItalic( false );
const QFontMetrics descMetrics( descFont );
QFont installFont = opt.font;
installFont.setPointSize( installFont.pointSize() - 1 );
const QFontMetrics installMetrics( descFont );
const int height = opt.rect.height();
const int center = height / 2 + opt.rect.top();
// Left account enable/disable checkbox
const AccountModel::RowType rowType = static_cast< AccountModel::RowType >( index.data( AccountModel::RowTypeRole ).toInt() );
int leftEdge = PADDING;
// draw checkbox first
const int checkboxYPos = ( center ) - ( WRENCH_SIZE / 2 );
QRect checkRect = QRect( leftEdge, checkboxYPos, WRENCH_SIZE, WRENCH_SIZE );
QStyleOptionViewItemV4 opt2 = opt;
opt2.rect = checkRect;
if ( !m_loadingSpinners.contains( index ) )
{
drawCheckBox( opt2, painter, opt.widget );
}
else
{
Q_ASSERT( m_loadingSpinners[ index ] );
if ( m_loadingSpinners[ index ] )
{
const QPixmap pm = m_loadingSpinners[index]->pixmap();
painter->drawPixmap( checkRect, pm );
}
}
leftEdge += WRENCH_SIZE + PADDING / 2;
// Pixmap
QPixmap p = index.data( Qt::DecorationRole ).value< QPixmap >();
QRect pixmapRect( leftEdge + PADDING, center - ICONSIZE/2, ICONSIZE, ICONSIZE );
if ( p.isNull() ) // default image... TODO
p = m_defaultCover;
else
p = p.scaled( pixmapRect.size(), Qt::KeepAspectRatio, Qt::SmoothTransformation );
painter->drawPixmap( pixmapRect, p );
// Draw config wrench if there is one
const bool hasConfigWrench = index.data( AccountModel::HasConfig ).toBool();
int rightEdge = opt.rect.right();
m_cachedConfigRects[ index ] = QRect();
if ( hasConfigWrench )
{
const QRect confRect = QRect( rightEdge - 2*PADDING - WRENCH_SIZE, center - WRENCH_SIZE / 2, WRENCH_SIZE, WRENCH_SIZE );
QStyleOptionToolButton topt;
topt.rect = confRect;
topt.pos = confRect.topLeft();
drawConfigWrench( painter, opt, topt );
m_cachedConfigRects[ index ] = confRect;
rightEdge = confRect.left();
}
// Draw individual accounts and add account button for factories
m_cachedButtonRects[ index ] = QRect();
bool canDelete = index.data( AccountModel::CanDeleteRole ) .toBool();
if ( rowType == Tomahawk::Accounts::AccountModel::TopLevelFactory )
{
const QList< Account* > accts = index.data( AccountModel::ChildrenOfFactoryRole ).value< QList< Tomahawk::Accounts::Account* > >();
QRect btnRect;
const QString btnText = tr( "Add Account" );
const int btnWidth = installMetrics.width( btnText ) + 2*PADDING;
if ( accts.isEmpty() )
{
Q_ASSERT( !hasConfigWrench );
// Draw button in center of row
btnRect= QRect( opt.rect.right() - PADDING - btnWidth, center - ( installMetrics.height() + 4 ) / 2, btnWidth, installMetrics.height() + 2*PADDING );
rightEdge = btnRect.left();
}
else
{
painter->save();
painter->setFont( installFont );
rightEdge = drawAccountList( painter, opt, accts, rightEdge );
painter->restore();
btnRect = QRect( opt.rect.right() - PADDING - btnWidth, opt.rect.bottom() - installMetrics.height() - 3*PADDING, btnWidth, installMetrics.height() + 2*PADDING );
#ifdef Q_WS_MAC
btnRect.adjust( -4, 0, 4, 0 );
#endif
}
leftEdge = btnRect.left();
m_cachedButtonRects[ index ] = btnRect;
painter->save();
painter->setPen( opt.palette.color( QPalette::Active, QPalette::AlternateBase ) );
drawRoundedButton( painter, btnRect );
painter->setFont( installFont );
painter->drawText( btnRect, Qt::AlignCenter, btnText );
painter->restore();
}
else if ( rowType == AccountModel::UniqueFactory )
{
// Display as usual, except if it has an account, show the status.
const QList< Account* > accts = index.data( AccountModel::ChildrenOfFactoryRole ).value< QList< Tomahawk::Accounts::Account* > >();
if ( !accts.isEmpty() )
{
Q_ASSERT( accts.size() == 1 );
rightEdge = drawStatus( painter, QPointF( rightEdge, center - painter->fontMetrics().height()/2 ), accts.first(), true );
}
}
else if ( canDelete )
{
const QString btnText = tr( "Remove Account" );
const int btnWidth = installMetrics.width( btnText ) + 2*PADDING;
QRect btnRect;
if ( hasConfigWrench )
btnRect = QRect( opt.rect.right() - PADDING - btnWidth, opt.rect.bottom() - installMetrics.height() - 3*PADDING, btnWidth, installMetrics.height() + 2*PADDING );
else
btnRect = QRect( opt.rect.right() - PADDING - btnWidth, center - ( installMetrics.height() + 4 ) / 2, btnWidth, installMetrics.height() + 2*PADDING );
#ifdef Q_WS_MAC
btnRect.adjust( -4, 2, 4, -2 );
#endif
leftEdge = btnRect.left();
m_cachedButtonRects[ index ] = btnRect;
painter->save();
painter->setPen( opt.palette.color( QPalette::Active, QPalette::AlternateBase ) );
drawRoundedButton( painter, btnRect, true );
painter->setFont( installFont );
painter->drawText( btnRect, Qt::AlignCenter, btnText );
painter->restore();
}
// Draw the title and description
// title
QString title = index.data( Qt::DisplayRole ).toString();
const int rightTitleEdge = rightEdge - PADDING;
const int leftTitleEdge = pixmapRect.right() + PADDING;
painter->setFont( titleFont );
QRect textRect;
const bool canRate = index.data( AccountModel::CanRateRole ).toBool();
if ( canRate )
{
textRect = QRect( leftTitleEdge, opt.rect.top() + PADDING, rightTitleEdge - leftTitleEdge, painter->fontMetrics().height() );
}
else
{
textRect = QRect( leftTitleEdge, opt.rect.top() + PADDING, rightTitleEdge - leftTitleEdge, center - opt.rect.top() - PADDING );
}
painter->drawText( textRect, Qt::AlignVCenter | Qt::AlignLeft, title );
// author
QString author = index.data( AccountModel::AuthorRole ).toString();
int runningBottom = textRect.bottom();
if ( !author.isEmpty() && canRate )
{
painter->save();
painter->setFont( authorFont );
painter->setPen( QColor( Qt::gray ).darker( 150 ) );
const int authorWidth = authorMetrics.width( author );
const QRect authorRect( textRect.left(), textRect.bottom() + PADDING/2, authorWidth + 6, authorMetrics.height() );
painter->drawText( authorRect, Qt::AlignLeft | Qt::AlignVCenter, author );
painter->restore();
runningBottom = authorRect.bottom();
}
// description
QString desc = index.data( AccountModel::DescriptionRole ).toString();
const int descWidth = rightEdge - leftTitleEdge - PADDING;
painter->setFont( descFont );
const QRect descRect( leftTitleEdge, runningBottom + PADDING, descWidth, painter->fontMetrics().height() );
desc = painter->fontMetrics().elidedText( desc, Qt::ElideRight, descWidth );
painter->drawText( descRect, Qt::AlignLeft | Qt::TextWordWrap | Qt::AlignTop, desc );
runningBottom = descRect.bottom();
if ( index.data( AccountModel::CanRateRole ).toBool() )
{
// rating stars
const int rating = index.data( AccountModel::RatingRole ).toInt();
// int runningEdge = opt.rect.right() - 2*PADDING - ratingWidth;
int runningEdge = textRect.left();
// int starsTop = opt.rect.bottom() - 3*PADDING - m_ratingStarNegative.height();
int starsTop = runningBottom + PADDING;
for ( int i = 1; i < 6; i++ )
{
QRect r( runningEdge, starsTop, m_ratingStarPositive.width(), m_ratingStarPositive.height() );
// QRect r( runningEdge, opt.rect.top() + PADDING, m_ratingStarPositive.width(), m_ratingStarPositive.height() );
if ( i == 1 )
m_cachedStarRects[ index ] = r;
const bool userHasRated = index.data( AccountModel::UserHasRatedRole ).toBool();
if ( !userHasRated && // Show on-hover animation if the user hasn't rated it yet, and is hovering over it
m_hoveringOver > -1 &&
m_hoveringItem == index )
{
if ( i <= m_hoveringOver ) // positive star
painter->drawPixmap( r, m_onHoverStar );
else
painter->drawPixmap( r, m_ratingStarNegative );
}
else
{
if ( i <= rating ) // positive or rated star
{
if ( userHasRated )
painter->drawPixmap( r, m_onHoverStar );
else
painter->drawPixmap( r, m_ratingStarPositive );
}
else
painter->drawPixmap( r, m_ratingStarNegative );
}
runningEdge += m_ratingStarPositive.width() + PADDING_BETWEEN_STARS;
}
// downloaded num times
QString count = tr( "%1 downloads" ).arg( index.data( AccountModel::DownloadCounterRole ).toInt() );
painter->setFont( descFont );
const int countW = painter->fontMetrics().width( count );
const QRect countRect( runningEdge + 50, starsTop, countW, painter->fontMetrics().height() );
count = painter->fontMetrics().elidedText( count, Qt::ElideRight, rightEdge - PADDING - countRect.left() );
painter->drawText( countRect, Qt::AlignLeft | Qt::TextWordWrap, count );
// runningEdge = authorRect.x();
}
// Title and description!
return;
}
int
AccountDelegate::drawAccountList( QPainter* painter, QStyleOptionViewItemV4& opt, const QList< Account* > accts, int rightEdge ) const
{
// list each account name, and show the online, offline icon
const int textHeight = painter->fontMetrics().height() + 1;
const int mid = opt.rect.bottom() - opt.rect.height() / 2;
int runningRightEdge = rightEdge;
int current = 0;
int leftOfAccounts = rightEdge;
if ( accts.size() % 2 == 1 )
{
// If there's an odd number, the center one is centered
current = mid - ((textHeight + PADDING/2) * (accts.size()/2) ) - textHeight / 2;
}
else
{
// Even number, center between the middle ones
current = mid - ((textHeight + PADDING/2) * (accts.size()/2) );
}
for ( int i = 0; i < accts.size(); i++ )
{
// draw lightbulb and text
runningRightEdge = drawStatus( painter, QPointF( rightEdge - PADDING, current), accts.at( i ) );
const QString label = accts.at( i )->accountFriendlyName();
const QPoint textTopLeft( runningRightEdge - PADDING - painter->fontMetrics().width( label ), current);
painter->drawText( QRect( textTopLeft, QSize( painter->fontMetrics().width( label ) + 1, textHeight ) ), label );
current += textHeight + PADDING/2;
leftOfAccounts = qMin( leftOfAccounts, textTopLeft.x() );
}
return leftOfAccounts;
}
bool
AccountDelegate::editorEvent( QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option, const QModelIndex& index )
{
if ( event->type() != QEvent::MouseButtonPress &&
event->type() != QEvent::MouseButtonRelease &&
event->type() != QEvent::MouseButtonDblClick &&
event->type() != QEvent::MouseMove )
return false;
if ( event->type() == QEvent::MouseButtonPress )
{
// Show the config wrench as depressed on click
QMouseEvent* me = static_cast< QMouseEvent* >( event );
if ( me->button() == Qt::LeftButton && m_cachedConfigRects.contains( index ) && m_cachedConfigRects[ index ].contains( me->pos() ) )
{
m_configPressed = index;
const AccountModel::RowType rowType = static_cast< AccountModel::RowType >( index.data( AccountModel::RowTypeRole ).toInt() );
if ( rowType == AccountModel::TopLevelAccount ||
rowType == AccountModel::CustomAccount )
{
Account* acct = qobject_cast< Account* >( index.data( AccountModel::AccountData ).value< QObject* >() );
Q_ASSERT( acct ); // Should not be showing a config wrench if there is no account!
emit openConfig( acct );
}
else if ( rowType == AccountModel::TopLevelFactory )
{
AccountFactory* fac = qobject_cast< AccountFactory* >( index.data( AccountModel::AccountData ).value< QObject* >() );
Q_ASSERT( fac ); // Should not be showing a config wrench if there is no account!
emit openConfig( fac );
}
else if ( rowType == AccountModel::UniqueFactory )
{
const QList< Account* > accts = index.data( AccountModel::ChildrenOfFactoryRole ).value< QList< Tomahawk::Accounts::Account* > >();
Q_ASSERT( !accts.isEmpty() ); // If there's no account, why is there a config widget for this factory?
Q_ASSERT( accts.size() == 1 );
emit openConfig( accts.first() );
}
return true;
}
} else if ( event->type() == QEvent::MouseButtonRelease || event->type() == QEvent::MouseButtonDblClick )
{
QMouseEvent* me = static_cast< QMouseEvent* >( event );
if ( m_configPressed.isValid() )
emit update( m_configPressed );
m_configPressed = QModelIndex();
if ( checkRectForIndex( option, index ).contains( me->pos() ) )
{
// Check box for this row
// eat the double click events inside the check rect
if( event->type() == QEvent::MouseButtonDblClick ) {
return true;
}
Qt::CheckState curState = static_cast< Qt::CheckState >( index.data( Qt::CheckStateRole ).toInt() );
Qt::CheckState newState = curState == Qt::Checked ? Qt::Unchecked : Qt::Checked;
return model->setData( index, newState, AccountModel::CheckboxClickedRole );
}
else if ( m_cachedButtonRects.contains( index ) && m_cachedButtonRects[ index ].contains( me->pos() ) )
{
// Install/create/etc button for this row
model->setData( index, true, AccountModel::CustomButtonRole );
}
}
if ( m_cachedStarRects.contains( index ) )
{
QRect fullStars = m_cachedStarRects[ index ];
const int starsWidth = 5 * ( m_ratingStarPositive.width() + PADDING_BETWEEN_STARS );
fullStars.setWidth( starsWidth );
QMouseEvent* me = static_cast< QMouseEvent* >( event );
if ( fullStars.contains( me->pos() ) )
{
const int eachStar = starsWidth / 5;
const int clickOffset = me->pos().x() - fullStars.x();
const int whichStar = (clickOffset / eachStar) + 1;
if ( event->type() == QEvent::MouseButtonRelease )
{
model->setData( index, whichStar, AccountModel::RatingRole );
}
else if ( event->type() == QEvent::MouseMove )
{
// 0-indexed
m_hoveringOver = whichStar;
m_hoveringItem = index;
}
return true;
}
}
if ( m_hoveringOver > -1 )
{
emit update( m_hoveringItem );
m_hoveringOver = -1;
m_hoveringItem = QPersistentModelIndex();
}
return false;
}
void
AccountDelegate::drawRoundedButton( QPainter* painter, const QRect& btnRect, bool red ) const
{
QPainterPath btnPath;
const int radius = 3;
// draw top half gradient
const int btnCenter = btnRect.bottom() - ( btnRect.height() / 2 );
btnPath.moveTo( btnRect.left(), btnCenter );
btnPath.lineTo( btnRect.left(), btnRect.top() + radius );
btnPath.quadTo( QPoint( btnRect.topLeft() ), QPoint( btnRect.left() + radius, btnRect.top() ) );
btnPath.lineTo( btnRect.right() - radius, btnRect.top() );
btnPath.quadTo( QPoint( btnRect.topRight() ), QPoint( btnRect.right(), btnRect.top() + radius ) );
btnPath.lineTo( btnRect.right(),btnCenter );
btnPath.lineTo( btnRect.left(), btnCenter );
QLinearGradient g;
if ( !red )
{
g.setColorAt( 0, QColor(54, 127, 211) );
g.setColorAt( 0.5, QColor(43, 104, 182) );
}
else
{
g.setColorAt( 0, QColor(206, 63, 63) );
g.setColorAt( 0.5, QColor(170, 52, 52) );
}
//painter->setPen( bg.darker() );
painter->fillPath( btnPath, g );
//painter->drawPath( btnPath );
btnPath = QPainterPath();
btnPath.moveTo( btnRect.left(), btnCenter );
btnPath.lineTo( btnRect.left(), btnRect.bottom() - radius );
btnPath.quadTo( QPoint( btnRect.bottomLeft() ), QPoint( btnRect.left() + radius, btnRect.bottom() ) );
btnPath.lineTo( btnRect.right() - radius, btnRect.bottom() );
btnPath.quadTo( QPoint( btnRect.bottomRight() ), QPoint( btnRect.right(), btnRect.bottom() - radius ) );
btnPath.lineTo( btnRect.right(), btnCenter );
btnPath.lineTo( btnRect.left(), btnCenter );
if ( !red )
{
g.setColorAt( 0, QColor(34, 85, 159) );
g.setColorAt( 0.5, QColor(35, 79, 147) );
}
else
{
g.setColorAt( 0, QColor(150, 50, 50) );
g.setColorAt( 0.5, QColor(130, 40, 40) );
}
painter->fillPath( btnPath, g );
}
int
AccountDelegate::drawStatus( QPainter* painter, const QPointF& rightTopEdge, Account* acct, bool drawText ) const
{
QPixmap p;
QString statusText;
const Account::ConnectionState state = acct->connectionState();
if ( state == Account::Connected )
{
p = m_onlineIcon;
statusText = tr( "Online" );
}
else if ( state == Account::Connecting )
{
p = m_offlineIcon;
statusText = tr( "Connecting..." );
}
else
{
p = m_offlineIcon;
statusText = tr( "Offline" );
}
const int yPos = rightTopEdge.y();
const QRect connectIconRect( rightTopEdge.x() - STATUS_ICON_SIZE, yPos, STATUS_ICON_SIZE, STATUS_ICON_SIZE );
if ( state == Account::Connecting )
{
if ( !m_connectingSpinners.contains( acct ) )
{
AnimatedSpinner* anim = new AnimatedSpinner( connectIconRect.size(), true );
_detail::Closure* closure = new _detail::Closure( anim, SIGNAL( requestUpdate() ), const_cast<AccountDelegate*>(this), SLOT( doUpdateIndexWithAccount( Tomahawk::Accounts::Account* ) ), C_ARG( Tomahawk::Accounts::Account*, acct ) );
closure->setAutoDelete( false );
m_connectingSpinners[ acct ] = anim;
}
const QPixmap pm = m_connectingSpinners[acct]->pixmap();
painter->drawPixmap( connectIconRect, pm );
}
else
{
if ( m_connectingSpinners.contains( acct ) )
delete m_connectingSpinners.take( acct );
painter->drawPixmap( connectIconRect, p );
}
int leftEdge = connectIconRect.x();
if ( drawText )
{
int width = painter->fontMetrics().width( statusText );
int statusTextX = connectIconRect.x() - PADDING - width;
painter->drawText( QRect( statusTextX, yPos, width, painter->fontMetrics().height() ), statusText );
leftEdge = statusTextX;
}
return leftEdge;
}
void
AccountDelegate::drawCheckBox( QStyleOptionViewItemV4& opt, QPainter* p, const QWidget* w ) const
{
QStyle* style = w ? w->style() : QApplication::style();
opt.checkState == Qt::Checked ? opt.state |= QStyle::State_On : opt.state |= QStyle::State_Off;
style->drawPrimitive( QStyle::PE_IndicatorViewItemCheck, &opt, p, w );
}
void
AccountDelegate::drawConfigWrench ( QPainter* painter, QStyleOptionViewItemV4& opt, QStyleOptionToolButton& topt ) const
{
const QWidget* w = opt.widget;
QStyle* style = w ? w->style() : QApplication::style();
// draw it the same size as the check belox
topt.font = opt.font;
topt.icon = QIcon( RESPATH "images/configure.png" );
topt.iconSize = QSize( 14, 14 );
topt.subControls = QStyle::SC_ToolButton;
topt.activeSubControls = QStyle::SC_None;
topt.features = QStyleOptionToolButton::None;
bool pressed = ( m_configPressed == opt.index );
topt.state = pressed ? QStyle::State_On : QStyle::State_Raised;
if( opt.state & QStyle::State_MouseOver || pressed )
topt.state |= QStyle::State_HasFocus;
style->drawComplexControl( QStyle::CC_ToolButton, &topt, painter, w );
}
QRect
AccountDelegate::checkRectForIndex( const QStyleOptionViewItem& option, const QModelIndex& idx ) const
{
QStyleOptionViewItemV4 opt = option;
initStyleOption( &opt, idx );
// Top level item, return the corresponding rect
const int ypos = ( opt.rect.top() + opt.rect.height() / 2 ) - ( WRENCH_SIZE / 2 );
const QRect checkRect = QRect( PADDING, ypos, WRENCH_SIZE, WRENCH_SIZE );
return checkRect;
}
void
AccountDelegate::startInstalling( const QPersistentModelIndex& idx )
{
qDebug() << "START INSTALLING:" << idx.data( Qt::DisplayRole ).toString();
QStyleOptionViewItemV4 opt;
initStyleOption( &opt, idx );
AnimatedSpinner* anim = new AnimatedSpinner( checkRectForIndex( opt, idx ).size(), true );
_detail::Closure* closure = NewClosure( anim, SIGNAL( requestUpdate() ), this, SLOT( doUpdateIndex( const QPersistentModelIndex& ) ), idx );
closure->setAutoDelete( false );
m_loadingSpinners[ idx ] = anim;
update( idx );
}
void
AccountDelegate::doneInstalling ( const QPersistentModelIndex& idx )
{
qDebug() << "STOP INSTALLING:" << idx.data( Qt::DisplayRole ).toString();
Q_ASSERT( m_loadingSpinners.contains( idx ) );
if ( !m_loadingSpinners.contains( idx ) )
return;
delete m_loadingSpinners.take( idx );
update( idx );
}
void
AccountDelegate::errorInstalling( const QPersistentModelIndex& idx )
{
// Just hide the loading spinner as we do after a successful install
qDebug() << "ERROR INSTALLING index:" << idx;
doneInstalling( idx );
}
void
AccountDelegate::doUpdateIndex( const QPersistentModelIndex& idx )
{
emit update( idx );
}
void
AccountDelegate::doUpdateIndexWithAccount( Account* account )
{
// Urgh, have to go through the list and check based on the type
for ( int i = 0; i < m_model->rowCount(); i++ )
{
const QModelIndex index = m_model->index( i, 0, QModelIndex() );
const AccountModel::RowType rowType = static_cast< AccountModel::RowType >( index.data( AccountModel::RowTypeRole ).toInt() );
if ( rowType == AccountModel::TopLevelAccount ||
rowType == AccountModel::CustomAccount )
{
Account* acct = qobject_cast< Account* >( index.data( AccountModel::AccountData ).value< QObject* >() );
if ( account == acct )
{
emit update( index );
return;
}
}
else if ( rowType == AccountModel::TopLevelFactory || rowType == AccountModel::UniqueFactory )
{
const QList< Account* > accts = index.data( AccountModel::ChildrenOfFactoryRole ).value< QList< Tomahawk::Accounts::Account* > >();
if ( accts.contains( account ) )
{
emit update( index );
return;
}
}
}
}

View File

@@ -1,90 +0,0 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2011-2012 Leo Franchi <lfranchi@kde.org>
*
* Tomahawk is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Tomahawk is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Tomahawk. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef ACCOUNTDELEGATE_H
#define ACCOUNTDELEGATE_H
#include <QStyledItemDelegate>
#include "accounts/AccountModel.h"
class AnimatedSpinner;
namespace Tomahawk
{
namespace Accounts
{
class Account;
class AccountDelegate : public QStyledItemDelegate
{
Q_OBJECT
public:
AccountDelegate( QObject* parent = 0);
virtual void paint ( QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index ) const;
virtual QSize sizeHint ( const QStyleOptionViewItem& option, const QModelIndex& index ) const;
public slots:
void startInstalling( const QPersistentModelIndex& idx );
void doneInstalling ( const QPersistentModelIndex& idx );
void errorInstalling ( const QPersistentModelIndex& idx );
void doUpdateIndex( const QPersistentModelIndex& idx );
protected:
virtual bool editorEvent( QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option, const QModelIndex& index );
signals:
void update( const QModelIndex& idx );
void openConfig( Tomahawk::Accounts::Account* );
void openConfig( Tomahawk::Accounts::AccountFactory* );
private slots:
void doUpdateIndexWithAccount( Tomahawk::Accounts::Account* account );
private:
void drawRoundedButton( QPainter* painter, const QRect& buttonRect, bool red = false ) const;
// Returns new left edge
int drawStatus( QPainter* painter, const QPointF& rightTopEdge, Account* acct, bool drawText = false ) const;
void drawCheckBox( QStyleOptionViewItemV4& opt, QPainter* p, const QWidget* w ) const;
void drawConfigWrench( QPainter* painter, QStyleOptionViewItemV4& option, QStyleOptionToolButton& topt ) const;
// returns new left edge
int drawAccountList( QPainter* painter, QStyleOptionViewItemV4& option, const QList< Account* > accounts, int rightEdge ) const;
QRect checkRectForIndex( const QStyleOptionViewItem &option, const QModelIndex &idx ) const;
QPixmap m_offlineIcon, m_onlineIcon, m_defaultCover, m_onHoverStar, m_ratingStarPositive, m_ratingStarNegative, m_removeIcon;
int m_hoveringOver;
QPersistentModelIndex m_hoveringItem, m_configPressed;
mutable QHash< QPersistentModelIndex, QRect > m_cachedButtonRects;
mutable QHash< QPersistentModelIndex, QRect > m_cachedStarRects;
mutable QHash< QPersistentModelIndex, QRect > m_cachedConfigRects;
mutable QHash< QPersistentModelIndex, QSize > m_sizeHints;
mutable QHash< QPersistentModelIndex, AnimatedSpinner* > m_loadingSpinners;
mutable QHash< Account*, AnimatedSpinner* > m_connectingSpinners;
mutable int m_accountRowHeight;
mutable QAbstractItemModel* m_model;
};
}
}
#endif // ACCOUNTDELEGATE_H

View File

@@ -1,138 +0,0 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2010-2011, Leo Franchi <lfranchi@kde.org>
*
* Tomahawk is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Tomahawk is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Tomahawk. If not, see <http://www.gnu.org/licenses/>.
*/
#include "AccountFactoryWrapper.h"
#include "accounts/Account.h"
#include "accounts/AccountManager.h"
#include "GuiHelpers.h"
#include "AccountFactoryWrapperDelegate.h"
#include "DelegateConfigWrapper.h"
#include "ui_AccountFactoryWrapper.h"
#include "Source.h"
using namespace Tomahawk::Accounts;
AccountFactoryWrapper::AccountFactoryWrapper( AccountFactory* factory, QWidget* parent )
: QDialog( parent, Qt::Sheet )
, m_factory( factory )
, m_ui( new Ui_AccountFactoryWrapper )
{
m_ui->setupUi( this );
setWindowTitle( factory->prettyName() );
m_ui->factoryIcon->setPixmap( factory->icon() );
m_ui->factoryDescription->setText( factory->description() );
m_addButton = m_ui->buttonBox->addButton( tr( "Add Account" ), QDialogButtonBox::ActionRole );
AccountFactoryWrapperDelegate* del = new AccountFactoryWrapperDelegate( m_ui->accountsList );
m_ui->accountsList->setItemDelegate( del );
connect( del, SIGNAL( openConfig( Tomahawk::Accounts::Account* ) ), this, SLOT( openAccountConfig( Tomahawk::Accounts::Account* ) ) );
connect( del, SIGNAL( removeAccount( Tomahawk::Accounts::Account* ) ), this, SLOT( removeAccount( Tomahawk::Accounts::Account* ) ) );
connect( del, SIGNAL( checkOrUncheck( QModelIndex, Tomahawk::Accounts::Account* , Qt::CheckState ) ), this, SLOT( accountCheckedOrUnchecked( QModelIndex ,Tomahawk::Accounts::Account* ,Qt::CheckState ) ) );
load();
connect( m_ui->buttonBox, SIGNAL( rejected() ), this, SLOT( reject() ) );
connect( m_ui->buttonBox, SIGNAL( accepted() ), this, SLOT( accept() ) );
connect( m_ui->buttonBox, SIGNAL( clicked( QAbstractButton*) ), this, SLOT( buttonClicked( QAbstractButton* ) ) );
connect ( AccountManager::instance(), SIGNAL( added( Tomahawk::Accounts::Account* ) ), this, SLOT( load() ) );
connect ( AccountManager::instance(), SIGNAL( removed( Tomahawk::Accounts::Account* ) ), this, SLOT( load() ) );
#ifdef Q_OS_MAC
setContentsMargins( 0, 0, 0, 0 );
m_ui->verticalLayout->setSpacing( 6 );
#endif
}
void
AccountFactoryWrapper::load()
{
m_ui->accountsList->clear();
foreach ( Account* acc, AccountManager::instance()->accounts() )
{
if ( AccountManager::instance()->factoryForAccount( acc ) == m_factory )
{
QTreeWidgetItem* item = new QTreeWidgetItem( m_ui->accountsList );
item->setData( 0, AccountRole, QVariant::fromValue< QObject *>( acc ) );
item->setCheckState( 0, acc->enabled() ? Qt::Checked : Qt::Unchecked );
}
}
if ( m_ui->accountsList->model()->rowCount() == 0 )
accept();
#ifndef Q_OS_MAC
const int padding = 7;
#else
const int padding = 8;
#endif
const int height = m_ui->accountsList->model()->rowCount( QModelIndex() ) * ACCOUNT_ROW_HEIGHT + padding;
m_ui->accountsList->setFixedHeight( height );
}
void
AccountFactoryWrapper::openAccountConfig( Account* account )
{
TomahawkUtils::openAccountConfig( account, this, false );
}
void
AccountFactoryWrapper::removeAccount( Tomahawk::Accounts::Account* acct )
{
AccountManager::instance()->removeAccount( acct );
load();
}
void
AccountFactoryWrapper::accountCheckedOrUnchecked( const QModelIndex& index, Account* acct, Qt::CheckState newstate )
{
QTreeWidgetItem* item = m_ui->accountsList->topLevelItem( index.row() );
Q_ASSERT( item );
if ( newstate == Qt::Checked )
{
item->setCheckState( 0, Qt::Checked );
AccountManager::instance()->enableAccount( acct );
}
else if ( newstate == Qt::Unchecked )
{
item->setCheckState( 0, Qt::Unchecked );
AccountManager::instance()->disableAccount( acct );
}
}
void
AccountFactoryWrapper::buttonClicked( QAbstractButton* button )
{
if ( button == m_addButton )
{
TomahawkUtils::createAccountFromFactory( m_factory, this );
}
else
reject();
}

View File

@@ -1,62 +0,0 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2010-2011, Leo Franchi <lfranchi@kde.org>
*
* Tomahawk is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Tomahawk is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Tomahawk. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef ACCOUNTFACTORYWRAPPER_H
#define ACCOUNTFACTORYWRAPPER_H
#include <QDialog>
#include <QModelIndex>
class QAbstractButton;
namespace Tomahawk {
namespace Accounts {
class AccountFactory;
class Account;
}
}
class Ui_AccountFactoryWrapper;
// class AccountFactoryWrapper_
class AccountFactoryWrapper : public QDialog
{
Q_OBJECT
public:
enum ExtraRoles {
AccountRole = Qt::UserRole + 140
};
explicit AccountFactoryWrapper( Tomahawk::Accounts::AccountFactory* factory, QWidget* parent = 0 );
virtual ~AccountFactoryWrapper() {}
public slots:
void openAccountConfig( Tomahawk::Accounts::Account* );
void removeAccount( Tomahawk::Accounts::Account* );
void accountCheckedOrUnchecked( const QModelIndex& , Tomahawk::Accounts::Account* , Qt::CheckState );
private slots:
void buttonClicked( QAbstractButton* );
void load();
private:
Tomahawk::Accounts::AccountFactory* m_factory;
Ui_AccountFactoryWrapper* m_ui;
QPushButton* m_addButton;
};
#endif // ACCOUNTFACTORYWRAPPER_H

View File

@@ -1,110 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>AccountFactoryWrapper</class>
<widget class="QDialog" name="AccountFactoryWrapper">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>507</width>
<height>150</height>
</rect>
</property>
<property name="windowTitle">
<string>Dialog</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="factoryIcon">
<property name="text">
<string/>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="factoryDescription">
<property name="text">
<string>Description goes here</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="Line" name="line">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<widget class="QTreeWidget" name="accountsList">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="verticalScrollBarPolicy">
<enum>Qt::ScrollBarAlwaysOff</enum>
</property>
<property name="horizontalScrollBarPolicy">
<enum>Qt::ScrollBarAlwaysOff</enum>
</property>
<property name="autoScroll">
<bool>false</bool>
</property>
<property name="selectionMode">
<enum>QAbstractItemView::NoSelection</enum>
</property>
<property name="verticalScrollMode">
<enum>QAbstractItemView::ScrollPerItem</enum>
</property>
<property name="rootIsDecorated">
<bool>false</bool>
</property>
<property name="headerHidden">
<bool>true</bool>
</property>
<property name="expandsOnDoubleClick">
<bool>false</bool>
</property>
<column>
<property name="text">
<string notr="true">1</string>
</property>
</column>
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Close</set>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View File

@@ -1,187 +0,0 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2010-2011, Leo Franchi <lfranchi@kde.org>
*
* Tomahawk is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Tomahawk is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Tomahawk. If not, see <http://www.gnu.org/licenses/>.
*/
#include "AccountFactoryWrapperDelegate.h"
#include "accounts/Account.h"
#include "AccountFactoryWrapper.h"
#include "utils/TomahawkUtils.h"
#include "Source.h"
#include <QApplication>
#include <QPainter>
#include <QMouseEvent>
using namespace Tomahawk::Accounts;
#define ICON_SIZE 15
#define CONFIG_WRENCH_SIZE 20
#define PADDING 4
AccountFactoryWrapperDelegate::AccountFactoryWrapperDelegate( QObject* parent )
: QStyledItemDelegate( parent )
{
m_removePixmap.load( RESPATH "images/list-remove.png" );
m_onlineIcon.load( RESPATH "images/sipplugin-online.png" );
m_offlineIcon.load( RESPATH "images/sipplugin-offline.png" );
m_removePixmap = m_removePixmap.scaled( ICON_SIZE, ICON_SIZE, Qt::KeepAspectRatio, Qt::SmoothTransformation );
m_onlineIcon = m_onlineIcon.scaled( ICON_SIZE, ICON_SIZE, Qt::KeepAspectRatio, Qt::SmoothTransformation );
m_offlineIcon = m_offlineIcon.scaled( ICON_SIZE, ICON_SIZE, Qt::KeepAspectRatio, Qt::SmoothTransformation );
m_configIcon.addFile( RESPATH "images/configure.png", QSize( CONFIG_WRENCH_SIZE - 8, CONFIG_WRENCH_SIZE - 8 ) );
}
void
AccountFactoryWrapperDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const
{
QStyleOptionViewItemV4 opt = option;
initStyleOption( &opt, index );
const int center = opt.rect.height() / 2 + opt.rect.top();
const int topIcon = center - ICON_SIZE/2;
// draw the background
const QWidget* w = opt.widget;
QStyle* style = w ? w->style() : QApplication::style();
style->drawPrimitive( QStyle::PE_PanelItemViewItem, &opt, painter, w );
Account* acc = qobject_cast< Account* >( index.data( AccountFactoryWrapper::AccountRole ).value< QObject* >() );
Q_ASSERT( acc );
// Checkbox on left edge, then text
const QRect checkRect( PADDING/4, PADDING/4 + opt.rect.top(), opt.rect.height() - PADDING/4, opt.rect.height() - PADDING/4 );
m_cachedCheckRects[ index ] = checkRect;
QStyleOptionViewItemV4 opt2 = opt;
opt2.rect = checkRect;
opt.checkState == Qt::Checked ? opt2.state |= QStyle::State_On : opt2.state |= QStyle::State_Off;
style->drawPrimitive( QStyle::PE_IndicatorViewItemCheck, &opt2, painter, w );
// name on left
painter->drawText( opt.rect.adjusted( checkRect.right() + PADDING, PADDING, -PADDING, -PADDING ), Qt::AlignLeft | Qt::AlignVCenter, acc->accountFriendlyName() );
// remove, config, status on right
const QRect pmRect( opt.rect.right() - PADDING - m_removePixmap.width(), topIcon, ICON_SIZE, ICON_SIZE );
painter->drawPixmap( pmRect, m_removePixmap );
m_cachedButtonRects[ index ] = pmRect;
const QRect confRect( pmRect.left() - PADDING - CONFIG_WRENCH_SIZE, center - CONFIG_WRENCH_SIZE/2, CONFIG_WRENCH_SIZE, CONFIG_WRENCH_SIZE );
QStyleOptionToolButton topt;
topt.rect = confRect;
topt.pos = confRect.topLeft();
topt.font = opt.font;
topt.icon = m_configIcon;
topt.iconSize = QSize( CONFIG_WRENCH_SIZE - 8, CONFIG_WRENCH_SIZE - 8 );
topt.subControls = QStyle::SC_ToolButton;
topt.activeSubControls = QStyle::SC_None;
topt.features = QStyleOptionToolButton::None;
bool pressed = ( m_configPressed == opt.index );
topt.state = pressed ? QStyle::State_On : QStyle::State_Raised;
if( opt.state & QStyle::State_MouseOver || pressed )
topt.state |= QStyle::State_HasFocus;
style->drawComplexControl( QStyle::CC_ToolButton, &topt, painter, w );
m_cachedConfigRects[ index ] = confRect;
QPixmap p;
QString statusText;
Account::ConnectionState state = acc->connectionState();
if ( state == Account::Connected )
{
p = m_onlineIcon;
statusText = tr( "Online" );
}
else if ( state == Account::Connecting )
{
p = m_offlineIcon;
statusText = tr( "Connecting..." );
}
else
{
p = m_offlineIcon;
statusText = tr( "Offline" );
}
const QRect connectIconRect( confRect.left() - PADDING - ICON_SIZE, topIcon, ICON_SIZE, ICON_SIZE );
painter->drawPixmap( connectIconRect, p );
int width = painter->fontMetrics().width( statusText );
painter->drawText( QRect( connectIconRect.left() - PADDING - width, center - painter->fontMetrics().height()/2, width, painter->fontMetrics().height() ), statusText );
}
QSize
AccountFactoryWrapperDelegate::sizeHint(const QStyleOptionViewItem&, const QModelIndex&) const
{
return QSize( 200, ACCOUNT_ROW_HEIGHT );
}
bool
AccountFactoryWrapperDelegate::editorEvent( QEvent* event, QAbstractItemModel*, const QStyleOptionViewItem&, const QModelIndex& index )
{
if ( event->type() != QEvent::MouseButtonPress &&
event->type() != QEvent::MouseButtonRelease &&
event->type() != QEvent::MouseButtonDblClick &&
event->type() != QEvent::MouseMove )
return false;
if ( event->type() == QEvent::MouseButtonPress )
{
// Show the config wrench as depressed on click
QMouseEvent* me = static_cast< QMouseEvent* >( event );
if ( me->button() == Qt::LeftButton && m_cachedConfigRects.contains( index ) && m_cachedConfigRects[ index ].contains( me->pos() ) )
{
m_configPressed = index;
Account* acct = qobject_cast< Account* >( index.data( AccountFactoryWrapper::AccountRole ).value< QObject* >() );
Q_ASSERT( acct ); // Should not be showing a config wrench if there is no account!
emit openConfig( acct );
return true;
}
} else if ( event->type() == QEvent::MouseButtonRelease || event->type() == QEvent::MouseButtonDblClick )
{
QMouseEvent* me = static_cast< QMouseEvent* >( event );
if ( m_configPressed.isValid() )
emit update( m_configPressed );
m_configPressed = QModelIndex();
Account* acct = qobject_cast< Account* >( index.data( AccountFactoryWrapper::AccountRole ).value< QObject* >() );
if ( m_cachedCheckRects.contains( index ) && m_cachedCheckRects[ index ].contains( me->pos() ) )
{
// Check box for this row
// eat the double click events inside the check rect
if( event->type() == QEvent::MouseButtonDblClick ) {
return true;
}
Qt::CheckState curState = static_cast< Qt::CheckState >( index.data( Qt::CheckStateRole ).toInt() );
Qt::CheckState newState = curState == Qt::Checked ? Qt::Unchecked : Qt::Checked;
emit checkOrUncheck( index, acct, newState );
}
if ( m_cachedButtonRects.contains( index ) && m_cachedButtonRects[ index ].contains( me->pos() ) )
{
emit removeAccount( acct );
return true;
}
}
return false;
}

View File

@@ -1,61 +0,0 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2010-2012, Leo Franchi <lfranchi@kde.org>
*
* Tomahawk is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Tomahawk is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Tomahawk. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef ACCOUNTFACTORYWRAPPERDELEGATE_H
#define ACCOUNTFACTORYWRAPPERDELEGATE_H
#include <QStyledItemDelegate>
#define ACCOUNT_ROW_HEIGHT 20
namespace Tomahawk {
namespace Accounts {
class Account;
}
}
class AccountFactoryWrapperDelegate : public QStyledItemDelegate
{
Q_OBJECT
public:
explicit AccountFactoryWrapperDelegate( QObject* parent = 0 );
virtual void paint( QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index ) const;
virtual QSize sizeHint( const QStyleOptionViewItem& option, const QModelIndex& index ) const;
virtual bool editorEvent(QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option, const QModelIndex& index);
signals:
void update( const QModelIndex& );
void openConfig( Tomahawk::Accounts::Account* );
void removeAccount( Tomahawk::Accounts::Account* );
void checkOrUncheck( const QModelIndex& row, Tomahawk::Accounts::Account* account, Qt::CheckState newState );
private:
QPixmap m_removePixmap, m_offlineIcon, m_onlineIcon;
QIcon m_configIcon;
QModelIndex m_configPressed;
mutable QHash< QPersistentModelIndex, QRect > m_cachedCheckRects;
mutable QHash< QPersistentModelIndex, QRect > m_cachedButtonRects;
mutable QHash< QPersistentModelIndex, QRect > m_cachedConfigRects;
};
#endif // ACCOUNTFACTORYWRAPPERDELEGATE_H

View File

@@ -37,78 +37,115 @@ ENDIF()
#ENDFOREACH( moddir )
SET( tomahawkSources ${tomahawkSources}
web/Api_v1.cpp
web/api_v1.cpp
MusicScanner.cpp
ShortcutHandler.cpp
ScanManager.cpp
UbuntuUnityHack.cpp
TomahawkApp.cpp
musicscanner.cpp
shortcuthandler.cpp
scanmanager.cpp
ubuntuunityhack.cpp
tomahawkapp.cpp
main.cpp
)
IF(LIBLASTFM_FOUND)
SET(tomahawkSources ${tomahawkSources}
Scrobbler.cpp
scrobbler.cpp
)
ENDIF(LIBLASTFM_FOUND)
SET( tomahawkSourcesGui ${tomahawkSourcesGui}
sourcetree/SourcesModel.cpp
sourcetree/SourcesProxyModel.cpp
sourcetree/SourceTreeView.cpp
sourcetree/SourceDelegate.cpp
sourcetree/AnimationHelper.cpp
sourcetree/items/SourceTreeItem.cpp
sourcetree/items/SourceItem.cpp
sourcetree/items/PlaylistItems.cpp
sourcetree/items/CategoryItems.cpp
sourcetree/items/GenericPageItems.cpp
sourcetree/items/TemporaryPageItem.cpp
sourcetree/items/GroupItem.cpp
sourcetree/items/HistoryItem.cpp
sourcetree/sourcesmodel.cpp
sourcetree/sourcesproxymodel.cpp
sourcetree/sourcetreeview.cpp
sourcetree/sourcedelegate.cpp
sourcetree/animationhelper.cpp
sourcetree/items/sourcetreeitem.cpp
sourcetree/items/sourceitem.cpp
sourcetree/items/playlistitems.cpp
sourcetree/items/categoryitems.cpp
sourcetree/items/genericpageitems.cpp
sourcetree/items/temporarypageitem.cpp
sourcetree/items/groupitem.cpp
sourcetree/items/historyitem.cpp
utils/GuiHelpers.cpp
breakpad/BreakPad.cpp
accounts/lastfm/LastFmAccount.cpp
accounts/lastfm/LastFmConfig.cpp
accounts/lastfm/LastFmInfoPlugin.cpp
accounts/spotify/SpotifyAccount.cpp
accounts/spotify/SpotifyAccountConfig.cpp
accounts/spotify/SpotifyPlaylistUpdater.cpp
TomahawkTrayIcon.cpp
AudioControls.cpp
SettingsDialog.cpp
DiagnosticsDialog.cpp
AccountDelegate.cpp
SettingsListDelegate.cpp
DelegateConfigWrapper.cpp
TomahawkWindow.cpp
tomahawktrayicon.cpp
audiocontrols.cpp
settingsdialog.cpp
diagnosticsdialog.cpp
configdelegatebase.cpp
sipconfigdelegate.cpp
resolverconfigdelegate.cpp
settingslistdelegate.cpp
resolversmodel.cpp
tomahawkwindow.cpp
LoadXSPFDialog.cpp
AccountFactoryWrapper.cpp
AccountFactoryWrapperDelegate.cpp
SocialWidget.cpp
)
IF( WITH_BREAKPAD )
LIST(APPEND tomahawkSourcesGui breakpad/BreakPad.cpp)
ENDIF()
SET( tomahawkHeaders ${tomahawkHeaders}
tomahawkapp.h
web/api_v1.h
musicscanner.h
scanmanager.h
ubuntuunityhack.h
shortcuthandler.h
)
IF(LIBLASTFM_FOUND)
SET(tomahawkHeaders ${tomahawkHeaders}
scrobbler.h
)
ENDIF(LIBLASTFM_FOUND)
IF(LIBATTICA_FOUND)
SET( tomahawkSourcesGui ${tomahawkSourcesGui} GetNewStuffDialog.cpp GetNewStuffDelegate.cpp GetNewStuffModel.cpp )
SET( tomahawkHeadersGui ${tomahawkHeadersGui} GetNewStuffDialog.h GetNewStuffDelegate.h GetNewStuffModel.h )
INCLUDE_DIRECTORIES( ${LIBATTICA_INCLUDE_DIR} )
ENDIF(LIBATTICA_FOUND)
SET( tomahawkHeadersGui ${tomahawkHeadersGui}
sourcetree/sourcesmodel.h
sourcetree/sourcesproxymodel.h
sourcetree/sourcetreeview.h
sourcetree/sourcedelegate.h
sourcetree/animationhelper.h
sourcetree/items/sourcetreeitem.h
sourcetree/items/sourceitem.h
sourcetree/items/playlistitems.h
sourcetree/items/categoryitems.h
sourcetree/items/genericpageitems.h
sourcetree/items/temporarypageitem.h
sourcetree/items/groupitem.h
sourcetree/items/historyitem.h
tomahawktrayicon.h
audiocontrols.h
settingsdialog.h
diagnosticsdialog.h
configdelegatebase.h
resolverconfigdelegate.h
sipconfigdelegate.h
settingslistdelegate.h
resolversmodel.h
delegateconfigwrapper.h
tomahawkwindow.h
LoadXSPFDialog.h
)
SET( tomahawkUI ${tomahawkUI}
TomahawkWindow.ui
DiagnosticsDialog.ui
StackedSettingsDialog.ui
ProxyDialog.ui
tomahawkwindow.ui
diagnosticsdialog.ui
stackedsettingsdialog.ui
proxydialog.ui
accounts/lastfm/LastFmConfig.ui
accounts/spotify/SpotifyAccountConfig.ui
audiocontrols.ui
AudioControls.ui
GetNewStuffDialog.ui
LoadXSPFDialog.ui
AccountFactoryWrapper.ui
SocialWidget.ui
)
INCLUDE_DIRECTORIES(
@@ -134,7 +171,6 @@ INCLUDE_DIRECTORIES(
${TAGLIB_INCLUDES}
${PHONON_INCLUDES}
${QJSON_INCLUDE_DIR}
${LIBATTICA_INCLUDE_DIR}
${LIBECHONEST_INCLUDE_DIR}
${LIBECHONEST_INCLUDE_DIR}/..
)
@@ -151,41 +187,42 @@ ENDIF( UNIX )
IF( APPLE )
INCLUDE_DIRECTORIES( ${CMAKE_SOURCE_DIR}/thirdparty/SPMediaKeyTap )
SET( tomahawkSources ${tomahawkSources} mac/TomahawkApp_Mac.mm mac/MacShortcutHandler.cpp )
SET( tomahawkHeaders ${tomahawkHeaders} mac/tomahawkapp_mac.h mac/macshortcuthandler.h )
SET( tomahawkSources ${tomahawkSources} mac/tomahawkapp_mac.mm mac/macshortcuthandler.cpp )
IF(HAVE_SPARKLE)
SET( tomahawkHeaders ${tomahawkHeaders} ${SPARKLE}/Headers )
ENDIF(HAVE_SPARKLE)
ENDIF( APPLE )
IF(GLOOX_FOUND)
INCLUDE_DIRECTORIES( ${GLOOX_INCLUDE_DIR} )
SET( tomahawkSources ${tomahawkSources} xmppbot/XmppBot.cpp )
SET( tomahawkHeaders ${tomahawkHeaders} xmppbot/xmppbot.h )
SET( tomahawkSources ${tomahawkSources} xmppbot/xmppbot.cpp )
ENDIF(GLOOX_FOUND)
ADD_SUBDIRECTORY( accounts )
ADD_SUBDIRECTORY( infoplugins )
ADD_SUBDIRECTORY( sip )
IF(QCA2_FOUND)
INCLUDE_DIRECTORIES( ${QCA2_INCLUDE_DIR} )
ENDIF(QCA2_FOUND)
INCLUDE(GNUInstallDirs)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/Config.h.in
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/config.h.in
${CMAKE_CURRENT_BINARY_DIR}/config.h)
include( ${CMAKE_SOURCE_DIR}/lang/translations.cmake )
SET( final_src ${final_src} ${tomahawkMoc} ${tomahawkSources} ${trans_outfile})
SET( final_src ${final_src} ${tomahawkMoc} ${tomahawkSources} ${tomahawkHeaders} ${trans_outfile})
IF( BUILD_GUI )
LIST(APPEND tomahawkHeaders ${tomahawkHeadersGui})
LIST(APPEND tomahawkSources ${tomahawkSourcesGui})
qt4_wrap_ui( tomahawkUI_H ${tomahawkUI} )
IF( WITH_CRASHREPORTER )
ADD_SUBDIRECTORY( breakpad/CrashReporter )
ENDIF()
ENDIF()
kde4_add_app_icon( tomahawkSources "${CMAKE_SOURCE_DIR}/data/icons/tomahawk-icon-*.png" )
qt4_add_resources( RC_SRCS "../resources.qrc" )
qt4_wrap_cpp( tomahawkMoc ${tomahawkHeaders} )
SET( final_src ${final_src} ${tomahawkUI_H} ${tomahawkMoc} ${tomahawkSources} ${RC_SRCS} )
IF( UNIX AND NOT APPLE )
@@ -193,14 +230,13 @@ IF( UNIX AND NOT APPLE )
ENDIF( UNIX AND NOT APPLE )
IF( APPLE )
ADD_EXECUTABLE( tomahawk MACOSX_BUNDLE ${final_src} )
SET_TARGET_PROPERTIES(tomahawk PROPERTIES MACOSX_BUNDLE_INFO_PLIST "${CMAKE_BINARY_DIR}/Info.plist")
SET_TARGET_PROPERTIES(tomahawk PROPERTIES MACOSX_BUNDLE_INFO_PLIST "${CMAKE_BINARY_DIR}/Info.plist"
)
ENDIF( APPLE )
IF( WIN32 )
ADD_EXECUTABLE( tomahawk WIN32 ${final_src} )
ENDIF( WIN32 )
SET_TARGET_PROPERTIES(tomahawk PROPERTIES AUTOMOC TRUE)
MESSAGE( STATUS "OS_SPECIFIC_LINK_LIBRARIES: ${OS_SPECIFIC_LINK_LIBRARIES}" )
SET(LINK_LIBRARIES "")
@@ -213,11 +249,9 @@ ENDIF(GLOOX_FOUND)
IF(QCA2_FOUND)
SET(LINK_LIBRARIES ${LINK_LIBRARIES} ${QCA2_LIBRARIES} )
ENDIF(QCA2_FOUND)
IF(WITH_BREAKPAD)
SET(LINK_LIBRARIES ${LINK_LIBRARIES} tomahawk_breakpad)
ENDIF()
TARGET_LINK_LIBRARIES( tomahawk
tomahawk_breakpad
${LINK_LIBRARIES}
${TOMAHAWK_LIBRARIES}
${PHONON_LIBS}

View File

@@ -1,158 +0,0 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2010-2011, Leo Franchi <lfranchi@kde.org>
*
* Tomahawk is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Tomahawk is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Tomahawk. If not, see <http://www.gnu.org/licenses/>.
*/
#include "DelegateConfigWrapper.h"
#include <QMessageBox>
DelegateConfigWrapper::DelegateConfigWrapper( QWidget* conf, QWidget* aboutWidget, const QString& title, QWidget* parent, Qt::WindowFlags flags )
: QDialog( parent, flags )
, m_widget( conf )
, m_aboutW( aboutWidget )
, m_deleted( false )
{
m_widget->setWindowFlags( Qt::Sheet );
#ifdef Q_WS_MAC
m_widget->setVisible( true );
#endif
setWindowTitle( title );
QVBoxLayout* v = new QVBoxLayout( this );
v->setContentsMargins( 0, 0, 0, 0 );
v->addWidget( m_widget );
QDialogButtonBox::StandardButtons buttons = QDialogButtonBox::Ok | QDialogButtonBox::Cancel;
if ( m_aboutW )
{
m_aboutW->hide();
buttons |= QDialogButtonBox::Help;
}
m_buttons = new QDialogButtonBox( buttons, Qt::Horizontal, this );
m_okButton = m_buttons->button( QDialogButtonBox::Ok );
connect( m_buttons, SIGNAL( clicked( QAbstractButton*) ), this, SLOT( closed( QAbstractButton* ) ) );
connect( this, SIGNAL( rejected() ), this, SLOT( rejected() ) );
if ( m_aboutW )
{
connect( m_buttons->button( QDialogButtonBox::Help ), SIGNAL( clicked( bool ) ), this, SLOT( aboutClicked( bool ) ) );
m_buttons->button( QDialogButtonBox::Help )->setText( tr( "About" ) );
}
v->addWidget( m_buttons );
setLayout( v );
#ifdef Q_WS_MAC
setSizeGripEnabled( false );
setMinimumSize( sizeHint() );
setMaximumSize( sizeHint() ); // to remove the resize grip on osx this is the only way
if( conf->metaObject()->indexOfSignal( "sizeHintChanged()" ) > -1 )
connect( conf, SIGNAL( sizeHintChanged() ), this, SLOT( updateSizeHint() ) );
#else
m_widget->setVisible( true );
#endif
}
void
DelegateConfigWrapper::setShowDelete( bool del )
{
if ( del )
m_deleteButton = m_buttons->addButton( tr( "Delete Account" ), QDialogButtonBox::DestructiveRole );
}
void
DelegateConfigWrapper::toggleOkButton( bool dataError )
{
// if dataError is True we want to set the button enabled to false
m_okButton->setEnabled( !dataError );
}
void
DelegateConfigWrapper::closed( QAbstractButton* b )
{
QDialogButtonBox* buttons = qobject_cast< QDialogButtonBox* >( sender() );
if ( buttons->standardButton( b ) == QDialogButtonBox::Help )
return;
// let the config widget live to see another day
layout()->removeWidget( m_widget );
m_widget->setParent( 0 );
m_widget->setVisible( false );
if ( buttons->standardButton( b ) == QDialogButtonBox::Ok )
done( QDialog::Accepted );
else if ( b == m_deleteButton )
{
m_deleted = true;
emit closedWithDelete();
reject();
}
else
done( QDialog::Rejected );
}
void
DelegateConfigWrapper::rejected()
{
layout()->removeWidget( m_widget );
m_widget->setParent( 0 );
m_widget->setVisible( false );
}
void
DelegateConfigWrapper::updateSizeHint()
{
hide();
setSizeGripEnabled( false );
setMinimumSize( sizeHint() );
setMaximumSize( sizeHint() );
show();
}
void
DelegateConfigWrapper::aboutClicked( bool )
{
Q_ASSERT( m_aboutW );
m_aboutW->show();
QDialog d( this );
d.setWindowTitle( tr( "About this Account" ) );
QVBoxLayout* v = new QVBoxLayout( &d );
v->addWidget( m_aboutW );
QDialogButtonBox* bbox = new QDialogButtonBox( QDialogButtonBox::Ok, Qt::Horizontal, &d );
v->addWidget( bbox );
d.setLayout( v );
connect( bbox, SIGNAL( clicked( QAbstractButton* ) ), &d, SLOT( accept() ) );
d.exec();
v->removeWidget( m_aboutW );
m_aboutW->setParent( 0 );
m_aboutW->hide();
}

View File

@@ -1,61 +0,0 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2010-2011, Leo Franchi <lfranchi@kde.org>
*
* Tomahawk is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Tomahawk is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Tomahawk. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef RESOLVER_CONFIG_WRAPPER
#define RESOLVER_CONFIG_WRAPPER
#include <QDialog>
#include <QDialogButtonBox>
#include <QVBoxLayout>
#include <QPushButton>
#include <QDebug>
class DelegateConfigWrapper : public QDialog
{
Q_OBJECT
public:
DelegateConfigWrapper( QWidget* conf, QWidget* aboutWidget, const QString& title, QWidget* parent, Qt::WindowFlags flags = 0 );
~DelegateConfigWrapper() {}
void setShowDelete( bool del );
bool deleted() const { return m_deleted; }
public slots:
void toggleOkButton( bool dataError );
void closed( QAbstractButton* b );
// we get a rejected() signal emitted if the user presses escape (and no clicked() signal )
void rejected();
void updateSizeHint();
signals:
void closedWithDelete();
private slots:
void aboutClicked( bool );
private:
QDialogButtonBox* m_buttons;
QWidget* m_widget, *m_aboutW;
QPushButton *m_okButton, *m_deleteButton;
bool m_deleted;
};
#endif

323
src/GetNewStuffDelegate.cpp Normal file
View File

@@ -0,0 +1,323 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2010-2011, Leo Franchi <lfranchi@kde.org>
*
* Tomahawk is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Tomahawk is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Tomahawk. If not, see <http://www.gnu.org/licenses/>.
*/
#include "GetNewStuffDelegate.h"
#include "GetNewStuffModel.h"
#include "utils/tomahawkutils.h"
#include "utils/logger.h"
#include <QtGui/QPainter>
#include <QApplication>
#include <QMouseEvent>
#include "AtticaManager.h"
#define PADDING 4
#define PADDING_BETWEEN_STARS 2
#define STAR_SIZE 12
#ifdef Q_WS_MAC
#define SIZEHINT_HEIGHT 70
#else
#define SIZEHINT_HEIGHT 60
#endif
GetNewStuffDelegate::GetNewStuffDelegate( QObject* parent )
: QStyledItemDelegate ( parent )
, m_widestTextWidth( 0 )
, m_hoveringOver( -1 )
{
m_defaultCover.load( RESPATH "images/sipplugin-online.png" );
m_ratingStarPositive.load( RESPATH "images/starred.png" );
m_ratingStarNegative.load( RESPATH "images/star-unstarred.png" );
m_onHoverStar.load( RESPATH "images/star-hover.png" );
m_ratingStarPositive = m_ratingStarPositive.scaled( STAR_SIZE, STAR_SIZE, Qt::KeepAspectRatio, Qt::SmoothTransformation );
m_ratingStarNegative = m_ratingStarNegative.scaled( STAR_SIZE, STAR_SIZE, Qt::KeepAspectRatio, Qt::SmoothTransformation );
m_onHoverStar = m_onHoverStar.scaled( STAR_SIZE, STAR_SIZE, Qt::KeepAspectRatio, Qt::SmoothTransformation );
const int w = SIZEHINT_HEIGHT - 2*PADDING;
m_defaultCover = m_defaultCover.scaled( w, w, Qt::KeepAspectRatio, Qt::SmoothTransformation );
// save the widest wifth
QFont f( QApplication::font() );
f.setPointSize( f.pointSize() - 1 );
QFontMetrics fm( f );
QStringList l = QStringList() << tr( "Installed" ) << tr( "Installing" ) << tr( "Failed" ) << tr( "Uninstalling" );
foreach ( const QString& str, l )
{
if ( fm.width( str ) > m_widestTextWidth )
m_widestTextWidth = fm.width( str );
}
}
void
GetNewStuffDelegate::paint( QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index ) const
{
QStyleOptionViewItemV4 opt = option;
initStyleOption( &opt, index );
QApplication::style()->drawPrimitive( QStyle::PE_PanelItemViewItem, &opt, painter, opt.widget );
painter->setRenderHint( QPainter::Antialiasing );
QFont titleFont = opt.font;
titleFont.setBold( true );
titleFont.setPointSize( titleFont.pointSize() + 2 );
QFontMetrics titleMetrics( titleFont );
QFont authorFont = opt.font;
authorFont.setItalic( true );
authorFont.setPointSize( authorFont.pointSize() - 1 );
QFontMetrics authorMetrics( authorFont );
QFont descFont = authorFont;
descFont.setItalic( false );
QFontMetrics descMetrics( descFont );
QFont installFont = opt.font;
installFont.setPointSize( installFont.pointSize() - 1 );
QFontMetrics installMetrics( descFont );
const int height = opt.rect.height();
const int center = height / 2 + opt.rect.top();
// Pixmap
QPixmap p = index.data( Qt::DecorationRole ).value< QPixmap >();
const int pixmapWidth = height - 2*PADDING;
QRect pixmapRect( PADDING, PADDING + opt.rect.top(), pixmapWidth, pixmapWidth );
if ( p.isNull() ) // default image... TODO
p = m_defaultCover;
else
p = p.scaled( pixmapRect.size(), Qt::KeepAspectRatio, Qt::SmoothTransformation );
painter->drawPixmap( pixmapRect, p );
// Go from right edge now, stars, install button, and downloaded info
// install / status button
AtticaManager::ResolverState state = static_cast< AtticaManager::ResolverState >( index.data( GetNewStuffModel::StateRole ).toInt() );
QString actionText;
switch( state )
{
case AtticaManager::Uninstalled:
actionText = tr( "Install" );
break;
case AtticaManager::Installing:
actionText = tr( "Installing" );
break;
case AtticaManager::Upgrading:
actionText = tr( "Upgrading" );
break;
case AtticaManager::Failed:
actionText = tr( "Failed" );
break;
case AtticaManager::Installed:
actionText = tr( "Uninstall" );
break;
case AtticaManager::NeedsUpgrade:
actionText = tr( "Upgrade" );
break;
}
const int btnWidth = m_widestTextWidth + 7;
const int leftEdge = opt.rect.width() - PADDING - btnWidth - 3;
const QRect btnRect( leftEdge, center - ( installMetrics.height() + 4 ) / 2, btnWidth, installMetrics.height() + 4 );
m_cachedButtonRects[ QPair<int, int>(index.row(), index.column()) ] = btnRect;
QPen saved = painter->pen();
painter->setPen( opt.palette.color( QPalette::Active, QPalette::AlternateBase ) );
QPainterPath btnPath;
const int radius = 3;
//btnPath.addRoundedRect( btnRect, 3, 3 );
// draw top half gradient
const int btnCenter = btnRect.bottom() - ( btnRect.height() / 2 );
btnPath.moveTo( btnRect.left(), btnCenter );
btnPath.lineTo( btnRect.left(), btnRect.top() + radius );
btnPath.quadTo( QPoint( btnRect.topLeft() ), QPoint( btnRect.left() + radius, btnRect.top() ) );
btnPath.lineTo( btnRect.right() - radius, btnRect.top() );
btnPath.quadTo( QPoint( btnRect.topRight() ), QPoint( btnRect.right(), btnRect.top() + radius ) );
btnPath.lineTo( btnRect.right(),btnCenter );
btnPath.lineTo( btnRect.left(), btnCenter );
QLinearGradient g;
g.setColorAt( 0, QColor(54, 127, 211) );
g.setColorAt( 0.5, QColor(43, 104, 182) );
//painter->setPen( bg.darker() );
painter->fillPath( btnPath, g );
//painter->drawPath( btnPath );
btnPath = QPainterPath();
btnPath.moveTo( btnRect.left(), btnCenter );
btnPath.lineTo( btnRect.left(), btnRect.bottom() - radius );
btnPath.quadTo( QPoint( btnRect.bottomLeft() ), QPoint( btnRect.left() + radius, btnRect.bottom() ) );
btnPath.lineTo( btnRect.right() - radius, btnRect.bottom() );
btnPath.quadTo( QPoint( btnRect.bottomRight() ), QPoint( btnRect.right(), btnRect.bottom() - radius ) );
btnPath.lineTo( btnRect.right(), btnCenter );
btnPath.lineTo( btnRect.left(), btnCenter );
g.setColorAt( 0, QColor(34, 85, 159) );
g.setColorAt( 0.5, QColor(35, 79, 147) );
painter->fillPath( btnPath, g );
painter->setFont( installFont );
painter->drawText( btnRect, Qt::AlignCenter, actionText );
painter->setPen( saved );
// rating stars
int rating = index.data( GetNewStuffModel::RatingRole ).toInt();
const int ratingWidth = 5 * ( m_ratingStarPositive.width() + PADDING_BETWEEN_STARS );
int runningEdge = ( btnRect.right() - btnRect.width() / 2 ) - ratingWidth / 2;
for ( int i = 1; i < 6; i++ )
{
QRect r( runningEdge, btnRect.top() - m_ratingStarPositive.height() - PADDING, m_ratingStarPositive.width(), m_ratingStarPositive.height() );
if ( i == 1 )
m_cachedStarRects[ QPair<int, int>(index.row(), index.column()) ] = r;
const bool userHasRated = index.data( GetNewStuffModel::UserHasRatedRole ).toBool();
if ( !userHasRated && // Show on-hover animation if the user hasn't rated it yet, and is hovering over it
m_hoveringOver > -1 &&
m_hoveringItem == index )
{
if ( i <= m_hoveringOver ) // positive star
painter->drawPixmap( r, m_onHoverStar );
else
painter->drawPixmap( r, m_ratingStarNegative );
}
else
{
if ( i <= rating ) // positive or rated star
{
if ( userHasRated )
painter->drawPixmap( r, m_onHoverStar );
else
painter->drawPixmap( r, m_ratingStarPositive );
}
else
painter->drawPixmap( r, m_ratingStarNegative );
}
runningEdge += m_ratingStarPositive.width() + PADDING_BETWEEN_STARS;
}
// downloaded num times, underneath button
QString count = tr( "%1 downloads" ).arg( index.data( GetNewStuffModel::DownloadCounterRole ).toInt() );
const QRect countRect( btnRect.left(), btnRect.bottom() + PADDING, btnRect.width(), opt.rect.bottom() - PADDING - btnRect.bottom() );
QFont countFont = descFont;
countFont.setPointSize( countFont.pointSize() - 2 );
countFont.setBold( true );
painter->setFont( countFont );
painter->drawText( countRect, Qt::AlignCenter | Qt::TextWordWrap, count );
// author and version
QString author = index.data( GetNewStuffModel::AuthorRole ).toString();
const int authorWidth = authorMetrics.width( author );
const int topTextLine = opt.rect.top() + PADDING;
const QRect authorRect( btnRect.x() - 3*PADDING - authorWidth, topTextLine, authorWidth + 6, authorMetrics.height() );
painter->setFont( authorFont );
painter->drawText( authorRect, Qt::AlignCenter, author );
const QRect versionRect = authorRect.translated( 0, authorRect.height() );
QString version = index.data( GetNewStuffModel::VersionRole ).toString();
painter->drawText( versionRect, Qt::AlignCenter, version );
// title
QString title = index.data( Qt::DisplayRole ).toString();
const int rightTitleEdge = authorRect.left() - PADDING;
const int leftTitleEdge = pixmapRect.right() + PADDING;
const QRect textRect( leftTitleEdge, topTextLine, rightTitleEdge - leftTitleEdge, versionRect.bottom() - opt.rect.top() - PADDING );
painter->setFont( titleFont );
painter->drawText( textRect, Qt::AlignVCenter | Qt::AlignLeft, title );
// description
QString desc = index.data( GetNewStuffModel::DescriptionRole ).toString();
const int descWidth = btnRect.left() - leftTitleEdge - PADDING;
const QRect descRect( leftTitleEdge, versionRect.bottom(), descWidth, opt.rect.bottom() - versionRect.bottom() + PADDING );
painter->setFont( descFont );
painter->drawText( descRect, Qt::AlignLeft | Qt::TextWordWrap, desc );
}
QSize
GetNewStuffDelegate::sizeHint( const QStyleOptionViewItem& option, const QModelIndex& index ) const
{
Q_UNUSED( option );
Q_UNUSED( index );
return QSize( 200, SIZEHINT_HEIGHT );
}
bool
GetNewStuffDelegate::editorEvent( QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option, const QModelIndex& index )
{
Q_UNUSED( option );
if ( event->type() != QEvent::MouseButtonRelease &&
event->type() != QEvent::MouseMove )
return false;
if ( event->type() == QEvent::MouseButtonRelease && m_cachedButtonRects.contains( QPair<int, int>( index.row(), index.column() ) ) )
{
QRect rect = m_cachedButtonRects[ QPair<int, int>( index.row(), index.column() ) ];
QMouseEvent* me = static_cast< QMouseEvent* >( event );
if ( rect.contains( me->pos() ) )
{
model->setData( index, true );
return true;
}
}
if ( m_cachedStarRects.contains( QPair<int, int>( index.row(), index.column() ) ) )
{
QRect fullStars = m_cachedStarRects[ QPair<int, int>( index.row(), index.column() ) ];
const int starsWidth = 5 * ( m_ratingStarPositive.width() + PADDING_BETWEEN_STARS );
fullStars.setWidth( starsWidth );
QMouseEvent* me = static_cast< QMouseEvent* >( event );
if ( fullStars.contains( me->pos() ) )
{
const int eachStar = starsWidth / 5;
const int clickOffset = me->pos().x() - fullStars.x();
const int whichStar = (clickOffset / eachStar) + 1;
if ( event->type() == QEvent::MouseButtonRelease )
{
model->setData( index, whichStar, GetNewStuffModel::RatingRole );
}
else if ( event->type() == QEvent::MouseMove )
{
// 0-indexed
m_hoveringOver = whichStar;
m_hoveringItem = index;
}
return true;
}
}
if ( m_hoveringOver > -1 )
{
emit update( m_hoveringItem );
m_hoveringOver = -1;
m_hoveringItem = QPersistentModelIndex();
}
return false;
}

50
src/GetNewStuffDelegate.h Normal file
View File

@@ -0,0 +1,50 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2010-2011, Leo Franchi <lfranchi@kde.org>
*
* Tomahawk is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Tomahawk is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Tomahawk. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef GETNEWSTUFFDELEGATE_H
#define GETNEWSTUFFDELEGATE_H
#include <QStyledItemDelegate>
class
GetNewStuffDelegate : public QStyledItemDelegate
{
Q_OBJECT
public:
explicit GetNewStuffDelegate( QObject* parent = 0 );
virtual void paint( QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index ) const;
virtual QSize sizeHint( const QStyleOptionViewItem& option, const QModelIndex& index ) const;
signals:
void update( const QModelIndex& idx );
protected:
virtual bool editorEvent( QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option, const QModelIndex& index );
private:
QPixmap m_defaultCover, m_onHoverStar, m_ratingStarPositive, m_ratingStarNegative;
int m_widestTextWidth;
int m_hoveringOver;
QPersistentModelIndex m_hoveringItem;
mutable QHash< QPair<int, int>, QRect > m_cachedButtonRects;
mutable QHash< QPair<int, int>, QRect > m_cachedStarRects;
};
#endif // GETNEWSTUFFDELEGATE_H

54
src/GetNewStuffDialog.cpp Normal file
View File

@@ -0,0 +1,54 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2011, Leo Franchi <lfranchi@kde.org>
*
* Tomahawk is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Tomahawk is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Tomahawk. If not, see <http://www.gnu.org/licenses/>.
*/
#include "GetNewStuffDialog.h"
#include "ui_GetNewStuffDialog.h"
#include "GetNewStuffDelegate.h"
#include "GetNewStuffModel.h"
GetNewStuffDialog::GetNewStuffDialog( QWidget* parent, Qt::WindowFlags f )
: QDialog( parent, f )
, ui( new Ui::GetNewStuffDialog )
, m_model( new GetNewStuffModel( this ) )
{
ui->setupUi( this );
ui->listView->setModel( m_model );
GetNewStuffDelegate* del = new GetNewStuffDelegate( ui->listView );
connect( del, SIGNAL( update( QModelIndex ) ), ui->listView, SLOT( update( QModelIndex ) ) );
ui->listView->setItemDelegate( del );
ui->listView->setVerticalScrollMode( QAbstractItemView::ScrollPerPixel );
ui->listView->setMouseTracking( true );
setMinimumSize( 560, 350 );
#ifdef Q_WS_MAC
setMaximumSize( 560, 350 );
setSizeGripEnabled( false );
ui->listView->setAttribute( Qt::WA_MacShowFocusRect, false );
#endif
}
GetNewStuffDialog::~GetNewStuffDialog()
{
delete ui;
}

41
src/GetNewStuffDialog.h Normal file
View File

@@ -0,0 +1,41 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2011, Leo Franchi <lfranchi@kde.org>
*
* Tomahawk is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Tomahawk is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Tomahawk. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef GETNEWSTUFFDIALOG_H
#define GETNEWSTUFFDIALOG_H
#include <QDialog>
class GetNewStuffModel;
namespace Ui {
class GetNewStuffDialog;
}
class GetNewStuffDialog : public QDialog
{
Q_OBJECT
public:
explicit GetNewStuffDialog( QWidget *parent = 0, Qt::WindowFlags f = 0 );
~GetNewStuffDialog();
private:
Ui::GetNewStuffDialog *ui;
GetNewStuffModel* m_model;
};
#endif // GETNEWSTUFFDIALOG_H

67
src/GetNewStuffDialog.ui Normal file
View File

@@ -0,0 +1,67 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>GetNewStuffDialog</class>
<widget class="QDialog" name="GetNewStuffDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>449</width>
<height>282</height>
</rect>
</property>
<property name="windowTitle">
<string>Download New Resolvers</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QListView" name="listView"/>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>GetNewStuffDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>GetNewStuffDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>

156
src/GetNewStuffModel.cpp Normal file
View File

@@ -0,0 +1,156 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2010-2011, Leo Franchi <lfranchi@kde.org>
*
* Tomahawk is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Tomahawk is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Tomahawk. If not, see <http://www.gnu.org/licenses/>.
*/
#include "GetNewStuffModel.h"
#include "utils/tomahawkutils.h"
#include "utils/logger.h"
#include <QPixmap>
#include <QUrl>
#include "AtticaManager.h"
GetNewStuffModel::GetNewStuffModel( QObject* parent )
: QAbstractListModel ( parent )
{
if ( AtticaManager::instance()->resolversLoaded() )
m_contentList = AtticaManager::instance()->resolvers();
connect( AtticaManager::instance(), SIGNAL( resolversReloaded( Attica::Content::List ) ), this, SLOT( resolversReloaded( Attica::Content::List ) ) );
connect( AtticaManager::instance(), SIGNAL( resolverStateChanged( QString ) ), this, SLOT( resolverStateChanged( QString ) ) );
}
GetNewStuffModel::~GetNewStuffModel()
{
}
void
GetNewStuffModel::resolversReloaded( const Attica::Content::List& resolvers )
{
beginResetModel();
m_contentList = resolvers;
endResetModel();
}
void
GetNewStuffModel::resolverStateChanged( const QString& resolverId )
{
for ( int i = 0; i < m_contentList.count(); i++ )
{
const Attica::Content resolver = m_contentList[ i ];
if ( resolver.id() == resolverId )
{
QModelIndex idx = index( i, 0, QModelIndex() );
emit dataChanged( idx, idx );
}
}
}
QVariant
GetNewStuffModel::data( const QModelIndex& index, int role ) const
{
if ( !index.isValid() || !hasIndex( index.row(), index.column(), index.parent() ) )
return QVariant();
Attica::Content resolver = m_contentList[ index.row() ];
switch ( role )
{
case Qt::DisplayRole:
return resolver.name();
case Qt::DecorationRole:
return QVariant::fromValue< QPixmap >( AtticaManager::instance()->iconForResolver( resolver ) );
case DownloadUrlRole:
// TODO
return QUrl();
case RatingRole:
return resolver.rating() / 20; // rating is out of 100
case DownloadCounterRole:
return resolver.downloads();
case VersionRole:
return resolver.version();
case DescriptionRole:
return resolver.description();
case TypeRole:
return ResolverType;
case AuthorRole:
return resolver.author();
case StateRole:
return (int)AtticaManager::instance()->resolverState( resolver );
case UserHasRatedRole:
return AtticaManager::instance()->userHasRated( resolver );
}
return QVariant();
}
int
GetNewStuffModel::rowCount( const QModelIndex& parent ) const
{
Q_UNUSED( parent );
return m_contentList.count();
}
bool
GetNewStuffModel::setData( const QModelIndex &index, const QVariant &value, int role )
{
Q_UNUSED( value );
if ( !hasIndex( index.row(), index.column(), index.parent() ) )
return false;
Attica::Content resolver = m_contentList[ index.row() ];
AtticaManager::ResolverState state = AtticaManager::instance()->resolverState( resolver );
if ( role == Qt::EditRole )
{
switch( state )
{
case AtticaManager::Uninstalled:
// install
AtticaManager::instance()->installResolver( resolver );
break;
case AtticaManager::Installing:
case AtticaManager::Upgrading:
// Do nothing, busy
break;
case AtticaManager::Installed:
// Uninstall
AtticaManager::instance()->uninstallResolver( resolver );
break;
case AtticaManager::NeedsUpgrade:
AtticaManager::instance()->upgradeResolver( resolver );
break;
default:
//FIXME -- this handles e.g. Failed
break;
};
} else if ( role == RatingRole )
{
// For now only allow rating if a resolver is installed!
if ( state != AtticaManager::Installed && state != AtticaManager::NeedsUpgrade )
return false;
if ( AtticaManager::instance()->userHasRated( resolver ) )
return false;
m_contentList[ index.row() ].setRating( value.toInt() * 20 );
AtticaManager::instance()->uploadRating( m_contentList[ index.row() ] );
}
emit dataChanged( index, index );
return true;
}

64
src/GetNewStuffModel.h Normal file
View File

@@ -0,0 +1,64 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2010-2011, Leo Franchi <lfranchi@kde.org>
*
* Tomahawk is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Tomahawk is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Tomahawk. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef GETNEWSTUFFMODEL_H
#define GETNEWSTUFFMODEL_H
#include <QModelIndex>
#include <attica/content.h>
#include <QPixmap>
class GetNewStuffModel: public QAbstractListModel
{
Q_OBJECT
public:
enum NewStuffRoles {
// DisplayRole is title
// DecorationRole is qicon for item
DownloadUrlRole = Qt::UserRole + 1,
RatingRole = Qt::UserRole + 2,
DownloadCounterRole = Qt::UserRole + 3,
VersionRole = Qt::UserRole + 4,
DescriptionRole = Qt::UserRole + 5,
TypeRole = Qt::UserRole + 6, // Category in attica-speak. What sort of item this is (resolver, etc).
AuthorRole = Qt::UserRole + 7,
StateRole = Qt::UserRole + 8,
UserHasRatedRole = Qt::UserRole + 9
};
enum Types {
ResolverType = 0,
};
explicit GetNewStuffModel( QObject* parent = 0 );
virtual ~GetNewStuffModel();
virtual QVariant data( const QModelIndex& index, int role = Qt::DisplayRole ) const;
virtual int rowCount( const QModelIndex& parent = QModelIndex() ) const;
virtual bool setData( const QModelIndex &index, const QVariant &value, int role );
private slots:
void resolversReloaded( const Attica::Content::List& );
void resolverStateChanged( const QString& resolverId );
private:
Attica::Content::List m_contentList;
};
#endif // GETNEWSTUFFMODEL_H

View File

@@ -17,8 +17,6 @@
*/
#include "LoadXSPFDialog.h"
#include "TomahawkSettings.h"
#include "Source.h"
#include "ui_LoadXSPFDialog.h"
#include <QFileDialog>
@@ -46,11 +44,7 @@ LoadXSPFDialog::~LoadXSPFDialog()
void
LoadXSPFDialog::getLocalFile()
{
const QString path = TomahawkSettings::instance()->importXspfPath();
QString url = QFileDialog::getOpenFileName( this, tr( "Load XSPF File" ), path, tr( "XSPF Files (*.xspf)" ) );
if ( !url.isEmpty() )
TomahawkSettings::instance()->setImportXspfPath( QFileInfo( url ).absoluteDir().absolutePath() );
QString url = QFileDialog::getOpenFileName( this, tr( "Load XSPF File" ), QDir::homePath(), tr( "XSPF Files (*.xspf)" ) );
m_ui->lineEdit->setText( url );
}

View File

@@ -19,7 +19,7 @@
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>Playlist URL</string>
<string>Playlist Url</string>
</property>
</widget>
</item>

View File

@@ -22,9 +22,9 @@
#include <QHeaderView>
#include <QVBoxLayout>
#include "libtomahawk/Pipeline.h"
#include "libtomahawk/pipeline.h"
#include "utils/Logger.h"
#include "utils/logger.h"
using namespace Tomahawk;

View File

@@ -22,9 +22,9 @@
#include <QTreeWidget>
#include "Typedefs.h"
#include "widgets/AnimatedSplitter.h"
#include "Query.h"
#include "typedefs.h"
#include "widgets/animatedsplitter.h"
#include "query.h"
class StreamConnection;

View File

@@ -1,549 +0,0 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2010-2011, Christian Muehlhaeuser <muesli@tomahawk-player.org>
* Copyright 2010-2011, Leo Franchi <lfranchi@kde.org>
* Copyright 2010-2012, Jeff Mitchell <jeff@tomahawk-player.org>
*
* Tomahawk is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Tomahawk is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Tomahawk. If not, see <http://www.gnu.org/licenses/>.
*/
#include "SettingsDialog.h"
#include "config.h"
#include <QtGui/QDesktopServices>
#include <QtGui/QFileDialog>
#include <QtGui/QMessageBox>
#include <QtNetwork/QNetworkConfiguration>
#include <QtNetwork/QNetworkProxy>
#include <QtGui/QVBoxLayout>
#include <QtGui/QSizeGrip>
#include "AtticaManager.h"
#include "TomahawkApp.h"
#include "TomahawkSettings.h"
#include "DelegateConfigWrapper.h"
#include "MusicScanner.h"
#include "Pipeline.h"
#include "Resolver.h"
#include "ExternalResolverGui.h"
#include "utils/TomahawkUtilsGui.h"
#include "GuiHelpers.h"
#include "ScanManager.h"
#include "SettingsListDelegate.h"
#include "AccountDelegate.h"
#include "database/Database.h"
#include "network/Servent.h"
#include "utils/AnimatedSpinner.h"
#include "accounts/AccountModel.h"
#include "accounts/Account.h"
#include "accounts/AccountManager.h"
#include <accounts/AccountModelFilterProxy.h>
#include <accounts/ResolverAccount.h>
#include "utils/Logger.h"
#include "AccountFactoryWrapper.h"
#include "accounts/spotify/SpotifyAccount.h"
#include "ui_ProxyDialog.h"
#include "ui_StackedSettingsDialog.h"
using namespace Tomahawk;
using namespace Accounts;
SettingsDialog::SettingsDialog( QWidget *parent )
: QDialog( parent )
, ui( new Ui_StackedSettingsDialog )
, m_proxySettings( this )
, m_rejected( false )
, m_restartRequired( false )
, m_accountModel( 0 )
, m_sipSpinner( 0 )
{
ui->setupUi( this );
TomahawkSettings* s = TomahawkSettings::instance();
TomahawkUtils::unmarginLayout( layout() );
ui->stackedWidget->setContentsMargins( 4, 4, 4, 0 );
ui->checkBoxReporter->setChecked( s->crashReporterEnabled() );
ui->checkBoxHttp->setChecked( s->httpEnabled() );
//Network settings
TomahawkSettings::ExternalAddressMode mode = TomahawkSettings::instance()->externalAddressMode();
if ( mode == TomahawkSettings::Lan )
ui->lanOnlyRadioButton->setChecked( true );
else if ( mode == TomahawkSettings::Static )
ui->staticIpRadioButton->setChecked( true );
else
ui->upnpRadioButton->setChecked( true );
ui->staticHostNamePortLabel->setEnabled( ui->staticIpRadioButton->isChecked() );
ui->staticHostName->setEnabled( ui->staticIpRadioButton->isChecked() );
ui->staticPort->setEnabled( ui->staticIpRadioButton->isChecked() );
ui->staticHostNameLabel->setEnabled( ui->staticIpRadioButton->isChecked() );
ui->staticPortLabel->setEnabled( ui->staticIpRadioButton->isChecked() );
bool useProxy = TomahawkSettings::instance()->proxyType() == QNetworkProxy::Socks5Proxy;
ui->enableProxyCheckBox->setChecked( useProxy );
ui->proxyButton->setEnabled( useProxy );
createIcons();
#ifdef Q_WS_X11
ui->listWidget->setFrameShape( QFrame::StyledPanel );
ui->listWidget->setFrameShadow( QFrame::Sunken );
setContentsMargins( 4, 4, 4, 4 );
#else
setContentsMargins( 0, 4, 4, 4 );
#endif
#ifdef Q_WS_MAC
ui->listWidget->setFixedWidth( 83 );
#endif
#ifdef Q_WS_MAC
// Avoid resize handles on sheets on osx
m_proxySettings.setSizeGripEnabled( true );
QSizeGrip* p = m_proxySettings.findChild< QSizeGrip* >();
p->setFixedSize( 0, 0 );
ui->groupBoxNetworkAdvanced->layout()->removeItem( ui->verticalSpacer );
ui->groupBoxNetworkAdvanced->layout()->removeItem( ui->verticalSpacer_2 );
ui->groupBoxNetworkAdvanced->layout()->removeItem( ui->verticalSpacer_4 );
delete ui->verticalSpacer;
delete ui->verticalSpacer_2;
delete ui->verticalSpacer_4;
#endif
// Accounts
AccountDelegate* accountDelegate = new AccountDelegate( this );
ui->accountsView->setItemDelegate( accountDelegate );
ui->accountsView->setContextMenuPolicy( Qt::CustomContextMenu );
ui->accountsView->setVerticalScrollMode( QAbstractItemView::ScrollPerPixel );
ui->accountsView->setMouseTracking( true );
connect( accountDelegate, SIGNAL( openConfig( Tomahawk::Accounts::Account* ) ), this, SLOT( openAccountConfig( Tomahawk::Accounts::Account* ) ) );
connect( accountDelegate, SIGNAL( openConfig( Tomahawk::Accounts::AccountFactory* ) ), this, SLOT( openAccountFactoryConfig( Tomahawk::Accounts::AccountFactory* ) ) );
connect( accountDelegate, SIGNAL( update( QModelIndex ) ), ui->accountsView, SLOT( update( QModelIndex ) ) );
m_accountModel = new AccountModel( this );
m_accountProxy = new AccountModelFilterProxy( m_accountModel );
m_accountProxy->setSourceModel( m_accountModel );
connect( m_accountProxy, SIGNAL( startInstalling( QPersistentModelIndex ) ), accountDelegate, SLOT( startInstalling(QPersistentModelIndex) ) );
connect( m_accountProxy, SIGNAL( doneInstalling( QPersistentModelIndex ) ), accountDelegate, SLOT( doneInstalling(QPersistentModelIndex) ) );
connect( m_accountProxy, SIGNAL( errorInstalling( QPersistentModelIndex ) ), accountDelegate, SLOT( errorInstalling(QPersistentModelIndex) ) );
connect( m_accountProxy, SIGNAL( scrollTo( QModelIndex ) ), this, SLOT( scrollTo( QModelIndex ) ) );
ui->accountsView->setModel( m_accountProxy );
connect( ui->installFromFileBtn, SIGNAL( clicked( bool ) ), this, SLOT( installFromFile() ) );
connect( m_accountModel, SIGNAL( createAccount( Tomahawk::Accounts::AccountFactory* ) ), this, SLOT( createAccountFromFactory( Tomahawk::Accounts::AccountFactory* ) ) );
ui->accountsFilterCombo->addItem( tr( "All" ), Accounts::NoType );
ui->accountsFilterCombo->addItem( accountTypeToString( SipType ), SipType );
ui->accountsFilterCombo->addItem( accountTypeToString( ResolverType ), ResolverType );
ui->accountsFilterCombo->addItem( accountTypeToString( StatusPushType ), StatusPushType );
connect( ui->accountsFilterCombo, SIGNAL( activated( int ) ), this, SLOT( accountsFilterChanged( int ) ) );
if ( !Servent::instance()->isReady() )
{
m_sipSpinner = new AnimatedSpinner( ui->accountsView );
m_sipSpinner->fadeIn();
connect( Servent::instance(), SIGNAL( ready() ), this, SLOT( serventReady() ) );
}
// ADVANCED
ui->staticHostName->setText( s->externalHostname() );
ui->staticPort->setValue( s->externalPort() );
ui->proxyButton->setVisible( true );
ui->checkBoxWatchForChanges->setChecked( s->watchForChanges() );
ui->scannerTimeSpinBox->setValue( s->scannerTime() );
ui->enableEchonestCatalog->setChecked( s->enableEchonestCatalogs() );
connect( ui->checkBoxWatchForChanges, SIGNAL( clicked( bool ) ), SLOT( updateScanOptionsView() ) );
if ( ui->checkBoxWatchForChanges->isChecked() )
{
ui->scanTimeLabel->show();
ui->scannerTimeSpinBox->show();
}
else
{
ui->scanTimeLabel->hide();
ui->scannerTimeSpinBox->hide();
}
foreach ( const QString& dir, TomahawkSettings::instance()->scannerPaths() )
{
ui->dirTree->checkPath( dir, Qt::Checked );
}
// NOW PLAYING
// #ifdef Q_WS_MAC
// ui->checkBoxEnableAdium->setChecked( s->nowPlayingEnabled() );
// #else
// ui->checkBoxEnableAdium->hide();
// #endif
connect( ui->proxyButton, SIGNAL( clicked() ), SLOT( showProxySettings() ) );
connect( ui->lanOnlyRadioButton, SIGNAL( toggled(bool) ), SLOT( requiresRestart() ) );
connect( ui->staticIpRadioButton, SIGNAL( toggled(bool) ), SLOT( requiresRestart() ) );
connect( ui->upnpRadioButton, SIGNAL( toggled(bool) ), SLOT( requiresRestart() ) );
connect( ui->lanOnlyRadioButton, SIGNAL( toggled(bool) ), SLOT( toggleRemoteMode() ) );
connect( ui->staticIpRadioButton, SIGNAL( toggled(bool) ), SLOT( toggleRemoteMode() ) );
connect( ui->upnpRadioButton, SIGNAL( toggled(bool) ), SLOT( toggleRemoteMode() ) );
connect( ui->enableProxyCheckBox, SIGNAL( toggled(bool) ), SLOT( toggleProxyEnabled() ) );
connect( this, SIGNAL( rejected() ), SLOT( onRejected() ) );
ui->listWidget->setCurrentRow( 0 );
ui->listWidget->setItemDelegate(new SettingsListDelegate());
}
SettingsDialog::~SettingsDialog()
{
qDebug() << Q_FUNC_INFO;
if ( !m_rejected )
{
TomahawkSettings* s = TomahawkSettings::instance();
s->setCrashReporterEnabled( ui->checkBoxReporter->checkState() == Qt::Checked );
s->setHttpEnabled( ui->checkBoxHttp->checkState() == Qt::Checked );
s->setProxyType( ui->enableProxyCheckBox->isChecked() ? QNetworkProxy::Socks5Proxy : QNetworkProxy::NoProxy );
s->setExternalAddressMode( ui->upnpRadioButton->isChecked() ? TomahawkSettings::Upnp : ( ui->lanOnlyRadioButton->isChecked() ? TomahawkSettings::Lan : TomahawkSettings::Static ) );
s->setExternalHostname( ui->staticHostName->text() );
s->setExternalPort( ui->staticPort->value() );
s->setScannerPaths( ui->dirTree->getCheckedPaths() );
s->setWatchForChanges( ui->checkBoxWatchForChanges->isChecked() );
s->setScannerTime( ui->scannerTimeSpinBox->value() );
s->setEnableEchonestCatalogs( ui->enableEchonestCatalog->isChecked() );
// s->setNowPlayingEnabled( ui->checkBoxEnableAdium->isChecked() );
s->applyChanges();
s->sync();
if ( m_restartRequired )
QMessageBox::information( this, tr( "Information" ), tr( "Some changed settings will not take effect until Tomahawk is restarted" ) );
TomahawkUtils::NetworkProxyFactory* proxyFactory = TomahawkUtils::proxyFactory();
if ( !ui->enableProxyCheckBox->isChecked() )
{
tDebug() << Q_FUNC_INFO << "Got NoProxy selected";
proxyFactory->setProxy( QNetworkProxy::NoProxy );
}
else
{
tDebug() << Q_FUNC_INFO << "Got Socks5Proxy selected";
proxyFactory->setProxy( QNetworkProxy( QNetworkProxy::Socks5Proxy, s->proxyHost(), s->proxyPort(), s->proxyUsername(), s->proxyPassword() ) );
if ( !s->proxyNoProxyHosts().isEmpty() )
{
tDebug() << Q_FUNC_INFO << "noproxy hosts:" << s->proxyNoProxyHosts();
tDebug() << Q_FUNC_INFO << "split noproxy line edit is " << s->proxyNoProxyHosts().split( ' ', QString::SkipEmptyParts );
proxyFactory->setNoProxyHosts( s->proxyNoProxyHosts().split( ' ', QString::SkipEmptyParts ) );
}
}
}
else
qDebug() << "Settings dialog cancelled, NOT saving prefs.";
delete ui;
}
void
SettingsDialog::serventReady()
{
m_sipSpinner->fadeOut();
}
void
SettingsDialog::createIcons()
{
/// Not fun but QListWidget sucks. Do our max-width calculation manually
/// so the icons arre lined up.
// Resolvers is the longest string... in english. fml.
ensurePolished();
int maxlen = 0;
QFontMetrics fm( font() );
QListWidgetItem *accountsButton = new QListWidgetItem( ui->listWidget );
accountsButton->setIcon( QIcon( RESPATH "images/account-settings.png" ) );
accountsButton->setText( tr( "Services" ) );
accountsButton->setTextAlignment( Qt::AlignHCenter );
accountsButton->setFlags( Qt::ItemIsSelectable | Qt::ItemIsEnabled );
maxlen = fm.width( accountsButton->text() );
QListWidgetItem *musicButton = new QListWidgetItem( ui->listWidget );
musicButton->setIcon( QIcon( RESPATH "images/music-settings.png" ) );
musicButton->setText( tr( "Collection" ) );
musicButton->setTextAlignment( Qt::AlignHCenter );
musicButton->setFlags( Qt::ItemIsSelectable | Qt::ItemIsEnabled );
maxlen = qMax( fm.width( musicButton->text() ), maxlen );
QListWidgetItem *advancedButton = new QListWidgetItem( ui->listWidget );
advancedButton->setIcon( QIcon( RESPATH "images/advanced-settings.png" ) );
advancedButton->setText( tr( "Advanced" ) );
advancedButton->setTextAlignment( Qt::AlignHCenter );
advancedButton->setFlags( Qt::ItemIsSelectable | Qt::ItemIsEnabled );
maxlen = qMax( fm.width( advancedButton->text() ), maxlen );
maxlen += 15; // padding
accountsButton->setSizeHint( QSize( maxlen, 60 ) );
musicButton->setSizeHint( QSize( maxlen, 60 ) );
advancedButton->setSizeHint( QSize( maxlen, 60 ) );
#ifndef Q_WS_MAC
// doesn't listen to sizehint...
ui->listWidget->setFixedWidth( maxlen + 8 );
#endif
connect( ui->listWidget, SIGNAL( currentItemChanged( QListWidgetItem*, QListWidgetItem* ) ), SLOT( changePage( QListWidgetItem*, QListWidgetItem* ) ) );
}
void
SettingsDialog::changePage( QListWidgetItem* current, QListWidgetItem* previous )
{
if ( !current )
current = previous;
ui->stackedWidget->setCurrentIndex( ui->listWidget->row(current) );
}
void
SettingsDialog::onRejected()
{
m_rejected = true;
}
void
SettingsDialog::changeEvent( QEvent *e )
{
QDialog::changeEvent( e );
switch ( e->type() )
{
case QEvent::LanguageChange:
ui->retranslateUi( this );
break;
default:
break;
}
}
void
SettingsDialog::showProxySettings()
{
m_proxySettings.exec();
if ( m_proxySettings.result() == QDialog::Accepted )
m_proxySettings.saveSettings();
}
void
SettingsDialog::toggleRemoteMode()
{
ui->staticHostNamePortLabel->setEnabled( ui->staticIpRadioButton->isChecked() );
ui->staticHostName->setEnabled( ui->staticIpRadioButton->isChecked() );
ui->staticPort->setEnabled( ui->staticIpRadioButton->isChecked() );
ui->staticHostNameLabel->setEnabled( ui->staticIpRadioButton->isChecked() );
ui->staticPortLabel->setEnabled( ui->staticIpRadioButton->isChecked() );
}
void
SettingsDialog::toggleProxyEnabled()
{
ui->proxyButton->setEnabled( ui->enableProxyCheckBox->isChecked() );
}
void
SettingsDialog::updateScanOptionsView()
{
if ( ui->checkBoxWatchForChanges->isChecked() )
{
ui->scanTimeLabel->show();
ui->scannerTimeSpinBox->show();
}
else
{
ui->scanTimeLabel->hide();
ui->scannerTimeSpinBox->hide();
}
}
void
SettingsDialog::accountsFilterChanged( int )
{
AccountType filter = static_cast< AccountType >( ui->accountsFilterCombo->itemData( ui->accountsFilterCombo->currentIndex() ).toInt() );
m_accountProxy->setFilterType( filter );
}
void
SettingsDialog::openAccountFactoryConfig( AccountFactory* factory )
{
QList< Account* > accts;
foreach ( Account* acct, AccountManager::instance()->accounts() )
{
if ( AccountManager::instance()->factoryForAccount( acct ) == factory )
accts << acct;
if ( accts.size() > 1 )
break;
}
Q_ASSERT( accts.size() > 0 ); // Shouldn't have a config wrench if there are no accounts!
if ( accts.size() == 1 )
{
// If there's just one, open the config directly w/ the delete button. Otherwise open the multi dialog
openAccountConfig( accts.first(), true );
return;
}
#ifndef Q_OS_MAC
AccountFactoryWrapper dialog( factory, this );
QWeakPointer< AccountFactoryWrapper > watcher( &dialog );
dialog.exec();
#else
// on osx a sheet needs to be non-modal
AccountFactoryWrapper* dialog = new AccountFactoryWrapper( factory, this );
dialog->show();
#endif
}
void
SettingsDialog::createAccountFromFactory( AccountFactory* factory )
{
TomahawkUtils::createAccountFromFactory( factory, this );
}
void
SettingsDialog::openAccountConfig( Account* account, bool showDelete )
{
TomahawkUtils::openAccountConfig( account, this, showDelete );
}
void
SettingsDialog::installFromFile()
{
const QString resolver = QFileDialog::getOpenFileName( this, tr( "Install resolver from file" ), TomahawkSettings::instance()->scriptDefaultPath() );
if( !resolver.isEmpty() )
{
const QFileInfo resolverAbsoluteFilePath( resolver );
TomahawkSettings::instance()->setScriptDefaultPath( resolverAbsoluteFilePath.absolutePath() );
if ( resolverAbsoluteFilePath.baseName() == "spotify_tomahawkresolver" )
{
// HACK if this is a spotify resolver, we treat is specially.
// usually we expect the user to just download the spotify resolver from attica,
// however developers, those who build their own tomahawk, can't do that, or linux
// users can't do that. However, we have an already-existing SpotifyAccount that we
// know exists that we need to use this resolver path.
//
// Hence, we special-case the spotify resolver and directly set the path on it here.
SpotifyAccount* acct = 0;
foreach ( Account* account, AccountManager::instance()->accounts() )
{
if ( SpotifyAccount* spotify = qobject_cast< SpotifyAccount* >( account ) )
{
acct = spotify;
break;
}
}
if ( acct )
{
acct->setManualResolverPath( resolver );
return;
}
}
Account* acct = AccountManager::instance()->accountFromPath( resolver );
AccountManager::instance()->addAccount( acct );
TomahawkSettings::instance()->addAccount( acct->accountId() );
AccountManager::instance()->enableAccount( acct );
}
}
void
SettingsDialog::scrollTo( const QModelIndex& idx )
{
ui->accountsView->scrollTo( idx, QAbstractItemView::PositionAtBottom );
}
void
SettingsDialog::requiresRestart()
{
m_restartRequired = true;
}
ProxyDialog::ProxyDialog( QWidget *parent )
: QDialog( parent )
, ui( new Ui::ProxyDialog )
{
ui->setupUi( this );
// ugly, I know, but...
TomahawkSettings* s = TomahawkSettings::instance();
ui->hostLineEdit->setText( s->proxyHost() );
ui->portSpinBox->setValue( s->proxyPort() );
ui->userLineEdit->setText( s->proxyUsername() );
ui->passwordLineEdit->setText( s->proxyPassword() );
ui->checkBoxUseProxyForDns->setChecked( s->proxyDns() );
ui->noHostLineEdit->setText( s->proxyNoProxyHosts() );
}
void
ProxyDialog::saveSettings()
{
qDebug() << Q_FUNC_INFO;
//First set settings
TomahawkSettings* s = TomahawkSettings::instance();
s->setProxyHost( ui->hostLineEdit->text() );
int port = ui->portSpinBox->value();
s->setProxyPort( port );
s->setProxyNoProxyHosts( ui->noHostLineEdit->text() );
s->setProxyUsername( ui->userLineEdit->text() );
s->setProxyPassword( ui->passwordLineEdit->text() );
s->setProxyDns( ui->checkBoxUseProxyForDns->checkState() == Qt::Checked );
s->sync();
}

View File

@@ -1,271 +0,0 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2012, Christian Muehlhaeuser <muesli@tomahawk-player.org>
*
* Tomahawk is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Tomahawk is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Tomahawk. If not, see <http://www.gnu.org/licenses/>.
*/
#include "SocialWidget.h"
#include "ui_SocialWidget.h"
#include <QPainter>
#include <QDialog>
#include <QPropertyAnimation>
#include "GlobalActionManager.h"
#include "utils/Logger.h"
#include "Source.h"
#define CORNER_ROUNDNESS 8.0
#define FADING_DURATION 500
#define FONT_SIZE 16
#define OPACITY 0.70
SocialWidget::SocialWidget( QWidget* parent )
: QWidget( parent ) // this is on purpose!
, ui( new Ui::SocialWidget )
, m_opacity( 0.00 )
, m_parent( parent )
, m_parentRect( parent->rect() )
{
ui->setupUi( this );
setAttribute( Qt::WA_TranslucentBackground, true );
setOpacity( m_opacity );
m_timer.setSingleShot( true );
connect( &m_timer, SIGNAL( timeout() ), this, SLOT( hide() ) );
#ifdef Q_WS_MAC
QFont f( font() );
f.setPointSize( f.pointSize() - 2 );
setFont( f );
#endif
ui->charsLeftLabel->setForegroundRole( QPalette::HighlightedText );
m_parent->installEventFilter( this );
connect( ui->buttonBox, SIGNAL( accepted() ), SLOT( accept() ) );
connect( ui->buttonBox, SIGNAL( rejected() ), SLOT( deleteLater() ) );
connect( ui->textEdit, SIGNAL( textChanged() ), SLOT( onChanged() ) );
connect( ui->facebookButton, SIGNAL( clicked( bool ) ), SLOT( onChanged() ) );
connect( ui->twitterButton, SIGNAL( clicked( bool ) ), SLOT( onChanged() ) );
connect( GlobalActionManager::instance(), SIGNAL( shortLinkReady( QUrl, QUrl, QVariant ) ), SLOT( onShortLinkReady( QUrl, QUrl, QVariant ) ) );
onChanged();
}
SocialWidget::~SocialWidget()
{
delete ui;
}
void
SocialWidget::setOpacity( qreal opacity )
{
m_opacity = opacity;
if ( m_opacity == 0.00 && !isHidden() )
{
QWidget::hide();
}
else if ( m_opacity > 0.00 && isHidden() )
{
QWidget::show();
}
repaint();
}
void
SocialWidget::setPosition( QPoint position )
{
m_position = position;
onGeometryUpdate();
}
void
SocialWidget::show( int timeoutSecs )
{
if ( !isEnabled() )
return;
QPropertyAnimation* animation = new QPropertyAnimation( this, "opacity" );
animation->setDuration( FADING_DURATION );
animation->setEndValue( 1.0 );
animation->start();
if( timeoutSecs > 0 )
m_timer.start( timeoutSecs * 1000 );
}
void
SocialWidget::hide()
{
if ( !isEnabled() )
return;
QPropertyAnimation* animation = new QPropertyAnimation( this, "opacity" );
animation->setDuration( FADING_DURATION );
animation->setEndValue( 0.00 );
animation->start();
}
bool
SocialWidget::shown() const
{
if ( !isEnabled() )
return false;
return m_opacity == OPACITY;
}
void
SocialWidget::paintEvent( QPaintEvent* event )
{
Q_UNUSED( event );
QPainter p( this );
QRect r = contentsRect();
p.setBackgroundMode( Qt::TransparentMode );
p.setRenderHint( QPainter::Antialiasing );
p.setOpacity( m_opacity );
QPen pen( palette().dark().color(), .5 );
p.setPen( pen );
p.setBrush( QColor( 30, 30, 30, 255.0 * OPACITY ) );
p.drawRoundedRect( r, CORNER_ROUNDNESS, CORNER_ROUNDNESS );
QWidget::paintEvent( event );
return;
QTextOption to( Qt::AlignCenter );
to.setWrapMode( QTextOption::WrapAtWordBoundaryOrAnywhere );
// shrink to fit if needed
QFont f( font() );
f.setPointSize( FONT_SIZE );
f.setBold( true );
QRectF textRect = r.adjusted( 8, 8, -8, -8 );
qreal availHeight = textRect.height();
QFontMetricsF fm( f );
qreal textHeight = fm.boundingRect( textRect, Qt::AlignCenter | Qt::TextWordWrap, "SocialWidget" ).height();
while( textHeight > availHeight )
{
if( f.pointSize() <= 4 ) // don't try harder
break;
f.setPointSize( f.pointSize() - 1 );
fm = QFontMetricsF( f );
textHeight = fm.boundingRect( textRect, Qt::AlignCenter | Qt::TextWordWrap, "SocialWidget" ).height();
}
p.setFont( f );
p.setPen( palette().highlightedText().color() );
p.drawText( r.adjusted( 8, 8, -8, -8 ), "SocialWidget", to );
}
void
SocialWidget::onShortLinkReady( const QUrl& longUrl, const QUrl& shortUrl, const QVariant& callbackObj )
{
Q_UNUSED( longUrl );
Q_UNUSED( callbackObj );
if ( m_query->album().isEmpty() )
ui->textEdit->setText( tr( "Listening to \"%1\" by %2 and loving it! %3" ).arg( m_query->track() ).arg( m_query->artist() ).arg( shortUrl.toString() ) );
else
ui->textEdit->setText( tr( "Listening to \"%1\" by %2 on \"%3\" and loving it! %4" ).arg( m_query->track() ).arg( m_query->artist() ).arg( m_query->album() ).arg( shortUrl.toString() ) );
}
void
SocialWidget::setQuery( const Tomahawk::query_ptr& query )
{
m_query = query;
ui->coverImage->setPixmap( query->cover( ui->coverImage->size() ) );
onShortLinkReady( QString(), QString(), QVariant() );
onChanged();
QUrl longUrl = GlobalActionManager::instance()->openLinkFromQuery( query );
GlobalActionManager::instance()->shortenLink( longUrl );
}
void
SocialWidget::onChanged()
{
const int remaining = charsAvailable() - ui->textEdit->toPlainText().length();
ui->charsLeftLabel->setText( tr( "%1 characters left" ).arg( remaining ) );
ui->buttonBox->button( QDialogButtonBox::Ok )->setEnabled( remaining >= 0 && ( ui->facebookButton->isChecked() || ui->twitterButton->isChecked() ) );
}
void
SocialWidget::accept()
{
tDebug() << "Sharing social link!";
deleteLater();
}
unsigned int
SocialWidget::charsAvailable() const
{
if ( ui->twitterButton->isChecked() )
return 140;
return 420; // facebook max length
}
void
SocialWidget::onGeometryUpdate()
{
QPoint p( m_parent->rect().width() - m_parentRect.width(), m_parent->rect().height() - m_parentRect.height() );
m_position += p;
m_parentRect = m_parent->rect();
QPoint position( m_position - QPoint( size().width(), size().height() ) );
if ( position != pos() )
{
move( position );
}
}
bool
SocialWidget::eventFilter( QObject* object, QEvent* event )
{
if ( event->type() == QEvent::Resize )
{
onGeometryUpdate();
}
return QObject::eventFilter( object, event );
}

View File

@@ -1,84 +0,0 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2012, Christian Muehlhaeuser <muesli@tomahawk-player.org>
*
* Tomahawk is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Tomahawk is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Tomahawk. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef SOCIALWIDGET_H
#define SOCIALWIDGET_H
#include <QWidget>
#include <QAbstractItemView>
#include <QTimer>
#include "Query.h"
namespace Ui
{
class SocialWidget;
}
class SocialWidget : public QWidget
{
Q_OBJECT
Q_PROPERTY( qreal opacity READ opacity WRITE setOpacity )
public:
SocialWidget( QWidget* parent );
~SocialWidget();
Tomahawk::query_ptr query() const { return m_query; }
void setQuery( const Tomahawk::query_ptr& query );
qreal opacity() const { return m_opacity; }
void setOpacity( qreal opacity );
QPoint position() const { return m_position; }
void setPosition( QPoint position );
bool shown() const;
public slots:
void show( int timeoutSecs = 0 );
void hide();
protected:
// void changeEvent( QEvent* e );
void paintEvent( QPaintEvent* event );
bool eventFilter( QObject* object, QEvent* event );
private slots:
void accept();
void onChanged();
void onShortLinkReady( const QUrl& longUrl, const QUrl& shortUrl, const QVariant& callbackObj );
void onGeometryUpdate();
private:
unsigned int charsAvailable() const;
Ui::SocialWidget* ui;
Tomahawk::query_ptr m_query;
qreal m_opacity;
QPoint m_position;
QWidget* m_parent;
QRect m_parentRect;
QTimer m_timer;
};
#endif // SOCIALWIDGET_H

View File

@@ -1,118 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>SocialWidget</class>
<widget class="QWidget" name="SocialWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>365</width>
<height>190</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="leftMargin">
<number>8</number>
</property>
<property name="topMargin">
<number>8</number>
</property>
<property name="rightMargin">
<number>8</number>
</property>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QPushButton" name="facebookButton">
<property name="text">
<string>Facebook</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="twitterButton">
<property name="text">
<string>Twitter</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QLabel" name="coverImage">
<property name="minimumSize">
<size>
<width>96</width>
<height>96</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>96</width>
<height>96</height>
</size>
</property>
<property name="text">
<string>Cover</string>
</property>
</widget>
</item>
<item>
<widget class="QTextEdit" name="textEdit">
<property name="maximumSize">
<size>
<width>16777215</width>
<height>96</height>
</size>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="charsLeftLabel">
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View File

@@ -1,500 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>StackedSettingsDialog</class>
<widget class="QDialog" name="StackedSettingsDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>655</width>
<height>500</height>
</rect>
</property>
<property name="windowTitle">
<string>Tomahawk Settings</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_12">
<property name="margin">
<number>0</number>
</property>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QListWidget" name="listWidget">
<property name="focusPolicy">
<enum>Qt::NoFocus</enum>
</property>
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Sunken</enum>
</property>
<property name="verticalScrollBarPolicy">
<enum>Qt::ScrollBarAlwaysOff</enum>
</property>
<property name="horizontalScrollBarPolicy">
<enum>Qt::ScrollBarAlwaysOff</enum>
</property>
<property name="autoScroll">
<bool>true</bool>
</property>
<property name="iconSize">
<size>
<width>32</width>
<height>32</height>
</size>
</property>
<property name="movement">
<enum>QListView::Static</enum>
</property>
<property name="resizeMode">
<enum>QListView::Adjust</enum>
</property>
<property name="layoutMode">
<enum>QListView::Batched</enum>
</property>
<property name="spacing">
<number>1</number>
</property>
<property name="viewMode">
<enum>QListView::IconMode</enum>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer_3">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>1</width>
<height>4</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<widget class="QStackedWidget" name="stackedWidget">
<property name="currentIndex">
<number>2</number>
</property>
<widget class="QWidget" name="accountsPage">
<layout class="QVBoxLayout" name="verticalLayout_11">
<property name="margin">
<number>0</number>
</property>
<item>
<widget class="QGroupBox" name="groupBox_4">
<property name="title">
<string>Internet Services</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_8">
<property name="margin">
<number>2</number>
</property>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QPushButton" name="installFromFileBtn">
<property name="text">
<string>Install from file...</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>Filter by capability:</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="accountsFilterCombo"/>
</item>
</layout>
</item>
<item>
<widget class="QListView" name="accountsView">
<property name="alternatingRowColors">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="scannerPage">
<layout class="QVBoxLayout" name="verticalLayout_5">
<property name="margin">
<number>0</number>
</property>
<item>
<widget class="QGroupBox" name="groupBox_2">
<property name="title">
<string>Local Music Information</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_17">
<item>
<layout class="QVBoxLayout" name="verticalLayout_4">
<item>
<widget class="QLabel" name="label_5">
<property name="text">
<string>Path to scan for music files:</string>
</property>
</widget>
</item>
<item>
<widget class="CheckDirTree" name="dirTree"/>
</item>
</layout>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout_16">
<item>
<widget class="QCheckBox" name="enableEchonestCatalog">
<property name="toolTip">
<string>The Echo Nest supports keeping track of your catalog metadata
and using it to craft personalized radios. Enabling this option
will allow you (and all your friends) to create automatic playlists
and stations based on your personal taste profile.</string>
</property>
<property name="text">
<string>Upload collection list to The Echo Nest to enable user radio</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="checkBoxWatchForChanges">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Watch for changes</string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_7">
<item>
<widget class="QLabel" name="scanTimeLabel">
<property name="text">
<string>Time between scans, in seconds:</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="scannerTimeSpinBox">
<property name="minimum">
<number>60</number>
</property>
<property name="maximum">
<number>999999999</number>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="advancedPage">
<layout class="QVBoxLayout" name="verticalLayout_3">
<property name="margin">
<number>0</number>
</property>
<item>
<widget class="QGroupBox" name="groupBoxNetworkAdvanced">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="title">
<string>Advanced Settings</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_13">
<property name="margin">
<number>2</number>
</property>
<item>
<widget class="QGroupBox" name="remoteConnectionsGroupBox">
<property name="title">
<string>Remote Peer Connection Method</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_6">
<item>
<widget class="QRadioButton" name="lanOnlyRadioButton">
<property name="text">
<string>None (outgoing connections only)</string>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="upnpRadioButton">
<property name="text">
<string>Use UPnP to establish port forward (recommended)</string>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="staticIpRadioButton">
<property name="text">
<string>Use static external IP address/host name and port</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="staticHostNamePortLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Set this to your external IP address or host name. Make sure to forward the port to this host!</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="staticHostNamePortLayout">
<item>
<widget class="QLabel" name="staticHostNameLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Static Host Name:</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="staticHostName"/>
</item>
<item>
<widget class="QLabel" name="staticPortLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Static Port:</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="staticPort">
<property name="maximum">
<number>65535</number>
</property>
<property name="value">
<number>50210</number>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QGroupBox" name="proxyGroupBox">
<property name="title">
<string>SOCKS Proxy</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QCheckBox" name="enableProxyCheckBox">
<property name="text">
<string>Use SOCKS Proxy</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="proxyButton">
<property name="text">
<string>Proxy Settings...</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="verticalSpacer_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QGroupBox" name="otherSettingsGroupBox">
<property name="title">
<string>Other Settings</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QCheckBox" name="checkBoxHttp">
<property name="layoutDirection">
<enum>Qt::RightToLeft</enum>
</property>
<property name="text">
<string>Allow web browsers to interact with Tomahawk (recommended)</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="checkBoxReporter">
<property name="layoutDirection">
<enum>Qt::RightToLeft</enum>
</property>
<property name="text">
<string>Send reports after Tomahawk crashed</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="verticalSpacer_4">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>CheckDirTree</class>
<extends>QTreeView</extends>
<header location="global">widgets/CheckDirTree.h</header>
</customwidget>
</customwidgets>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>StackedSettingsDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>222</x>
<y>347</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>StackedSettingsDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>290</x>
<y>353</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

@@ -1,33 +0,0 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2010-2011, Christian Muehlhaeuser <muesli@tomahawk-player.org>
* Copyright 2010-2011, Jeff Mitchell <jeff@tomahawk-player.org>
*
* Tomahawk is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Tomahawk is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Tomahawk. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef ACCOUNTDLLMACRO_H
#define ACCOUNTDLLMACRO_H
#include <QtCore/qglobal.h>
#ifndef ACCOUNTDLLEXPORT
# if defined (ACCOUNTDLLEXPORT_PRO)
# define ACCOUNTDLLEXPORT Q_DECL_EXPORT
# else
# define ACCOUNTDLLEXPORT Q_DECL_IMPORT
# endif
#endif
#endif

View File

@@ -1,301 +0,0 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2010-2012, Leo Franchi <lfranchi@kde.org>
*
* Tomahawk is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Tomahawk is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Tomahawk. If not, see <http://www.gnu.org/licenses/>.
*/
#include "LastFmAccount.h"
#include "LastFmConfig.h"
#include "infosystem/InfoSystem.h"
#include "LastFmInfoPlugin.h"
#include "utils/TomahawkUtils.h"
#include "resolvers/QtScriptResolver.h"
#include "AtticaManager.h"
#include "Pipeline.h"
#include "accounts/AccountManager.h"
#include "Source.h"
using namespace Tomahawk;
using namespace InfoSystem;
using namespace Accounts;
LastFmAccountFactory::LastFmAccountFactory()
{
m_icon.load( RESPATH "images/lastfm-icon.png" );
}
Account*
LastFmAccountFactory::createAccount( const QString& accountId )
{
return new LastFmAccount( accountId.isEmpty() ? generateId( factoryId() ) : accountId );
}
QPixmap
LastFmAccountFactory::icon() const
{
return m_icon;
}
LastFmAccount::LastFmAccount( const QString& accountId )
: CustomAtticaAccount( accountId )
{
setAccountFriendlyName( "Last.Fm" );
m_icon.load( RESPATH "images/lastfm-icon.png" );
AtticaManager::instance()->registerCustomAccount( "lastfm", this );
connect( AtticaManager::instance(), SIGNAL( resolverInstalled( QString ) ), this, SLOT( resolverInstalled( QString ) ) );
const Attica::Content res = AtticaManager::instance()->resolverForId( "lastfm" );
const AtticaManager::ResolverState state = AtticaManager::instance()->resolverState( res );
if ( state == AtticaManager::Installed )
{
hookupResolver();
}
if ( infoPlugin() && Tomahawk::InfoSystem::InfoSystem::instance()->workerThread() )
{
infoPlugin().data()->moveToThread( Tomahawk::InfoSystem::InfoSystem::instance()->workerThread().data() );
Tomahawk::InfoSystem::InfoSystem::instance()->addInfoPlugin( infoPlugin() );
QMetaObject::invokeMethod( infoPlugin().data(), "init", Qt::QueuedConnection );
}
}
LastFmAccount::~LastFmAccount()
{
if ( m_infoPlugin )
Tomahawk::InfoSystem::InfoSystem::instance()->removeInfoPlugin( infoPlugin() );
delete m_resolver.data();
}
void
LastFmAccount::authenticate()
{
if ( !AtticaManager::instance()->resolversLoaded() )
{
// If we're still waiting to load, wait for the attica resolvers to come down the pipe
connect( AtticaManager::instance(), SIGNAL(resolversLoaded(Attica::Content::List)), this, SLOT( atticaLoaded( Attica::Content::List ) ), Qt::UniqueConnection );
return;
}
const Attica::Content res = AtticaManager::instance()->resolverForId( "lastfm" );
const AtticaManager::ResolverState state = AtticaManager::instance()->resolverState( res );
qDebug() << "Last.FM account authenticating...";
if ( m_resolver.isNull() && state == AtticaManager::Installed )
{
hookupResolver();
}
else if ( m_resolver.isNull() )
{
qDebug() << "Got null resolver but asked to authenticate, so installing i we have one from attica:" << res.isValid() << res.id();
if ( res.isValid() && !res.id().isEmpty() )
AtticaManager::instance()->installResolver( res, false );
}
else
{
m_resolver.data()->start();
}
emit connectionStateChanged( connectionState() );
}
void
LastFmAccount::atticaLoaded( Attica::Content::List )
{
disconnect( AtticaManager::instance(), SIGNAL( resolversLoaded( Attica::Content::List ) ), this, SLOT( atticaLoaded( Attica::Content::List ) ) );
authenticate();
}
void
LastFmAccount::deauthenticate()
{
if ( !m_resolver.isNull() && m_resolver.data()->running() )
m_resolver.data()->stop();
emit connectionStateChanged( connectionState() );
}
QWidget*
LastFmAccount::configurationWidget()
{
if ( m_configWidget.isNull() )
m_configWidget = QWeakPointer<LastFmConfig>( new LastFmConfig( this ) );
return m_configWidget.data();
}
Account::ConnectionState
LastFmAccount::connectionState() const
{
return (!m_resolver.isNull() && m_resolver.data()->running()) ? Account::Connected : Account::Disconnected;
}
QPixmap
LastFmAccount::icon() const
{
return m_icon;
}
InfoPluginPtr
LastFmAccount::infoPlugin()
{
if ( m_infoPlugin.isNull() )
m_infoPlugin = QWeakPointer< LastFmInfoPlugin >( new LastFmInfoPlugin( this ) );
return InfoPluginPtr( m_infoPlugin.data() );
}
bool
LastFmAccount::isAuthenticated() const
{
return !m_resolver.isNull() && m_resolver.data()->running();
}
void
LastFmAccount::saveConfig()
{
if ( !m_configWidget.isNull() )
{
setUsername( m_configWidget.data()->username() );
setPassword( m_configWidget.data()->password() );
setScrobble( m_configWidget.data()->scrobble() );
}
sync();
if ( m_infoPlugin )
QTimer::singleShot( 0, m_infoPlugin.data(), SLOT( settingsChanged() ) );
}
QString
LastFmAccount::password() const
{
return credentials().value( "password" ).toString();
}
void
LastFmAccount::setPassword( const QString& password )
{
QVariantHash creds = credentials();
creds[ "password" ] = password;
setCredentials( creds );
}
QString
LastFmAccount::sessionKey() const
{
return credentials().value( "sessionkey" ).toString();
}
void
LastFmAccount::setSessionKey( const QString& sessionkey )
{
QVariantHash creds = credentials();
creds[ "sessionkey" ] = sessionkey;
setCredentials( creds );
}
QString
LastFmAccount::username() const
{
return credentials().value( "username" ).toString();
}
void
LastFmAccount::setUsername( const QString& username )
{
QVariantHash creds = credentials();
creds[ "username" ] = username;
setCredentials( creds );
}
bool
LastFmAccount::scrobble() const
{
return configuration().value( "scrobble" ).toBool();
}
void
LastFmAccount::setScrobble( bool scrobble )
{
QVariantHash conf;
conf[ "scrobble" ] = scrobble;
setConfiguration( conf );
}
void
LastFmAccount::resolverInstalled( const QString &resolverId )
{
if ( resolverId == "lastfm" )
{
// We requested this install, so we want to launch it
hookupResolver();
AccountManager::instance()->enableAccount( this );
}
}
void
LastFmAccount::resolverChanged()
{
emit connectionStateChanged( connectionState() );
}
void
LastFmAccount::hookupResolver()
{
// If there is a last.fm resolver from attica installed, create the corresponding ExternalResolver* and hook up to it
const Attica::Content res = AtticaManager::instance()->resolverForId( "lastfm" );
const AtticaManager::ResolverState state = AtticaManager::instance()->resolverState( res );
Q_ASSERT( state == AtticaManager::Installed );
Q_UNUSED( state );
const AtticaManager::Resolver data = AtticaManager::instance()->resolverData( res.id() );
m_resolver = QWeakPointer< ExternalResolverGui >( qobject_cast< ExternalResolverGui* >( Pipeline::instance()->addScriptResolver( data.scriptPath, enabled() ) ) );
connect( m_resolver.data(), SIGNAL( changed() ), this, SLOT( resolverChanged() ) );
}
Attica::Content
LastFmAccount::atticaContent() const
{
return AtticaManager::instance()->resolverForId( "lastfm" );
}

View File

@@ -1,115 +0,0 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2010-2012, Leo Franchi <lfranchi@kde.org>
*
* Tomahawk is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Tomahawk is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Tomahawk. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef LASTFMACCOUNT_H
#define LASTFMACCOUNT_H
#include "accounts/Account.h"
#include "AtticaManager.h"
#include <attica/content.h>
#include <QObject>
namespace Tomahawk {
class ExternalResolverGui;
namespace InfoSystem {
class LastFmInfoPlugin;
}
namespace Accounts {
class LastFmConfig;
class LastFmAccountFactory : public AccountFactory
{
Q_OBJECT
public:
LastFmAccountFactory();
virtual Account* createAccount(const QString& accountId = QString());
virtual QString description() const { return tr( "Scrobble your tracks to last.fm, and find freely downloadable tracks to play" ); }
virtual QString factoryId() const { return "lastfmaccount"; }
virtual QString prettyName() const { return "Last.fm"; }
virtual AccountTypes types() const { return AccountTypes( InfoType | StatusPushType ); }
virtual bool allowUserCreation() const { return false; }
virtual QPixmap icon() const;
virtual bool isUnique() const { return true; }
private:
QPixmap m_icon;
};
/**
* 3.Last.Fm account is special. It is both an attica resolver *and* a InfoPlugin. We always want the infoplugin,
* but the user can install the attica resolver on-demand. So we take care of both there.
*
*/
class LastFmAccount : public CustomAtticaAccount
{
Q_OBJECT
public:
explicit LastFmAccount( const QString& accountId );
~LastFmAccount();
virtual void deauthenticate();
virtual void authenticate();
virtual SipPlugin* sipPlugin() { return 0; }
virtual Tomahawk::InfoSystem::InfoPluginPtr infoPlugin();
virtual bool isAuthenticated() const;
virtual ConnectionState connectionState() const;
virtual QPixmap icon() const;
virtual QWidget* aclWidget() { return 0; }
virtual QWidget* configurationWidget();
virtual void saveConfig();
QString username() const;
void setUsername( const QString& );
QString password() const;
void setPassword( const QString& );
QString sessionKey() const;
void setSessionKey( const QString& );
bool scrobble() const;
void setScrobble( bool scrobble );
Attica::Content atticaContent() const;
private slots:
void atticaLoaded( Attica::Content::List );
void resolverInstalled( const QString& resolverId );
void resolverChanged();
private:
void hookupResolver();
QWeakPointer<Tomahawk::ExternalResolverGui> m_resolver;
QWeakPointer<Tomahawk::InfoSystem::LastFmInfoPlugin> m_infoPlugin;
QWeakPointer<LastFmConfig> m_configWidget;
QPixmap m_icon;
};
}
}
#endif // LASTFMACCOUNT_H

View File

@@ -1,232 +0,0 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2010-2011, Leo Franchi <lfranchi@kde.org>
*
* Tomahawk is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Tomahawk is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Tomahawk. If not, see <http://www.gnu.org/licenses/>.
*/
#include "LastFmConfig.h"
#include "ui_LastFmConfig.h"
#include "LastFmAccount.h"
#include "database/Database.h"
#include "database/DatabaseCommand_LogPlayback.h"
#include "utils/TomahawkUtils.h"
#include "utils/Logger.h"
#include "lastfm/ws.h"
#include "lastfm/User"
#include "lastfm/XmlQuery"
using namespace Tomahawk::Accounts;
LastFmConfig::LastFmConfig( LastFmAccount* account )
: QWidget( 0 )
, m_account( account )
, m_page( 1 )
, m_lastTimeStamp( 0 )
{
m_ui = new Ui_LastFmConfig;
m_ui->setupUi( this );
m_ui->progressBar->hide();
m_ui->username->setText( m_account->username() );
m_ui->password->setText( m_account->password() );
m_ui->enable->setChecked( m_account->scrobble() );
connect( m_ui->testLogin, SIGNAL( clicked( bool ) ), SLOT( testLogin() ) );
connect( m_ui->importHistory, SIGNAL( clicked( bool ) ), SLOT( loadHistory() ) );
connect( m_ui->username, SIGNAL( textChanged( QString ) ), SLOT( enableButton() ) );
connect( m_ui->password, SIGNAL( textChanged( QString ) ), SLOT( enableButton() ) );
}
QString
LastFmConfig::password() const
{
return m_ui->password->text();
}
bool
LastFmConfig::scrobble() const
{
return m_ui->enable->isChecked();
}
QString
LastFmConfig::username() const
{
return m_ui->username->text().trimmed();
}
void
LastFmConfig::testLogin()
{
m_ui->testLogin->setEnabled( false );
m_ui->testLogin->setText( tr( "Testing..." ) );
QString authToken = TomahawkUtils::md5( ( m_ui->username->text().toLower() + TomahawkUtils::md5( m_ui->password->text().toUtf8() ) ).toUtf8() );
// now authenticate w/ last.fm and get our session key
QMap<QString, QString> query;
query[ "method" ] = "auth.getMobileSession";
query[ "username" ] = m_ui->username->text().toLower();
query[ "authToken" ] = authToken;
// ensure they have up-to-date settings
lastfm::setNetworkAccessManager( TomahawkUtils::nam() );
QNetworkReply* authJob = lastfm::ws::post( query );
connect( authJob, SIGNAL( finished() ), SLOT( onLastFmFinished() ) );
}
void
LastFmConfig::enableButton()
{
m_ui->testLogin->setText( tr( "Test Login" ) );
m_ui->testLogin->setEnabled( true );
}
void
LastFmConfig::loadHistory()
{
if ( m_lastTimeStamp )
{
m_ui->importHistory->setText( tr( "Importing %1", "e.g. Importing 2012/01/01" ).arg( QDateTime::fromTime_t( m_lastTimeStamp ).toString( "MMMM d yyyy" ) ) );
}
else
m_ui->importHistory->setText( tr( "Importing History..." ) );
m_ui->importHistory->setEnabled( false );
m_ui->progressBar->show();
QNetworkReply* reply = lastfm::User( m_ui->username->text().toLower() ).getRecentTracks( 200, m_page );
connect( reply, SIGNAL( finished() ), SLOT( onHistoryLoaded() ) );
}
void
LastFmConfig::onHistoryLoaded()
{
int total = 0;
bool finished = false;
QNetworkReply* reply = qobject_cast< QNetworkReply* >( sender() );
try
{
lastfm::XmlQuery lfm = reply->readAll();
foreach ( lastfm::XmlQuery e, lfm.children( "track" ) )
{
// tDebug() << "Found:" << e["artist"].text() << e["name"].text() << e["date"].attribute( "uts" ).toUInt();
Tomahawk::query_ptr query = Query::get( e["artist"].text(), e["name"].text(), QString(), QString(), false );
m_lastTimeStamp = e["date"].attribute( "uts" ).toUInt();
DatabaseCommand_LogPlayback* cmd = new DatabaseCommand_LogPlayback( query, DatabaseCommand_LogPlayback::Finished, m_lastTimeStamp );
Database::instance()->enqueue( QSharedPointer<DatabaseCommand>(cmd) );
}
if ( !lfm.children( "recenttracks" ).isEmpty() )
{
lastfm::XmlQuery stats = lfm.children( "recenttracks" ).first();
int page = stats.attribute( "page" ).toInt();
total = stats.attribute( "totalPages" ).toInt();
m_ui->progressBar->setMaximum( total );
m_ui->progressBar->setValue( page );
if ( page < total )
{
m_page = page + 1;
loadHistory();
}
else
finished = true;
}
else
finished = true;
}
catch( lastfm::ws::ParseError e )
{
tDebug() << "XmlQuery error:" << e.what();
finished = true;
}
if ( finished )
{
if ( m_page != total )
{
m_ui->importHistory->setText( tr( "History Incomplete. Resume" ) );
m_ui->importHistory->setEnabled( true );
}
else
{
m_ui->importHistory->setText( tr( "Playback History Imported" ) );
}
}
}
void
LastFmConfig::onLastFmFinished()
{
QNetworkReply* authJob = dynamic_cast<QNetworkReply*>( sender() );
if( !authJob )
{
qDebug() << Q_FUNC_INFO << "No auth job returned!";
return;
}
if( authJob->error() == QNetworkReply::NoError )
{
lastfm::XmlQuery lfm = lastfm::XmlQuery( authJob->readAll() );
if( lfm.children( "error" ).size() > 0 )
{
qDebug() << "ERROR from last.fm:" << lfm.text();
m_ui->testLogin->setText( tr( "Failed" ) );
m_ui->testLogin->setEnabled( true );
}
else
{
m_ui->testLogin->setText( tr( "Success" ) );
m_ui->testLogin->setEnabled( false );
}
}
else
{
switch( authJob->error() )
{
case QNetworkReply::ContentOperationNotPermittedError:
case QNetworkReply::AuthenticationRequiredError:
m_ui->testLogin->setText( tr( "Failed" ) );
m_ui->testLogin->setEnabled( true );
break;
default:
qDebug() << "Couldn't get last.fm auth result";
m_ui->testLogin->setText( tr( "Could not contact server" ) );
m_ui->testLogin->setEnabled( true );
return;
}
}
}

View File

@@ -1,64 +0,0 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2010-2011, Leo Franchi <lfranchi@kde.org>
*
* Tomahawk is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Tomahawk is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Tomahawk. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef LASTFMCONFIG_H
#define LASTFMCONFIG_H
#include <QWidget>
class Ui_LastFmConfig;
namespace Tomahawk {
namespace Accounts {
class LastFmAccount;
class LastFmConfig : public QWidget
{
Q_OBJECT
public:
explicit LastFmConfig( LastFmAccount* account );
QString username() const;
QString password() const;
bool scrobble() const;
public slots:
void testLogin();
void onLastFmFinished();
private slots:
void enableButton();
void loadHistory();
void onHistoryLoaded();
private:
LastFmAccount* m_account;
Ui_LastFmConfig* m_ui;
unsigned int m_page;
unsigned int m_lastTimeStamp;
};
}
}
#endif // LASTFMCONFIG_H

View File

@@ -1,102 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>LastFmConfig</class>
<widget class="QWidget" name="LastFmConfig">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>253</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="margin">
<number>4</number>
</property>
<item>
<widget class="QLabel" name="label">
<property name="text">
<string/>
</property>
<property name="pixmap">
<pixmap resource="../../../resources.qrc">:/data/images/lastfm-icon.png</pixmap>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QCheckBox" name="enable">
<property name="layoutDirection">
<enum>Qt::LeftToRight</enum>
</property>
<property name="text">
<string>Scrobble tracks to Last.fm</string>
</property>
</widget>
</item>
<item>
<layout class="QGridLayout" name="gridLayout_3">
<item row="0" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Username:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="username"/>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>Password:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="password">
<property name="echoMode">
<enum>QLineEdit::Password</enum>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QPushButton" name="testLogin">
<property name="text">
<string>Test Login</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="importHistory">
<property name="text">
<string>Import Playback History</string>
</property>
</widget>
</item>
<item>
<widget class="QProgressBar" name="progressBar">
<property name="value">
<number>0</number>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<resources>
<include location="../../../resources.qrc"/>
</resources>
<connections/>
</ui>

View File

@@ -1,989 +0,0 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2010-2011, Leo Franchi <lfranchi@kde.org>
* Copyright 2010-2011, Hugo Lindström <hugolm84@gmail.com>
*
* Tomahawk is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Tomahawk is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Tomahawk. If not, see <http://www.gnu.org/licenses/>.
*/
#include "SpotifyAccount.h"
#include "Playlist.h"
#include "playlist/PlaylistUpdaterInterface.h"
#include "SourceList.h"
#include "SpotifyAccountConfig.h"
#include "SpotifyPlaylistUpdater.h"
#include "resolvers/ScriptResolver.h"
#include "utils/TomahawkUtils.h"
#include "ActionCollection.h"
#include "Pipeline.h"
#include "accounts/AccountManager.h"
#include "utils/Closure.h"
#ifndef ENABLE_HEADLESS
#include "jobview/JobStatusView.h"
#include "jobview/JobStatusModel.h"
#include "jobview/ErrorStatusMessage.h"
#endif
#include <QPixmap>
#include <QAction>
#include <QHBoxLayout>
#include <QLabel>
#include <QMessageBox>
using namespace Tomahawk;
using namespace Accounts;
static QPixmap* s_icon = 0;
#ifdef Q_OS_MAC
static QString s_resolverId = "spotify-osx";
#elif defined(Q_OS_WIN)
static QString s_resolverId = "spotify-win";
#elif defined(Q_OS_LINUX) && defined(__GNUC__) && defined(__x86_64__)
static QString s_resolverId = "spotify-linux-x64";
#elif defined(Q_OS_LINUX)
static QString s_resolverId = "spotify-linux-x86";
#else
static QString s_resolverId = "spotify-unknown";
#endif
Account*
SpotifyAccountFactory::createAccount( const QString& accountId )
{
return new SpotifyAccount( accountId );
}
QPixmap
SpotifyAccountFactory::icon() const
{
if ( !s_icon )
s_icon = new QPixmap( RESPATH "images/spotify-logo.png" );
return *s_icon;
}
SpotifyAccount::SpotifyAccount( const QString& accountId )
: CustomAtticaAccount( accountId )
, m_preventEnabling( false )
{
init();
}
SpotifyAccount::~SpotifyAccount()
{
clearUser();
}
void
SpotifyAccount::init()
{
setAccountFriendlyName( "Spotify" );
setAccountServiceName( "spotify" );
AtticaManager::instance()->registerCustomAccount( s_resolverId, this );
qRegisterMetaType< Tomahawk::Accounts::SpotifyPlaylistInfo* >( "Tomahawk::Accounts::SpotifyPlaylist*" );
if ( !AtticaManager::instance()->resolversLoaded() )
{
// If we're still waiting to load, wait for the attica resolvers to come down the pipe
connect( AtticaManager::instance(), SIGNAL( resolversLoaded( Attica::Content::List ) ), this, SLOT( delayedInit() ), Qt::UniqueConnection );
}
else
{
delayedInit();
}
}
void
SpotifyAccount::delayedInit()
{
connect( AtticaManager::instance(), SIGNAL( resolverInstalled( QString ) ), this, SLOT( resolverInstalled( QString ) ) );
const Attica::Content res = AtticaManager::instance()->resolverForId( s_resolverId );
const AtticaManager::ResolverState state = AtticaManager::instance()->resolverState( res );
const QString path = configuration().value( "path" ).toString(); // Manual path override
if ( !checkForResolver() && state != AtticaManager::Uninstalled )
{
// If the user manually deleted the resolver, mark it as uninstalled, so we re-fetch for the user
AtticaManager::instance()->uninstallResolver( res );
}
else if ( state == AtticaManager::Installed || !path.isEmpty() )
{
if ( !path.isEmpty() )
{
QFileInfo info( path );
// Resolver was deleted, so abort.
if ( !info.exists() )
return;
}
hookupResolver();
}
}
void
SpotifyAccount::hookupResolver()
{
// initialize the resolver itself. this is called if the account actually has an installed spotify resolver,
// as it might not.
// If there is a spotify resolver from attica installed, create the corresponding ExternalResolver* and hook up to it
QString path = configuration().value( "path" ).toString();
if ( path.isEmpty() )
{
const Attica::Content res = AtticaManager::instance()->resolverForId( s_resolverId );
const AtticaManager::ResolverState state = AtticaManager::instance()->resolverState( res );
Q_ASSERT( state == AtticaManager::Installed );
Q_UNUSED( state );
const AtticaManager::Resolver data = AtticaManager::instance()->resolverData( res.id() );
path = data.scriptPath;
}
qDebug() << "Starting spotify resolver with path:" << path;
m_spotifyResolver = QWeakPointer< ScriptResolver >( qobject_cast< ScriptResolver* >( Pipeline::instance()->addScriptResolver( path, enabled() ) ) );
connect( m_spotifyResolver.data(), SIGNAL( changed() ), this, SLOT( resolverChanged() ) );
connect( m_spotifyResolver.data(), SIGNAL( customMessage( QString,QVariantMap ) ), this, SLOT( resolverMessage( QString, QVariantMap ) ) );
const bool hasMigrated = configuration().value( "hasMigrated" ).toBool();
if ( !hasMigrated )
{
qDebug() << "Getting credentials from spotify resolver to migrate to in-app config";
QVariantMap msg;
msg[ "_msgtype" ] = "getCredentials";
m_spotifyResolver.data()->sendMessage( msg );
}
}
bool
SpotifyAccount::checkForResolver()
{
#if defined(Q_OS_MAC)
const QDir path = QCoreApplication::applicationDirPath();
QFile file( path.absoluteFilePath( "spotify_tomahawkresolver" ) );
return file.exists();
#elif defined(Q_OS_WIN)
QDir appDataDir = TomahawkUtils::appDataDir();
return appDataDir.exists( QString( "atticaresolvers/%1/spotify_tomahawkresolver.exe" ).arg( s_resolverId ) );
#endif
return false;
}
void
SpotifyAccount::resolverChanged()
{
emit connectionStateChanged( connectionState() );
}
Attica::Content
SpotifyAccount::atticaContent() const
{
return AtticaManager::instance()->resolverForId( s_resolverId );
}
void
SpotifyAccount::authenticate()
{
if ( !AtticaManager::instance()->resolversLoaded() )
{
// If we're still waiting to load, wait for the attica resolvers to come down the pipe
connect( AtticaManager::instance(), SIGNAL( resolversLoaded( Attica::Content::List ) ), this, SLOT( atticaLoaded( Attica::Content::List ) ), Qt::UniqueConnection );
return;
}
const Attica::Content res = AtticaManager::instance()->resolverForId( s_resolverId );
const AtticaManager::ResolverState state = AtticaManager::instance()->resolverState( res );
qDebug() << "Spotify account authenticating...";
if ( m_spotifyResolver.isNull() && state == AtticaManager::Installed )
{
// We don;t have the resolver but it has been installed via attica already, so lets just turn it on
qDebug() << "No valid spotify resolver running, but attica reports it is installed, so start it up";
hookupResolver();
}
else if ( m_spotifyResolver.isNull() )
{
qDebug() << "Got null resolver but asked to authenticate, so installing if we have one from attica:" << res.isValid() << res.id();
if ( res.isValid() && !res.id().isEmpty() )
AtticaManager::instance()->installResolver( res, false );
else
{
#ifdef Q_OS_LINUX
m_preventEnabling = true;
#endif
}
}
else if ( !m_spotifyResolver.data()->running() )
{
qDebug() << "Spotify resolver exists but stopped, starting";
m_spotifyResolver.data()->start();
}
else
{
qDebug() << "Spotify resolver exists and is running, ignore authentication attempt";
}
emit connectionStateChanged( connectionState() );
}
void
SpotifyAccount::deauthenticate()
{
if ( !m_spotifyResolver.isNull() && m_spotifyResolver.data()->running() )
m_spotifyResolver.data()->stop();
emit connectionStateChanged( connectionState() );
}
bool
SpotifyAccount::isAuthenticated() const
{
return !m_spotifyResolver.isNull() && m_spotifyResolver.data()->running();
}
Account::ConnectionState
SpotifyAccount::connectionState() const
{
return (!m_spotifyResolver.isNull() && m_spotifyResolver.data()->running()) ? Account::Connected : Account::Disconnected;
}
void
SpotifyAccount::resolverInstalled(const QString& resolverId)
{
if ( resolverId == s_resolverId )
{
// We requested this install, so we want to launch it
hookupResolver();
AccountManager::instance()->enableAccount( this );
}
}
void
SpotifyAccount::atticaLoaded( Attica::Content::List )
{
disconnect( AtticaManager::instance(), SIGNAL( resolversLoaded( Attica::Content::List ) ), this, SLOT( atticaLoaded( Attica::Content::List ) ) );
authenticate();
}
void
SpotifyAccount::setManualResolverPath( const QString &resolverPath )
{
Q_ASSERT( !resolverPath.isEmpty() );
QVariantHash conf = configuration();
conf[ "path" ] = resolverPath;
setConfiguration( conf );
sync();
m_preventEnabling = false;
if ( !m_spotifyResolver.isNull() )
{
// replace
NewClosure( m_spotifyResolver.data(), SIGNAL( destroyed() ), this, SLOT( hookupAfterDeletion( bool ) ), true );
m_spotifyResolver.data()->deleteLater();
}
else
{
hookupResolver();
AccountManager::instance()->enableAccount( this );
}
}
void
SpotifyAccount::hookupAfterDeletion( bool autoEnable )
{
hookupResolver();
if ( autoEnable )
AccountManager::instance()->enableAccount( this );
}
void
SpotifyAccount::aboutToShow( QAction* action, const playlist_ptr& playlist )
{
if ( !m_customActions.contains( action ) )
return;
// If it's not being synced, allow the option to sync
bool found = false;
QList<PlaylistUpdaterInterface*> updaters = playlist->updaters();
foreach ( PlaylistUpdaterInterface* updater, updaters )
{
if ( SpotifyPlaylistUpdater* spotifyUpdater = qobject_cast< SpotifyPlaylistUpdater* >( updater ) )
{
if ( spotifyUpdater->sync() )
found = true;
}
}
if ( !found )
{
action->setText( tr( "Sync with Spotify" ) );
}
else
{
action->setText( tr( "Stop syncing with Spotify" ) );
}
}
void
SpotifyAccount::syncActionTriggered( bool checked )
{
Q_UNUSED( checked );
QAction* action = qobject_cast< QAction* >( sender() );
if ( !action || !m_customActions.contains( action ) )
return;
const playlist_ptr playlist = action->property( "payload" ).value< playlist_ptr >();
if ( playlist.isNull() )
{
qWarning() << "Got context menu spotify sync action triggered, but invalid playlist payload!";
Q_ASSERT( false );
return;
}
SpotifyPlaylistUpdater* updater = 0;
QList<PlaylistUpdaterInterface*> updaters = playlist->updaters();
foreach ( PlaylistUpdaterInterface* u, updaters )
{
if ( SpotifyPlaylistUpdater* spotifyUpdater = qobject_cast< SpotifyPlaylistUpdater* >( u ) )
{
updater = spotifyUpdater;
}
}
if ( !updater )
{
QVariantMap msg;
msg[ "_msgtype" ] = "createPlaylist";
msg[ "sync" ] = true;
msg[ "title" ] = playlist->title();
QList< query_ptr > queries;
foreach ( const plentry_ptr& ple, playlist->entries() )
queries << ple->query();
QVariantList tracks = SpotifyPlaylistUpdater::queriesToVariant( queries );
msg[ "tracks" ] = tracks;
const QString qid = sendMessage( msg, this, "playlistCreated" );
m_waitingForCreateReply[ qid ] = playlist;
}
else
{
SpotifyPlaylistInfo* info = 0;
foreach ( SpotifyPlaylistInfo* ifo, m_allSpotifyPlaylists )
{
if ( ifo->plid == updater->spotifyId() )
{
info = ifo;
break;
}
}
Q_ASSERT( info );
if ( info )
info->sync = !updater->sync();
if ( m_configWidget.data() )
m_configWidget.data()->setPlaylists( m_allSpotifyPlaylists );
if ( !updater->sync() )
{
startPlaylistSync( info );
}
else
{
stopPlaylistSync( info, true );
}
}
}
void
SpotifyAccount::resolverMessage( const QString &msgType, const QVariantMap &msg )
{
if ( msgType == "credentials" )
{
QVariantHash creds = credentials();
creds[ "username" ] = msg.value( "username" );
creds[ "password" ] = msg.value( "password" );
creds[ "highQuality" ] = msg.value( "highQuality" );
setCredentials( creds );
qDebug() << "Set creds:" << creds.value( "username" ) << creds.value( "password" ) << msg.value( "username" ) << msg.value( "password" );
QVariantHash config = configuration();
config[ "hasMigrated" ] = true;
setConfiguration( config );
sync();
return;
}
const QString qid = msg.value( "qid" ).toString();
if ( m_qidToSlotMap.contains( qid ) )
{
QObject* receiver = m_qidToSlotMap[ qid ].first;
QString slot = m_qidToSlotMap[ qid ].second;
m_qidToSlotMap.remove( qid );
QMetaObject::invokeMethod( receiver, slot.toLatin1(), Q_ARG( QString, msgType ), Q_ARG( QVariantMap, msg ) );
}
else if ( msgType == "allPlaylists" )
{
const QVariantList playlists = msg.value( "playlists" ).toList();
qDeleteAll( m_allSpotifyPlaylists );
m_allSpotifyPlaylists.clear();
foreach ( const QVariant& playlist, playlists )
{
const QVariantMap plMap = playlist.toMap();
const QString name = plMap.value( "name" ).toString();
const QString plid = plMap.value( "id" ).toString();
const QString revid = plMap.value( "revid" ).toString();
const bool sync = plMap.value( "sync" ).toBool();
if ( name.isNull() || plid.isNull() || revid.isNull() )
{
qDebug() << "Did not get name and plid and revid for spotify playlist:" << name << plid << revid << plMap;
continue;
}
m_allSpotifyPlaylists << new SpotifyPlaylistInfo( name, plid, revid, sync );
}
if ( !m_configWidget.isNull() )
{
m_configWidget.data()->setPlaylists( m_allSpotifyPlaylists );
}
}
else if ( msgType == "tracksAdded" )
{
const QString plid = msg.value( "playlistid" ).toString();
// We should already be syncing this playlist if we get updates for it
// Q_ASSERT( m_updaters.contains( plid ) );
if ( !m_updaters.contains( plid ) )
return;
SpotifyPlaylistUpdater* updater = m_updaters[ plid ];
Q_ASSERT( updater->sync() );
const QString startPos = msg.value( "startPosition" ).toString();
const QVariantList tracksList = msg.value( "tracks" ).toList();
const QString newRev = msg.value( "revid" ).toString();
const QString oldRev = msg.value( "oldRev" ).toString();
updater->spotifyTracksAdded( tracksList, startPos, newRev, oldRev );
}
else if ( msgType == "tracksRemoved" )
{
const QString plid = msg.value( "playlistid" ).toString();
// We should already be syncing this playlist if we get updates for it
// Q_ASSERT( m_updaters.contains( plid ) );
if ( !m_updaters.contains( plid ) )
return;
SpotifyPlaylistUpdater* updater = m_updaters[ plid ];
// If we're not syncing with this, the resolver is quite misinformed.
// Q_ASSERT( updater && updater->sync() );
if ( !updater || !updater->sync() )
return;
const QVariantList tracksList = msg.value( "trackPositions" ).toList();
const QString newRev = msg.value( "revid" ).toString();
const QString oldRev = msg.value( "oldRev" ).toString();
updater->spotifyTracksRemoved( tracksList, newRev, oldRev );
}
else if ( msgType == "tracksMoved" )
{
const QString plid = msg.value( "playlistid" ).toString();
// We should already be syncing this playlist if we get updates for it
Q_ASSERT( m_updaters.contains( plid ) );
if ( !m_updaters.contains( plid ) )
return;
SpotifyPlaylistUpdater* updater = m_updaters[ plid ];
Q_ASSERT( updater->sync() );
const QString newStartPos = msg.value( "newStartPosition" ).toString();
const QVariantList tracksList = msg.value( "tracks" ).toList();
const QString newRev = msg.value( "revid" ).toString();
const QString oldRev = msg.value( "oldRev" ).toString();
updater->spotifyTracksMoved( tracksList, newStartPos, newRev, oldRev );
}
else if( msgType == "playlistRenamed" )
{
const QString plid = msg.value( "id" ).toString();
// We should already be syncing this playlist if we get updates for it
//Q_ASSERT( m_updaters.contains( plid ) );
qDebug() << Q_FUNC_INFO;
if ( !m_updaters.contains( plid ) )
return;
SpotifyPlaylistUpdater* updater = m_updaters[ plid ];
Q_ASSERT( updater->sync() );
qDebug() << "Playlist renamed fetched in tomahawk";
const QString title = msg.value( "name" ).toString();
const QString newRev = msg.value( "revid" ).toString();
const QString oldRev = msg.value( "oldRev" ).toString();
updater->spotifyPlaylistRenamed( title, newRev, oldRev );
}
else if( msgType == "spotifyError" )
{
const QString error = msg.value( "msg" ).toString();
if( error.isEmpty() )
return;
if( msg.value( "isDebugMsg" ).toBool() )
tDebug( LOGVERBOSE ) << "SpotifyResolverError: " << error;
else
JobStatusView::instance()->model()->addJob( new ErrorStatusMessage( QString( "Spotify: %1" ).arg( error ) ) );
}
else if( msgType == "userChanged" )
{
const QString rmsg = msg.value( "msg" ).toString();
clearUser( true );
if ( m_configWidget.data() )
m_configWidget.data()->setPlaylists( QList< SpotifyPlaylistInfo* >() );
qDebug() << "User changed message from spotify:" << rmsg;
}
else if ( msgType == "loginResponse" )
{
QVariantHash creds = credentials();
creds[ "username" ] = msg.value( "username" ).toString();
creds[ "password" ] = msg.value( "password" ).toString();
creds[ "highQuality" ] = msg.value( "highQuality" ).toString();
setCredentials( creds );
sync();
const bool success = msg.value( "success" ).toBool();
if ( success )
createActions();
configurationWidget(); // ensure it's created so we can set the login button
if ( m_configWidget.data() )
{
const QString message = msg.value( "message" ).toString();
m_configWidget.data()->loginResponse( success, message );
}
}
else if ( msgType == "playlistDeleted" )
{
const QString plid = msg.value( "playlistid" ).toString();
if ( !m_updaters.contains( plid ) )
return;
SpotifyPlaylistUpdater* updater = m_updaters.take( plid );
updater->remove( false );
}
}
void
SpotifyAccount::clearUser( bool permanentlyDelete )
{
foreach( SpotifyPlaylistUpdater* updater, m_updaters.values() )
{
if ( permanentlyDelete )
updater->remove( false );
else
updater->deleteLater();
}
m_updaters.clear();
qDeleteAll( m_allSpotifyPlaylists );
m_allSpotifyPlaylists.clear();
m_qidToSlotMap.clear();
m_waitingForCreateReply.clear();
removeActions();
}
QPixmap
SpotifyAccount::icon() const
{
if ( !s_icon )
s_icon = new QPixmap( RESPATH "images/spotify-logo.png" );
return *s_icon;
}
QWidget*
SpotifyAccount::configurationWidget()
{
if ( m_spotifyResolver.isNull() )
return 0;
if ( m_configWidget.isNull() )
{
m_configWidget = QWeakPointer< SpotifyAccountConfig >( new SpotifyAccountConfig( this ) );
connect( m_configWidget.data(), SIGNAL( login( QString,QString ) ), this, SLOT( login( QString,QString ) ) );
m_configWidget.data()->setPlaylists( m_allSpotifyPlaylists );
}
return static_cast< QWidget* >( m_configWidget.data() );
}
QWidget*
SpotifyAccount::aboutWidget()
{
if ( m_aboutWidget.isNull() )
{
QWidget* w = new QWidget();
w->hide();
QHBoxLayout* l = new QHBoxLayout( w );
QLabel* pm = new QLabel( w );
pm->setPixmap( QPixmap( RESPATH "images/spotifycore-logo" ) );
QLabel* text = new QLabel( "This product uses SPOTIFY(R) CORE but is not endorsed, certified or otherwise approved in any way by Spotify. Spotify is the registered trade mark of the Spotify Group.", w );
text->setWordWrap( true );
l->addWidget( pm );
l->addWidget( text );
w->setLayout( l );
m_aboutWidget = QWeakPointer< QWidget >( w );
}
return m_aboutWidget.data();
}
void
SpotifyAccount::saveConfig()
{
Q_ASSERT( !m_configWidget.isNull() );
if ( m_configWidget.isNull() )
return;
QVariantHash creds = credentials();
if ( creds.value( "username" ).toString() != m_configWidget.data()->username() ||
creds.value( "password" ).toString() != m_configWidget.data()->password() ||
creds.value( "highQuality" ).toBool() != m_configWidget.data()->highQuality() )
{
creds[ "username" ] = m_configWidget.data()->username();
creds[ "password" ] = m_configWidget.data()->password();
creds[ "highQuality" ] = m_configWidget.data()->highQuality();
setCredentials( creds );
}
QVariantHash config = configuration();
config[ "deleteOnUnsync" ] = m_configWidget.data()->deleteOnUnsync();
setConfiguration( config );
m_configWidget.data()->saveSettings();
foreach ( SpotifyPlaylistInfo* pl, m_allSpotifyPlaylists )
{
// qDebug() << "Checking changed state:" << pl->changed << pl->name << pl->sync;
if ( pl->changed )
{
pl->changed = false;
if ( pl->sync )
{
// Fetch full playlist contents, then begin the sync
startPlaylistSync( pl );
}
else
stopPlaylistSync( pl );
}
}
sync();
if ( !m_configWidget.data()->loggedInManually() && !m_configWidget.data()->username().isEmpty() && !m_configWidget.data()->password().isEmpty() )
{
// If the user never pressed log in, he might have just pressed ok or hit enter. So log in anyway
login( m_configWidget.data()->username(), m_configWidget.data()->password() );
}
}
void
SpotifyAccount::login( const QString& username, const QString& password )
{
// Send the result to the resolver
QVariantMap msg;
msg[ "_msgtype" ] = "login";
msg[ "username" ] = username;
msg[ "password" ] = password;
msg[ "highQuality" ] = m_configWidget.data()->highQuality();
m_spotifyResolver.data()->sendMessage( msg );
}
void
SpotifyAccount::startPlaylistSync( SpotifyPlaylistInfo* playlist )
{
if ( !playlist )
return;
QVariantMap msg;
msg[ "_msgtype" ] = "getPlaylist";
msg[ "playlistid" ] = playlist->plid;
msg[ "sync" ] = playlist->sync;
sendMessage( msg, this, "startPlaylistSyncWithPlaylist" );
}
void
SpotifyAccount::startPlaylistSyncWithPlaylist( const QString& msgType, const QVariantMap& msg )
{
Q_UNUSED( msgType );
qDebug() << Q_FUNC_INFO << "Got full spotify playlist body, creating a tomahawk playlist and enabling sync!!";
const QString id = msg.value( "id" ).toString();
const QString name = msg.value( "name" ).toString();
const QString revid = msg.value( "revid" ).toString();
qDebug() << "Starting sync with pl:" << id << name;
QVariantList tracks = msg.value( "tracks" ).toList();
// create a list of query/plentries directly
QList< query_ptr > queries = SpotifyPlaylistUpdater::variantToQueries( tracks );
/**
* Begin syncing a playlist. Two options:
* 1) This is a playlist that has never been synced to tomahawk. Create a new one
* and attach a new SpotifyPlaylistUpdater to it
* 2) This was previously synced, and has since been unsynced. THe playlist is still around
* with an inactive SpotifyPlaylistUpdater, so just enable it and bring it up to date by merging current with new
* TODO: show a warning( "Do you want to overwrite with spotify's version?" )
*/
if ( m_updaters.contains( id ) )
{
Q_ASSERT( m_updaters[ id ]->sync() == false ); /// Should have been unchecked/off before
m_updaters[ id ]->setSync( true );
// m_updaters[ id ]->
// TODO
}
else
{
playlist_ptr plPtr = Tomahawk::Playlist::create( SourceList::instance()->getLocal(),
uuid(),
name,
QString(),
QString(),
false,
queries );
SpotifyPlaylistUpdater* updater = new SpotifyPlaylistUpdater( this, revid, id, plPtr );
updater->setSync( true );
m_updaters[ id ] = updater;
}
}
void
SpotifyAccount::playlistCreated( const QString& msgType, const QVariantMap& msg )
{
Q_UNUSED( msgType );
qDebug() << Q_FUNC_INFO << "Got response from our createPlaylist command, now creating updater and attaching";
const bool success = msg.value( "success" ).toBool();
if ( !success )
{
qWarning() << "Got FAILED return code from spotify resolver createPlaylist command, aborting sync";
return;
}
const QString id = msg.value( "playlistid" ).toString();
const QString revid = msg.value( "playlistid" ).toString();
const QString qid = msg.value( "qid" ).toString();
if ( !m_waitingForCreateReply.contains( qid ) )
{
qWarning() << "Got a createPlaylist reply for a playlist/qid we were not waiting for :-/ " << qid << m_waitingForCreateReply;
return;
}
playlist_ptr playlist = m_waitingForCreateReply.take( qid );
SpotifyPlaylistUpdater* updater = new SpotifyPlaylistUpdater( this, revid, id, playlist );
updater->setSync( true );
m_updaters[ id ] = updater;
}
QString
SpotifyAccount::sendMessage( const QVariantMap &m, QObject* obj, const QString& slot )
{
QVariantMap msg = m;
QString qid;
if ( obj )
{
qid = QUuid::createUuid().toString().replace( "{", "" ).replace( "}", "" );
m_qidToSlotMap[ qid ] = qMakePair( obj, slot );
msg[ "qid" ] = qid;
}
m_spotifyResolver.data()->sendMessage( msg );
return qid;
}
void
SpotifyAccount::registerUpdaterForPlaylist( const QString& plId, SpotifyPlaylistUpdater* updater )
{
m_updaters[ plId ] = updater;
}
void
SpotifyAccount::unregisterUpdater( const QString& plid )
{
m_updaters.remove( plid );
}
void
SpotifyAccount::fetchFullPlaylist( SpotifyPlaylistInfo* playlist )
{
Q_UNUSED( playlist );
}
bool
SpotifyAccount::deleteOnUnsync() const
{
return configuration().value( "deleteOnUnsync", false ).toBool();
}
void
SpotifyAccount::stopPlaylistSync( SpotifyPlaylistInfo* playlist, bool forceDontDelete )
{
if ( !playlist )
return;
QVariantMap msg;
msg[ "_msgtype" ] = "removeFromSyncList";
msg[ "playlistid" ] = playlist->plid;
m_spotifyResolver.data()->sendMessage( msg );
if ( m_updaters.contains( playlist->plid ) )
{
SpotifyPlaylistUpdater* updater = m_updaters[ playlist->plid ];
updater->setSync( false );
if ( deleteOnUnsync() && !forceDontDelete )
{
playlist_ptr tomahawkPl = updater->playlist();
if ( !tomahawkPl.isNull() )
Playlist::remove( tomahawkPl );
updater->deleteLater();
}
updater->save();
}
}
void
SpotifyAccount::loadPlaylists()
{
// TODO cache this and only get changed?
QVariantMap msg;
msg[ "_msgtype" ] = "getAllPlaylists";
sendMessage( msg, this, "allPlaylistsLoaded" );
}
void
SpotifyAccount::setSyncForPlaylist( const QString& spotifyPlaylistId, bool sync )
{
foreach ( SpotifyPlaylistInfo* info, m_allSpotifyPlaylists )
{
if( info->plid == spotifyPlaylistId )
info->sync = sync;
}
if ( !m_configWidget.isNull() )
m_configWidget.data()->setPlaylists( m_allSpotifyPlaylists );
}
void
SpotifyAccount::createActions()
{
if ( !m_customActions.isEmpty() )
return;
QAction* action = new QAction( 0 );
action->setIcon( QIcon( RESPATH "images/spotify-logo.png" ) );
connect( action, SIGNAL( triggered( bool ) ), this, SLOT( syncActionTriggered( bool ) ) );
ActionCollection::instance()->addAction( ActionCollection::LocalPlaylists, action, this );
m_customActions.append( action );
}
void
SpotifyAccount::removeActions()
{
foreach( QAction* action, m_customActions )
ActionCollection::instance()->removeAction( action );
m_customActions.clear();
}

View File

@@ -1,164 +0,0 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2010-2011, Leo Franchi <lfranchi@kde.org>
* Copyright 2010-2011, Hugo Lindström <hugolm84@gmail.com>
*
* Tomahawk is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Tomahawk is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Tomahawk. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef SpotifyAccount_H
#define SpotifyAccount_H
#include "accounts/ResolverAccount.h"
#include "SourceList.h"
#include "AtticaManager.h"
#include "Playlist.h"
#include "utils/TomahawkUtils.h"
#include "utils/SmartPointerList.h"
class QAction;
class SpotifyPlaylistUpdater;
class QTimer;
class ScriptResolver;
namespace Tomahawk {
namespace Accounts {
class SpotifyAccountConfig;
// metadata for a playlist
struct SpotifyPlaylistInfo {
QString name, plid, revid;
bool sync, changed;
SpotifyPlaylistInfo( const QString& nname, const QString& pid, const QString& rrevid, bool ssync )
: name( nname ), plid( pid ), revid( rrevid ), sync( ssync ), changed( false ) {}
SpotifyPlaylistInfo() : sync( false ), changed( false ) {}
};
class SpotifyAccountFactory : public AccountFactory
{
Q_OBJECT
public:
SpotifyAccountFactory() {}
virtual Account* createAccount( const QString& accountId = QString() );
virtual QString description() const { return tr( "Play music from and sync your playlists with Spotify Premium" ); }
virtual QString factoryId() const { return "spotifyaccount"; }
virtual QString prettyName() const { return "Spotify"; }
virtual AccountTypes types() const { return AccountTypes( ResolverType ); }
virtual bool allowUserCreation() const { return false; }
virtual QPixmap icon() const;
virtual bool isUnique() const { return true; }
};
class SpotifyAccount : public CustomAtticaAccount
{
Q_OBJECT
public:
SpotifyAccount( const QString& accountId );
SpotifyAccount( const QString& accountId, const QString& path );
virtual ~SpotifyAccount();
virtual QPixmap icon() const;
virtual QWidget* configurationWidget();
virtual QWidget* aboutWidget();
virtual void saveConfig();
virtual Attica::Content atticaContent() const;
virtual void authenticate();
virtual ConnectionState connectionState() const;
virtual bool isAuthenticated() const;
virtual void deauthenticate();
virtual QWidget* aclWidget() { return 0; }
virtual Tomahawk::InfoSystem::InfoPluginPtr infoPlugin() { return Tomahawk::InfoSystem::InfoPluginPtr(); }
virtual SipPlugin* sipPlugin() { return 0; }
virtual bool preventEnabling() const { return m_preventEnabling; }
QString sendMessage( const QVariantMap& msg, QObject* receiver = 0, const QString& slot = QString() );
void registerUpdaterForPlaylist( const QString& plId, SpotifyPlaylistUpdater* updater );
void unregisterUpdater( const QString& plid );
bool deleteOnUnsync() const;
void setManualResolverPath( const QString& resolverPath );
public slots:
void aboutToShow( QAction* action, const Tomahawk::playlist_ptr& playlist );
void syncActionTriggered( bool );
void atticaLoaded(Attica::Content::List);
private slots:
void resolverChanged();
void resolverInstalled( const QString& resolverId );
void resolverMessage( const QString& msgType, const QVariantMap& msg );
void login( const QString& username, const QString& password );
// SpotifyResolver message handlers, all take msgtype, msg as argument
// void <here>( const QString& msgType, const QVariantMap& msg );
void startPlaylistSyncWithPlaylist( const QString& msgType, const QVariantMap& msg );
void playlistCreated( const QString& msgType, const QVariantMap& msg );
void delayedInit();
void hookupAfterDeletion( bool autoEnable );
private:
void init();
bool checkForResolver();
void hookupResolver();
void loadPlaylists();
void clearUser( bool permanentlyDelete = false );
void startPlaylistSync( SpotifyPlaylistInfo* playlist );
void stopPlaylistSync( SpotifyPlaylistInfo* playlist, bool forceDontDelete = false );
void fetchFullPlaylist( SpotifyPlaylistInfo* playlist );
void setSyncForPlaylist( const QString& spotifyPlaylistId, bool sync );
void createActions();
void removeActions();
QWeakPointer<SpotifyAccountConfig> m_configWidget;
QWeakPointer<QWidget> m_aboutWidget;
QWeakPointer<ScriptResolver> m_spotifyResolver;
QMap<QString, QPair<QObject*, QString> > m_qidToSlotMap;
// List of synced spotify playlists in config UI
QList< SpotifyPlaylistInfo* > m_allSpotifyPlaylists;
QHash< QString, SpotifyPlaylistUpdater* > m_updaters;
QHash< QString, playlist_ptr > m_waitingForCreateReply;
bool m_preventEnabling;
SmartPointerList< QAction > m_customActions;
friend class ::SpotifyPlaylistUpdater;
};
}
}
Q_DECLARE_METATYPE( Tomahawk::Accounts::SpotifyPlaylistInfo* );
#endif // SpotifyAccount_H

View File

@@ -1,170 +0,0 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2012, Leo Franchi <lfranchi@kde.org>
*
* Tomahawk is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Tomahawk is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Tomahawk. If not, see <http://www.gnu.org/licenses/>.
*/
#include "SpotifyAccountConfig.h"
#include "SpotifyAccount.h"
#include "utils/AnimatedSpinner.h"
#include "ui_SpotifyAccountConfig.h"
#include <QListWidget>
#include <QListWidgetItem>
#include <QShowEvent>
using namespace Tomahawk;
using namespace Accounts;
SpotifyAccountConfig::SpotifyAccountConfig( SpotifyAccount *account )
: QWidget( 0 )
, m_ui( new Ui::SpotifyConfig )
, m_account( account )
, m_playlistsLoading( 0 )
, m_loggedInManually( false )
{
m_ui->setupUi( this );
connect( m_ui->loginButton, SIGNAL( clicked( bool ) ), this, SLOT( doLogin() ) );
connect( m_ui->usernameEdit, SIGNAL( textChanged( QString ) ), this, SLOT( resetLoginButton() ) );
connect( m_ui->passwordEdit, SIGNAL( textChanged( QString ) ), this, SLOT( resetLoginButton() ) );
loadFromConfig();
m_playlistsLoading = new AnimatedSpinner( m_ui->playlistList );
}
void
SpotifyAccountConfig::showEvent( QShowEvent *event )
{
Q_UNUSED( event );
loadFromConfig();
m_loggedInManually = false;
}
void
SpotifyAccountConfig::loadFromConfig()
{
m_ui->usernameEdit->setText( m_account->credentials().value( "username" ).toString() );
m_ui->passwordEdit->setText( m_account->credentials().value( "password" ).toString() );
m_ui->streamingCheckbox->setChecked( m_account->credentials().value( "highQuality" ).toBool() );
m_ui->deleteOnUnsync->setChecked( m_account->deleteOnUnsync() );
}
void
SpotifyAccountConfig::saveSettings()
{
for( int i = 0; i < m_ui->playlistList->count(); i++ )
{
const QListWidgetItem* item = m_ui->playlistList->item( i );
SpotifyPlaylistInfo* pl = item->data( Qt::UserRole ).value< SpotifyPlaylistInfo* >();
const bool toSync = ( item->checkState() == Qt::Checked );
if ( pl->sync != toSync )
{
pl->changed = true;
pl->sync = toSync;
}
}
}
QString
SpotifyAccountConfig::username() const
{
return m_ui->usernameEdit->text().trimmed();
}
QString
SpotifyAccountConfig::password() const
{
return m_ui->passwordEdit->text().trimmed();
}
bool
SpotifyAccountConfig::highQuality() const
{
return m_ui->streamingCheckbox->isChecked();
}
bool
SpotifyAccountConfig::deleteOnUnsync() const
{
return m_ui->deleteOnUnsync->isChecked();
}
void
SpotifyAccountConfig::setPlaylists( const QList<SpotifyPlaylistInfo *>& playlists )
{
// User always has at least 1 playlist (starred tracks)
if ( !playlists.isEmpty() )
m_playlistsLoading->fadeOut();
m_ui->playlistList->clear();
foreach ( SpotifyPlaylistInfo* pl, playlists )
{
QListWidgetItem* item = new QListWidgetItem( pl->name, m_ui->playlistList );
item->setData( Qt::UserRole, QVariant::fromValue< SpotifyPlaylistInfo* >( pl ) );
item->setFlags( Qt::ItemIsUserCheckable | Qt::ItemIsSelectable | Qt::ItemIsEnabled );
item->setCheckState( pl->sync ? Qt::Checked : Qt::Unchecked );
}
}
void
SpotifyAccountConfig::doLogin()
{
m_ui->loginButton->setText( tr( "Logging in..." ) );
m_ui->loginButton->setEnabled( false );
m_playlistsLoading->fadeIn();
m_loggedInManually = true;
emit login( username(), password() );
}
void
SpotifyAccountConfig::loginResponse( bool success, const QString& msg )
{
if ( success )
{
m_ui->loginButton->setText( tr( "Logged in!" ) );
m_ui->loginButton->setEnabled( false );
}
else
{
setPlaylists( QList< SpotifyPlaylistInfo* >() );
m_playlistsLoading->fadeOut();
m_ui->loginButton->setText( tr( "Failed: %1" ).arg( msg ) );
m_ui->loginButton->setEnabled( true );
}
}
void
SpotifyAccountConfig::resetLoginButton()
{
m_ui->loginButton->setText( tr( "Log In" ) );
m_ui->loginButton->setEnabled( true );
}

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